-
Notifications
You must be signed in to change notification settings - Fork 205
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
Changes from 1 commit
040ef56
0205507
3476a5e
4675fdd
afc3766
c329b37
f3e4d17
a7ef445
c99827d
28eb1bb
80bb27a
220d21d
2bad89e
733f929
5545943
b736d85
4bc25cb
310d7b0
4b08356
e04944f
18b0b97
2f3bf70
0911a90
6651d8d
a8e6c94
50c56c3
d880fa5
aef855a
6c86c22
1c21ff2
a33365c
64370c2
f3bee74
13eb8a4
16aa3e3
642c1b9
c791f31
ba1fad5
344eca8
1cdf6e4
cdbed09
c62c276
ed91ada
b803eb9
a3ee542
91c989c
337b0ca
49563b0
ae4a276
77ea060
2d7c840
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
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", | ||
], | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
cabal-version: 2.4 | ||
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 |
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you reexport |
||
, 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." | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to deeply force |
||
|
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. | ||||||||||||
} | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't we need to account for fetches as well? |
||||||||||||
|
||||||||||||
makeLenses ''UpdateSet | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there some deeper meaning behind calling this |
||||||||||||
data Delta = Delta | ||||||||||||
{ _devars :: ![ExprVarName] | ||||||||||||
-- ^ The skolemised term variables. | ||||||||||||
, _devals :: !(HashMap (Qualified ExprValName) (Expr, UpdateSet)) | ||||||||||||
-- ^ The bound values. | ||||||||||||
, _dchs :: !(HashMap ((Qualified TypeConName), ChoiceName) UpdateSet) | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are a few more unnecessary parentheses around, but
Suggested change
|
||||||||||||
-- ^ 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) | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess you want |
||||||||||||
|
||||||||||||
-- | 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)) | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||||||||||||
|
||||||||||||
lookupDExprVar :: MonadDelta m => ExprVarName -> m () | ||||||||||||
lookupDExprVar x = ask >>= \ del -> unless (elem x $ del ^. devars) | ||||||||||||
$ throwError $ EUnknownExprVar x | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think lenses buy us anything here. I also think
Suggested change
|
||||||||||||
|
||||||||||||
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 | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||||||||
|
There was a problem hiding this comment.
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.