Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Draft version constraint generation #5472

Merged
merged 51 commits into from
May 20, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
040ef56
Draft version constraint generation
Gertjan423 Apr 7, 2020
0205507
Some code cleanup
Gertjan423 Apr 8, 2020
3476a5e
Substitution functions
Gertjan423 Apr 9, 2020
4675fdd
Template generation
Gertjan423 Apr 9, 2020
afc3766
Reformatted
Gertjan423 Apr 9, 2020
c329b37
Attempt to fix failing check
Gertjan423 Apr 9, 2020
f3e4d17
Rework generation for value definitions into 2 phase approach
Gertjan423 Apr 15, 2020
a7ef445
Various small changes
Gertjan423 Apr 16, 2020
c99827d
Rework delta to env state
Gertjan423 Apr 16, 2020
28eb1bb
Incorporate data type definitions
Gertjan423 Apr 17, 2020
80bb27a
Various constraint generation bug fixes and improvements
Gertjan423 Apr 21, 2020
220d21d
First part of constraint solving
Gertjan423 Apr 21, 2020
2bad89e
Minor bugfixes
Gertjan423 Apr 22, 2020
733f929
Added simple-smt to bazel
Gertjan423 Apr 22, 2020
5545943
Depend on z3
cocreature Apr 22, 2020
b736d85
Working pipeline
Gertjan423 Apr 23, 2020
4bc25cb
Fix overwriting value definitions, and delayed choice bindings
Gertjan423 Apr 23, 2020
310d7b0
Keep track of contract id's after fetching
Gertjan423 Apr 24, 2020
4b08356
Renaming of choice parameters
Gertjan423 Apr 24, 2020
e04944f
Cleanup
Gertjan423 Apr 24, 2020
18b0b97
Reals instead of ints
Gertjan423 Apr 27, 2020
2f3bf70
Various TODOs
Gertjan423 Apr 27, 2020
0911a90
Added function documentation
Gertjan423 Apr 27, 2020
6651d8d
Small bugfix
Gertjan423 Apr 29, 2020
a8e6c94
Conditionals and forward references
Gertjan423 Apr 29, 2020
50c56c3
Small missing arithmetic
Gertjan423 May 6, 2020
d880fa5
More small missing arithmetic
Gertjan423 May 6, 2020
aef855a
Fix daml 1.0.1 bug
Gertjan423 May 6, 2020
6c86c22
Read arguments and support fields in different templates
Gertjan423 May 7, 2020
1c21ff2
Proper verify output for testing, generate counter example, proper va…
Gertjan423 May 7, 2020
a33365c
Working testing framework, with some basic test cases
Gertjan423 May 8, 2020
64370c2
Testing: build daml instead of storing dar files
Gertjan423 May 12, 2020
f3bee74
Proper handling of update types; constraint synonyms
Gertjan423 May 11, 2020
13eb8a4
Additional equality constraints after create update
Gertjan423 May 14, 2020
16aa3e3
Make assertion print conditional
Gertjan423 May 14, 2020
642c1b9
Various improvements from PR feedback
Gertjan423 May 14, 2020
c791f31
Apply suggestions from code review
Gertjan423 May 14, 2020
ba1fad5
Small fix: Add missing import
Gertjan423 May 14, 2020
344eca8
Small fix: convert numerics
Gertjan423 May 15, 2020
1cdf6e4
Replace PRSelf with correct package reference
Gertjan423 May 15, 2020
cdbed09
Style fixes
Gertjan423 May 15, 2020
c62c276
Rework conditionals to remove When
Gertjan423 May 19, 2020
ed91ada
Cleanup test daml files
Gertjan423 May 19, 2020
b803eb9
Merge branch 'master' into daml-lf-verification
Gertjan423 May 19, 2020
a3ee542
Small style fixes
Gertjan423 May 19, 2020
91c989c
Bugfix after merge
Gertjan423 May 19, 2020
337b0ca
Attempt to fix bazel windows error
Gertjan423 May 19, 2020
49563b0
Attempt #2 to fix bazel windows error
Gertjan423 May 19, 2020
ae4a276
Attempt #3 to fix bazel windows error
Gertjan423 May 19, 2020
77ea060
Attempt #4 to fix bazel windows error
Gertjan423 May 19, 2020
2d7c840
Attempt #5 to fix bazel windows error
Gertjan423 May 19, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Draft version constraint generation
  • Loading branch information
Gertjan423 committed Apr 7, 2020
commit 040ef56c4239ceaad510d8427df32a3a3a28b11b
41 changes: 41 additions & 0 deletions compiler/daml-lf-verify/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

load("//bazel_tools:haskell.bzl", "da_haskell_library")

da_haskell_library(
name = "daml-lf-verify",
srcs = glob(["src/**/*.hs"]),
hackage_deps = [
"aeson",
"base",
"bytestring",
"containers",
"deepseq",
"Decimal",
"extra",
"filepath",
"ghc-lib-parser",
"hashable",
"lens",
"mtl",
"optparse-applicative",
"recursion-schemes",
"safe",
"scientific",
"template-haskell",
"text",
"time",
"unordered-containers",
"zip-archive",
],
src_strip_prefix = "src",
visibility = ["//visibility:public"],
deps = [
"//compiler/daml-lf-ast",
"//compiler/daml-lf-tools",
"//compiler/daml-lf-proto",
"//compiler/daml-lf-reader",
"//libs-haskell/da-hs-base",
],
)
57 changes: 57 additions & 0 deletions compiler/daml-lf-verify/daml-lf-verify.cabal
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
cabal-version: 2.4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don’t need a cabal file, we build our code with Bazel.

name: daml-lf-verify
build-type: Simple
version: 0.1.0
synopsis: DAML-LF Verification
license: Apache-2.0
author: Digital Asset
maintainer: Digital Asset
copyright: Digital Asset 2020
homepage: https://github.com/digital-asset/daml#readme
bug-reports: https://github.com/digital-asset/daml/issues

source-repository head
type: git
location: https://github.com/digital-asset/daml.git

library
default-language: Haskell2010
hs-source-dirs: src
build-depends:
base,
containers,
da-hs-base,
daml-lf-ast,
deepseq,
extra,
hashable,
lens,
mtl,
recursion-schemes,
safe,
scientific,
template-haskell,
text,
time,
unordered-containers
exposed-modules:
DA.Daml.LF.Verify
DA.Daml.LF.Verify.Generate
default-extensions:
BangPatterns
DeriveDataTypeable
DeriveFoldable
DeriveFunctor
DeriveGeneric
DeriveTraversable
FlexibleContexts
GeneralizedNewtypeDeriving
LambdaCase
NamedFieldPuns
PackageImports
OverloadedStrings
RecordWildCards
ScopedTypeVariables
StandaloneDeriving
TypeApplications
ViewPatterns
22 changes: 22 additions & 0 deletions compiler/daml-lf-verify/src/DA/Daml/LF/Verify.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0

-- | Static verification of DAML packages.
module DA.Daml.LF.Verify
( module LF
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you reexport LF if this is main?

, main
) where

import Options.Applicative

import DA.Daml.LF.Verify.Generate as LF
import DA.Daml.LF.Verify.Read as LF
import DA.Daml.LF.Verify.Context as LF

main :: IO ()
main = do
Options{..} <- execParser optionsParserInfo
pkgs <- readPackages optInputDars
let _delta = runDelta $ genPackages pkgs
putStrLn "Constraints generated."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to deeply force _delta here, otherwise nothing will really happen in the test runs. Maybe just print it?


138 changes: 138 additions & 0 deletions compiler/daml-lf-verify/src/DA/Daml/LF/Verify/Context.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
-- Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}

-- | Contexts for DAML LF static verification
module DA.Daml.LF.Verify.Context
( Delta
, MonadDelta, devars, devals
, UpdateSet(..)
, UpdCreate(..), usCre, usArc, usCho
, UpdArchive(..)
, UpdChoice(..)
, runDelta
, emptyDelta
, introDelta, extVarDelta
, lookupDExprVar, lookupDVal, lookupDChoice
, concatDelta
, emptyUpdateSet
, concatUpdateSet
) where

import Control.Lens hiding (Context)
import Control.Monad.Error.Class (MonadError (..))
import Control.Monad.Reader
import Data.HashMap.Strict (HashMap, union, empty)

import DA.Daml.LF.Ast
import DA.Daml.LF.TypeChecker.Error

data UpdCreate = UpdCreate
{ _creTemp :: !(Qualified TypeConName)
-- ^ Qualified type constructor corresponding to the contract template.
, _creField :: ![(FieldName, Expr)]
-- ^ The fields to be verified, together with their value.
}
data UpdArchive = UpdArchive
{ _arcTemp :: !(Qualified TypeConName)
-- ^ Qualified type constructor corresponding to the contract template.
, _arcField :: ![(FieldName, Expr)]
-- ^ The fields to be verified, together with their value.
}
data UpdChoice = UpdChoice
{ _choTemp :: !(Qualified TypeConName)
-- ^ Qualified type constructor corresponding to the contract template.
, _choName :: !ChoiceName
-- ^ The name of the choice.
}

-- | The List of updates being performed
data UpdateSet = UpdateSet
{ _usCre :: ![UpdCreate]
-- ^ The list of create updates.
, _usArc :: ![UpdArchive]
-- ^ The list of archive updates.
, _usCho :: ![UpdChoice]
-- ^ The list of choice updates.
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need to account for fetches as well?


makeLenses ''UpdateSet
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally avoid TemplateHaskell for the most part. Part of that is just a code style guideline since we generally try write fairly simple Haskell code but for TemplateHaskell there is also a technical resaon: We use a statically linked GHC (I won’t go into the reasons here but I’m happy to expand on that in another context) which makes TemplateHaskell very slow. So if you really do want to use template haskell. You are better off putting all the template Haskell code into a separate Bazel target that changes rarely (Bazel caches at the granularity of individual targets). Otherwise, you will suffer from long compile times while working on this.

However, we also mostly avoid lenses. This is not quite as strict and there is not really a technical reason for this but I’m not aware of any module that uses them as pervasively as you use them here.

So overall, I think you’re better off removing the TH lens generation, switch away from using lenses by default for everything and potentially define a few lenses by hand for the things where they do provide a significant benefit.


emptyUpdateSet :: UpdateSet
emptyUpdateSet = UpdateSet [] [] []

concatUpdateSet :: UpdateSet -> UpdateSet -> UpdateSet
concatUpdateSet (UpdateSet cres1 arcs1 chos1) (UpdateSet cres2 arcs2 chos2) =
UpdateSet (cres1 ++ cres2) (arcs1 ++ arcs2) (chos1 ++ chos2)

-- | The environment for the DAML-LF verifier
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some deeper meaning behind calling this Delta apart from the fact that this is commonly used in type theory? If not, maybe just call it Environment or some variation thereof?

data Delta = Delta
{ _devars :: ![ExprVarName]
-- ^ The skolemised term variables.
, _devals :: !(HashMap (Qualified ExprValName) (Expr, UpdateSet))
-- ^ The bound values.
, _dchs :: !(HashMap ((Qualified TypeConName), ChoiceName) UpdateSet)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few more unnecessary parentheses around, but hlint should catch that in the end anyway.

Suggested change
, _dchs :: !(HashMap ((Qualified TypeConName), ChoiceName) UpdateSet)
, _dchs :: !(HashMap (Qualified TypeConName, ChoiceName) UpdateSet)

-- ^ The set of relevant choices.
-- TODO: split this off into data types for readability?
}

makeLenses ''Delta

emptyDelta :: Delta
emptyDelta = Delta [] empty empty

concatDelta :: Delta -> Delta -> Delta
concatDelta (Delta vars1 vals1 chs1) (Delta vars2 vals2 chs2) =
Delta (vars1 ++ vars2) (union vals1 vals2) (union chs1 chs2)
-- TODO: union makes me slightly nervous, as it allows overlapping keys
-- (and just uses the first)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you want unionWith concatUpdateSet.


-- | Type class constraint with the required monadic effects for functions
-- manipulating the verification environment.
type MonadDelta m = (MonadError Error m, MonadReader Delta m)

runDelta :: ReaderT Delta (Either Error) a -> Either Error a
runDelta act = runReaderT act emptyDelta

-- | Run a computation in the current environment, with an additional
-- environment extension.
introDelta :: MonadDelta m => Delta -> m a -> m a
introDelta delta = local (concatDelta delta)

extVarDelta :: MonadDelta m => ExprVarName -> m a -> m a
extVarDelta x = local (over devars ((:) x))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to have a function

varDelta :: ExprVarName -> Delta

and use introDelta (varDelta x) instead of extVarDelta x? At least to me, these primitives feel "more orthogonal" to me.


lookupDExprVar :: MonadDelta m => ExprVarName -> m ()
lookupDExprVar x = ask >>= \ del -> unless (elem x $ del ^. devars)
$ throwError $ EUnknownExprVar x
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think lenses buy us anything here. I also think a >>= \x -> ... is a sign to use do notation.

Suggested change
lookupDExprVar x = ask >>= \ del -> unless (elem x $ del ^. devars)
$ throwError $ EUnknownExprVar x
lookupDExprVar x = do
del <- ask
unless (x `elem` _devars del) $ throwError $ EUnknownExprVar x


lookupDVal :: MonadDelta m => (Qualified ExprValName) -> m (Expr, UpdateSet)
lookupDVal w = view (devals . at w) >>= match _Just (EEmptyCase)
-- TODO: This is a random error. The thing we really want to write is:
-- lookupDVal w = view (devals . at w) >>= match _Just (EUnknownDefinition $ LEValue w)
-- The issue here is that our values are currently stored in Delta instead
-- of in the world like in the type checker.
-- This means that we can't throw the error we want to throw here.
-- 2 options:
-- + either define our own errors, as they don't correspond 100% to the type
-- checking errors anyway.
-- + or create our own world environment to store values. This also makes
-- sense as these values work similar to how they work in the type checker,
-- except that we need to store a partially evaluated definition as well.
-- Both approaches make sense, but both imply a lot of code duplication, so they
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn’t worry too much about duplicating code for now.

-- don't sound that enticing...

lookupDChoice :: MonadDelta m => (Qualified TypeConName) -> ChoiceName
-> m UpdateSet
lookupDChoice tem ch = view (dchs . at (tem, ch)) >>= match _Just EEmptyCase
-- TODO: Random error.

-- | Helper functions mirrored from Env.
-- TODO: Reduce duplication by abstracting over MonadGamma and MonadDelta?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, I wouldn't bother with it for now. You don't want to tie yourself into unnecessary constraints. 😃

match :: MonadDelta m => Prism' a b -> Error -> a -> m b
match p e x = either (const (throwError e)) pure (matching p x)
-- TODO: no context, like in Gamma

Loading