├── papers ├── first-paper │ ├── conclusion.tex │ ├── example.tex │ ├── relating.tex │ ├── dafny.tex │ ├── local.sty │ ├── main.tex │ └── introduction.tex └── lib │ ├── references.bib │ ├── dafny.sty │ └── florrabe.sty ├── examples ├── sample-project │ ├── typecartignore │ ├── new │ │ └── toy │ │ │ ├── .typecartignore │ │ │ ├── impl.dfy │ │ │ ├── main.dfy │ │ │ └── utils │ │ │ └── util.dfy │ └── old │ │ └── toy │ │ ├── .typecartignore │ │ ├── impl.dfy │ │ ├── main.dfy │ │ └── utils │ │ └── util.dfy ├── map_display_expr │ └── MapDisplayExpr.dfy ├── higher_order_functions │ ├── old.dfy │ └── new.dfy ├── list_and_length │ ├── old.dfy │ └── new.dfy ├── signed_int_constrained │ ├── README.md │ ├── old.dfy │ └── new.dfy ├── sequences │ ├── old.dfy │ └── new.dfy ├── function_translation │ ├── new.dfy │ └── old.dfy ├── set_comprehension │ └── SetComprehension.dfy ├── binary_tree_opt │ ├── new.dfy │ └── old.dfy ├── constructors │ ├── old.dfy │ └── new.dfy ├── constructor_arguments │ ├── old.dfy │ └── new.dfy ├── sign │ ├── old.dfy │ └── new.dfy ├── non_bc_change_in_strings │ ├── old.dfy │ └── new.dfy ├── polymorphic_types │ ├── old.dfy │ └── new.dfy ├── logic_formulas_opt │ ├── new.dfy │ └── old.dfy ├── complex │ ├── new.dfy │ └── old.dfy ├── logic_formulas_function │ ├── old.dfy │ └── new.dfy ├── multiple_functions │ ├── old.dfy │ └── new.dfy ├── binary_tree_insert │ ├── old.dfy │ └── new.dfy ├── binary_tree_insert_field │ ├── old.dfy │ └── new.dfy ├── binary_tree_replace │ ├── old.dfy │ └── new.dfy ├── logic_formulas_rewrite │ ├── old.dfy │ └── new.dfy ├── option │ └── Option.dfy ├── binary_tree_delete │ ├── new.dfy │ └── old.dfy ├── import_export │ ├── old.dfy │ └── new.dfy ├── multiple_types │ ├── old.dfy │ └── new.dfy ├── CommitExamples.md ├── datatype_members │ ├── old.dfy │ └── new.dfy ├── unchanged_classes │ ├── old.dfy │ └── new.dfy ├── signed_int │ ├── old.dfy │ └── new.dfy ├── multiple_imports │ ├── new.dfy │ └── old.dfy ├── arithmetic_return_type │ ├── old.dfy │ └── new.dfy ├── binary_tree_alter_field │ ├── old.dfy │ └── new.dfy ├── context_with_variables │ ├── old.dfy │ ├── new.dfy │ └── proofs.patch ├── policy_evaluator │ ├── Old.dfy │ ├── New.dfy │ └── Compat.dfy ├── request_eval │ ├── old.dfy │ └── new.dfy └── command_exec │ ├── old.dfy │ └── new.dfy ├── TypeInjections ├── Tool.Test │ ├── Resources │ │ ├── Local │ │ │ ├── NoDafnyTest │ │ │ │ └── OnlyText │ │ │ │ │ └── notDafny.txt │ │ │ └── GoodInput │ │ │ │ ├── Expected │ │ │ │ ├── new.dfy │ │ │ │ ├── old.dfy │ │ │ │ ├── joint.dfy │ │ │ │ └── translations.dfy │ │ │ │ ├── New │ │ │ │ └── data.dfy │ │ │ │ └── Old │ │ │ │ └── data.dfy │ │ └── Git │ │ │ └── BadLocation │ │ │ ├── file2.dfy │ │ │ └── file1.dfy │ ├── DummyMain.fs │ ├── Tool.Test.fsproj │ ├── TestUtils.fs │ └── ToolTests.fs ├── TypeInjections.Test │ ├── Resources │ │ ├── SimpleFolderTest │ │ │ ├── OutputDirectory │ │ │ │ └── .gitignore │ │ │ ├── InputDirectory │ │ │ │ ├── Old │ │ │ │ │ ├── file1.dfy │ │ │ │ │ └── sub1 │ │ │ │ │ │ └── subFile1.dfy │ │ │ │ └── New │ │ │ │ │ ├── sub1 │ │ │ │ │ └── subFile1.dfy │ │ │ │ │ └── file1.dfy │ │ │ └── ExpectedDirectory │ │ │ │ ├── file1_old.dfy │ │ │ │ ├── file1_joint.dfy │ │ │ │ ├── sub1 │ │ │ │ ├── subFile1_new.dfy │ │ │ │ ├── subFile1_old.dfy │ │ │ │ ├── subFile1_joint.dfy │ │ │ │ └── n_combine.dfy │ │ │ │ ├── a_combine.dfy │ │ │ │ └── file1_new.dfy │ │ ├── Utils.dfy │ │ ├── NewtypeTest │ │ │ └── InputDirectory │ │ │ │ ├── New │ │ │ │ └── New.dfy │ │ │ │ └── Old │ │ │ │ └── Old.dfy │ │ ├── SeqTest │ │ │ └── InputDirectory │ │ │ │ ├── New │ │ │ │ └── New.dfy │ │ │ │ └── Old │ │ │ │ └── Old.dfy │ │ ├── SetTest │ │ │ └── InputDirectory │ │ │ │ ├── New │ │ │ │ └── New.dfy │ │ │ │ └── Old │ │ │ │ └── Old.dfy │ │ ├── DatatypeConstructorTest │ │ │ └── InputDirectory │ │ │ │ ├── Old │ │ │ │ └── Old.dfy │ │ │ │ └── New │ │ │ │ └── New.dfy │ │ ├── DatatypeRefTest │ │ │ └── InputDirectory │ │ │ │ ├── New │ │ │ │ └── New.dfy │ │ │ │ └── Old │ │ │ │ └── Old.dfy │ │ ├── RecursiveDatatypeTest │ │ │ └── InputDirectory │ │ │ │ ├── New │ │ │ │ └── New.dfy │ │ │ │ └── Old │ │ │ │ └── Old.dfy │ │ ├── SimpleDatatypeTest │ │ │ └── InputDirectory │ │ │ │ ├── Old │ │ │ │ └── Old.dfy │ │ │ │ └── New │ │ │ │ └── New.dfy │ │ ├── CollectionsTest │ │ │ └── InputDirectory │ │ │ │ ├── New │ │ │ │ └── New.dfy │ │ │ │ └── Old │ │ │ │ └── Old.dfy │ │ ├── LimitTest │ │ │ └── InputDirectory │ │ │ │ ├── Old │ │ │ │ └── Old.dfy │ │ │ │ └── New │ │ │ │ └── New.dfy │ │ ├── ResultTest │ │ │ └── InputDirectory │ │ │ │ ├── New │ │ │ │ └── New.dfy │ │ │ │ └── Old │ │ │ │ └── Old.dfy │ │ ├── GenericDatatypeTest │ │ │ └── InputDirectory │ │ │ │ ├── New │ │ │ │ └── New.dfy │ │ │ │ └── Old │ │ │ │ └── Old.dfy │ │ ├── MapTest │ │ │ └── InputDirectory │ │ │ │ ├── New │ │ │ │ └── New.dfy │ │ │ │ └── Old │ │ │ │ └── Old.dfy │ │ ├── RelateBuiltinTypes.dfy │ │ ├── MapBuiltinTypes.dfy │ │ └── SubsetSynTest │ │ │ └── InputDirectory │ │ │ ├── New │ │ │ └── New.dfy │ │ │ └── Old │ │ │ └── Old.dfy │ ├── Tester.fs │ ├── IOTests.fs │ ├── TypeInjections.Test.fsproj │ ├── GenFunctionTests.fs │ └── TestUtils.fs ├── .config │ └── dotnet-tools.json ├── TypeInjections │ ├── Resources │ │ ├── UsingResources.txt │ │ └── MapBuiltinTypes.dfy │ ├── TypeInjections.fsproj │ ├── Program.fs │ └── DiffAnalysis.fs ├── Tool │ ├── Tool.fsproj │ ├── README.md │ ├── Make_Tool.md │ └── Tool.fs └── TypeInjections.sln ├── docs ├── imgs │ ├── proofCI.png │ └── typeCart-example.png ├── Troubleshooting.md └── ExamplesFeatures.md ├── CODE_OF_CONDUCT.md ├── scripts ├── run_cryptotools.sh ├── run_cedar.sh ├── result_to_table.py ├── countVerified.py ├── proofs_111.patch └── proofs_111_no_proof.patch ├── .gitignore ├── .github └── workflows │ └── dotnet.yml ├── LICENSE.txt ├── README.md └── CONTRIBUTING.md /papers/first-paper/conclusion.tex: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /papers/first-paper/example.tex: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /papers/first-paper/relating.tex: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/sample-project/typecartignore: -------------------------------------------------------------------------------- 1 | impl.dfy 2 | -------------------------------------------------------------------------------- /examples/sample-project/new/toy/.typecartignore: -------------------------------------------------------------------------------- 1 | impl.dfy 2 | -------------------------------------------------------------------------------- /examples/sample-project/old/toy/.typecartignore: -------------------------------------------------------------------------------- 1 | impl.dfy 2 | -------------------------------------------------------------------------------- /TypeInjections/Tool.Test/Resources/Local/NoDafnyTest/OnlyText/notDafny.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /papers/first-paper/dafny.tex: -------------------------------------------------------------------------------- 1 | We introduce the fragment of Dafny that \typeCart covers. -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleFolderTest/OutputDirectory/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /docs/imgs/proofCI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/typecart/HEAD/docs/imgs/proofCI.png -------------------------------------------------------------------------------- /TypeInjections/Tool.Test/DummyMain.fs: -------------------------------------------------------------------------------- 1 | module Program = 2 | 3 | [] 4 | let main _ = 0 5 | -------------------------------------------------------------------------------- /docs/imgs/typeCart-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/typecart/HEAD/docs/imgs/typeCart-example.png -------------------------------------------------------------------------------- /examples/sample-project/old/toy/impl.dfy: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Impl { 4 | 5 | /* this should not be included */ 6 | } 7 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleFolderTest/InputDirectory/Old/file1.dfy: -------------------------------------------------------------------------------- 1 | module A { 2 | 3 | datatype singleNoArgs = A1 4 | 5 | 6 | } -------------------------------------------------------------------------------- /examples/sample-project/new/toy/impl.dfy: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Impl { 4 | 5 | /* this should not be included */ 6 | 7 | class Blah { 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleFolderTest/InputDirectory/New/sub1/subFile1.dfy: -------------------------------------------------------------------------------- 1 | module N { 2 | 3 | newtype foo = x : int | 0 <= x < 9 || x == 100 4 | } -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleFolderTest/InputDirectory/Old/sub1/subFile1.dfy: -------------------------------------------------------------------------------- 1 | module N { 2 | 3 | newtype foo = x : int | 0 <= x < 9 || x == 100 4 | } -------------------------------------------------------------------------------- /examples/map_display_expr/MapDisplayExpr.dfy: -------------------------------------------------------------------------------- 1 | module MapDisplayExpr { 2 | 3 | class Test { 4 | method TestMethod() { 5 | var m := map[1 := "a", 2 := "b"]; 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/Utils.dfy: -------------------------------------------------------------------------------- 1 | module Translations.Utils { 2 | function ???(): X 3 | requires false 4 | { 5 | var tmp: X :| true; 6 | tmp 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /TypeInjections/Tool.Test/Resources/Local/GoodInput/Expected/new.dfy: -------------------------------------------------------------------------------- 1 | include "joint.dfy" 2 | 3 | 4 | 5 | 6 | module New.ExprEval { 7 | import Joint 8 | 9 | 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /TypeInjections/Tool.Test/Resources/Local/GoodInput/Expected/old.dfy: -------------------------------------------------------------------------------- 1 | include "joint.dfy" 2 | 3 | 4 | 5 | 6 | module Old.ExprEval { 7 | import Joint 8 | 9 | 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleFolderTest/ExpectedDirectory/file1_old.dfy: -------------------------------------------------------------------------------- 1 | module Old.A { 2 | datatype singleNoArgs = A1() { 3 | 4 | } 5 | 6 | 7 | 8 | } 9 | 10 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleFolderTest/ExpectedDirectory/file1_joint.dfy: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Joint.A { 4 | datatype singleNoArgs = A1() { 5 | 6 | } 7 | 8 | 9 | 10 | } 11 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleFolderTest/ExpectedDirectory/sub1/subFile1_new.dfy: -------------------------------------------------------------------------------- 1 | module New.N { 2 | 3 | newtype foo = x : int | 0 <= x < 9 || x == 100 4 | 5 | newtype bar = y : real | -0.9 <= y <= 10.0 && y < 90.0 6 | } -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleFolderTest/ExpectedDirectory/sub1/subFile1_old.dfy: -------------------------------------------------------------------------------- 1 | module Old.N { 2 | 3 | newtype foo = x : int | 0 <= x < 9 || x == 100 4 | 5 | newtype bar = y : real | -0.9 <= y <= 10.0 && y < 90.0 6 | } -------------------------------------------------------------------------------- /TypeInjections/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "fantomas-tool": { 6 | "version": "4.4.0", 7 | "commands": [ 8 | "fantomas" 9 | ] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleFolderTest/ExpectedDirectory/sub1/subFile1_joint.dfy: -------------------------------------------------------------------------------- 1 | module N { 2 | 3 | newtype foo = x : int | 0 <= x < 9 || x == 100 4 | 5 | newtype bar = y : real | -0.9 <= y <= 10.0 && y < 90.0 6 | } 7 | 8 | -------------------------------------------------------------------------------- /papers/first-paper/local.sty: -------------------------------------------------------------------------------- 1 | \newcommand{\amazon}{\if@ACM@anonymous BigCompany\else Amazon\fi\xspace} 2 | \newcommand{\amazonListing}{\if@ACM@anonymous bigcompany\else amazon\fi} 3 | 4 | \newcommand{\typeCart}{typeCart\xspace} 5 | \newcommand{\yil}{YIL\xspace} 6 | -------------------------------------------------------------------------------- /examples/higher_order_functions/old.dfy: -------------------------------------------------------------------------------- 1 | module Functions { 2 | function get_adder(): (int, int) -> int { 3 | (a: int, b: int) => a + b 4 | } 5 | function add(adder: (int, int) -> int, a: int, b: int): int { 6 | adder(a, b) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/NewtypeTest/InputDirectory/New/New.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module N { 3 | 4 | newtype foo = x : int | 0 <= x < 9 || x == 100 5 | 6 | newtype bar = y : real | -0.9 <= y <= 10.0 && y < 90.0 7 | } 8 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/NewtypeTest/InputDirectory/Old/Old.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module N { 3 | 4 | newtype foo = x : int | 0 <= x < 9 || x == 100 5 | 6 | newtype bar = y : real | -0.9 <= y <= 10.0 && y < 90.0 7 | } 8 | -------------------------------------------------------------------------------- /examples/higher_order_functions/new.dfy: -------------------------------------------------------------------------------- 1 | module Functions { 2 | function get_adder(): (real, real) -> real { 3 | (a: real, b: real) => a + b 4 | } 5 | function add(adder: (real, real) -> real, a: real, b: real): real { 6 | adder(a, b) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/list_and_length/old.dfy: -------------------------------------------------------------------------------- 1 | module List { 2 | 3 | datatype list = Nil | Cons (T, list) 4 | 5 | function length(l: list): int 6 | { 7 | match l { 8 | case Nil => 0 9 | case Cons (h, t) => 10 | 1 + length(t) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /TypeInjections/Tool.Test/Resources/Local/GoodInput/New/data.dfy: -------------------------------------------------------------------------------- 1 | module ExprEval { 2 | 3 | datatype expr = Const(x: int) | Sub(e1: expr, e2: expr) 4 | 5 | function eval(e: expr): int { 6 | match e { 7 | case Const(n) => n 8 | case Sub(e1, e2) => eval(e1) - eval(e2) 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /TypeInjections/Tool.Test/Resources/Local/GoodInput/Old/data.dfy: -------------------------------------------------------------------------------- 1 | module ExprEval { 2 | 3 | datatype expr = Const(x: int) | Sub(e1: expr, e2: expr) 4 | 5 | function eval(e: expr): int { 6 | match e { 7 | case Const(n) => n 8 | case Sub(e1, e2) => eval(e1) - eval(e2) 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Tester.fs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT 3 | 4 | module TypeInjections.Test.Tester 5 | // Gets rid of "warning FS0988: Main module of program is empty: nothing will happen when it is run" 6 | [] 7 | let main _ = 0 8 | -------------------------------------------------------------------------------- /TypeInjections/Tool.Test/Resources/Git/BadLocation/file2.dfy: -------------------------------------------------------------------------------- 1 | 2 | module S { 3 | 4 | 5 | 6 | // BOOM NOW YOU SEE ME 7 | datatype foo = Bar(x: int) | Baz(y: string) 8 | 9 | datatype seqOfFoo = A1(s: seq) | A2 10 | 11 | datatype seqOfBuiltin = B1(s: seq, c: char) 12 | | B2(y: seq>) 13 | } -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SeqTest/InputDirectory/New/New.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module S { 3 | 4 | datatype foo = Bar(x: int) | Baz(y: string) 5 | 6 | datatype seqOfFoo = A1(s: seq) | A2 7 | 8 | datatype seqOfBuiltin = B1(s: seq, c: char) 9 | | B2(y: seq>) 10 | } 11 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SeqTest/InputDirectory/Old/Old.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module S { 3 | 4 | datatype foo = Bar(x: int) | Baz(y: string) 5 | 6 | datatype seqOfFoo = A1(s: seq) | A2 7 | 8 | datatype seqOfBuiltin = B1(s: seq, c: char) 9 | | B2(y: seq>) 10 | } 11 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SetTest/InputDirectory/New/New.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module S { 3 | 4 | datatype foo = Bar(x: int) | Baz(y: string) 5 | 6 | datatype setOfFoo = A1(s: set) | A2 7 | 8 | datatype setOfBuiltin = B1(s: set, c: char) 9 | | B2(y: set>) 10 | } 11 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SetTest/InputDirectory/Old/Old.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module S { 3 | 4 | datatype foo = Bar(x: int) | Baz(y: string) 5 | 6 | datatype setOfFoo = A1(s: set) | A2 7 | 8 | datatype setOfBuiltin = B1(s: set, c: char) 9 | | B2(y: set>) 10 | } 11 | -------------------------------------------------------------------------------- /examples/signed_int_constrained/README.md: -------------------------------------------------------------------------------- 1 | This is the same example as the signed_int example with the exception that 2 | 3 | * we use constraints in specifying the datatypes 4 | * when forward/backward translating, we translate across data constructors. 5 | specifically we translate POS(0) <==> ZERO 6 | 7 | Under this translation, forward and backward compatibility both hold. 8 | -------------------------------------------------------------------------------- /examples/sequences/old.dfy: -------------------------------------------------------------------------------- 1 | module Seq { 2 | 3 | function seqmap(f: int -> A, s: seq): seq 4 | { 5 | if s == [] then 6 | [] 7 | else 8 | [f(s[0])] + seqmap(f, s[1..]) 9 | } 10 | 11 | function reduce(f: (int, int) -> int, id: int, s: seq): int 12 | { 13 | if s == [] then 14 | id 15 | else 16 | f(s[0], reduce(f, id, s[1..])) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleFolderTest/ExpectedDirectory/a_combine.dfy: -------------------------------------------------------------------------------- 1 | include "file1_old.dfy" 2 | include "file1_new.dfy" 3 | 4 | module Combine { 5 | 6 | import Old 7 | 8 | import New 9 | function method singleNoArgsOldToNew(s: Old.A.singleNoArgs): New.A.singleNoArgs 10 | decreases s 11 | { 12 | match s 13 | case A1 => 14 | New.A.singleNoArgs.A1 15 | } 16 | 17 | 18 | } -------------------------------------------------------------------------------- /examples/function_translation/new.dfy: -------------------------------------------------------------------------------- 1 | module Functions { 2 | type Input = i: nat | i < 1 3 | type Output = i: nat | i < 2 4 | function g(f: Input -> Output, x: Input): Output { 5 | f(x) 6 | } 7 | function f1(x: Input): Output { 8 | x 9 | } 10 | function f2(x: Input): Output { 11 | 0 12 | } 13 | function test(x: Input): nat { 14 | f1(x) as nat + f2(x) as nat 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/function_translation/old.dfy: -------------------------------------------------------------------------------- 1 | module Functions { 2 | type Input = i: nat | i < 2 3 | type Output = i: nat | i < 2 4 | function g(f: Input -> Output, x: Input): Output { 5 | f(x) 6 | } 7 | function f1(x: Input): Output { 8 | x 9 | } 10 | function f2(x: Input): Output { 11 | 0 12 | } 13 | function test(x: Input): nat { 14 | f1(x) as nat + f2(x) as nat 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /scripts/run_cryptotools.sh: -------------------------------------------------------------------------------- 1 | OWNER_NAME=aws 2 | REPO_NAME=aws-cryptographic-material-providers-library-java 3 | if [ ! -d "$REPO_NAME" ]; then 4 | git clone git@github.com:$OWNER_NAME/$REPO_NAME.git --recursive 5 | fi 6 | cd $REPO_NAME 7 | git fetch origin 8 | # git checkout origin/main 9 | git checkout a0416d96bdba385e1988067715db26ba3d4cd33c 10 | git log --raw > ../commit_logs.txt 11 | cd .. 12 | python3 parse_commit_logs.py -r cryptotools 13 | -------------------------------------------------------------------------------- /examples/sample-project/new/toy/main.dfy: -------------------------------------------------------------------------------- 1 | 2 | 3 | include "utils/util.dfy" 4 | 5 | module Main { 6 | 7 | import Util /* utils/util.dfy */ 8 | 9 | datatype FileTypes = Cfg | Conf | Log | Txt 10 | 11 | 12 | function testAdd() : (res: int) 13 | ensures res == 5 14 | { 15 | Util.sum(2, 3) 16 | } 17 | 18 | function testMul() : (res: int) 19 | ensures res == 20 20 | { 21 | Util.prod(Util.prod(2, 5), 2) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /examples/sample-project/old/toy/main.dfy: -------------------------------------------------------------------------------- 1 | 2 | 3 | include "utils/util.dfy" 4 | 5 | module Main { 6 | 7 | import Util /* utils/util.dfy */ 8 | 9 | datatype FileTypes = Cfg | Conf | Log | Txt 10 | 11 | 12 | function testAdd() : (res: int) 13 | ensures res == 5 14 | { 15 | Util.sum(2, 3) 16 | } 17 | 18 | function testMul() : (res: int) 19 | ensures res == 20 20 | { 21 | Util.prod(Util.prod(2, 5), 2) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /examples/set_comprehension/SetComprehension.dfy: -------------------------------------------------------------------------------- 1 | 2 | module M { 3 | 4 | method blah() { 5 | var mapDisplay := map[1 := "a", 2 := "b"]; // map display expression 6 | var S := set x : nat, y : nat | x < 2 && y < 2 :: (x, y); // set comprehension 7 | var m1 := map x : int | 1 <= x <= 10 :: 2*x := 3*x; // more complicated instance of map comprehension 8 | var m2 := map x : int | 0 <= x <= 10 :: x * x; // map comprehension 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/binary_tree_opt/new.dfy: -------------------------------------------------------------------------------- 1 | module BinaryTree { 2 | 3 | // Keyed binary trees and optimized search 4 | datatype tree = Leaf | Node (tree, int, tree) 5 | 6 | function search(t: tree, key:int): bool 7 | { 8 | match t 9 | case Leaf => false 10 | case Node (l, k, r) => 11 | if k == key then 12 | true 13 | else 14 | if search(l, key) then 15 | true 16 | else 17 | search(r, key) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/constructors/old.dfy: -------------------------------------------------------------------------------- 1 | module ExprEval { 2 | datatype expr = Const(x: int) | Add(e1: expr, e2: expr) | Sub(e1: expr, e2: expr) 3 | 4 | function eval(e: expr): int { 5 | match e { 6 | case Const(n) => n 7 | case Add(e1, e2) => eval(e1) + eval(e2) 8 | case Sub(e1, e2) => eval(e1) - eval(e2) 9 | } 10 | } 11 | 12 | lemma commutative(e1: expr, e2: expr) 13 | ensures eval(Add(e1, e2)) == eval(Add(e2, e1)) 14 | {} 15 | } 16 | -------------------------------------------------------------------------------- /examples/constructor_arguments/old.dfy: -------------------------------------------------------------------------------- 1 | module ExprEval { 2 | datatype expr = Const(x: int) | Add(e1: expr, e2: expr) | Sub(e1: expr, e2: expr) 3 | 4 | function eval(e: expr): int { 5 | match e { 6 | case Const(n) => n 7 | case Add(e1, e2) => eval(e1) + eval(e2) 8 | case Sub(e1, e2) => eval(e1) - eval(e2) 9 | } 10 | } 11 | 12 | lemma inversion(e1: expr, e2: expr) 13 | ensures eval(Sub(Add(e1, e2),e2)) == eval(e1) 14 | {} 15 | } 16 | -------------------------------------------------------------------------------- /examples/binary_tree_opt/old.dfy: -------------------------------------------------------------------------------- 1 | module BinaryTree { 2 | 3 | // Keyed binary trees and search 4 | datatype tree = Leaf | Node (tree, int, tree) 5 | 6 | function search(t: tree, key:int): bool 7 | { 8 | match t 9 | case Leaf => false 10 | case Node (l, k, r) => 11 | if k == key then 12 | true 13 | else 14 | var lfound := search(l, key); 15 | var rfound := search(r, key); 16 | lfound || rfound 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/sample-project/new/toy/utils/util.dfy: -------------------------------------------------------------------------------- 1 | 2 | module Util 3 | { 4 | export provides sum, prod, sub 5 | 6 | function sum(a: int, b: int) : (res: int) 7 | ensures res == a + b 8 | { 9 | a + b 10 | } 11 | 12 | function prod(a: int, b: int) : (res: int) 13 | ensures res == a * b 14 | { 15 | a * b 16 | } 17 | 18 | function sub(a: int, b: int) : (res: int) 19 | ensures res == a - b 20 | { 21 | a - b 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /examples/sample-project/old/toy/utils/util.dfy: -------------------------------------------------------------------------------- 1 | 2 | module Util 3 | { 4 | export provides sum, prod, sub 5 | 6 | function sum(a: int, b: int) : (res: int) 7 | ensures res == a + b 8 | { 9 | a + b 10 | } 11 | 12 | function prod(a: int, b: int) : (res: int) 13 | ensures res == a * b 14 | { 15 | a * b 16 | } 17 | 18 | function sub(a: int, b: int) : (res: int) 19 | ensures res == a - b 20 | { 21 | a - b 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /examples/sign/old.dfy: -------------------------------------------------------------------------------- 1 | module Sign { 2 | function sign(i: int): (x: int) { 3 | if i == 0 then 4 | 0 5 | else if i > 0 then 6 | 1 7 | else 8 | -1 9 | } by method { 10 | if 11 | case i == 0 => x := 0; 12 | case i > 0 => x := 1; 13 | case i < 0 => x := -1; 14 | } 15 | method test(i: int) { 16 | var x := 0; 17 | if 18 | case i == 0 => x := 0; 19 | case i > 0 => x := 1; 20 | case i < 0 => x := -1; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /scripts/run_cedar.sh: -------------------------------------------------------------------------------- 1 | # OWNER_NAME=aws 2 | # REPO_NAME=aws-cryptographic-material-providers-library-java 3 | OWNER_NAME=cedar-policy 4 | REPO_NAME=cedar-spec 5 | if [ ! -d "$REPO_NAME" ]; then 6 | git clone git@github.com:$OWNER_NAME/$REPO_NAME.git 7 | fi 8 | cd $REPO_NAME 9 | git fetch origin 10 | # git checkout origin/main 11 | git checkout c519e0f7722673e21f86479cff8f4c2a68c1c8a6 12 | git log --raw > ../commit_logs.txt 13 | cd .. 14 | python3 parse_commit_logs.py -r cedar 15 | -------------------------------------------------------------------------------- /examples/non_bc_change_in_strings/old.dfy: -------------------------------------------------------------------------------- 1 | module Matching { 2 | datatype pattern = Pattern(field: string, value: string) 3 | datatype record = Empty | Cons(field: string, value: string, tl: record) 4 | 5 | function hasMatchingField(r: record, p: pattern): bool { 6 | match r { 7 | case Empty => false 8 | case Cons(f, v, tl) => 9 | if p.field == f then 10 | p.value == v 11 | else 12 | hasMatchingField(tl, p) 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/constructors/new.dfy: -------------------------------------------------------------------------------- 1 | module ExprEval { 2 | // one added, one deleted constructor 3 | datatype expr = Const(x: int) | Add(e1: expr, e2: expr) | Mul(e1: expr, e2: expr) 4 | 5 | function eval(e: expr): int { 6 | match e { 7 | case Const(n) => n 8 | case Add(e1, e2) => eval(e1) + eval(e2) 9 | case Mul(e1, e2) => eval(e1) * eval(e2) 10 | } 11 | } 12 | 13 | lemma commutative(e1: expr, e2: expr) 14 | ensures eval(Add(e1, e2)) == eval(Add(e2, e1)) 15 | {} 16 | } 17 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/DatatypeConstructorTest/InputDirectory/Old/Old.dfy: -------------------------------------------------------------------------------- 1 | module ExprEval { 2 | datatype expr = Const(x: int) | Add(e1: expr, e2: expr) | Sub(e1: expr, e2: expr) 3 | 4 | function eval(e: expr): int { 5 | match e { 6 | case Const(n) => n 7 | case Add(e1, e2) => eval(e1) + eval(e2) 8 | case Sub(e1, e2) => eval(e1) - eval(e2) 9 | } 10 | } 11 | 12 | lemma inversion(e1: expr, e2: expr) 13 | ensures eval(Sub(Add(e1, e2),e2)) == eval(e1) 14 | {} 15 | } 16 | -------------------------------------------------------------------------------- /examples/constructor_arguments/new.dfy: -------------------------------------------------------------------------------- 1 | module ExprEval { 2 | // one constructor with more, one with fewer arguments 3 | datatype expr = Const(x: int) | Add(e1: expr, e2: expr, e3: expr) | Sub(e1: expr) 4 | 5 | function eval(e: expr): int { 6 | match e { 7 | case Const(n) => n 8 | case Add(e1,e2,e3) => eval(e1)+eval(e2)+eval(e3) 9 | case Sub(e) => -eval(e) 10 | } 11 | } 12 | 13 | lemma inversion(e1: expr, e2: expr) 14 | ensures eval(Add(e1, e2, Sub(e2))) == eval(e1) 15 | {} 16 | } 17 | -------------------------------------------------------------------------------- /examples/polymorphic_types/old.dfy: -------------------------------------------------------------------------------- 1 | module Simplify { 2 | datatype expr = Const(x: int) | Add(e1: expr, e2: expr) | Sub(e1: expr, e2: expr) 3 | datatype form = Equal(x: a, y: a) | True | And(f1: form, f2: form) 4 | 5 | function simplify(f: form): form { 6 | match f { 7 | case Equal(e1,e2) => Equal(e1,e2) 8 | case True => True 9 | case And(True,f2) => simplify(f2) 10 | case And(f1,True) => simplify(f1) 11 | case And(f1,f2) => And(simplify(f1), simplify(f2)) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/sign/new.dfy: -------------------------------------------------------------------------------- 1 | module Sign { 2 | // change input type from int to real 3 | function sign(i: real): (x: int) { 4 | if i == 0.0 then 5 | 0 6 | else if i > 0.0 then 7 | 1 8 | else 9 | -1 10 | } by method { 11 | if 12 | case i == 0.0 => x := 0; 13 | case i > 0.0 => x := 1; 14 | case i < 0.0 => x := -1; 15 | } 16 | method test(i: real) { 17 | var x := 0; 18 | if 19 | case i == 0.0 => x := 0; 20 | case i > 0.0 => x := 1; 21 | case i < 0.0 => x := -1; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /TypeInjections/Tool.Test/Resources/Git/BadLocation/file1.dfy: -------------------------------------------------------------------------------- 1 | module A { 2 | 3 | datatype singleNoArgs = A1 4 | 5 | datatype twoNoArgs = B2 | B1 6 | 7 | datatype manyNoArgsDiffOrder = C1 | C2 | C3 | C4 8 | 9 | datatype singleArg = D(x: int) 10 | 11 | datatype manyCtorsSingleArg = E1(x: int) | E2(y: char) | E3(s: string) 12 | 13 | datatype singleCtorManyArgs = F(b: bool, c: char, d: char) 14 | 15 | datatype manyCtorsManyArgs = G1(x: int, y: int) 16 | | G2(s: string, c: char, b: bool) 17 | | G3 (i: int, str: string) 18 | } -------------------------------------------------------------------------------- /TypeInjections/Tool.Test/Resources/Local/GoodInput/Expected/joint.dfy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | module Joint.ExprEval { 5 | datatype expr = Const(x: int) | Sub(e1: expr, e2: expr) { 6 | 7 | } 8 | 9 | 10 | function eval(e: expr):int 11 | { 12 | match e { 13 | case Const(mcc_0) => 14 | var n:int:=mcc_0; n 15 | case Sub(mcc_1, mcc_2) => 16 | var e2:expr:=mcc_2; var e1:expr:=mcc_1; (eval(e1) - eval(e2)) 17 | } 18 | 19 | } 20 | 21 | 22 | 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /examples/logic_formulas_opt/new.dfy: -------------------------------------------------------------------------------- 1 | module LogicFormulas { 2 | // change the implementation of eval() 3 | datatype formula = True | False | Not(formula) | And(formula, formula) | Or(formula, formula) | Implies(formula, formula) 4 | 5 | function eval(f: formula): bool 6 | { 7 | match f { 8 | case True => true 9 | case False => false 10 | case Not(g) => !(eval(g)) 11 | case And(g, h) => eval(g) && eval(h) 12 | case Or(g, h) => eval(g) || eval(h) 13 | case Implies(g, h) => !eval(g) || eval(h) 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/complex/new.dfy: -------------------------------------------------------------------------------- 1 | module Complex { 2 | // change int to real 3 | type complex = (real, real) 4 | 5 | function conjugate (x: complex): complex 6 | { 7 | (x.0, -1.0 * x.1) 8 | } 9 | 10 | function add(x: complex, y: complex): complex 11 | { 12 | (x.0 + y.0, x.1 + y.1) 13 | } 14 | 15 | function mult(x: complex, y: complex): complex 16 | { 17 | (x.0 * y.0 - x.1 * y.1, x.0 * y.1 + x.1 * y.0) 18 | } 19 | 20 | function normsquared (conjugate: complex -> complex, x: complex): real 21 | { 22 | mult(x, conjugate(x)).0 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/logic_formulas_function/old.dfy: -------------------------------------------------------------------------------- 1 | module LogicFormulas { 2 | datatype formula = True | False | Not(formula) | And(formula, formula) | Or(formula, formula) 3 | 4 | function eval(f: formula): bool 5 | { 6 | match f { 7 | case True => true 8 | case False => false 9 | case Not(g) => !(eval(g)) 10 | case And(g, h) => eval(g) && eval(h) 11 | case Or(g, h) => eval(g) || eval(h) 12 | } 13 | } 14 | 15 | function simplify(f: formula, eval: formula -> bool): formula 16 | { 17 | if eval(f) then True else False 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/multiple_functions/old.dfy: -------------------------------------------------------------------------------- 1 | module FormEval { 2 | datatype expr = Const(x: int) | Add(e1: expr, e2: expr) 3 | datatype form = Equal(x: expr, y: expr) | And(f1: form, f2: form) 4 | 5 | function evalExpr(e: expr): int { 6 | match e { 7 | case Const(n) => n 8 | case Add(e1, e2) => 9 | evalExpr(e1) + evalExpr(e2) 10 | } 11 | } 12 | 13 | function evalForm(f: form): bool { 14 | match f { 15 | case Equal(e1,e2) => evalExpr(e1) == evalExpr(e2) 16 | case And(f1,f2) => evalForm(f1) && evalForm(f2) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/DatatypeConstructorTest/InputDirectory/New/New.dfy: -------------------------------------------------------------------------------- 1 | module ExprEval { 2 | // one constructor with more, one with fewer arguments 3 | datatype expr = Const(x: int) | Add(e1: expr, e2: expr, e3: expr) | Sub(e1: expr) 4 | 5 | function eval(e: expr): int { 6 | match e { 7 | case Const(n) => n 8 | case Add(e1,e2,e3) => eval(e1)+eval(e2)+eval(e3) 9 | case Sub(e) => -eval(e) 10 | } 11 | } 12 | 13 | lemma inversion(e1: expr, e2: expr) 14 | ensures eval(Add(e1, e2, Sub(e2))) == eval(e1) 15 | {} 16 | } 17 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/DatatypeRefTest/InputDirectory/New/New.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | // datatypes that reference others 3 | module Ref { 4 | datatype unaryNat = Z | S(n: unaryNat) 5 | 6 | datatype natRef = A(y: unaryNat) | B(z: int) 7 | 8 | datatype natRefRef = Foo(n1: natRef, n2: natRef) 9 | | Bar(u: unaryNat) 10 | 11 | datatype recAndRef = A1(fst : recAndRef, snd: natRefRef) 12 | | A2(x: recAndRef, y: string, z: recAndRef, aaa : natRef) 13 | | A3 14 | } 15 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/DatatypeRefTest/InputDirectory/Old/Old.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | // datatypes that reference others 3 | module Ref { 4 | datatype unaryNat = Z | S(n: unaryNat) 5 | 6 | datatype natRef = A(y: unaryNat) | B(z: int) 7 | 8 | datatype natRefRef = Foo(n1: natRef, n2: natRef) 9 | | Bar(u: unaryNat) 10 | 11 | datatype recAndRef = A1(fst : recAndRef, snd: natRefRef) 12 | | A2(x: recAndRef, y: string, z: recAndRef, aaa : natRef) 13 | | A3 14 | } 15 | -------------------------------------------------------------------------------- /examples/binary_tree_insert/old.dfy: -------------------------------------------------------------------------------- 1 | module BinaryTree { 2 | 3 | datatype tree = Leaf | Node (tree, tree) 4 | 5 | function depth(t: tree): int 6 | { 7 | match t { 8 | case Leaf => 0 9 | case Node (l, r) => 10 | var dl := depth(l); 11 | var dr := depth(r); 12 | if (dl < dr) then 13 | dl + 1 14 | else 15 | dr + 1 16 | } 17 | } 18 | 19 | function copy(t: tree): tree 20 | { 21 | match t { 22 | case Leaf => Leaf 23 | case Node (l, r) => Node(copy(l), copy(r)) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleFolderTest/InputDirectory/New/file1.dfy: -------------------------------------------------------------------------------- 1 | module A { 2 | 3 | datatype singleNoArgs = A1 4 | 5 | datatype twoNoArgs = B2 | B1 6 | 7 | datatype manyNoArgsDiffOrder = C1 | C2 | C3 | C4 8 | 9 | datatype singleArg = D(x: int) 10 | 11 | datatype manyCtorsSingleArg = E1(x: int) | E2(y: char) | E3(s: string) 12 | 13 | datatype singleCtorManyArgs = F(b: bool, c: char, d: char) 14 | 15 | datatype manyCtorsManyArgs = G1(x: int, y: int) 16 | | G2(s: string, c: char, b: bool) 17 | | G3 (i: int, str: string) 18 | } -------------------------------------------------------------------------------- /examples/binary_tree_insert_field/old.dfy: -------------------------------------------------------------------------------- 1 | module BinaryTree { 2 | datatype tree = Leaf | Node (l: tree, r: tree) 3 | 4 | function depth(t: tree): int 5 | { 6 | match t { 7 | case Leaf => 0 8 | case Node (l, r) => 9 | var dl := depth(l); 10 | var dr := depth(r); 11 | if (dl < dr) then 12 | dl + 1 13 | else 14 | dr + 1 15 | } 16 | } 17 | 18 | function copy(t: tree): tree 19 | { 20 | match t { 21 | case Leaf => Leaf 22 | case Node (l, r) => Node(copy(l), copy(r)) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/binary_tree_replace/old.dfy: -------------------------------------------------------------------------------- 1 | module BinaryTree { 2 | 3 | datatype tree = Leaf | Node (tree, tree) 4 | 5 | function depth(t: tree): int 6 | { 7 | match t { 8 | case Leaf => 0 9 | case Node (l, r) => 10 | var dl := depth(l); 11 | var dr := depth(r); 12 | if (dl < dr) then 13 | dl + 1 14 | else 15 | dr + 1 16 | } 17 | } 18 | 19 | function copy(t: tree): tree 20 | { 21 | match t { 22 | case Leaf => Leaf 23 | case Node (l, r) => Node(copy(l), copy(r)) 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /examples/logic_formulas_opt/old.dfy: -------------------------------------------------------------------------------- 1 | module LogicFormulas { 2 | 3 | datatype formula = True | False | Not(formula) | And(formula, formula) | Or(formula, formula) | Implies(formula, formula) 4 | 5 | function eval(f: formula): bool 6 | { 7 | match f { 8 | case True => true 9 | case False => false 10 | case Not(g) => !(eval(g)) 11 | case And(g, h) => eval(g) && eval(h) 12 | case Or(g, h) => eval(g) || eval(h) 13 | case Implies(g, h) => 14 | if eval(g) then 15 | eval(h) 16 | else 17 | true 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/logic_formulas_rewrite/old.dfy: -------------------------------------------------------------------------------- 1 | module LogicFormulas { 2 | 3 | datatype formula = True | False | Not(formula) | And(formula, formula) | Or(formula, formula) | Implies(formula, formula) 4 | 5 | function eval(f: formula): bool 6 | { 7 | match f { 8 | case True => true 9 | case False => false 10 | case Not(g) => !(eval(g)) 11 | case And(g, h) => eval(g) && eval(h) 12 | case Or(g, h) => eval(g) || eval(h) 13 | case Implies(g, h) => 14 | if eval(g) then 15 | eval(h) 16 | else 17 | true 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/RecursiveDatatypeTest/InputDirectory/New/New.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module A { 3 | datatype unaryNat = Z | S(n: unaryNat) 4 | 5 | datatype stringAlt = C(c: char) 6 | | A(hd: char, tl: stringAlt) 7 | 8 | datatype intTree = Left(l: intTree) 9 | | Node (n: int) 10 | | Right (r: intTree) 11 | 12 | datatype complicated = A(x: complicated, y: complicated, z: int) 13 | | B(zero: complicated, r: string) 14 | | C(u: char) 15 | } 16 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/RecursiveDatatypeTest/InputDirectory/Old/Old.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module A { 3 | datatype unaryNat = Z | S(n: unaryNat) 4 | 5 | datatype stringAlt = C(c: char) 6 | | A(hd: char, tl: stringAlt) 7 | 8 | datatype intTree = Left(l: intTree) 9 | | Node (n: int) 10 | | Right (r: intTree) 11 | 12 | datatype complicated = A(x: complicated, y: complicated, z: int) 13 | | B(zero: complicated, r: string) 14 | | C(u: char) 15 | } 16 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleFolderTest/ExpectedDirectory/sub1/n_combine.dfy: -------------------------------------------------------------------------------- 1 | include "subFile1_old.dfy" 2 | include "subFile1_new.dfy" 3 | 4 | module Combine.N { 5 | 6 | import Old 7 | 8 | import New 9 | function method fooOldToNew(f: Old.N.foo): (f': New.N.foo) 10 | ensures f as int == f' as int 11 | decreases f 12 | { 13 | f as int as New.N.foo 14 | } 15 | 16 | function method barOldToNew(b: Old.N.bar): (b': New.N.bar) 17 | ensures b as real == b' as real 18 | decreases b 19 | { 20 | b as real as New.N.bar 21 | } 22 | } -------------------------------------------------------------------------------- /examples/option/Option.dfy: -------------------------------------------------------------------------------- 1 | module Options { 2 | datatype {:rust "option", "Option"} Option<+U> = None | Some(val: U) { 3 | function FMap(f: U -> V): Option { 4 | match this 5 | case None => None 6 | case Some(x) => Some(f(x)) 7 | } 8 | 9 | function Fold(default: V, f: U -> V): V { 10 | match this 11 | case None => default 12 | case Some(x) => f(x) 13 | } 14 | 15 | function Option(default: U): U { 16 | Fold(default, x => x) 17 | } 18 | 19 | function GetOrElse(default: U): U { 20 | Option(default) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/binary_tree_delete/new.dfy: -------------------------------------------------------------------------------- 1 | module BinaryTree { 2 | // Remove Node3 (tree, tree, tree) 3 | datatype tree = Leaf | Node (tree, tree) 4 | 5 | function depth(t: tree): int 6 | { 7 | match t { 8 | case Leaf => 0 9 | case Node (l, r) => 10 | var dl := depth(l); 11 | var dr := depth(r); 12 | if (dl > dr) then 13 | dl + 1 14 | else 15 | dr + 1 16 | } 17 | } 18 | 19 | function copy(t: tree): tree 20 | { 21 | match t { 22 | case Leaf => Leaf 23 | case Node (l, r) => Node(copy(l), copy(r)) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/binary_tree_replace/new.dfy: -------------------------------------------------------------------------------- 1 | module BinaryTree { 2 | // Rename "Node" to "Branch" 3 | datatype tree = Leaf | Branch (tree, tree) 4 | 5 | function depth(t: tree): int 6 | { 7 | match t { 8 | case Leaf => 0 9 | case Branch (l, r) => 10 | var dl := depth(l); 11 | var dr := depth(r); 12 | if (dl < dr) then 13 | dl + 1 14 | else 15 | dr + 1 16 | } 17 | } 18 | 19 | function copy(t: tree): tree 20 | { 21 | match t { 22 | case Leaf => Leaf 23 | case Branch (l, r) => Branch(copy(l), copy(r)) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/polymorphic_types/new.dfy: -------------------------------------------------------------------------------- 1 | module Simplify { 2 | // add one case in the function simplify 3 | datatype expr = Const(x: int) | Add(e1: expr, e2: expr) | Sub(e1: expr, e2: expr) 4 | datatype form = Equal(x: a, y: a) | True | And(f1: form, f2: form) 5 | 6 | function simplify(f: form): form { 7 | match f { 8 | case Equal(e1,e2) => Equal(e1,e2) 9 | case True => True 10 | case And(True,True) => True 11 | case And(True,f2) => simplify(f2) 12 | case And(f1,True) => simplify(f1) 13 | case And(f1,f2) => And(simplify(f1), simplify(f2)) 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/binary_tree_insert_field/new.dfy: -------------------------------------------------------------------------------- 1 | module BinaryTree { 2 | // Add the int field 3 | datatype tree = Leaf | Node (l: tree, int, r: tree) 4 | 5 | function depth(t: tree): int 6 | { 7 | match t { 8 | case Leaf => 0 9 | case Node (l, k, r) => 10 | var dl := depth(l); 11 | var dr := depth(r); 12 | if (dl < dr) then 13 | dl + 1 14 | else 15 | dr + 1 16 | } 17 | } 18 | 19 | function copy(t: tree): tree 20 | { 21 | match t { 22 | case Leaf => Leaf 23 | case Node (l, k, r) => Node(copy(l), k, copy(r)) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/import_export/old.dfy: -------------------------------------------------------------------------------- 1 | module Option { 2 | export provides * 3 | export E provides a 4 | export F provides Option, Option.a 5 | const a := 1 6 | datatype Option = A|B { static const a := 2 } 7 | } 8 | 9 | module X { 10 | import opened XYZ = Option 11 | method M() { 12 | print XYZ.a; 13 | print Option.a; 14 | } 15 | } 16 | 17 | module Y { 18 | import opened Option`E 19 | method M() { 20 | print Option.a; 21 | } 22 | } 23 | 24 | module Z { 25 | import opened Option`F 26 | method M() { 27 | print Option.a; 28 | } 29 | } 30 | 31 | method Main() { 32 | X.M(); 33 | Y.M(); 34 | Z.M(); 35 | } 36 | -------------------------------------------------------------------------------- /examples/multiple_types/old.dfy: -------------------------------------------------------------------------------- 1 | module FormEval { 2 | datatype expr = Const(x: int) | Add(e1: expr, e2: expr) | Sub(e1: expr, e2: expr) 3 | datatype form = Equal(x: expr, y: expr) | And(f1: form, f2: form) 4 | 5 | function evalExpr(e: expr): int { 6 | match e { 7 | case Const(n) => n 8 | case Add(e1, e2) => evalExpr(e1) + evalExpr(e2) 9 | case Sub(e1, e2) => evalExpr(e1) - evalExpr(e2) 10 | } 11 | } 12 | 13 | function evalForm(f: form): bool { 14 | match f { 15 | case Equal(e1,e2) => evalExpr(e1) == evalExpr(e2) 16 | case And(f1,f2) => evalForm(f1) && evalForm(f2) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/non_bc_change_in_strings/new.dfy: -------------------------------------------------------------------------------- 1 | // types unchanged, function headers unchanged 2 | // but function body changed in a way that falsifies the generated backwards-compatibility lemma 3 | module Matching { 4 | datatype pattern = Pattern(field: string, value: string) 5 | datatype record = Empty | Cons(field: string, value: string, tl: record) 6 | 7 | function hasMatchingField(r: record, p: pattern): bool { 8 | match r { 9 | case Empty => false 10 | case Cons(f, v, tl) => 11 | if p.field == f then 12 | (p.value == "*") || (p.value == v) 13 | else 14 | hasMatchingField(tl, p) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleDatatypeTest/InputDirectory/Old/Old.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module A { 3 | 4 | datatype singleNoArgs = A1 5 | 6 | datatype twoNoArgs = B2 | B1 7 | 8 | datatype manyNoArgsDiffOrder = C4 | C1 | C3 | C2 9 | 10 | datatype singleArg = D(x: int) 11 | 12 | datatype manyCtorsSingleArg = E1(x: int) | E2(y: char) 13 | 14 | datatype singleCtorManyArgs = F(b: bool, c: char, d: char) 15 | 16 | datatype manyCtorsManyArgs = G1(x: int, y: int) 17 | | G2(s: string, c: char, b: bool) 18 | 19 | function ident (x: manyCtorsManyArgs): manyCtorsManyArgs { 20 | G1(1,1) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/CommitExamples.md: -------------------------------------------------------------------------------- 1 | ### The following examples are real code commits used for testing typeCart: 2 | 3 | ### Cedar-RecordType 4 | - This commit (https://github.com/cedar-policy/cedar-spec/commit/8fa712d7b92eb6c3582ed54cea8439d433b3af15), modifying RecordType 5 | 6 | ### Cryptools-Lib-ConcurrentCall 7 | - This commit (https://github.com/aws/aws-cryptographic-material-providers-library-java/commit/8577627627086063c842a6d0e7459562f1e1b493), adding ConcurrentCall 8 | 9 | ### Cryptools-Lib-Constant 10 | - This commit (https://github.com/aws/aws-cryptographic-material-providers-library-java/commit/a0416d96bdba385e1988067715db26ba3d4cd33c), adjusting constants and bounds 11 | 12 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/CollectionsTest/InputDirectory/New/New.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module S { 3 | 4 | datatype foo = Bar(x: int) | Baz(y: string) 5 | 6 | datatype list = Nil | Cons(h: T, tl: list) 7 | 8 | //This test shows limitation of our tool 9 | // NOTE: the tool does not upport ex: set> 10 | datatype multipleColl = A1(s: seq, t: set>) 11 | | A2(m: multipleColl) 12 | | A3(f: map, 13 | x: seq>>, 14 | y: map, list>>>) 15 | } 16 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/CollectionsTest/InputDirectory/Old/Old.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module S { 3 | 4 | datatype foo = Bar(x: int) | Baz(y: string) 5 | 6 | datatype list = Nil | Cons(h: T, tl: list) 7 | 8 | //This test shows limitation of our tool 9 | // NOTE: the tool does not upport ex: set> 10 | datatype multipleColl = A1(s: seq, t: set>) 11 | | A2(m: multipleColl) 12 | | A3(f: map, 13 | x: seq>>, 14 | y: map, list>>>) 15 | } 16 | -------------------------------------------------------------------------------- /examples/multiple_types/new.dfy: -------------------------------------------------------------------------------- 1 | module FormEval { 2 | // one deleted, one added constructor 3 | datatype expr = Const(x: int) | Add(e1: expr, e2: expr) | Mul(e1: expr, e2: expr) 4 | datatype form = Equal(x: expr, y: expr) | And(f1: form, f2: form) 5 | 6 | function evalExpr(e: expr): int { 7 | match e { 8 | case Const(n) => n 9 | case Add(e1, e2) => evalExpr(e1) + evalExpr(e2) 10 | case Mul(e1, e2) => evalExpr(e1) * evalExpr(e2) 11 | } 12 | } 13 | 14 | function evalForm(f: form): bool { 15 | match f { 16 | case Equal(e1,e2) => evalExpr(e1) == evalExpr(e2) 17 | case And(f1,f2) => evalForm(f1) && evalForm(f2) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/complex/old.dfy: -------------------------------------------------------------------------------- 1 | module Complex { 2 | 3 | type complex = (int, int) 4 | 5 | function conjugate (x: complex): complex 6 | { 7 | (x.0, -1 * x.1) 8 | } 9 | 10 | function add(x: complex, y: complex): complex 11 | { 12 | (x.0 + y.0, x.1 + y.1) 13 | } 14 | 15 | function mult(x: complex, y: complex): complex 16 | { 17 | (x.0 * y.0 - x.1 * y.1, x.0 * y.1 + x.1 * y.0) 18 | } 19 | 20 | // this does not have to be implemented as a high order function 21 | // but it can be and I am doing so to understand how higher order behaves 22 | function normsquared (conjugate: complex -> complex, x: complex): int 23 | { 24 | mult(x, conjugate(x)).0 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/datatype_members/old.dfy: -------------------------------------------------------------------------------- 1 | module Lists { 2 | datatype Nat = Zero | Succ(n: Nat) { 3 | function plus(m: Nat): Nat { 4 | match m { 5 | case Zero => this 6 | case Succ(n) => Succ(this.plus(n)) 7 | } 8 | } 9 | } 10 | datatype List = Nil | Cons(h: a, t: List) { 11 | function concat(l: List): List { 12 | match this { 13 | case Nil => l 14 | case Cons(h,t) => Cons(h, t.concat(l)) 15 | } 16 | } 17 | function filter(f: a -> bool): List { 18 | match this { 19 | case Nil => Nil 20 | case Cons(h,t) => if f(h) then Cons(h, t.filter(f)) else t.filter(f) 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/unchanged_classes/old.dfy: -------------------------------------------------------------------------------- 1 | module Classes { 2 | class T { 3 | var x: int 4 | constructor (x: int) { 5 | this.x := x; 6 | } 7 | function getX(): int 8 | reads this 9 | { 10 | // dummy variables to ensure that we support "requires" here 11 | var es := [x]; 12 | var _ := seq(|es|, i requires 0 <= i < |es| => es[i]); 13 | x 14 | } 15 | } 16 | 17 | class Wrapper { 18 | var a: A 19 | constructor (a: A) { 20 | this.a := a; 21 | } 22 | method Amap(f: A -> B) returns (m: Wrapper) { 23 | m := new Wrapper(f(this.a)); 24 | } 25 | } 26 | 27 | trait F { 28 | function apply(a: A): B 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/logic_formulas_rewrite/new.dfy: -------------------------------------------------------------------------------- 1 | module LogicFormulas { 2 | // "Implies" is removed. 3 | // There is a stub for "Implies" in the translation function. 4 | // We can prove the backward compatibility by writing "New.LogicFormulas.formula.Or(New.LogicFormulas.formula.Not(formula_forward(x5_O)), formula_forward(x6_O))" in the stub. 5 | datatype formula = True | False | Not(formula) | And(formula, formula) | Or(formula, formula) 6 | 7 | function eval(f: formula): bool 8 | { 9 | match f { 10 | case True => true 11 | case False => false 12 | case Not(g) => !(eval(g)) 13 | case And(g, h) => eval(g) && eval(h) 14 | case Or(g, h) => eval(g) || eval(h) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/unchanged_classes/new.dfy: -------------------------------------------------------------------------------- 1 | // unchanged 2 | module Classes { 3 | class T { 4 | var x: int 5 | constructor (x: int) { 6 | this.x := x; 7 | } 8 | function getX(): int 9 | reads this 10 | { 11 | // dummy variables to ensure that we support "requires" here 12 | var es := [x]; 13 | var _ := seq(|es|, i requires 0 <= i < |es| => es[i]); 14 | x 15 | } 16 | } 17 | 18 | class Wrapper { 19 | var a: A 20 | constructor (a: A) { 21 | this.a := a; 22 | } 23 | method Amap(f: A -> B) returns (m: Wrapper) { 24 | m := new Wrapper(f(this.a)); 25 | } 26 | } 27 | 28 | trait F { 29 | function apply(a: A): B 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/IOTests.fs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT 3 | 4 | namespace TypeInjections.Test 5 | 6 | module IOTests = 7 | open NUnit.Framework 8 | 9 | type internal Marker = 10 | interface 11 | end 12 | 13 | let moduleName = typeof.DeclaringType.Name 14 | // set the module name, alternatively we could look into determining 15 | // the calling module via reflection in TestUtils 16 | 17 | let testRunner = 18 | TestUtils.testRunnerGen 19 | 20 | [] 21 | let SimpleFolderTest () = 22 | testRunner "InputDirectory" "OutputDirectory" 23 | 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | **/Output.dfy 3 | **/Combine.dfy 4 | **/Output.txt 5 | **/Combine.txt 6 | **/OutputDirectory/** 7 | **/Output/** 8 | .idea/ 9 | TypeInjections/TypeInjections/bin 10 | TypeInjections/TypeInjections/obj 11 | TypeInjections/packages 12 | TypeInjections/TypeInjections/nupkg 13 | build 14 | dafny/ 15 | /TypeInjections/TypeInjections.sln.DotSettings.user 16 | TypeInjections/TypeInjections.Test/bin 17 | TypeInjections/TypeInjections.Test/obj 18 | TypeInjections/Tool/bin 19 | TypeInjections/Tool/obj 20 | TypeInjections/Tool.Test/bin 21 | TypeInjections/Tool.Test/obj 22 | *.dll 23 | *.runtimeconfig.json 24 | scripts/cedar-spec 25 | scripts/proofs 26 | scripts/old 27 | scripts/new 28 | scripts/result.csv 29 | scripts/*.txt 30 | -------------------------------------------------------------------------------- /examples/import_export/new.dfy: -------------------------------------------------------------------------------- 1 | module Option { 2 | // change provides to reveals; should not affect anything 3 | export reveals * 4 | export E reveals a 5 | export F reveals Option, Option.a 6 | const a := 1 7 | datatype Option = A|B { static const a := 2 } 8 | } 9 | 10 | module X { 11 | import opened XYZ = Option 12 | method M() { 13 | print XYZ.a; 14 | print Option.a; 15 | } 16 | } 17 | 18 | module Y { 19 | // unsupported by typeCart now! 20 | import opened Option`E 21 | method M() { 22 | print Option.a; 23 | } 24 | } 25 | 26 | module Z { 27 | import opened Option`F 28 | method M() { 29 | print Option.a; 30 | } 31 | } 32 | 33 | method Main() { 34 | X.M(); 35 | Y.M(); 36 | Z.M(); 37 | } 38 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleDatatypeTest/InputDirectory/New/New.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module A { 3 | 4 | datatype singleNoArgs = A1 5 | 6 | datatype twoNoArgs = B2 | B1 7 | 8 | datatype manyNoArgsDiffOrder = C1 | C2 | C3 | C4 9 | 10 | datatype singleArg = D(x: int) 11 | 12 | // Add E3 13 | datatype manyCtorsSingleArg = E1(x: int) | E2(y: char) | E3(s: string) 14 | 15 | datatype singleCtorManyArgs = F(b: bool, c: char, d: char) 16 | 17 | // Add G3 18 | datatype manyCtorsManyArgs = G1(x: int, y: int) 19 | | G2(s: string, c: char, b: bool) 20 | | G3 (i: int, str: string) 21 | 22 | function ident (x: manyCtorsManyArgs): manyCtorsManyArgs { 23 | x 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/datatype_members/new.dfy: -------------------------------------------------------------------------------- 1 | module Lists { 2 | // implementation of filter() is changed 3 | datatype Nat = Zero | Succ(n: Nat) { 4 | function plus(m: Nat): Nat { 5 | match m { 6 | case Zero => this 7 | case Succ(n) => Succ(this.plus(n)) 8 | } 9 | } 10 | } 11 | datatype List = Nil | Cons(h: a, t: List) { 12 | function concat(l: List): List { 13 | match this { 14 | case Nil => l 15 | case Cons(h,t) => Cons(h, t.concat(l)) 16 | } 17 | } 18 | function filter(f: a -> bool): List { 19 | match this { 20 | case Nil => Nil 21 | case Cons(h,t) => 22 | var r := t.filter(f); 23 | if f(h) then Cons(h, r) else r 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/signed_int/old.dfy: -------------------------------------------------------------------------------- 1 | module SignedInt { 2 | 3 | datatype signed_int = Pos(nat) | Neg(nat) 4 | 5 | function int_to_signed_int (i: int): signed_int 6 | { 7 | if i >= 0 then 8 | Pos(i as nat) 9 | else 10 | Neg((-1 * i) as nat) 11 | } 12 | 13 | function signed_int_add (i: signed_int, j: signed_int): signed_int 14 | { 15 | match (i, j) { 16 | case (Pos(i), Pos(j)) => Pos (i + j) 17 | case (Pos(i), Neg(j)) => int_to_signed_int(i - j) 18 | case (Neg(i), Pos(j)) => int_to_signed_int(j - i) 19 | case (Neg(i), Neg(j)) => int_to_signed_int(0 - i - j) 20 | } 21 | } 22 | 23 | function add_signed_int (i: int, j: int): signed_int 24 | { 25 | signed_int_add (int_to_signed_int(i), int_to_signed_int(j)) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /examples/multiple_imports/new.dfy: -------------------------------------------------------------------------------- 1 | module IntType { 2 | newtype my_type = x: int | -0x80 <= x <= 0x7f 3 | function zero(): my_type 4 | { 5 | 0 6 | } 7 | // A ghost function is removed here. 8 | } 9 | 10 | module RealType { 11 | newtype my_type = x: real | -128.0 <= x < 128.0 12 | } 13 | 14 | module Sqr { 15 | import opened IntType 16 | import opened RealType 17 | function squareInt(x: IntType.my_type): IntType.my_type 18 | requires -11 <= x <= 11 19 | { 20 | x * x 21 | } 22 | function squareReal(x: RealType.my_type): RealType.my_type 23 | requires x == 0.0 24 | { 25 | x * x 26 | } 27 | } 28 | 29 | module SqrInt { 30 | import opened IntType 31 | function square(x: my_type): my_type 32 | requires -11 <= x <= 11 33 | { 34 | x * x 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections/Resources/UsingResources.txt: -------------------------------------------------------------------------------- 1 | How to make resource file 2 | 1. 'Add file' to Resources folder (need to make new file, 3 | can't figure out how to do this for imported dafny files) 4 | 2. Right click on created file and go to 'properties' 5 | 3. Change 'Build Action' to 'Embedded Resource' 6 | 4. Change 'Copy to output directory' to 'Copy Always' 7 | 8 | How to access resource file 9 | 1. get current assemby object: 10 | let asmb = Assembly.GetExecutingAssembly(); 11 | 2. inspect list of assembly resources for your file's assembly name: 12 | let listOfNames = asmb.GetManifestResourceNames() 13 | 3. Create stream object for Assembly file 14 | let stream = asmb.GetManifestResourceStream([ASSEMBLY_NAME_HERE]) 15 | 4. Read stream 16 | let reader = new StreamReader(stream) 17 | reader.ReadToEnd() -------------------------------------------------------------------------------- /examples/multiple_imports/old.dfy: -------------------------------------------------------------------------------- 1 | module IntType { 2 | newtype my_type = x: int | -0x80 <= x <= 0x7f 3 | function zero(): my_type 4 | { 5 | 0 6 | } 7 | ghost function Zero(): my_type 8 | { 9 | 0 10 | } 11 | } 12 | 13 | module RealType { 14 | newtype my_type = x: real | -128.0 <= x < 128.0 15 | } 16 | 17 | module Sqr { 18 | import opened IntType 19 | import opened RealType 20 | function squareInt(x: IntType.my_type): IntType.my_type 21 | requires -11 <= x <= 11 22 | { 23 | x * x 24 | } 25 | function squareReal(x: RealType.my_type): RealType.my_type 26 | requires x == 0.0 27 | { 28 | x * x + 0.0 29 | } 30 | } 31 | 32 | module SqrInt { 33 | import opened IntType 34 | function square(x: my_type): my_type 35 | requires -11 <= x <= 11 36 | { 37 | x * x 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/LimitTest/InputDirectory/Old/Old.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | // the tool generates idnetically named functions for types 3 | // with same names declared in separate modules 4 | module MessageA { 5 | datatype msg = Send(value: string) 6 | | Recieve(value: string) { 7 | function method GetValue(): (ret: string) 8 | ensures ret == value 9 | } 10 | } 11 | 12 | module MessageB { 13 | datatype msg = Send(value: string) 14 | | Recieve(value: string) { 15 | function method GetValue(): (ret: string) 16 | ensures ret == value 17 | } 18 | } 19 | 20 | module Together{ 21 | import MessageA 22 | import MessageB 23 | 24 | datatype msg = MsgA(aMsg: MessageA.msg) | MsgB(bMsg: MessageB.msg) 25 | 26 | datatype comm = Channel(value: msg) 27 | } -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/ResultTest/InputDirectory/New/New.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | // test for multiple modules 3 | module ExceptionA { 4 | datatype exnA = Runtime(msg: string) 5 | | IOException(msg: string) { 6 | function method GetMessage(): (ret: string) 7 | ensures ret == msg 8 | } 9 | } 10 | 11 | module ExceptionB { 12 | datatype exnB = Runtime(msg: string) 13 | | IOException(msg: string) { 14 | function method GetMessage(): (ret: string) 15 | ensures ret == msg 16 | } 17 | } 18 | 19 | module Eval{ 20 | import ExceptionA 21 | import ExceptionB 22 | 23 | datatype exnAB = 24 | ExnA(aExn: ExceptionA.exnA) 25 | | ExnB(bExn: ExceptionB.exnB) 26 | 27 | datatype result = 28 | Success(value: U) 29 | | Failure(exn: exnAB) 30 | } -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/ResultTest/InputDirectory/Old/Old.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | // test for multiple modules 3 | module ExceptionA { 4 | datatype exnA = Runtime(msg: string) 5 | | IOException(msg: string) { 6 | function method GetMessage(): (ret: string) 7 | ensures ret == msg 8 | } 9 | } 10 | 11 | module ExceptionB { 12 | datatype exnB = Runtime(msg: string) 13 | | IOException(msg: string) { 14 | function method GetMessage(): (ret: string) 15 | ensures ret == msg 16 | } 17 | } 18 | 19 | module Eval{ 20 | import ExceptionA 21 | import ExceptionB 22 | 23 | datatype exnAB = 24 | ExnA(aExn: ExceptionA.exnA) 25 | | ExnB(bExn: ExceptionB.exnB) 26 | 27 | datatype result = 28 | Success(value: U) 29 | | Failure(exn: exnAB) 30 | } -------------------------------------------------------------------------------- /examples/sequences/new.dfy: -------------------------------------------------------------------------------- 1 | module Seq { 2 | // Change int to real. The user needs to add the following lemma in order to prove 3 | // the backward compatibility of seqmap: 4 | // lemma bc_map(A_forward: A_O->A_N, f:int -> A_O, s: seq) 5 | // ensures Translations.MapBuiltinTypes.Seq(A_forward, Old.Seq.seqmap(f, s)) == New.Seq.seqmap(((x1_N: real) => A_forward(f(x1_N.Floor))), Translations.MapBuiltinTypes.Seq(((sq_O: int) => sq_O as real), s)) 6 | // {} 7 | function seqmap(f: real -> A, s: seq): seq 8 | { 9 | if s == [] then 10 | [] 11 | else 12 | [f(s[0])] + seqmap(f, s[1..]) 13 | } 14 | 15 | function reduce(f: (real, real) -> real, id: real, s: seq): real 16 | { 17 | if s == [] then 18 | id 19 | else 20 | f(s[0], reduce(f, id, s[1..])) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/LimitTest/InputDirectory/New/New.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | // the tool generates idnetically named functions for types 3 | // with same names declared in separate modules 4 | module MessageA { 5 | datatype msg = Send(value: string) 6 | | Recieve(value: string) { 7 | function method GetValue(): (ret: string) 8 | ensures ret == value 9 | } 10 | } 11 | 12 | module MessageB { 13 | datatype msg = Send(value: string) 14 | | Recieve(value: string) { 15 | function method GetValue(): (ret: string) 16 | ensures ret == value 17 | } 18 | } 19 | 20 | module Together{ 21 | import MessageA 22 | import MessageB 23 | 24 | datatype msg = MsgA(aMsg: MessageA.msg) | MsgB(bMsg: MessageB.msg) 25 | 26 | datatype comm = Channel(value: msg) 27 | } 28 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SimpleFolderTest/ExpectedDirectory/file1_new.dfy: -------------------------------------------------------------------------------- 1 | module New.A { 2 | datatype singleNoArgs = A1() { 3 | 4 | } 5 | 6 | 7 | datatype twoNoArgs = B2() | B1() { 8 | 9 | } 10 | 11 | 12 | datatype manyNoArgsDiffOrder = C1() | C2() | C3() | C4() { 13 | 14 | } 15 | 16 | 17 | datatype singleArg = D(x: int) { 18 | 19 | } 20 | 21 | 22 | datatype manyCtorsSingleArg = E1(x: int) | E2(y: char) | E3(s: string) { 23 | 24 | } 25 | 26 | 27 | datatype singleCtorManyArgs = F(b: bool, c: char, d: char) { 28 | 29 | } 30 | 31 | 32 | datatype manyCtorsManyArgs = G1(x: int, y: int) | G2(s: string, c: char, b: bool) | G3(i: int, str: string) { 33 | 34 | } 35 | 36 | 37 | 38 | } -------------------------------------------------------------------------------- /examples/list_and_length/new.dfy: -------------------------------------------------------------------------------- 1 | module List { 2 | // The new list is intended to store the length of each sublist in the data structure. 3 | // The forward function cannot ensure this, but we can have a separate requirement for this. 4 | 5 | datatype list = Nil | Cons (T, list, int) 6 | 7 | // Check that a list is correctly "lengthed" 8 | // And return the length 9 | function check_list(ll:list): (bool, int) 10 | { 11 | match ll { 12 | case Nil => (true, 0) 13 | case Cons(h, t, len) => 14 | var (is_good_t, len_t):= check_list(t); 15 | (1 + len_t == len && is_good_t, len) 16 | 17 | } 18 | } 19 | 20 | function length(l: list): (len: int) 21 | requires (check_list(l)).0 == true; 22 | ensures (check_list(l).1 == len) 23 | { 24 | match l { 25 | case Nil => 0 26 | case Cons (h, t, n) => n 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/signed_int_constrained/old.dfy: -------------------------------------------------------------------------------- 1 | module SignedInt { 2 | 3 | datatype signed_int = Pos(nonnegnat) | Neg(posnat) 4 | newtype nonnegnat = n: nat | n >= 0 5 | newtype posnat = n: nat | n > 0 witness(1) 6 | 7 | function int_to_signed_int (i: int): (s: signed_int) 8 | { 9 | if i >= 0 then 10 | Pos(i as nonnegnat) 11 | else 12 | Neg((-1 * i) as posnat) 13 | } 14 | 15 | function signed_int_add (i: signed_int, j: signed_int): signed_int 16 | { 17 | match (i, j) { 18 | case (Pos(i), Pos(j)) => Pos (i + j) 19 | case (Pos(i), Neg(j)) => int_to_signed_int(i as int - j as int) 20 | case (Neg(i), Pos(j)) => int_to_signed_int(j as int - i as int) 21 | case (Neg(i), Neg(j)) => int_to_signed_int(0 - i as int - j as int) 22 | } 23 | } 24 | 25 | function add_signed_int (i: int, j: int): signed_int 26 | { 27 | signed_int_add (int_to_signed_int(i), int_to_signed_int(j)) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/GenericDatatypeTest/InputDirectory/New/New.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module Gen { 3 | datatype list = Nil 4 | | Cons(x: A, tl: list) 5 | 6 | datatype tree = Left(l: tree) 7 | | Node(a: A) 8 | | Right(r: tree) 9 | 10 | datatype option = None 11 | | Some(x: A) 12 | 13 | datatype either = Left(s: S) 14 | | Right (t: T) 15 | 16 | // cannot yet handle arbitrary seqs, but can handle with generics 17 | datatype withSeq = Fst 18 | | Snd(l: seq) 19 | 20 | datatype complicated = C1(x: A, y: B, z: C) 21 | | C2(c: complicated) 22 | | C3(c2: complicated) 23 | 24 | datatype genRef = Left(l: list) 25 | | Right(r: tree, z: either) 26 | } 27 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/GenericDatatypeTest/InputDirectory/Old/Old.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module Gen { 3 | datatype list = Nil 4 | | Cons(x: A, tl: list) 5 | 6 | datatype tree = Left(l: tree) 7 | | Node(a: A) 8 | | Right(r: tree) 9 | 10 | datatype option = None 11 | | Some(x: A) 12 | 13 | datatype either = Left(s: S) 14 | | Right (t: T) 15 | 16 | // cannot yet handle arbitrary seqs, but can handle with generics 17 | datatype withSeq = Fst 18 | | Snd(l: seq) 19 | 20 | datatype complicated = C1(x: A, y: B, z: C) 21 | | C2(c: complicated) 22 | | C3(c2: complicated) 23 | 24 | datatype genRef = Left(l: list) 25 | | Right(r: tree, z: either) 26 | } 27 | -------------------------------------------------------------------------------- /examples/logic_formulas_function/new.dfy: -------------------------------------------------------------------------------- 1 | module LogicFormulas { 2 | // "Implies" is added. 3 | // There is a stub for "Implies" in the translation function. 4 | // We can prove the backward compatibility by writing "Old.LogicFormulas.formula.Or(Old.LogicFormulas.formula.Not(formula_backward(x5_N)), formula_backward(x6_N))" in the stub. 5 | datatype formula = True | False | Not(formula) | And(formula, formula) | Or(formula, formula) | Implies(formula, formula) 6 | 7 | function eval(f: formula): bool 8 | { 9 | match f { 10 | case True => true 11 | case False => false 12 | case Not(g) => !(eval(g)) 13 | case And(g, h) => eval(g) && eval(h) 14 | case Or(g, h) => eval(g) || eval(h) 15 | case Implies(g, h) => 16 | if eval(g) then 17 | eval(h) 18 | else 19 | true 20 | } 21 | } 22 | 23 | function simplify(f: formula, eval: formula -> bool): formula 24 | { 25 | if eval(f) then True else False 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/arithmetic_return_type/old.dfy: -------------------------------------------------------------------------------- 1 | module Arith { 2 | type Ident = string 3 | 4 | datatype Aexp = 5 | | CONST(n: int) 6 | | VAR(x: Ident) 7 | | PLUS(a1: Aexp, a2: Aexp) 8 | | MINUS(a1: Aexp, a2: Aexp) 9 | | MUL(a1: Aexp, a2: Aexp) 10 | 11 | predicate IdInAexp(id: Ident, a: Aexp) { 12 | match a 13 | case CONST(n) => false 14 | case VAR(id') => id == id' 15 | case PLUS(a1, a2) => IdInAexp(id,a1) || IdInAexp(id,a2) 16 | case MINUS(a1, a2) => IdInAexp(id,a1) || IdInAexp(id,a2) 17 | case MUL(a1, a2) => IdInAexp(id,a1) || IdInAexp(id, a2) 18 | } 19 | 20 | type Store = map 21 | 22 | function Aeval(s: Store, a: Aexp): int 23 | requires forall id: Ident :: IdInAexp(id,a) ==> id in s 24 | { 25 | match a 26 | case CONST(n) => n 27 | case VAR(id) => s[id] 28 | case PLUS(a1, a2) => Aeval(s,a1) + Aeval(s,a2) 29 | case MINUS(a1, a2) => Aeval(s,a1) - Aeval(s,a2) 30 | case MUL(a1, a2) => Aeval(s,a1) * Aeval(s, a2) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/binary_tree_alter_field/old.dfy: -------------------------------------------------------------------------------- 1 | module BinaryTree { 2 | 3 | datatype tree = Leaf | Node (tree, int, int, tree) 4 | 5 | function depth(t: tree): int 6 | { 7 | match t { 8 | case Leaf => 0 9 | case Node (l, i, j, r) => 10 | var dl := depth(l); 11 | var dr := depth(r); 12 | if (dl < dr) then 13 | dl + 1 14 | else 15 | dr + 1 16 | } 17 | } 18 | 19 | function copy(t: tree): tree 20 | { 21 | match t { 22 | case Leaf => Leaf 23 | case Node (l, i, j, r) => Node(copy(l), i, j, copy(r)) 24 | } 25 | } 26 | 27 | function sum(t: tree): int 28 | { 29 | match t { 30 | case Leaf => 0 31 | case Node (l, i, j, r) => 32 | j + sum(l) + sum(r) 33 | } 34 | } 35 | 36 | function reduce (t: tree, id: int, f: (int, int) -> int): int 37 | { 38 | match t { 39 | case Leaf => id 40 | case Node (l, k, v, r) => 41 | f(v, f(reduce(l, id, f), reduce(r, id, f))) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/binary_tree_delete/old.dfy: -------------------------------------------------------------------------------- 1 | module BinaryTree { 2 | 3 | datatype tree = Leaf | Node (tree, tree) | Node3 (tree, tree, tree) 4 | 5 | function depth(t: tree): int 6 | { 7 | match t { 8 | case Leaf => 0 9 | case Node (l, r) => 10 | var dl := depth(l); 11 | var dr := depth(r); 12 | if (dl > dr) then 13 | dl + 1 14 | else 15 | dr + 1 16 | case Node3 (l, m, r) => 17 | var dl := depth(l); 18 | var dm := depth(m); 19 | var dr := depth(r); 20 | if (dl > dr) then 21 | if dl > dm then 22 | dl + 1 23 | else 24 | dm + 1 25 | else 26 | if dm > dr then 27 | dm + 1 28 | else 29 | dr + 1 30 | } 31 | } 32 | 33 | function copy(t: tree): tree 34 | { 35 | match t { 36 | case Leaf => Leaf 37 | case Node (l, r) => Node(copy(l), copy(r)) 38 | case Node3 (l, m, r) => Node3(copy(l), copy(m), copy(r)) 39 | 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/binary_tree_insert/new.dfy: -------------------------------------------------------------------------------- 1 | module BinaryTree { 2 | // Add Node3 (tree, tree, tree) 3 | datatype tree = Leaf | Node (tree, tree) | Node3 (tree, tree, tree) 4 | 5 | function depth(t: tree): int 6 | { 7 | match t { 8 | case Leaf => 0 9 | case Node (l, r) => 10 | var dl := depth(l); 11 | var dr := depth(r); 12 | if (dl < dr) then 13 | dl + 1 14 | else 15 | dr + 1 16 | case Node3 (l, m, r) => 17 | var dl := depth(l); 18 | var dm := depth(m); 19 | var dr := depth(r); 20 | if (dl < dr) then 21 | if dm < dl then 22 | dm + 1 23 | else 24 | dl + 1 25 | else 26 | if dm > dr then 27 | dm + 1 28 | else 29 | dr + 1 30 | } 31 | } 32 | 33 | function copy(t: tree): tree 34 | { 35 | match t { 36 | case Leaf => Leaf 37 | case Node (l, r) => Node(copy(l), copy(r)) 38 | case Node3 (l, m, r) => Node3(copy(l), copy(m), copy(r)) 39 | 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /TypeInjections/Tool.Test/Tool.Test.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | false 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/binary_tree_alter_field/new.dfy: -------------------------------------------------------------------------------- 1 | module BinaryTree { 2 | // the 3rd field is changed from int to real 3 | datatype tree = Leaf | Node (tree, int, real, tree) 4 | 5 | function depth(t: tree): int 6 | { 7 | match t { 8 | case Leaf => 0 9 | case Node (l, k, v, r) => 10 | var dl := depth(l); 11 | var dr := depth(r); 12 | if (dl < dr) then 13 | dl + 1 14 | else 15 | dr + 1 16 | } 17 | } 18 | 19 | function copy(t: tree): tree 20 | { 21 | match t { 22 | case Leaf => Leaf 23 | case Node (l, k, v, r) => Node(copy(l), k, v, copy(r)) 24 | } 25 | } 26 | 27 | function sum(t: tree): real 28 | { 29 | match t { 30 | case Leaf => 0.0 31 | case Node (l, k, v, r) => 32 | v + sum(l) + sum(r) 33 | } 34 | } 35 | 36 | function reduce (t: tree, id: real, f: (real, real) -> real): real 37 | { 38 | match t { 39 | case Leaf => id 40 | case Node (l, k, v, r) => 41 | f(v, f(reduce(l, id, f), reduce(r, id, f))) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/multiple_functions/new.dfy: -------------------------------------------------------------------------------- 1 | // evalExpr is replaced with a different function evalExprAcc (different name, different header). 2 | // The compatibility lemma for evalExpr cannot be generated 3 | // The compatibility lemma for evalForm can still be generated and is still true, 4 | // but the proof cannot use a generated lemma for evalExpr and is therefore harder. 5 | // The user has to manually come up with the auxiliary lemma Old.evalExpr(e) = New.evalExprAcc(e,0). 6 | module FormEval { 7 | datatype expr = Const(x: int) | Add(e1: expr, e2: expr) 8 | datatype form = Equal(x: expr, y: expr) | And(f1: form, f2: form) 9 | 10 | function evalExprAcc(e: expr, acc: int): int { 11 | match e { 12 | case Const(n) => acc + n 13 | case Add(e1, e2) => 14 | var e1V := evalExprAcc(e1, acc); 15 | evalExprAcc(e2, e1V) 16 | } 17 | } 18 | 19 | function evalForm(f: form): bool { 20 | match f { 21 | case Equal(e1,e2) => evalExprAcc(e1,0) == evalExprAcc(e2, 0) 22 | case And(f1,f2) => evalForm(f1) && evalForm(f2) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | DAFNY_DOWNLOAD_LOCATION: https://github.com/dafny-lang/dafny/releases/download/v4.6.0/dafny-4.6.0-x64-ubuntu-20.04.zip 11 | DAFNY_ZIP: dafny-4.6.0-x64-ubuntu-20.04.zip 12 | TOOL_DLL: TypeInjections/TypeInjections/bin/Debug/net6.0/TypeInjections.dll 13 | TOOL_SLN: TypeInjections/TypeInjections.sln 14 | TOOL_TEST: TypeInjections/TypeInjections.Test/TypeInjections.Test.fsproj 15 | 16 | 17 | jobs: 18 | job0: 19 | name: TypeCart CI 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Setup .NET 24 | uses: actions/setup-dotnet@v1 25 | with: 26 | dotnet-version: 6.0.x 27 | - name: Install Dafny, build and test 28 | run: | 29 | curl $DAFNY_DOWNLOAD_LOCATION -L -o $DAFNY_ZIP 30 | unzip $DAFNY_ZIP && rm $DAFNY_ZIP 31 | dotnet build TypeInjections/TypeInjections.sln -p:Configuration=Debug 32 | dotnet test $TOOL_TEST 33 | -------------------------------------------------------------------------------- /examples/signed_int_constrained/new.dfy: -------------------------------------------------------------------------------- 1 | module SignedInt { 2 | 3 | datatype signed_int = Pos(posnat) | Neg(posnat) | Zero 4 | newtype nonnegnat = n: nat | n >= 0 5 | newtype posnat = n: nat | n > 0 witness(1) 6 | 7 | function int_to_signed_int (i: int): signed_int 8 | { 9 | if i > 0 then 10 | Pos(i as posnat) 11 | else if i < 0 then 12 | Neg((-1 * i) as posnat) 13 | else 14 | Zero 15 | } 16 | 17 | function signed_int_add (i: signed_int, j: signed_int): signed_int 18 | { 19 | match (i, j) { 20 | case (Pos(i), Pos(j)) => Pos (i + j) 21 | case (Pos(i), Neg(j)) => int_to_signed_int(i as int - j as int) 22 | case (Pos(i), Zero) => Pos(i) 23 | case (Neg(i), Pos(j)) => int_to_signed_int(j as int - i as int) 24 | case (Neg(i), Neg(j)) => Neg(i + j) 25 | case (Neg(i), Zero) => Neg(i) 26 | case (Zero, Zero) => Zero 27 | case (Zero, Pos(j)) => Pos(j) 28 | case (Zero, Neg(j)) => Neg(j) 29 | } 30 | } 31 | 32 | function add_signed_int (i: int, j: int): signed_int 33 | { 34 | signed_int_add (int_to_signed_int(i), int_to_signed_int(j)) 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | typeCart 2 | 3 | MIT License 4 | 5 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/MapTest/InputDirectory/New/New.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module S { 3 | 4 | datatype foo = Bar(x: int) | Baz(y: string) 5 | 6 | datatype mapFooKey = A1(s: map) | A2 7 | 8 | datatype mapFooVal = B1(s: map) | B2 9 | 10 | datatype mapFooBoth = C1(m: map) | C3 11 | 12 | datatype option = None | Some(x: T) 13 | 14 | datatype typ = D0(st: option) 15 | | D1(s: option) 16 | | D2(f: typ) 17 | | D3(g: typ) 18 | // incorrect output 19 | | D4(t: option>) 20 | // incorrect output 21 | | D5(h: typ>) 22 | 23 | // doesn't map correctly 24 | datatype mapBothDifferent = D1(m: map>) 25 | | D2(s: string) 26 | 27 | // doesn't map correctly 28 | datatype mapBothDifferentIn = D1(m: map>) 29 | | D2(s: string) 30 | 31 | datatype mapBuiltIn = E1(m: map, set>) 32 | | E2 (r: seq, s: set) 33 | } 34 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/MapTest/InputDirectory/Old/Old.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module S { 3 | 4 | datatype foo = Bar(x: int) | Baz(y: string) 5 | 6 | datatype mapFooKey = A1(s: map) | A2 7 | 8 | datatype mapFooVal = B1(s: map) | B2 9 | 10 | datatype mapFooBoth = C1(m: map) | C3 11 | 12 | datatype option = None | Some(x: T) 13 | 14 | datatype typ = D0(st: option) 15 | | D1(s: option) 16 | | D2(f: typ) 17 | | D3(g: typ) 18 | // incorrect output 19 | | D4(t: option>) 20 | // incorrect output 21 | | D5(h: typ>) 22 | 23 | // doesn't map correctly 24 | datatype mapBothDifferent = D1(m: map>) 25 | | D2(s: string) 26 | 27 | // doesn't map correctly 28 | datatype mapBothDifferentIn = D1(m: map>) 29 | | D2(s: string) 30 | 31 | datatype mapBuiltIn = E1(m: map, set>) 32 | | E2 (r: seq, s: set) 33 | } 34 | -------------------------------------------------------------------------------- /examples/signed_int/new.dfy: -------------------------------------------------------------------------------- 1 | // TODO Interesting example 2 | // TODO The invariant here is supposedly that Pos/Neg(n) where n > 0 3 | // TODO But this is not enforced by the code 4 | // TODO So the code ends up being BC when it "should" not be 5 | 6 | module SignedInt { 7 | 8 | datatype signed_int = Pos(nat) | Neg(nat) | Zero 9 | 10 | function int_to_signed_int (i: int): signed_int 11 | { 12 | if i > 0 then 13 | Pos(i as nat) 14 | else if i < 0 then 15 | Neg((-1 * i) as nat) 16 | else 17 | Zero 18 | } 19 | 20 | function signed_int_add (i: signed_int, j: signed_int): signed_int 21 | { 22 | match (i, j) { 23 | case (Pos(i), Pos(j)) => Pos (i + j) 24 | case (Pos(i), Neg(j)) => int_to_signed_int(i - j) 25 | case (Pos(i), Zero) => Pos(i) 26 | case (Neg(i), Pos(j)) => int_to_signed_int(j - i) 27 | case (Neg(i), Neg(j)) => int_to_signed_int(0 - i - j) 28 | case (Neg(i), Zero) => Neg(i) 29 | case (Zero, Zero) => Zero 30 | case (Zero, Pos(j)) => Pos(j) 31 | case (Zero, Neg(j)) => Neg(j) 32 | } 33 | } 34 | 35 | function add_signed_int (i: int, j: int): signed_int 36 | { 37 | signed_int_add (int_to_signed_int(i), int_to_signed_int(j)) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /scripts/result_to_table.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from natsort import natsorted 3 | 4 | 5 | def main(): 6 | total_lemmas = defaultdict(lambda: defaultdict(int)) 7 | verified = defaultdict(lambda: defaultdict(int)) 8 | with open('result.csv') as f: 9 | for line in f.readlines(): 10 | parts = line.split(',') 11 | if parts[-1].strip().endswith('error'): 12 | continue 13 | if len(parts) != 9: 14 | continue 15 | current_verified = parts[6].split('/') 16 | if len(current_verified) != 2 or not current_verified[0].strip().isnumeric(): 17 | continue 18 | total_lemmas[parts[0].strip()][parts[1].strip()] = int(current_verified[1].strip()) 19 | verified[parts[0].strip()][parts[1].strip()] = int(current_verified[0].strip()) 20 | for i in natsorted(verified.keys()): 21 | if total_lemmas[i]['-a 1 -p false -b true'] == 0 and ('/' not in i or int(i.split('/')[0]) not in [44, 111]): 22 | continue 23 | print(f"{i[:7]} & {total_lemmas[i]['-a 1 -p false -b true']} & {total_lemmas[i]['']} & {verified[i]['-p false -b true']} & {verified[i]['']} \\\\") 24 | 25 | if __name__ == '__main__': 26 | main() 27 | -------------------------------------------------------------------------------- /TypeInjections/Tool/Tool.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | 7 | true 8 | typecart 9 | ./nupkg 10 | typecart 11 | 1.0.0 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /TypeInjections/Tool.Test/TestUtils.fs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT 3 | 4 | namespace Tool.Test 5 | 6 | open System.IO 7 | open NUnit.Framework 8 | 9 | module TestUtils = 10 | 11 | let wd = System.Environment.CurrentDirectory 12 | 13 | let pd = 14 | 15 | Directory.GetParent(wd).Parent.Parent.FullName 16 | 17 | let resourcePath = Path.Combine([| pd; "Resources" |]) 18 | 19 | // compare text in files between output and expected 20 | let cmpFiles (path1: string) (path2: string) = 21 | // get all files from output and expected 22 | let expected = 23 | DirectoryInfo(path1) 24 | .GetFiles("*.dfy", SearchOption.AllDirectories) 25 | |> Seq.toList 26 | 27 | let output = 28 | DirectoryInfo(path2) 29 | .GetFiles("*.dfy", SearchOption.AllDirectories) 30 | |> Seq.toList 31 | // get text out of cloned repo and expected repo 32 | let expectedText = 33 | List.map (fun (x: FileInfo) -> x.OpenText().ReadToEnd()) expected 34 | 35 | let outputText = 36 | List.map (fun (x: FileInfo) -> x.OpenText().ReadToEnd()) output 37 | 38 | List.iter2 (fun (x: string) (y: string) -> Assert.AreEqual(x, y)) expectedText outputText 39 | -------------------------------------------------------------------------------- /papers/first-paper/main.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | 3 | \usepackage{../lib/dafny} 4 | \usepackage[T1]{fontenc} 5 | \usepackage{graphicx} % If possible, figure files should be included in EPS format. 6 | \usepackage{listings} 7 | \usepackage{xspace} 8 | \usepackage[show]{ed} 9 | 10 | \usepackage[colorlinks,urlcolor=blue,citecolor=blue,linkcolor=blue,bookmarks]{hyperref} 11 | 12 | \usepackage{local} 13 | \usepackage{../lib/florrabe} 14 | 15 | \begin{document} 16 | 17 | \author{TBD} 18 | \title{Proof Evolution for Dafny} 19 | 20 | \maketitle 21 | 22 | \begin{abstract} 23 | We introduce a system for formally relating two versions of the same formally verified software, e.g., to prove backwards-compatibility. 24 | We exemplify our ideas on a fragment of the Dafny language for which we introduce a proof evolution tool. 25 | \end{abstract} 26 | 27 | \section{Introduction and Related Work}\label{sec:intro} 28 | \input{introduction} 29 | 30 | \section{The Dafny Language}\label{sec:dafny} 31 | \input{dafny} 32 | 33 | \section{Relating Two Versions}\label{sec:pe} 34 | \input{relating} 35 | 36 | \section{Case Study}\label{sec:example} 37 | \input{example} 38 | 39 | \section{Conclusion and Future Work}\label{sec:conclusion} 40 | \input{conclusion} 41 | 42 | \bibliographystyle{alpha} 43 | \bibliography{../lib/references} 44 | 45 | %\appendix 46 | %\section{Meta-Information} 47 | %\input{meta} 48 | 49 | \end{document} 50 | 51 | -------------------------------------------------------------------------------- /TypeInjections/Tool/README.md: -------------------------------------------------------------------------------- 1 | ## typeCart features 2 | typeCart CLI offers two ways to compare projects: 3 | - projects on local computer: 4 | `$ typecart local [--old ] [--new ] [--print ] [--file ]` 5 | * Required Options 6 | * `-o` `--old` Give the absolute path of old project 7 | * `-n` `--new` Give the absolute path of new project 8 | * `-p` `--print` Tell typeCart which folder to print out generated files 9 | * Additional Options 10 | * (coming soon) `-e` `--entrypoint` Specify certain file(s) to run typeCart on 11 | - git commits in repository: `$ typecart git [--old ] [--new ] [--print ] [--clone ] [--file ]` 12 | * Required options 13 | * `-o` `--old` Give the absolute path of old project 14 | * `-n` `--new` Give the absolute path of new project 15 | * `-p` `--print` Tell typeCart which folder to print out generated files 16 | * Additional Options 17 | * `-c` `--clone` Provide git URL to checkout commits 18 | * `-e` `--entrypoint` Specify certain file(s) to run typeCart on 19 | 20 | External Libraries used in Tool project 21 | * [CommandLineParser.fsharp](https://github.com/commandlineparser/commandline) Popular .NET command line parsing library 22 | * [Lib2GitSharp](https://github.com/libgit2/libgit2sharp/wiki) and [Lib2Git Manual](https://libgit2.org/docs/guides/101-samples/) Easy to use .NET wrapper for git commands 23 | 24 | 25 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/RelateBuiltinTypes.dfy: -------------------------------------------------------------------------------- 1 | module Translations.RelateBuiltinTypes { 2 | 3 | function Seq(t: (o,n) -> bool, e: seq, f: seq) : (ret : bool) 4 | ensures (|e| == |f| && (forall i : int :: ((0 <= i < |f| && 0 <= i < |e|) ==> t(e[i],f[i])))) 5 | ==> ret == true 6 | { 7 | |e| == |f| && !(exists i : int :: 0 <= i < |e| && !t(e[i],f[i])) 8 | } 9 | 10 | function Set(t: (o,n) -> bool, e: set, f: set) : (ret : bool) 11 | { 12 | ((forall i : o :: i in e ==> exists j : n :: (j in f && t(i,j))) 13 | && (forall j : n :: j in f ==> exists i : o :: (i in e && t(i,j)))) 14 | } 15 | 16 | function Map(K : (K_O,K_N) -> bool, V : (V_O,V_N) -> bool, e : map, f: map) : (ret : bool) 17 | { 18 | (forall i : K_O :: i in e.Keys ==> (exists j : K_N :: j in f.Keys && K(i,j) && V(e[i],f[j]))) 19 | && (forall i : K_O :: i in e.Keys ==> exists j : K_N :: j in f.Keys && K(i,j) && V(e[i],f[j])) 20 | } 21 | 22 | /* 23 | function Array(t: (o,n) -> bool, e: array, f: array) : (ret : bool) 24 | reads e, f 25 | ensures (e.Length == f.Length && (forall i : int :: ((0 <= i < f.Length && 0 <= i < e.Length) ==> t(e[i],f[i])))) 26 | ==> ret == true 27 | { 28 | e.Length == f.Length && !(exists i : int :: 0 <= i < e.Length && !t(e[i],f[i])) 29 | } 30 | */ 31 | } 32 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections/TypeInjections.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net6.0 5 | TypeInjections 6 | 7 | 8 | 9 | 10 | 11 | 12 | Always 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /papers/lib/references.bib: -------------------------------------------------------------------------------- 1 | @inproceedings{dafny, 2 | title={{Dafny: An Automatic Program Verifier for Functional Correctness}}, 3 | author={Leino, K Rustan M}, 4 | booktitle={International Conference on Logic for Programming Artificial Intelligence and Reasoning}, 5 | pages={348--370}, 6 | year={2010}, 7 | organization={Springer} 8 | } 9 | 10 | @inproceedings{z3, 11 | title={{Z3: An efficient SMT solver}}, 12 | author={de Moura, Leonardo and Bj{\o}rner, Nikolaj}, 13 | booktitle={International conference on Tools and Algorithms for the Construction and Analysis of Systems}, 14 | pages={337--340}, 15 | year={2008}, 16 | organization={Springer} 17 | } 18 | 19 | @misc{spotbugs, 20 | key={SpotBugs}, 21 | title={SpotBugs Detectors}, 22 | year={2021}, 23 | url={https://spotbugs.readthedocs.io/en/latest/detectors.html}, 24 | } 25 | 26 | @misc{checkstyle, 27 | key={Checkstyle}, 28 | title={Checkstyle Checks}, 29 | year={2021}, 30 | url={https://checkstyle.sourceforge.io/checks.html}, 31 | } -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/TypeInjections.Test.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | false 7 | false 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /examples/context_with_variables/old.dfy: -------------------------------------------------------------------------------- 1 | // a simple term language with strings and propositions that is evaluated against a context holding variable definitions 2 | 3 | module CommonTypes { 4 | datatype Context = | Context(vars: seq<(string, string)>) 5 | 6 | datatype Term = | Var(n: string) | Concat(l: Term, r: Term) 7 | datatype Prop = | And(l: Prop, r: Prop) | Not(p: Prop) | Equal(tl: Term, tr: Term) 8 | 9 | datatype Option<+U> = | None() | Some(val: U) 10 | } 11 | 12 | module ContextEval { 13 | import opened CommonTypes 14 | function Eval(ctx: Context, condition: Prop) : bool { 15 | match condition { 16 | case And(l, r) => Eval(ctx, l) && Eval(ctx, r) 17 | case Not(p) => !Eval(ctx, p) 18 | case Equal(tl, tr) => EvalTerm(ctx, tl) == EvalTerm(ctx, tr) 19 | } 20 | } 21 | 22 | function EvalTerm(ctx: Context, t: Term): Option { 23 | match t { 24 | case Var(n) => Lookup(ctx, n) 25 | case Concat(l, r) => 26 | var lval := EvalTerm(ctx, l); 27 | var rval := EvalTerm(ctx, r); 28 | if lval.None? || rval.None? then 29 | None() 30 | else 31 | Some(lval.val + rval.val) 32 | } 33 | } 34 | 35 | function Lookup(ctx: Context, k: string): Option { 36 | Find(ctx.vars, k) 37 | } 38 | 39 | function Find(vars: seq<(string, string)>, k: string): Option { 40 | if vars == [] then 41 | None() 42 | else 43 | if vars[0].0 == k then Some(vars[0].1) else Find(vars[1..], k) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/policy_evaluator/Old.dfy: -------------------------------------------------------------------------------- 1 | module Types { 2 | import CommonTypes 3 | 4 | type Principal = string 5 | 6 | datatype Effect = ALLOW | DENY 7 | 8 | datatype StatementBlock = StatementBlock(pid: CommonTypes.Option, effect: Effect, action: string, resource: string) 9 | 10 | datatype PolicyBlock = PolicyBlock(id: CommonTypes.Option, statement: StatementBlock) 11 | 12 | datatype Policy = Policy(issuer : Principal, policyBlock : PolicyBlock) 13 | 14 | datatype Request = Request(principal : Principal, action: string, resource: string) 15 | 16 | datatype Answer = ALLOW | DENY 17 | } 18 | 19 | 20 | module CommonTypes { 21 | datatype Option<+U> = None() | Some(val: U) 22 | } 23 | 24 | module Spec { 25 | import Types 26 | 27 | function EvalStatementAndRequest(statement : Types.StatementBlock, request : Types.Request) : Types.Answer { 28 | if statement.pid.Some? && statement.pid.val == request.principal 29 | && statement.action == request.action 30 | && statement.resource == request.resource 31 | && statement.effect == Types.Effect.ALLOW then Types.Answer.ALLOW 32 | else Types.Answer.DENY 33 | } 34 | 35 | function Eval(policies : seq, request : Types.Request) : Types.Answer 36 | { 37 | if |policies| == 0 then Types.Answer.DENY 38 | else if |policies| == 1 then 39 | EvalStatementAndRequest(policies[0].policyBlock.statement, request) 40 | else 41 | if EvalStatementAndRequest(policies[0].policyBlock.statement, request).DENY? then Types.Answer.DENY 42 | else Eval(policies[1..], request) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/policy_evaluator/New.dfy: -------------------------------------------------------------------------------- 1 | module Types { 2 | import CommonTypes 3 | 4 | type Principal = string 5 | 6 | datatype Effect = ALLOW | DENY 7 | 8 | datatype StatementBlock = StatementBlock(pid: CommonTypes.Option, effect: Effect, action: string, resource: string) 9 | 10 | datatype PolicyBlock = PolicyBlock(id: CommonTypes.Option, statement: StatementBlock) 11 | 12 | datatype Policy = Policy(issuer : Principal, policyBlock : PolicyBlock) 13 | 14 | datatype Request = Request(principal : Principal, action: string, resource: string, filler: string) 15 | 16 | datatype Answer = ALLOW | DENY 17 | } 18 | 19 | 20 | module CommonTypes { 21 | datatype Option<+U> = None() | Some(val: U) 22 | } 23 | 24 | module Spec { 25 | import Types 26 | 27 | function EvalStatementAndRequest(statement : Types.StatementBlock, request : Types.Request) : Types.Answer { 28 | if statement.pid.Some? && statement.pid.val == request.principal 29 | && statement.action == request.action 30 | && statement.resource == request.resource 31 | && statement.effect == Types.Effect.ALLOW then Types.Answer.ALLOW 32 | else Types.Answer.DENY 33 | } 34 | 35 | function Eval(policies : seq, request : Types.Request) : Types.Answer 36 | { 37 | if |policies| == 0 then Types.Answer.DENY 38 | else if |policies| == 1 then 39 | EvalStatementAndRequest(policies[0].policyBlock.statement, request) 40 | else 41 | if EvalStatementAndRequest(policies[0].policyBlock.statement, request).DENY? then Types.Answer.DENY 42 | else Eval(policies[1..], request) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /docs/Troubleshooting.md: -------------------------------------------------------------------------------- 1 | ## Troubleshooting 2 | 3 | Here are some common verification errors by Dafny and corresponding possible solutions. 4 | 5 | #### Error: assertion might not hold 6 | 7 | Dafny often cannot verify the proof sketch automatically. Example (`complex`): 8 | 9 | ```dafny 10 | lemma mult_bc(x_O: Old.Complex.complex, y_O: Old.Complex.complex, x_N: New.Complex.complex, y_N: New.Complex.complex) 11 | requires x_N == complex_forward(x_O) 12 | requires y_N == complex_forward(y_O) 13 | ensures New.Complex.mult(x_N, y_N) == complex_forward(Old.Complex.mult(x_O, y_O)) 14 | { 15 | assert New.Complex.mult(x_N, y_N) == complex_forward((x_O.0 * y_O.0 - x_O.1 * y_O.1, x_O.0 * y_O.1 + x_O.1 * y_O.0)); 16 | } 17 | ``` 18 | 19 | In this specific case, an equivalent calc statement works: 20 | 21 | ```dafny 22 | lemma mult_bc(x_O: Old.Complex.complex, y_O: Old.Complex.complex, x_N: New.Complex.complex, y_N: New.Complex.complex) 23 | requires x_N == complex_forward(x_O) 24 | requires y_N == complex_forward(y_O) 25 | ensures New.Complex.mult(x_N, y_N) == complex_forward(Old.Complex.mult(x_O, y_O)) 26 | { 27 | calc { 28 | New.Complex.mult(x_N, y_N); 29 | == 30 | complex_forward((x_O.0 * y_O.0 - x_O.1 * y_O.1, x_O.0 * y_O.1 + x_O.1 * y_O.0)); 31 | } 32 | } 33 | ``` 34 | 35 | #### Error: cannot prove termination; try supplying a decreases clause 36 | 37 | See "parent trick" at https://leino.science/papers/krml283.html. 38 | 39 | #### Error: a precondition for this call could not be proved 40 | 41 | For backward compatibility lemma calls generated in proof sketches, we can try commenting out the lemma call first to see 42 | if Dafny can figure it out on its own. When Dafny cannot figure it out, it is recommended for the user to think how 43 | to prove the precondition. 44 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/GenFunctionTests.fs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT 3 | 4 | namespace TypeInjections.Test 5 | 6 | module GenFunctionTests = 7 | open NUnit.Framework 8 | 9 | type internal Marker = 10 | interface 11 | end 12 | 13 | let testRunner = 14 | TestUtils.testRunnerGen 15 | 16 | [] 17 | let SimpleDatatypeTest () = 18 | testRunner "InputDirectory" "OutputDirectory" 19 | 20 | [] 21 | let RecursiveDatatypeTest () = 22 | testRunner "InputDirectory" "OutputDirectory" 23 | 24 | [] 25 | let DatatypeRefTest () = 26 | testRunner "InputDirectory" "OutputDirectory" 27 | 28 | [] 29 | let GenericDatatypeTest () = 30 | testRunner "InputDirectory" "OutputDirectory" 31 | 32 | [] 33 | let ResultTest () = 34 | testRunner "InputDirectory" "OutputDirectory" 35 | 36 | [] 37 | let SeqTest () = 38 | testRunner "InputDirectory" "OutputDirectory" 39 | 40 | [] 41 | let SetTest () = 42 | testRunner "InputDirectory" "OutputDirectory" 43 | 44 | [] 45 | let MapTest () = 46 | testRunner "InputDirectory" "OutputDirectory" 47 | 48 | [] 49 | let CollectionsTest () = 50 | testRunner "InputDirectory" "OutputDirectory" 51 | 52 | [] 53 | let NewtypeTest () = 54 | testRunner "InputDirectory" "OutputDirectory" 55 | 56 | [] 57 | let SubsetSynTest () = 58 | testRunner "InputDirectory" "OutputDirectory" 59 | 60 | [] 61 | let LimitTest () = 62 | testRunner "InputDirectory" "OutputDirectory" 63 | 64 | 65 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections/Resources/MapBuiltinTypes.dfy: -------------------------------------------------------------------------------- 1 | module Translations.MapBuiltinTypes { 2 | function Seq(t: o -> n, e: seq) : (f : seq) 3 | ensures |e| == |f| 4 | ensures forall i : int :: (0 <= i < |e| ==> f[i] == t(e[i])) 5 | { 6 | if e == [] then [] else [t(e[0])] + Seq(t, e[1..]) 7 | } 8 | function Set(t: o -> n, e: set) : (f : set) 9 | ensures forall x : o :: x in e ==> t(x) in f 10 | ensures forall x : n :: x in f ==> exists y : o :: y in e && t(y) == x 11 | { 12 | set x:o | x in e :: t(x) 13 | } 14 | /* We need a translation function from K_N to K_O in order to translate from map to map. 15 | * For example, if we want to translate sqr(x) = x * x from map to map 16 | * where K(x) = round(x), we need K2(x) = x * 1.0 so that sqr(1.4) = 1.96 is translated to 17 | * sqr(round(1.4)) = (round(1.4) * 1.0) * (round(1.4) * 1.0), i.e., sqr(1) = 1, 18 | * instead of sqr(round(1.4)) = round(1.4 * 1.4), i.e., sqr(1) = 2. 19 | */ 20 | function Map(K: K_O -> K_N, K2: K_N -> K_O, V: V_O -> V_N, e: map) : (f: map) 21 | { 22 | var fKeys := set x: K_O | x in e.Keys && K2(K(x)) in e.Keys :: K(x); 23 | map a | a in fKeys :: V(e[K2(a)]) 24 | } 25 | } 26 | 27 | module Translations.Utils { 28 | /* We requires false here because Dafny 4 does not allow non-determinism like ":|" in functions. 29 | * This function is to convert name resolution errors in stubs generated in translation functions 30 | * to verification errors (cannot prove false), so Dafny will not stop verifying the entire program 31 | * when it sees "???". 32 | */ 33 | function ???(): X 34 | requires false 35 | { 36 | var tmp: X :| true; 37 | tmp 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/arithmetic_return_type/new.dfy: -------------------------------------------------------------------------------- 1 | module Arith { 2 | type Ident = string 3 | 4 | datatype Aexp = 5 | | CONST(n: int) 6 | | VAR(x: Ident) 7 | | PLUS(a1: Aexp, a2: Aexp) 8 | | MINUS(a1: Aexp, a2: Aexp) 9 | | MUL(a1: Aexp, a2: Aexp) 10 | // new constructor 11 | | DIV(a1: Aexp, a2: Aexp) 12 | 13 | predicate IdInAexp(id: Ident, a: Aexp) { 14 | match a 15 | case CONST(n) => false 16 | case VAR(id') => id == id' 17 | case PLUS(a1, a2) => IdInAexp(id,a1) || IdInAexp(id,a2) 18 | case MINUS(a1, a2) => IdInAexp(id,a1) || IdInAexp(id,a2) 19 | case MUL(a1, a2) => IdInAexp(id,a1) || IdInAexp(id, a2) 20 | // new case 21 | case DIV(a1, a2) => IdInAexp(id,a1) || IdInAexp(id, a2) 22 | } 23 | 24 | type Store = map 25 | 26 | // add option type 27 | datatype Option = None | Some(n: T) 28 | 29 | // change return type to option (not supported for now), add div case 30 | function Aeval(s: Store, a: Aexp): Option 31 | requires forall id: Ident :: IdInAexp(id,a) ==> id in s 32 | { 33 | match a 34 | case CONST(n) => Some(n) 35 | case VAR(id) => Some(s[id]) 36 | case PLUS(a1, a2) => 37 | match (Aeval(s,a1), Aeval(s,a2)) { 38 | case (Some(n1), Some(n2)) => Some(n1 + n2) 39 | case _ => None 40 | } 41 | case MINUS(a1, a2) => 42 | match (Aeval(s,a1), Aeval(s,a2)) { 43 | case (Some(n1), Some(n2)) => Some(n1 - n2) 44 | case _ => None 45 | } 46 | case MUL(a1, a2) => 47 | match (Aeval(s,a1), Aeval(s,a2)) { 48 | case (Some(n1), Some(n2)) => Some(n1 * n2) 49 | case _ => None 50 | } 51 | case DIV(a1, a2) => 52 | match (Aeval(s,a1), Aeval(s,a2)) { 53 | case (Some(n1), Some(n2)) => if n2 == 0 then None else Some(n1 / n2) 54 | case _ => None 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /TypeInjections/Tool/Make_Tool.md: -------------------------------------------------------------------------------- 1 | ## How to make dotnet tool 2 | 1. open `.fsproj` for the project you want to make a .NET tool for and include the following: 3 | ```xml 4 | 5 | Exe 6 | net6.0 7 | typeCart 8 | 9 | true 10 | typecart 11 | ./nupkg 12 | 13 | typecart 14 | 1.0.0 15 | [ADD_NAMES] 16 | [ADD_COMPANY] 17 | 18 | 19 | ``` 20 | 2. Open the terminal in the directory with `.fsproj` file 21 | 3. Build the project and create NuGet package 22 | ```zsh 23 | $ dotnet pack 24 | ``` 25 | 4. Deploy the tool on the local system using install global tool command 26 | ```zsh 27 | $ dotnet tool install --global --add-source ./nupkg typecart 28 | ``` 29 | 5. (optional) Publish tool to internet (NuGet library) 30 | - Create account on [NuGet.org](https://www.nuget.org/) 31 | - Click on username in top right corner and go to [API Keys](https://www.nuget.org/account/apikeys) 32 | - Create API key 33 | * Give KeyName 34 | * Set globe pattern to `*` 35 | * Create API 36 | * Copy API Key 37 | - (While still in directory with `.fsproj`) publish nupkg file 38 | ```zsh 39 | $ dotnet nuget push typecart.[VERSION_HERE].nupkg --api-key [API_KEY_HERE] --source https://api.nuget.org/v3/index.json 40 | ``` 41 | ## How users can download tool to use 42 | 1. If user has .NET SDK installed: go to [typeCart's Nuget Package](https://www.nuget.org/packages/typecart) page and run terminal command 43 | 2. [Coming soon] We will soon publish a typeCart CLI that can run independently of .NET SDK, not sure where we will host this package -------------------------------------------------------------------------------- /examples/request_eval/old.dfy: -------------------------------------------------------------------------------- 1 | // a simple language of statements that are evaluated against contexts 2 | 3 | module CommonTypes { 4 | datatype Option<+U> = | None() | Some(val: U) 5 | } 6 | 7 | module Types { 8 | // contexts are lists of key-value pairs 9 | datatype Context = | Context(entries: seq) 10 | datatype Entry = | Entry(key: string, value: string) 11 | 12 | // a statement is a conjunction of atoms 13 | datatype Statement = | Statement(atoms: seq) 14 | // an atom consists of an operator that is applied to (the context value corresponding to) a key and a list values 15 | datatype Atom = | Atom(operation: Operation, key: string, values: seq) 16 | // OneOf tests if the first context value with that key exists and is among the provided list of values 17 | // Exists tests if any context entry with that key exists 18 | datatype Operation = | OneOf | Exists 19 | } 20 | 21 | module Evaluation { 22 | import opened CommonTypes 23 | import opened Types 24 | 25 | function EvalStatement(ctx: Context, s: Statement): bool { 26 | forall a:: a in s.atoms ==> EvalAtom(ctx, a) 27 | } 28 | 29 | function EvalAtom(ctx: Context, a: Atom): bool { 30 | match a.operation { 31 | case OneOf => EvalOneOf(ctx, a) 32 | case Exists => EvalExists(ctx, a.key) 33 | } 34 | } 35 | 36 | function EvalOneOf(ctx: Context, a: Atom): bool { 37 | match Lookup(ctx.entries, a.key) { 38 | case None() => false 39 | case Some(v) => v in a.values 40 | } 41 | } 42 | 43 | function EvalExists(ctx: Context, k: string): bool { 44 | exists e:: e in ctx.entries && e.key == k 45 | } 46 | 47 | function Lookup(entries: seq, k: string): Option { 48 | if entries == [] then None() 49 | else 50 | var e := entries[0]; 51 | if e.key == k then Some(e.value) 52 | else Lookup(entries[1..], k) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /examples/context_with_variables/new.dfy: -------------------------------------------------------------------------------- 1 | // a simple term language with strings and propositions that is evaluated against a context holding variable definitions 2 | 3 | module CommonTypes { 4 | datatype Context = | Context(vars: seq<(string, string)>) 5 | 6 | datatype Term = | Var(n: string) | Concat(l: Term, r: Term) 7 | datatype Prop = | And(l: Prop, r: Prop) | Not(p: Prop) | Equal(tl: Term, tr: Term) 8 | 9 | datatype Option<+U> = | None() | Some(val: U) 10 | } 11 | 12 | module ContextEval { 13 | import opened CommonTypes 14 | function Eval(ctx: Context, condition: Prop) : bool { 15 | match condition { 16 | case And(l, r) => Eval(ctx, l) && Eval(ctx, r) 17 | case Not(p) => !Eval(ctx, p) 18 | case Equal(tl, tr) => EvalTerm(ctx, tl) == EvalTerm(ctx, tr) 19 | } 20 | } 21 | 22 | function EvalTerm(ctx: Context, t: Term): Option { 23 | match t { 24 | case Var(n) => Lookup(ctx, n) 25 | case Concat(l, r) => 26 | var lval := EvalTerm(ctx, l); 27 | var rval := EvalTerm(ctx, r); 28 | if lval.None? || rval.None? then 29 | None() 30 | else 31 | Some(lval.val + rval.val) 32 | } 33 | } 34 | 35 | function Lookup(ctx: Context, k: string): Option { 36 | Find(ctx.vars, k) 37 | } 38 | 39 | function RemoveSpace(v: string): string { 40 | if v == "" then 41 | "" 42 | else if v[0] == ' ' then 43 | RemoveSpace(v[1..]) 44 | else 45 | [v[0]] + RemoveSpace(v[1..]) 46 | } 47 | 48 | function Find(vars: seq<(string, string)>, k: string): Option { 49 | if vars == [] then 50 | None() 51 | else if vars[0].0 == k then 52 | if ' ' !in vars[0].1 then 53 | Some(vars[0].1) 54 | else 55 | Some(RemoveSpace(vars[0].1)) 56 | else 57 | Find(vars[1..], k) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /papers/lib/dafny.sty: -------------------------------------------------------------------------------- 1 | % dafny.sty 2 | % Dafny mode for the LaTeX listings package. 3 | % Rustan Leino, 22 June 2008. 4 | 5 | \usepackage{listings} 6 | 7 | \lstdefinelanguage{dafny}{ 8 | morekeywords={class,datatype,codatatype,newtype,type,iterator,trait,extends, 9 | bool,char,nat,int,real,object,set,iset,multiset,seq,string,map,imap,array,array2,array3, 10 | bv0,bv1,bv2,bv3,bv4,bv5,bv6,bv7,bv8,bv12,bv16,bv20,bv24,bv32,bv48,bv64,bv128, 11 | function,predicate,copredicate,inductive, 12 | ghost,var,static,protected,refines, 13 | method,lemma,constructor,colemma,twostate, 14 | returns,yields,abstract,module,import,default,opened,as,in, 15 | requires,modifies,ensures,reads,decreases,include,provides,reveals,witness, 16 | % expressions 17 | match,case,false,true,null,old,fresh,allocated,unchanged,this, 18 | % statements 19 | assert,by,assume,print,new,if,then,else,while,invariant,break,label,return,yield, 20 | where,calc,modify 21 | }, 22 | literate=% 23 | , 24 | sensitive=true, % case sensitive 25 | morecomment=[l]{//}, 26 | morecomment=[s]{/*}{*/}, 27 | morecomment=[s]{\{:}{\}}, 28 | morestring=[b]", 29 | numbers=none, 30 | firstnumber=0, 31 | numberstyle=\tiny, 32 | stepnumber=5, 33 | basicstyle=\scriptsize\sffamily, 34 | commentstyle=\itshape, 35 | keywordstyle=\bfseries, 36 | ndkeywordstyle=\bfseries, 37 | } 38 | \lstnewenvironment{dafny}[1][]{% 39 | \lstset{language=dafny, 40 | floatplacement={tbp},captionpos=b, 41 | frame=lines,xleftmargin=8pt,xrightmargin=8pt,basicstyle=\small\ttfamily,#1}}{} 42 | \lstnewenvironment{dafnyNoLines}[1][]{% 43 | \lstset{language=dafny, 44 | floatplacement={tbp},captionpos=b, 45 | xleftmargin=8pt,xrightmargin=8pt,basicstyle=\ttfamily,#1}}{} 46 | \def\inlinedafny{% 47 | \lstinline[language=dafny,basicstyle=\small\ttfamily,columns=fixed]} 48 | \newcommand{\lstfile}[1]{ 49 | \lstset{escapechar=`} 50 | \lstinputlisting[language=dafny,% 51 | frame=lines,xleftmargin=8pt,xrightmargin=8pt,basicstyle=\scriptsize\ttfamily,columns=fixed]{#1} 52 | } -------------------------------------------------------------------------------- /papers/lib/florrabe.sty: -------------------------------------------------------------------------------- 1 | % ifnonempty{a}{b} = if (a == empty) empty else b 2 | \newcommand{\ifnonempty}[3][]{\def\@empty{}\def\@test{#2}\ifx\@test\@empty#1\else#3\fi} 3 | % fold{a}{b1,...,bn} = b1 a ... a bn 4 | \newcommand{\fold}[2]{\def\@tmpop{\relax}\@for\@I:=#2\do{\@tmpop\@I\def\@tmpop{#1}}} 5 | 6 | % symbols 7 | \newcommand{\nin}{\not\in} 8 | \newcommand{\sm}{\setminus} 9 | \newcommand{\es}{\varnothing} 10 | \newcommand{\pwr}{\mathcal{P}} 11 | \newcommand{\rewrites}{\rightsquigarrow} 12 | 13 | % style shortcuts 14 | \newcommand{\ov}[1]{\overline{#1}} 15 | \newcommand{\und}[1]{\underline{#1}} 16 | 17 | 18 | % CFGs 19 | \newcommand{\tb}{\hspace*{.5cm}} 20 | 21 | \newenvironment{grammar}{\[\begin{array}{l@{\tb\bbc\tb}l@{\tb}l}}{\end{array}\]} 22 | \newcommand{\bnf}[1]{#1} 23 | \newcommand{\bbc}{\bnf{::=}} 24 | \newcommand{\bnfalt}{\ensuremath{\;\bnf{|}\;}} 25 | \providecommand{\alt}{\ensuremath{\;\bnf{|}\;}} % already used by beamer 26 | \newcommand{\opt}[1]{\bnf{[}#1\bnf{]}} 27 | \newcommand{\bopt}[1]{\opt{\bnfbracket{#1}}} 28 | \newcommand{\bnfbracket}[1]{\bnf{(}#1\bnf{)}} 29 | \newcommand{\rep}[1]{#1^{\bnf{\ast}}} 30 | \newcommand{\brep}[1]{\rep{\bnfbracket{#1}}} 31 | \newcommand{\bnfchoice}[1]{\bnf{[}#1\bnf{]}} 32 | \newcommand{\bnfnegchoice}[1]{\bnf{[\caret}#1\bnf{]}} 33 | 34 | \newenvironment{commgrammar}{\[\begin{array}{l@{\;}c@{\;}l@{\tb}l}}{\end{array}\]} 35 | \newcommand{\gcomment}[1]{\multicolumn{4}{l}{\rule{0pt}{4ex}\fbox{#1}\vspace{.3em}}} 36 | \newcommand{\gprod}[3]{#1 & \bbc & #2 & \text{#3}} 37 | \newcommand{\galtprod}[2]{ & \bnf{|} & #1 & \text{#2}} 38 | 39 | % defining names with consistent styles 40 | % \cn{int} for the name "int", in or outside math mode 41 | \providecommand{\cn}[1]{\ensuremath{\mathtt{#1}}\ifmmode\else\xspace\fi} 42 | % \makecn{int} to make \int a name 43 | \newcommand{\makecn}[1]{\@ifundefined{#1}{\makecnF{#1}}{\PackageError{basics}{#1 already defined}{use makecnF to force override}}} 44 | % like makecn but forcese overriding existing names 45 | \newcommand{\makecnF}[1]{\expandafter\gdef\csname#1\endcsname{\cn{#1}}} 46 | 47 | 48 | -------------------------------------------------------------------------------- /examples/context_with_variables/proofs.patch: -------------------------------------------------------------------------------- 1 | --- proofs.dfy 2024-07-01 15:10:48 2 | +++ proofs.dfy 2024-07-01 15:08:55 3 | @@ -68,6 +68,7 @@ 4 | 5 | lemma Eval_bc(ctx_O: Joint.CommonTypes.Context, condition_O: Joint.CommonTypes.Prop, ctx_N: Joint.CommonTypes.Context, condition_N: Joint.CommonTypes.Prop) 6 | decreases ctx_O, condition_O 7 | + requires NoSpaceInContextVars(ctx_O.vars) 8 | requires ctx_N == ctx_O 9 | requires condition_N == condition_O 10 | ensures New.ContextEval.Eval(ctx_N, condition_N) == Old.ContextEval.Eval(ctx_O, condition_O) 11 | \ No newline at end of file 12 | @@ -89,6 +90,7 @@ 13 | 14 | lemma EvalTerm_bc(ctx_O: Joint.CommonTypes.Context, t_O: Joint.CommonTypes.Term, ctx_N: Joint.CommonTypes.Context, t_N: Joint.CommonTypes.Term) 15 | decreases ctx_O, t_O 16 | + requires NoSpaceInContextVars(ctx_O.vars) 17 | requires ctx_N == ctx_O 18 | requires t_N == t_O 19 | ensures New.ContextEval.EvalTerm(ctx_N, t_N) == Old.ContextEval.EvalTerm(ctx_O, t_O) 20 | \ No newline at end of file 21 | @@ -112,6 +114,7 @@ 22 | 23 | lemma Lookup_bc(ctx_O: Joint.CommonTypes.Context, k_O: string, ctx_N: Joint.CommonTypes.Context, k_N: string) 24 | decreases ctx_O, k_O 25 | + requires NoSpaceInContextVars(ctx_O.vars) 26 | requires ctx_N == ctx_O 27 | requires k_N == k_O 28 | ensures New.ContextEval.Lookup(ctx_N, k_N) == Old.ContextEval.Lookup(ctx_O, k_O) 29 | \ No newline at end of file 30 | @@ -119,9 +122,14 @@ 31 | Find_bc(ctx_O.vars, k_O, ctx_N.vars, k_N); 32 | assert New.ContextEval.Lookup(ctx_N, k_N) == Old.ContextEval.Find(ctx_O.vars, k_O); 33 | } 34 | + 35 | + ghost predicate NoSpaceInContextVars(ctxVars: seq<(string,string)>) { 36 | + forall p :: p in ctxVars ==> ' ' !in p.1 37 | + } 38 | 39 | lemma Find_bc(vars_O: seq<(string,string)>, k_O: string, vars_N: seq<(string,string)>, k_N: string) 40 | decreases vars_O, k_O 41 | + requires NoSpaceInContextVars(vars_O) 42 | requires vars_N == vars_O 43 | requires k_N == k_O 44 | ensures New.ContextEval.Find(vars_N, k_N) == Old.ContextEval.Find(vars_O, k_O) 45 | \ No newline at end of file 46 | -------------------------------------------------------------------------------- /examples/request_eval/new.dfy: -------------------------------------------------------------------------------- 1 | // a simple language of statements that are evaluated against contexts 2 | 3 | module CommonTypes { 4 | datatype Option<+U> = | None() | Some(val: U) 5 | } 6 | 7 | module Types { 8 | // contexts are lists of key-value pairs 9 | datatype Context = | Context(entries: seq) 10 | datatype Entry = | Entry(key: string, value: string) 11 | 12 | // a statement is a conjunction of atoms 13 | datatype Statement = | Statement(atoms: seq) 14 | // an atom consists of an operator that is applied to (the context value corresponding to) a key and a list values 15 | datatype Atom = | Atom(operation: Operation, key: string, values: seq) 16 | // OneOf tests if the first context value with that key exists and is among the provided list of values 17 | // AllOf tests if all context values with that key are among the provided list of values 18 | // Exists tests if any context entry with that key exists 19 | datatype Operation = | OneOf | AllOf | Exists 20 | } 21 | 22 | module Evaluation { 23 | import opened CommonTypes 24 | import opened Types 25 | 26 | function EvalStatement(ctx: Context, s: Statement): bool { 27 | forall a:: a in s.atoms ==> EvalAtom(ctx, a) 28 | } 29 | 30 | function EvalAtom(ctx: Context, a: Atom): bool { 31 | match a.operation { 32 | case OneOf => EvalOneOf(ctx, a) 33 | case AllOf => EvalAllOf(ctx, a) 34 | case Exists => EvalExists(ctx, a.key) 35 | } 36 | } 37 | 38 | function EvalOneOf(ctx: Context, a: Atom): bool { 39 | match Lookup(ctx.entries, a.key) { 40 | case None() => false 41 | case Some(v) => v in a.values 42 | } 43 | } 44 | 45 | function EvalAllOf(ctx: Context, a: Atom): bool { 46 | forall v:: v in LookupAll(ctx.entries, a.key) ==> v in a.values 47 | } 48 | 49 | function EvalExists(ctx: Context, k: string): bool { 50 | exists e:: e in ctx.entries && e.key == k 51 | } 52 | 53 | function Lookup(entries: seq, k: string): Option { 54 | if entries == [] then None() 55 | else 56 | var e := entries[0]; 57 | if e.key == k then Some(e.value) 58 | else Lookup(entries[1..], k) 59 | } 60 | 61 | function LookupAll(entries: seq, k: string): seq { 62 | if entries == [] then [] 63 | else 64 | var e := entries[0]; 65 | if e.key == k then [e.value] + LookupAll(entries[1..], k) 66 | else LookupAll(entries[1..], k) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /docs/ExamplesFeatures.md: -------------------------------------------------------------------------------- 1 | ### Example: 2 | 3 | Given two identical modules `Old.A` and `New.A` that declare a simple datatype `foo` and a predicate `isBar`, typeCart generates a module `Combine` containing the mapping function `fooOldtoNew`. The function `fooOldtoNew`, as its name describes, converts a value of type `Old.A.foo` to the value of type `New.A.foo`. Please note that typeCart references the types by their top-level full Dafny names to avoid name clashes. 4 | 5 | 6 | 7 | We could then easily reason about two versions of predicate `isBar` as: 8 | 9 | 10 | 11 | 12 | ### Features 13 | 14 | typeCart generates functions for equivalent parametric datatypes, newtypes and subtypes of Dafny. It can also map over collection types such as sets, sequences and maps. 15 | 16 | **Example 1: mapping function for datatype** 17 | 18 | ```dafny 19 | datatype option = 20 | None 21 | | Some(x: A) 22 | 23 | function optionOldToNew(fA: A -> A', o: Old.M.option): New.M.option 24 | decreases o 25 | { 26 | match o 27 | case None => 28 | New.M.option.None 29 | case Some(x: A) => 30 | New.M.option.Some(fA(x)) 31 | } 32 | ``` 33 | **Example 2: mapping function for newtype:** 34 | 35 | ```dafny 36 | newtype int8 = x | -128 <= x < 128 37 | 38 | function int8OldToNew(i: Old.M.int8): (i': New.M.int8) 39 | ensures i as int == i' as int 40 | decreases i 41 | { 42 | i as int as New.M.int8 43 | } 44 | ``` 45 | **Example 3: mapping function for subtype:** 46 | ```dafny 47 | ghost const INT_MAX := 0x7fff_ffff 48 | 49 | type string32 = x: string | 0 <= |x| <= INT_MAX 50 | 51 | function string32OldToNew(s: Old.M.string32): New.M.string32 52 | requires 0 <= |s| <= New.M.INT_MAX 53 | decreases s 54 | { 55 | s 56 | } 57 | ``` 58 | 59 | **Example 4: mapping function for collection types:** 60 | ```dafny 61 | datatype collectionType = 62 | A(a: T) 63 | | B(b: seq) 64 | 65 | 66 | function seqMap(f: A -> B, s: seq): (r: seq) 67 | ensures |s| == |r| 68 | decreases s 69 | { 70 | if s == [] then 71 | [] 72 | else 73 | [f(s[0])] + seqMap(f, s[1..]) 74 | } 75 | 76 | function collectionTypeOldToNew(fT: T -> T', c: Old.M.collectionType): New.M.collectionType 77 | decreases c 78 | { 79 | match c 80 | case A(a: T) => 81 | New.M.collectionType.A(fT(a)) 82 | case B(b: seq) => 83 | New.M.collectionType.B(seqMap(fT, b)) 84 | } 85 | 86 | ``` 87 | 88 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TypeInjections", "TypeInjections\TypeInjections.fsproj", "{464C0F5E-9DD9-4A43-A5F7-DACA2C403BC1}" 4 | EndProject 5 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TypeInjections.Test", "TypeInjections.Test\TypeInjections.Test.fsproj", "{E5C31F7E-4EBB-48F1-BA22-AA1313E9A31E}" 6 | EndProject 7 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Tool", "Tool\Tool.fsproj", "{79ADC11B-2A5B-4CE6-9D3D-7F99050C79A0}" 8 | EndProject 9 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Tool.Test", "Tool.Test\Tool.Test.fsproj", "{8A4F91FD-2D0E-4505-9C08-0C78BD6C48B3}" 10 | EndProject 11 | Global 12 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 13 | Debug|Any CPU = Debug|Any CPU 14 | Release|Any CPU = Release|Any CPU 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {464C0F5E-9DD9-4A43-A5F7-DACA2C403BC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 18 | {464C0F5E-9DD9-4A43-A5F7-DACA2C403BC1}.Debug|Any CPU.Build.0 = Debug|Any CPU 19 | {464C0F5E-9DD9-4A43-A5F7-DACA2C403BC1}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {464C0F5E-9DD9-4A43-A5F7-DACA2C403BC1}.Release|Any CPU.Build.0 = Release|Any CPU 21 | {E5C31F7E-4EBB-48F1-BA22-AA1313E9A31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {E5C31F7E-4EBB-48F1-BA22-AA1313E9A31E}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {E5C31F7E-4EBB-48F1-BA22-AA1313E9A31E}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {E5C31F7E-4EBB-48F1-BA22-AA1313E9A31E}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {79ADC11B-2A5B-4CE6-9D3D-7F99050C79A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {79ADC11B-2A5B-4CE6-9D3D-7F99050C79A0}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {79ADC11B-2A5B-4CE6-9D3D-7F99050C79A0}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {79ADC11B-2A5B-4CE6-9D3D-7F99050C79A0}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {8A4F91FD-2D0E-4505-9C08-0C78BD6C48B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {8A4F91FD-2D0E-4505-9C08-0C78BD6C48B3}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {8A4F91FD-2D0E-4505-9C08-0C78BD6C48B3}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {8A4F91FD-2D0E-4505-9C08-0C78BD6C48B3}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {FE767FFA-CFC3-4AE6-A63F-BD4ED3298697}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {FE767FFA-CFC3-4AE6-A63F-BD4ED3298697}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {FE767FFA-CFC3-4AE6-A63F-BD4ED3298697}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {FE767FFA-CFC3-4AE6-A63F-BD4ED3298697}.Release|Any CPU.Build.0 = Release|Any CPU 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TypeCart 2 | ------------ 3 | 4 | [![.NET build](https://github.com/awslabs/typecart/actions/workflows/dotnet.yml/badge.svg?branch=dev)](https://github.com/awslabs/typecart/actions/workflows/dotnet.yml) 5 | 6 | **typeCart** is an analysis tool for proof evolution to facilitate proof maintenance for continuously integrated software. typeCart is constructed in F# and it 7 | 8 | 1. reads two Dafny files into Dafny AST, 9 | 2. analyses the ASTs to identify syntactically equivalent types between them, and 10 | 3. generates mapping functions between equivalent types. 11 | 12 | Additionally, typeCart generates the verification contracts (Dafny's `require` and `ensure` clauses) for the mapping functions, enabling the Dafny verifier to successfully verify the generated functions. Such functions helps proof engineers in estimating a quantitative measure about incremental changes between two version of the same specs written in Dafny. 13 | 14 | **Trivia:** `Cart` in `typeCart` represents cartography: typeCart generates mapping functions for equal types. 15 | 16 | See [examples and features](docs/ExamplesFeatures.md). 17 | 18 | ## Build 19 | 20 | typeCart builds on [.NET 6.0](https://dotnet.microsoft.com/en-us/download/dotnet/6.0). To build typeCart, simply invoke `dotnet build` in the following three project folders: 21 | 22 | - `TypeInjections/TypeInjections`: Main typeCart program. To use typeCart, run the compiled program on two dafny files, two directories containing a dafny project each, with an optional argument being a list of regex dictating what files typeCart should ignore in the directory. The regexes would match on the ignored filenames, and `.` and `/` needn't be escaped. 23 | - `TypeInjections/TypeInjections.Test`: Tests for typeCart 24 | - `TypeInjections/Tool`: dotnet CLI tool for typeCart. 25 | 26 | ## Run 27 | 28 | typeCart takes as input two Dafny files or folders, and generates the analysis results into a folder. 29 | Following is the command to run the `complex` example and output the result into the `tmp` folder: 30 | 31 | ```shell 32 | TypeInjections/TypeInjections/bin/Debug/net6.0/TypeInjections \ 33 | examples/complex/old.dfy \ 34 | examples/complex/new.dfy \ 35 | tmp 36 | ``` 37 | 38 | To run typeCart on the commit history of Cedar's Dafny specification: 39 | ```shell 40 | cd scripts 41 | bash run_cedar.sh 42 | ``` 43 | 44 | ## Troubleshooting 45 | 46 | typeCart may generate code that is not automatically verifiable by Dafny. See [Troubleshooting](docs/Troubleshooting.md). 47 | 48 | ## Contributing 49 | 50 | **Contributions are welcomed!** See [CONTRIBUTING](CONTRIBUTING.md) guidelines for more information. 51 | 52 | ## License 53 | 54 | typeCart is distributed under MIT license, see [LICENSE.txt](LICENSE.txt) for details. 55 | 56 | -------------------------------------------------------------------------------- /examples/command_exec/old.dfy: -------------------------------------------------------------------------------- 1 | module BigStep { 2 | type Ident = string 3 | 4 | datatype Aexp = 5 | | CONST(n: int) 6 | | VAR(x: Ident) 7 | | PLUS(a1: Aexp, a2: Aexp) 8 | | MINUS(a1: Aexp, a2: Aexp) 9 | 10 | predicate IdInAexp(id: Ident, a: Aexp) { 11 | match a 12 | case CONST(n) => false 13 | case VAR(id') => id == id' 14 | case PLUS(a1, a2) => IdInAexp(id,a1) || IdInAexp(id,a2) 15 | case MINUS(a1, a2) => IdInAexp(id,a1) || IdInAexp(id,a2) 16 | } 17 | 18 | type Store = map 19 | 20 | function Aeval(s: Store, a: Aexp): int 21 | requires forall id: Ident :: IdInAexp(id,a) ==> id in s 22 | { 23 | match a 24 | case CONST(n) => n 25 | case VAR(id) => s[id] 26 | case PLUS(a1, a2) => Aeval(s,a1) + Aeval(s,a2) 27 | case MINUS(a1, a2) => Aeval(s,a1) - Aeval(s,a2) 28 | } 29 | 30 | datatype Bexp = 31 | | TRUE 32 | | FALSE 33 | | EQUAL(a1: Aexp, a2: Aexp) 34 | | LESSEQUAL(a1: Aexp, a2: Aexp) 35 | | NOT(b1: Bexp) 36 | | AND(b1: Bexp, b2: Bexp) 37 | 38 | predicate IdInBexp(id: Ident, b: Bexp) { 39 | match b 40 | case TRUE => true 41 | case FALSE => false 42 | case EQUAL(a1,a2) => IdInAexp(id,a1) || IdInAexp(id,a2) 43 | case LESSEQUAL(a1,a2) => IdInAexp(id,a1) || IdInAexp(id,a2) 44 | case NOT(b) => IdInBexp(id,b) 45 | case AND(b1,b2) => IdInBexp(id,b1) || IdInBexp(id,b2) 46 | } 47 | 48 | function Beval(s: Store, b: Bexp): bool 49 | requires forall id: Ident :: IdInBexp(id,b) ==> id in s 50 | { 51 | match b 52 | case TRUE => true 53 | case FALSE => false 54 | case EQUAL(a1, a2) => Aeval(s,a1) == Aeval(s,a2) 55 | case LESSEQUAL(a1, a2) => Aeval(s,a1) <= Aeval(s,a2) 56 | case NOT(b) => ! Beval(s,b) 57 | case AND(b1,b2) => Beval(s,b1) && Beval(s,b2) 58 | } 59 | 60 | datatype Com = 61 | | SKIP 62 | | ASSIGN(x: Ident, a: Aexp) 63 | | SEQ(c1: Com, c2: Com) 64 | | IFTHENELSE(b: Bexp, c1: Com, c2: Com) 65 | | WHILE(b: Bexp, c1: Com) 66 | 67 | predicate IdInCom(id: Ident, c: Com) { 68 | match c 69 | case SKIP => false 70 | case ASSIGN(id',a) => IdInAexp(id,a) 71 | case SEQ(c1, c2) => IdInCom(id,c1) || IdInCom(id,c2) 72 | case IFTHENELSE(b,c1,c2) => IdInBexp(id,b) || IdInCom(id,c1) || IdInCom(id,c2) 73 | case WHILE(b,c) => IdInBexp(id,b) || IdInCom(id,c) 74 | } 75 | 76 | least predicate Cexec(s1: Store, c: Com, s2: Store) 77 | { 78 | match c 79 | case SKIP => s1 == s2 80 | case ASSIGN(id,a) => 81 | if (forall id: Ident :: IdInAexp(id,a) ==> id in s1) then s2 == s1[id := Aeval(s1,a)] else false 82 | case SEQ(c1, c2) => exists si: Store :: Cexec(s1,c1,si) && Cexec(si,c2,s2) 83 | case IFTHENELSE(b,c1,c2) => 84 | if (forall id: Ident :: IdInBexp(id,b) ==> id in s1) then 85 | (if Beval(s1,b) then Cexec(s1,c1,s2) else Cexec(s1,c2,s2)) 86 | else false 87 | case WHILE(b,c) => 88 | if (forall id: Ident :: IdInBexp(id,b) ==> id in s1) then 89 | if Beval(s1,b) then (exists si: Store :: Cexec(s1,c,si) && Cexec(si,WHILE(b,c),s2)) else s1 == s2 90 | else false 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /examples/policy_evaluator/Compat.dfy: -------------------------------------------------------------------------------- 1 | include "typecartOutput/joint.dfy" 2 | include "typecartOutput/old.dfy" 3 | include "typecartOutput/new.dfy" 4 | include "typecartOutput/combine.dfy" 5 | 6 | module Compat { 7 | 8 | import Old 9 | import New 10 | import Joint 11 | import Combine 12 | 13 | /* Translate version */ 14 | lemma EvalStatementAndRequest(statement: Old.Types.StatementBlock, request: Old.Types.Request) 15 | ensures Combine.Types.TranslateAnswer(Old.Spec.EvalStatementAndRequest(statement, request)) 16 | == New.Spec.EvalStatementAndRequest(Combine.Types.TranslateStatementBlock(statement), Combine.Types.TranslateRequest(request)); 17 | {} 18 | 19 | lemma Eval(policies: seq, request: Old.Types.Request) 20 | ensures Combine.Types.TranslateAnswer(Old.Spec.Eval(policies, request)) == 21 | New.Spec.Eval(Combine.MapBuiltinTypes.Seq(Combine.Types.TranslatePolicy, policies), Combine.Types.TranslateRequest(request)) 22 | { 23 | if |policies| == 0 { 24 | } else { 25 | if |policies| == 1 { 26 | } else { 27 | if Old.Spec.EvalStatementAndRequest(policies[0].policyBlock.statement, request).DENY? { 28 | EvalStatementAndRequest(policies[0].policyBlock.statement, request); 29 | } else { 30 | Eval(policies[1..],request); 31 | assert Combine.MapBuiltinTypes.Seq(Combine.Types.TranslatePolicy, policies[1..]) 32 | == Combine.MapBuiltinTypes.Seq(Combine.Types.TranslatePolicy, policies)[1..]; 33 | } 34 | } 35 | } 36 | } 37 | 38 | /* Relate version */ 39 | lemma RelateEvalStatementAndRequest(statement_O: Old.Types.StatementBlock, request_O: Old.Types.Request, statement_N: New.Types.StatementBlock, request_N: New.Types.Request) 40 | requires Combine.Types.T_StatementBlock(statement_O, statement_N) 41 | requires Combine.Types.T_Request(request_O, request_N) 42 | ensures Combine.Types.T_Answer(Old.Spec.EvalStatementAndRequest(statement_O, request_O), New.Spec.EvalStatementAndRequest(statement_N, request_N)) 43 | {} 44 | 45 | lemma RelateEval(policies_O: seq, request_O: Old.Types.Request, policies_N: seq, request_N: New.Types.Request) 46 | requires Combine.RelateBuiltinTypes.Seq((sq_O: Old.Types.Policy, sq_N: New.Types.Policy) => Combine.Types.T_Policy(sq_O, sq_N), policies_O, policies_N) 47 | requires Combine.Types.T_Request(request_O, request_N) 48 | ensures Combine.Types.T_Answer(Old.Spec.Eval(policies_O, request_O), New.Spec.Eval(policies_N, request_N)) 49 | { 50 | if |policies_O| == 0 { 51 | } else { 52 | if |policies_O| == 1 { 53 | } else { 54 | if Old.Spec.EvalStatementAndRequest(policies_O[0].policyBlock.statement, request_O).DENY? { 55 | RelateEvalStatementAndRequest(policies_O[0].policyBlock.statement, request_O, policies_N[0].policyBlock.statement, request_N); 56 | } else { 57 | RelateEval(policies_O[1..],request_O,policies_N[1..],request_N); 58 | } 59 | } 60 | } 61 | } 62 | 63 | 64 | } 65 | 66 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections/Program.fs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT 3 | 4 | namespace TypeInjections 5 | 6 | open Typecart 7 | 8 | module Program = 9 | 10 | [] 11 | let main (argv: string array) = 12 | // for now, typeCart requires fully qualified paths of input files or folders 13 | // Dafny fails with cryptic exception if we accidentally pass an empty list of files 14 | 15 | // check and read arguments 16 | if argv.Length < 3 then 17 | failwith "usage: program OLD[FILE|FOLDER] NEW[FILE|FOLDER] OUTPUTFOLDER\n\ 18 | [-i IGNORE-PATTERNS-FILE]\n\ 19 | [-a 0/1/2 (affected analysis: 0 = do not generate anything for unaffected,\n\ 20 | 1 = always generate lemmas,\n\ 21 | 2 = generate axioms for unaffected, default=2)]\n\ 22 | [-f 0/1/2 (use Forall in lemma pre-/post-conditions for function argument/return value f:\n\ 23 | 0 = (no forall) New.f == forward(Old.f),\n\ 24 | 1 = forall xN :: New.f(xN) == forward(Old.f)(xN),\n\ 25 | 2 = forall xO :: New.f(forward(xO)) == forward(Old.f(xO)), default=2)]\n\ 26 | [-b true/false (generate Backward translation functions, default=false)]\n\ 27 | [-p true/false (generate Proof sketch, default=true)]\n\ 28 | [-l true/false (generate Lemma calls, default=true)]\n\ 29 | [-h true/false (specialize Higher-order lemmas (experimental), default=false)]" 30 | let argvList = argv |> Array.toList 31 | let oldPath = argvList.Item(0) 32 | let newPath = argvList.Item(1) 33 | let outFolder = argvList.Item(2) 34 | 35 | // path to the file that specifies filenames to ignore when processing change. 36 | let ignorePatternsFile = 37 | if argv.Length >= 4 && (List.exists (fun s -> s = "-i") argvList[3..]) then 38 | Some(List.item ((List.findIndex (fun s -> s = "-i") argvList) + 1) argvList) 39 | else 40 | None 41 | 42 | // typecart config 43 | let config = 44 | if argv.Length >= 4 then 45 | Translation.parseConfig(argvList[3..]) 46 | else 47 | Translation.defaultConfig 48 | 49 | // initialise Dafny 50 | let reporter = Utils.initDafny 51 | 52 | // initialise typecart wrappers for Dafny projects 53 | let oldProject = TypecartProject(oldPath, ignorePatternsFile) 54 | let newProject = TypecartProject(newPath, ignorePatternsFile) 55 | 56 | // run Dafny 57 | Utils.log "***** calling Dafny to parse and type-check OLD project and convert it to YIL" 58 | let oldYIL = oldProject.toYILProgram ("Old", reporter) 59 | Utils.log "***** calling Dafny to parse and type-check NEW project and convert it to YIL" 60 | let newYIL = newProject.toYILProgram ("New", reporter) 61 | 62 | // run typecart 63 | Utils.log "***** calling typeCart to produce output project" 64 | Typecart(oldYIL, newYIL, Utils.log).go(config, DefaultTypecartOutput(outFolder)) 65 | 66 | 0 67 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE.txt) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /scripts/countVerified.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import argparse 3 | 4 | def process_dafny_output(lines): 5 | functions = {} 6 | current_function = None 7 | current_category = None 8 | 9 | for line in lines: 10 | result = line.split(',') 11 | parts = result[0].split(' ', 1) 12 | if not parts[0].endswith("_bc"): 13 | continue 14 | current_function = parts[0] 15 | current_category = parts[1] 16 | if result[1] == "Passed": 17 | functions.setdefault(current_function, {})[current_category] = 'verified' 18 | elif result[1] == "Failed": 19 | functions.setdefault(current_function, {})[current_category] = 'error' 20 | else: 21 | print(f'Unknown result: {result[1]}') 22 | 23 | verified_functions = {k: [c for c, v in categories.items() if v == 'verified'] for k, categories in functions.items() if all(v == 'verified' for v in categories.values())} 24 | unverified_functions = {k: [c for c, v in categories.items() if v == 'error'] for k, categories in functions.items() if any(v == 'error' for v in categories.values())} 25 | timed_out_functions = {k: [c for c, v in categories.items() if v == 'timed out'] for k, categories in functions.items() if any(v == 'timed out' for v in categories.values())} 26 | 27 | return verified_functions, unverified_functions, timed_out_functions 28 | 29 | def main(): 30 | parser = argparse.ArgumentParser(description="Process Dafny verification output.") 31 | parser.add_argument("--print-verified", action="store_true", help="Print verified functions.") 32 | parser.add_argument("--print-unverified", action="store_true", help="Print unverified functions.") 33 | parser.add_argument("--print", action="store_true", help="Print both verified and unverified functions.") 34 | parser.add_argument("--categories", action="store_true", help="Include categories in printed names.") 35 | 36 | args = parser.parse_args() 37 | 38 | # Read lines from standard input 39 | lines = [line.strip() for line in sys.stdin] 40 | 41 | verified, unverified, timed_out = process_dafny_output(lines) 42 | 43 | total_functions = len(verified) + len(unverified) 44 | 45 | if args.print or args.print_verified: 46 | print("Verified:") 47 | for function, categories in verified.items(): 48 | if args.categories: 49 | print(f"{function} in {', '.join(categories)}") 50 | else: 51 | print(f"{function}") 52 | 53 | if args.print or args.print_unverified: 54 | print("\nUnverified:") 55 | for function, categories in unverified.items(): 56 | if args.categories: 57 | print(f"{function} in {', '.join(categories)}") 58 | else: 59 | print(f"{function}") 60 | 61 | if args.print or args.print_verified or args.print_unverified or args.categories: 62 | print(f"Number of verified instances: {len(verified)}") 63 | if args.print or args.print_unverified: 64 | print(f"Number of unverified instances: {len(unverified)}") 65 | print(f"Total instances: {total_functions}") 66 | else: 67 | print(f', {len(verified)}/{len(verified) + len(unverified)}', end='') 68 | # print(f', {len(verified)}, {len(timed_out)}, {len(unverified)}', end='') 69 | 70 | if __name__ == "__main__": 71 | main() 72 | -------------------------------------------------------------------------------- /scripts/proofs_111.patch: -------------------------------------------------------------------------------- 1 | --- proofs_111_original.dfy 2024-10-11 08:52:49 2 | +++ proofs/proofs.dfy 2024-10-11 08:52:58 3 | @@ -1970,6 +1970,23 @@ 4 | } 5 | } 6 | 7 | + module strict { 8 | + import Old 9 | + import New 10 | + import opened types 11 | + function StrictTypeError_to_TypeError(x: Old.validation.strict.StrictTypeError): New.validation.types.TypeError 12 | + { 13 | + match x { 14 | + case TypeError(t) => TypeError_forward(t) 15 | + case _ => assume false; New.validation.types.EmptyLUB() 16 | + } 17 | + } 18 | + function Result_forward(T_forward: T_O->T_N, T_backward: T_N->T_O, R_O: Old.validation.strict.Result): New.validation.types.Result 19 | + { 20 | + R_O.MapErr(StrictTypeError_to_TypeError).Map(T_forward) 21 | + } 22 | + } 23 | + 24 | module validator { 25 | import Joint 26 | 27 | @@ -1989,6 +2006,8 @@ 28 | 29 | import opened util 30 | 31 | + import strict 32 | + 33 | function Schema_forward(S_O: Old.validation.validator.Schema): New.validation.validator.Schema 34 | { 35 | match S_O { 36 | @@ -2094,6 +2113,22 @@ 37 | /* unchanged constructor */ Old.validation.validator.ValidationError.AllFalse() 38 | case TypeError(x0_N) => 39 | /* deleted constructor */ Translations.Utils.???() 40 | + } 41 | + } 42 | + 43 | + function ValidationMode_forward(V_O: Old.validation.validator.ValidationMode): New.validation.types.ValidationMode 44 | + { 45 | + match V_O { 46 | + case Permissive() => New.validation.types.Permissive() 47 | + case Strict() => New.validation.types.Strict() 48 | + } 49 | + } 50 | + 51 | + function ValidationMode_backward(V_N: New.validation.types.ValidationMode): Old.validation.validator.ValidationMode 52 | + { 53 | + match V_N { 54 | + case Permissive() => Old.validation.validator.Permissive() 55 | + case Strict() => Old.validation.validator.Strict() 56 | } 57 | } 58 | 59 | @@ -2113,6 +2148,11 @@ 60 | } 61 | } 62 | 63 | + function TypeError_back_to_StrictTypeError(x: New.validation.types.TypeError): Old.validation.strict.StrictTypeError 64 | + { 65 | + Old.validation.strict.TypeError(TypeError_backward(x)) 66 | + } 67 | + 68 | lemma Validator_Typecheck_bc(V_O: Old.validation.validator.Validator, V_N: New.validation.validator.Validator, e_O: Joint.def.core.Expr, ets_O: Old.validation.typechecker.EntityTypeStore, acts_O: Old.validation.typechecker.ActionStore, reqty_O: Old.validation.typechecker.RequestType, e_N: Joint.def.core.Expr, ets_N: New.validation.typechecker.EntityTypeStore, acts_N: New.validation.typechecker.ActionStore, reqty_N: New.validation.typechecker.RequestType) 69 | decreases V_O, e_O, ets_O, acts_O, reqty_O 70 | requires V_N == Validator_forward(V_O) 71 | @@ -2689,6 +2729,8 @@ 72 | { 73 | var typechecker := Old.validation.strict.StrictTypechecker.StrictTypechecker(schema_O.ets, schema_O.acts, schema_O.reqty); assert New.validation.thm.toplevel.strictTypecheck(pid_N, policies_N, schema_N) == strict.Result_forward((x: Old.validation.types.Type) => Type_forward(x), (x: New.validation.types.Type) => Type_backward(x), typechecker.typecheck(policies_O.policies[pid_O].toExpr(), Old.validation.types.Type.Bool(Old.validation.types.BoolType.AnyBool()))); 74 | } 75 | + 76 | + import strict 77 | 78 | import opened typechecker 79 | 80 | -------------------------------------------------------------------------------- /scripts/proofs_111_no_proof.patch: -------------------------------------------------------------------------------- 1 | --- proofs_111_no_proof_origin.dfy 2024-10-01 20:43:49 2 | +++ proofs_111_no_proof.dfy 2024-10-01 20:44:29 3 | @@ -1128,6 +1128,23 @@ 4 | } 5 | } 6 | 7 | + module strict { 8 | + import Old 9 | + import New 10 | + import opened types 11 | + function StrictTypeError_to_TypeError(x: Old.validation.strict.StrictTypeError): New.validation.types.TypeError 12 | + { 13 | + match x { 14 | + case TypeError(t) => TypeError_forward(t) 15 | + case _ => assume false; New.validation.types.EmptyLUB() 16 | + } 17 | + } 18 | + function Result_forward(T_forward: T_O->T_N, T_backward: T_N->T_O, R_O: Old.validation.strict.Result): New.validation.types.Result 19 | + { 20 | + R_O.MapErr(StrictTypeError_to_TypeError).Map(T_forward) 21 | + } 22 | + } 23 | + 24 | module validator { 25 | import Joint 26 | 27 | @@ -1147,6 +1164,8 @@ 28 | 29 | import opened util 30 | 31 | + import strict 32 | + 33 | function Schema_forward(S_O: Old.validation.validator.Schema): New.validation.validator.Schema 34 | { 35 | match S_O { 36 | @@ -1246,6 +1265,22 @@ 37 | /* unchanged constructor */ Old.validation.validator.ValidationError.AllFalse() 38 | case TypeError(x0_N) => 39 | /* deleted constructor */ Translations.Utils.???() 40 | + } 41 | + } 42 | + 43 | + function ValidationMode_forward(V_O: Old.validation.validator.ValidationMode): New.validation.types.ValidationMode 44 | + { 45 | + match V_O { 46 | + case Permissive() => New.validation.types.Permissive() 47 | + case Strict() => New.validation.types.Strict() 48 | + } 49 | + } 50 | + 51 | + function ValidationMode_backward(V_N: New.validation.types.ValidationMode): Old.validation.validator.ValidationMode 52 | + { 53 | + match V_N { 54 | + case Permissive() => Old.validation.validator.Permissive() 55 | + case Strict() => Old.validation.validator.Strict() 56 | } 57 | } 58 | 59 | @@ -1265,6 +1300,11 @@ 60 | } 61 | } 62 | 63 | + function TypeError_back_to_StrictTypeError(x: New.validation.types.TypeError): Old.validation.strict.StrictTypeError 64 | + { 65 | + Old.validation.strict.TypeError(TypeError_backward(x)) 66 | + } 67 | + 68 | lemma Validator_Typecheck_bc(V_O: Old.validation.validator.Validator, V_N: New.validation.validator.Validator, e_O: Joint.def.core.Expr, ets_O: Old.validation.typechecker.EntityTypeStore, acts_O: Old.validation.typechecker.ActionStore, reqty_O: Old.validation.typechecker.RequestType, e_N: Joint.def.core.Expr, ets_N: New.validation.typechecker.EntityTypeStore, acts_N: New.validation.typechecker.ActionStore, reqty_N: New.validation.typechecker.RequestType) 69 | decreases V_O, e_O, ets_O, acts_O, reqty_O 70 | requires V_N == Validator_forward(V_O) 71 | @@ -1691,6 +1731,8 @@ 72 | /* unchanged constructor */ Old.validation.thm.soundness.SemanticSoundnessProof.SSP(RequestType_backward(reqty_N), EntityTypeStore_backward(ets_N), ActionStore_backward(acts_N), r_N, s_N) 73 | } 74 | } 75 | + 76 | + import strict 77 | 78 | lemma SemanticSoundnessProof_WellTyped_bc(S_O: Old.validation.thm.soundness.SemanticSoundnessProof, S_N: New.validation.thm.soundness.SemanticSoundnessProof, e_O: Joint.def.core.Expr, effs_O: Old.validation.typechecker.Effects, e_N: Joint.def.core.Expr, effs_N: New.validation.typechecker.Effects) 79 | decreases S_O, e_O, effs_O 80 | -------------------------------------------------------------------------------- /TypeInjections/Tool.Test/ToolTests.fs: -------------------------------------------------------------------------------- 1 | namespace Tool.Test 2 | 3 | open System 4 | open System.IO 5 | open TestUtils 6 | open Tool 7 | 8 | module IOTests = 9 | open NUnit.Framework 10 | 11 | [] 12 | // --old input is missing 13 | let MissingRequiredOption () = 14 | // since --old is missing, rest of args don't matter. Program breaks beforehand 15 | let input = 16 | $"local --new {resourcePath}/New -p {resourcePath}/Output" 17 | 18 | let args = input.Split(" ") 19 | 20 | Assert.Catch(fun () -> Tool.main args |> ignore) 21 | |> ignore 22 | //TODO assert type of error is correct 23 | 24 | [] 25 | // no dafny files given in input 26 | let NoDafnyTest () = 27 | // folder has only txt file 28 | let argsTxt = 29 | [ $"{resourcePath}/Local/NoDafnyTest/OnlyText" ] 30 | 31 | Assert.Catch(fun () -> Tool.checkInputs argsTxt) 32 | |> ignore 33 | //TODO assert type of error is correct 34 | 35 | // folder is empty 36 | let argsEmpty = 37 | [ $"{resourcePath}/Local/NoDafnyTest/Empty" ] 38 | 39 | Assert.Catch(fun () -> Tool.checkInputs argsEmpty) |> ignore 40 | //TODO assert type of error is correct 41 | 42 | [] 43 | // with correct inputs, typecart local works 44 | let LocalGoodInput () = 45 | let input = 46 | $"local --old {resourcePath}/Local/GoodInput/Old --new {resourcePath}/Local/GoodInput/New -p {resourcePath}/Local/GoodInput/Output" 47 | 48 | let args = input.Split(" ") 49 | Tool.main args |> ignore 50 | // ensure 4 files are printed out with correct filenames, since input is good 51 | let outputFilenames = 52 | List.map 53 | (fun (x: string) -> FileInfo(x).Name) 54 | (Directory.GetFiles($"{resourcePath}/Local/GoodInput/Output") 55 | |> Seq.toList) 56 | 57 | let expectedFilenames = 58 | List.map 59 | (fun (x: string) -> FileInfo(x).Name) 60 | (Directory.GetFiles($"{resourcePath}/Local/GoodInput/Expected") 61 | |> Seq.toList) 62 | 63 | Assert.AreEqual(outputFilenames, expectedFilenames) 64 | 65 | //TODO test long commit hashes 66 | 67 | //TODO test short commit hashes 68 | 69 | //TODO test commits not from "main" 70 | 71 | //TODO test typecart git --clone 72 | 73 | [] 74 | // raise exception for bad git URL 75 | let GitBadURLTest () = 76 | Assert.Catch 77 | (fun () -> 78 | Tool.checkoutCommit 79 | "0f836ae" 80 | "https://github.com/awslabs/IMAGINARY_REPO.git" 81 | $"{resourcePath}/Git/BadURLTest/Output") 82 | |> ignore 83 | 84 | [] 85 | // no repository / bad repository in directory 86 | let GitBadLocation () = 87 | // no git repo 88 | let exn = 89 | Assert.Catch 90 | (fun () -> 91 | Tool.checkoutCommit 92 | "0f836ae" 93 | $"{resourcePath}/Git/BadLocation" 94 | $"{resourcePath}/Git/BadLocation/Generated") 95 | 96 | Console.WriteLine $"Exn was: {exn.Message}" 97 | 98 | [] 99 | // user called unsupported verb 100 | let BadVerb () = 101 | // oldPath, newPath, outPath don't matter program fails beforehand 102 | let input = 103 | $"badVerb --old {resourcePath}/Old --new {resourcePath}/New -p {resourcePath}/Output" 104 | 105 | let args = input.Split(" ") 106 | 107 | Assert.Catch(fun () -> Tool.main args |> ignore) 108 | |> ignore 109 | 110 | [] 111 | let EnsureHelp () = 112 | // general help 113 | Assert.Catch(fun () -> Tool.main [| "--help" |] |> ignore) 114 | |> ignore 115 | // help for certain verb 116 | Assert.Catch(fun () -> Tool.main [| "local"; "--help" |] |> ignore) 117 | |> ignore 118 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/MapBuiltinTypes.dfy: -------------------------------------------------------------------------------- 1 | module Translations.MapBuiltinTypes { 2 | export reveals Seq, Set, Map 3 | 4 | function Seq(t: o -> n, e: seq) : (f : seq) 5 | ensures |e| == |f| 6 | ensures forall i : int :: (0 <= i < |e| ==> f[i] == t(e[i])) 7 | { 8 | if |e| == 0 then [] else 9 | seq(|e|, i => if 0 <= i < |e| then t(e[i]) else t(e[0])) 10 | } 11 | function Set(t: o -> n, e: set) : (f : set) 12 | ensures forall x : o :: x in e ==> t(x) in f 13 | ensures forall x : n :: x in f ==> exists y : o :: y in e && t(y) == x 14 | { 15 | set x:o | x in e :: t(x) 16 | } 17 | function Map(K : K_O -> K_N, V : V_O -> V_N, e : map) : (f: map) 18 | { 19 | var fKeys := set x: K_O | x in e.Keys :: K(x); 20 | var fSet := set x:K_O | x in e.Keys :: (K(x),V(e[x])); 21 | map a | a in fKeys :: var b :| (a,b) in fSet; b 22 | } 23 | /* Map helper functions and lemmas -- for proving map size meets standard of map31 */ 24 | function fKeysFunc(T: T_O->T_N, m_O: set) : set 25 | { 26 | set x: T_O | x in m_O :: T(x) 27 | } 28 | function fSetFunc(T: T_O->T_N, U: U_O->U_N, m_O: map) : (res : set<(T_N,U_N)>) 29 | { 30 | var fKeys := fKeysFunc(T,m_O.Keys); 31 | set x:T_O | x in m_O.Keys :: (T(x),U(m_O[x])) 32 | } 33 | function fMapFunc(T: T_O->T_N, U: U_O->U_N, m_O: map) : (res : map) 34 | { 35 | var fKeys := fKeysFunc(T,m_O.Keys); 36 | var fSet := fSetFunc(T,U,m_O); 37 | map a | a in fKeys :: var b :| (a,b) in fSet; b 38 | } 39 | lemma testfKeysSize(T: T_O->T_N, m_O: set) 40 | requires forall x : T_O :: !exists y : T_O :: x != y && T(x) == T(y) 41 | ensures |fKeysFunc(T,m_O)| == |m_O| 42 | { 43 | var fKeys := fKeysFunc(T,m_O); 44 | if |m_O| == 0 { 45 | assert |fKeysFunc(T,m_O)| == |m_O|; 46 | } else { 47 | var x :| x in m_O; 48 | var smallKeys := m_O - {x}; 49 | var fSmallKeys := fKeysFunc(T,smallKeys); 50 | assert fKeys == fSmallKeys + {T(x)}; 51 | assert |fKeys| == |fSmallKeys| + 1; 52 | testfKeysSize(T,smallKeys); 53 | } 54 | } 55 | lemma testMapSize(T: T_O->T_N, U: U_O->U_N, m_O: map) 56 | requires forall x : T_O :: !exists y : T_O :: x != y && T(x) == T(y) 57 | ensures |fMapFunc(T,U,m_O).Keys| == |m_O.Keys|; 58 | { 59 | var fKeys := fKeysFunc(T,m_O.Keys); 60 | testfKeysSize(T,m_O.Keys); 61 | assert |fKeys| == |m_O.Keys|; 62 | var fSet := fSetFunc(T,U,m_O); 63 | var fMap := map a | a in fKeys :: var b :| (a,b) in fSet; b ; 64 | if |m_O| == 0 { 65 | assert |fMap.Keys| == |m_O.Keys|; 66 | } else { 67 | var x :| x in fKeys; 68 | var smallKeys := fKeys - {x}; 69 | var fMapSmall := map a | a in smallKeys :: var b :| (a,b) in fSet; b; 70 | assert fMap.Keys == fKeys; 71 | assert |fMap.Keys| == |fKeys|; 72 | } 73 | } 74 | function mapCreateGeneric(T: T_O->T_N, U: U_O->U_N, m_O: map) : (res : map) 75 | requires forall x : T_O :: !exists y : T_O :: x != y && T(x) == T(y) 76 | ensures forall x :: x in m_O.Keys ==> T(x) in res.Keys 77 | ensures forall y :: y in m_O.Values ==> U(y) in res.Values 78 | ensures |m_O.Keys| == |res.Keys| 79 | { 80 | testfKeysSize(T,m_O.Keys); 81 | var fKeys := fKeysFunc(T,m_O.Keys); 82 | var fSet := fSetFunc(T,U,m_O); 83 | testMapSize(T,U,m_O); 84 | map a | a in fKeys :: var b :| (a,b) in fSet; b 85 | } 86 | /* Set helper functions and lemmas -- for proving set size meets standard of set31 */ 87 | function setCreate2Generic(P : o -> n, S : set) : set 88 | { set x: o | x in S :: P(x) } 89 | lemma setCreateSizeGeneric(P : o -> n, S : set) 90 | ensures |setCreate2Generic(P,S)| <= |S| 91 | { 92 | if |S| == 0 { 93 | } else { 94 | var x: o :| x in S; 95 | assert setCreate2Generic(P,S) == setCreate2Generic(P,S - {x}) + {P(x)}; 96 | setCreateSizeGeneric(P,S - {x}); 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /papers/first-paper/introduction.tex: -------------------------------------------------------------------------------- 1 | Formal software verification involves specifications, an implementation, and a proof that the implementation satisfies the specifications. When an actively-developed implementation evolves, the typical approach is to update the formal specifications and proof accordingly. The old version of specifications is simply relegated to history without attempting to formally relate the old and new versions. This approach is inefficient when the implementation changes to expand on input datatypes while retaining the functionality for legacy inputs. Such implementation changes produce comparable old and new specifications, we propose to formally relate and prove properties about the comparable specifications. A formal relation between comparable specifications could enable the proof engineers to utilize the existing proof infrastructure for proving backward compatibility. We call this process proof evolution: a technique for identifying and relating similarities and changes between comparable specifications and proofs. In this talk, I will present the active development of automated tools for enabling and facilitating proof evolution within Amazon Web Services (AWS). 2 | 3 | A particular use case, that has motivated the need of automated tools for proof evolution, occurs at AWS Identity [1]. AWS authenticates and authorizes over 34 trillion requests per day [2]. AWS Identity has a custom language for specifying access control policies and a corresponding authorization engine that determines access rights for each request by evaluating the relevant policies. We use Dafny, a verification-aware programming language, to verify that the authorization implementation satisfies the intended specifications. When there are changes to the policy language and actively-developed Java implementation, proof engineers at AWS are required to manually update Dafny specifications and the corresponding proofs. To help streamline this process, we propose that the specifications and proofs evolve alongside the code. 4 | 5 | We identify that quite often proof engineers initiate changes to existing specifications by altering types of objects by removing, augmenting and/or introducing new types. To prove properties between such comparable specifications, a conventional approach is to keep the old and new versions around, and manually build injections (mapping functions) between types within the two versions. The type injections provide the necessary infrastructure for proving properties of interest between the comparable specifications. To reduce the manual effort involved, we have developed and open-sourced an analysis tool called typeCart (name-blend of type and cartography) [3]. typeCart automatically identifies equivalent and inequivalent types between comparable specifications, and generates injections for the equivalent types. The generated injections i) mechanically relate the comparable specifications, ii) remove the need of keeping the two versions of specification around and iii) help proof engineers for proving backward compatibility for specifications. To achieve trustworthiness for the injections, typeCart generates verification contracts for the injections, enabling the Dafny verifier to automatically discharge verification conditions of the generated type injections. 6 | 7 | We are evolving typeCart to equip proof engineers with automated tools to prove compatibility between specifications when we introduce new types and augment existing types. We plan to implement workflows for typeCart integration with the version control system of Dafny-based developments at AWS, and its instrumentation to varying codebases of comparability and size. 8 | 9 | \ednote{change to cite} 10 | %[1] https://aws.amazon.com/identity/ 11 | %[2] https://aws.amazon.com/blogs/aws/happy-10th-birthday-aws-identity-and-access-management/ 12 | %[3] https://github.com/awslabs/typecart 13 | 14 | 15 | \paragraph{Related Work} 16 | % comment by Rustan via slack 17 | The symDiff and related tools, by Shuvendu Lahiri and others, takes two Boogie programs attempts to prove that they correspond with each other. This has been used extensively at Microsoft. For example, different compilers compile to x86, x64, or ARM, each of those is translated into Boogie, and then symDiff is run to see if the compilers semantically do the same thing for each target architecture. Another example is using the same target language and using two different versions of the compiler, so see if the new version introduced anything surprising. symDiff is implemented by weaving the two given Boogie programs into one other Boogie program that is correct iff the two input programs correspond. 18 | Still, symDiff only compares code, whereas typeCart also carries proofs of code forward. 19 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SubsetSynTest/InputDirectory/New/New.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module N { 3 | 4 | module C { 5 | ghost const INT_MAX := 0x7fff_ffff 6 | } 7 | 8 | ghost const INT_MAX := 0x7fff_ffff 9 | 10 | type string32 = x: string | 0 <= |x| <= INT_MAX 11 | 12 | datatype Foo = Bar(x: int) | Baz(z: string) 13 | 14 | type map32 = m : map | 0 <= |m| < INT_MAX 15 | 16 | // basic tests for subset types 17 | type FooSyn = Foo 18 | type FooSeq = s : seq | 4 < |s| 19 | witness [Bar (0), Bar (1), Bar (2), Bar (3), Bar (4)] 20 | 21 | type FooSet = s : set | |s| < 4 22 | 23 | // test for ChainingExpression 24 | type Map32Chain = m : map | |m| < 2 && INT_MAX == INT_MAX == INT_MAX 25 | 26 | // TODO: does not verify 27 | // test for ExistsExpr 28 | const INT5 := 5 29 | type SeqExistsExpr = s : seq | exists x :: 0 <= x <= INT5 && |s| == x 30 | witness [0, 1, 2, 3, 4] 31 | 32 | // test for ForallExpr 33 | type SeqForallExpr = s : seq | forall x :: x in s ==> x == INT5 34 | witness [5] 35 | 36 | // test for ExprDotName 37 | type Map32ExprDotName = m : map | 0 <= |m| < C.INT_MAX 38 | 39 | // test for MemberSelectExpr and FunctionCallExpr 40 | predicate MapPred(m: map) 41 | { 42 | 0 <= |m| < 10 43 | } 44 | type Context = m : map32 | MapPred(m) 45 | 46 | // test for ApplyExpr 47 | type ContextApplyExpr = m : map32 | (m => m) (MapPred(m)) 48 | 49 | // test for BinaryExpr 50 | type ContextBinExpr = m : map32 | MapPred(m) && MapPred(m) 51 | 52 | // test for ConversionExpr 53 | type C2 = i: int | 0 <= i 54 | const someC2 := 2 55 | type ConvMap = x: map32 | |x| <= someC2 as int 56 | 57 | // test for compound subset type 58 | type BoundedContext = c : Context | 0 <= |c| < 32 59 | 60 | // test for ITEExpr 61 | type SeqITE = s : seq | 2 < |s| && s[1] == if s[0] == 2 then 2 else INT5 62 | witness [2, 2, 3] 63 | 64 | // test for LambdaExpr 65 | type SeqLambda = s : seq | |s| == 1 && ((n:int) => INT5) == ((n:int) => INT5) 66 | witness [0, 1] 67 | 68 | // test for LetExpr 69 | type SeqLetExpr = s : seq | |s| == var x := INT5; var y := 1; x + y 70 | witness [0, 1, 2, 3, 4, 5] 71 | 72 | datatype Outcome = Success(value: T) 73 | | Failure(error: string) { 74 | 75 | predicate method IsFailure() 76 | { 77 | this.Failure? 78 | } 79 | 80 | function method PropagateFailure(): Outcome 81 | requires IsFailure() 82 | { 83 | Failure(this.error) 84 | } 85 | 86 | function method Extract(): T requires !IsFailure() 87 | { 88 | this.value 89 | } 90 | } 91 | 92 | // test for LetOrFailExpr 93 | type SeqLetOrFailExpr = s : seq 94 | | |s| == var n := var x :- Success(INT5); Success(INT5); n.Extract() 95 | witness [0, 1, 2, 3, 4] 96 | 97 | // test for MapComprehension 98 | type MapMapComprehension = x: map | x == map n: int | n == 0 :: INT5 * INT5 99 | witness map [0 := 25] 100 | 101 | // test for MapDisplayExpr 102 | type MapMapDisplayExpr = x: map | x == map[0 := 0, INT5 := INT5] 103 | witness map[0 := 0, INT5 := INT5] 104 | 105 | // test for MatchExpr 106 | datatype List = Nil | Cons(head: T, tail: List) 107 | 108 | type MapMatchExpr = x: map | 109 | x == 110 | match Cons(INT5, Nil) 111 | case Nil => map[0 := 0, INT5 := INT5] 112 | case Cons(_, _) => map[0 := 0, INT5 := INT5] 113 | witness map[0 := 0, INT5 := INT5] 114 | 115 | // test for MultiSelectExpr 116 | const INT1 := 1 117 | type SetMultiSetDisplayExpr = s : multiset | s == multiset {INT1,INT1} 118 | witness multiset {INT1,INT1} 119 | 120 | // test for NestedMatchExpr 121 | type MapNestedMatchExpr = x: map | 122 | x == 123 | match Cons(INT5, Nil) 124 | case Nil => 125 | (match Cons(INT5, Nil) 126 | case Nil => map[0 := 0, INT5 := INT5] 127 | case Cons(_, _) => map[0 := 0, INT5 := INT5]) 128 | case Cons(_, _) => map[0 := 0, INT5 := INT5] 129 | witness map[0 := 0, INT5 := INT5] 130 | 131 | // test for NegationExpression 132 | const NegInt1 := -1 133 | type SetNegationExpression = s : seq | |s| == - NegInt1 134 | witness [0] 135 | 136 | // test for ParensExpression 137 | const Int1 := 1 138 | type SetParensExpression = s : seq | |s| == (((Int1 + 0) - 0)+ 0) 139 | witness [0] 140 | 141 | // test for SeqConstructionExpr 142 | const Len := 1 143 | type SeqSeqConstructionExpr = s : seq | s == seq(Len,_ => Len) 144 | witness [1] 145 | 146 | // test for SeqDisplayExpr 147 | type SeqSeqDisplayExpr = x: seq | x == [INT5, INT5] 148 | witness [INT5, INT5] 149 | 150 | // test for SeqSelectExpr 151 | type SeqSeqSelectExpr = x: seq | |x| == 2 && x[Int1] == INT5 152 | witness [INT5, INT5] 153 | 154 | // test for SeqUpdateExpr 155 | type SeqSeqUpdateExpr = x: seq | |x| == 2 && x[Int1 := INT5] == x 156 | witness [INT5, INT5] 157 | 158 | // test for SetComprehension 159 | type SetSetComprehension = s: set | |s| == 1 && s == set x:int | x == INT5 160 | witness {INT5} 161 | 162 | // test for SetDisplayExpr 163 | type SetSetDisplayExpr = x: set | x == {INT5, Int1} 164 | witness {INT5, Int1} 165 | 166 | // test for StmtExpr 167 | type TypeStmtExpr = x: set | assert true; x == {INT5, Int1} 168 | witness {INT5, Int1} 169 | 170 | // test for TypeTestExpr 171 | type TypeTypeTestExpr = x: bool | x == INT5 is int 172 | witness true 173 | 174 | } 175 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/Resources/SubsetSynTest/InputDirectory/Old/Old.dfy: -------------------------------------------------------------------------------- 1 | // Old.dfy and New.dfy have identical types 2 | module N { 3 | 4 | module C { 5 | ghost const INT_MAX := 0x7fff_ffff 6 | } 7 | 8 | ghost const INT_MAX := 0x7fff_ffff 9 | 10 | type string32 = x: string | 0 <= |x| <= INT_MAX 11 | 12 | datatype Foo = Bar(x: int) | Baz(z: string) 13 | 14 | type map32 = m : map | 0 <= |m| < INT_MAX 15 | 16 | // basic tests for subset types 17 | type FooSyn = Foo 18 | type FooSeq = s : seq | 4 < |s| 19 | witness [Bar (0), Bar (1), Bar (2), Bar (3), Bar (4)] 20 | 21 | type FooSet = s : set | |s| < 4 22 | 23 | // test for ExistsExpr 24 | const INT5 := 5 25 | type SeqExistsExpr = s : seq | exists x :: 0 <= x <= INT5 && |s| == x 26 | witness [0, 1, 2, 3, 4] 27 | 28 | // test for ForallExpr 29 | type SeqForallExpr = s : seq | forall x :: x in s ==> x == INT5 30 | witness [5] 31 | 32 | // test for ExprDotName 33 | type Map32ExprDotName = m : map | 0 <= |m| < C.INT_MAX 34 | 35 | // test for MemberSelectExpr and FunctionCallExpr 36 | predicate MapPred(m: map) 37 | { 38 | 0 <= |m| < 10 39 | } 40 | type Context = m : map32 | MapPred(m) 41 | 42 | // test for ApplyExpr 43 | type ContextApplyExpr = m : map32 | (m => m) (MapPred(m)) 44 | 45 | // test for BinaryExpr 46 | type ContextBinExpr = m : map32 | MapPred(m) && MapPred(m) 47 | 48 | // test for ConversionExpr 49 | type C2 = i: int | 0 <= i 50 | const someC2 := 2 51 | type ConvMap = x: map32 | |x| <= someC2 as int 52 | 53 | // test for compound subset type 54 | type BoundedContext = c : Context | 0 <= |c| < 32 55 | 56 | // test for ITEExpr 57 | type SeqITE = s : seq | 2 < |s| && s[1] == if s[0] == 2 then 2 else INT5 58 | witness [2, 2, 3] 59 | 60 | // test for LambdaExpr 61 | // we need to identify why eclosing barckets are missing for a lambda expression in the output "(n: int) => New.N.INT5" 62 | // Line 185 of Expect.dfy 63 | type SeqLambda = s : seq | |s| == 1 && ((n:int) => INT5) == ((n:int) => INT5) 64 | witness [0, 1] 65 | 66 | // test for LetExpr 67 | type SeqLetExpr = s : seq | |s| == var x := INT5; var y := 1; x + y 68 | witness [0, 1, 2, 3, 4, 5] 69 | 70 | datatype Outcome = Success(value: T) 71 | | Failure(error: string) { 72 | 73 | predicate method IsFailure() 74 | { 75 | this.Failure? 76 | } 77 | 78 | function method PropagateFailure(): Outcome 79 | requires IsFailure() 80 | { 81 | Failure(this.error) 82 | } 83 | 84 | function method Extract(): T requires !IsFailure() 85 | { 86 | this.value 87 | } 88 | } 89 | 90 | // test for LetOrFailExpr 91 | type SeqLetOrFailExpr = s : seq 92 | | |s| == var n := var x :- Success(INT5); Success(INT5); n.Extract() 93 | witness [0, 1, 2, 3, 4] 94 | 95 | // test for MapComprehension 96 | type MapMapComprehension = x: map | x == map n: int | n == 0 :: INT5 * INT5 97 | witness map [0 := 25] 98 | 99 | // test for MapDisplayExpr 100 | type MapMapDisplayExpr = x: map | x == map[0 := 0, INT5 := INT5] 101 | witness map[0 := 0, INT5 := INT5] 102 | 103 | // test for MatchExpr 104 | datatype List = Nil | Cons(head: T, tail: List) 105 | 106 | type MapMatchExpr = x: map | 107 | x == 108 | match Cons(INT5, Nil) 109 | case Nil => map[0 := 0, INT5 := INT5] 110 | case Cons(_, _) => map[0 := 0, INT5 := INT5] 111 | witness map[0 := 0, INT5 := INT5] 112 | 113 | // test for MultiSelectExpr 114 | // the tool doesnot check for multiset, we should implement it 115 | const INT1 := 1 116 | type SetMultiSetDisplayExpr = s : multiset | s == multiset {INT1,INT1} 117 | witness multiset {INT1,INT1} 118 | 119 | // test for NestedMatchExpr 120 | type MapNestedMatchExpr = x: map | 121 | x == 122 | match Cons(INT5, Nil) 123 | case Nil => 124 | (match Cons(INT5, Nil) 125 | case Nil => map[0 := 0, INT5 := INT5] 126 | case Cons(_, _) => map[0 := 0, INT5 := INT5]) 127 | case Cons(_, _) => map[0 := 0, INT5 := INT5] 128 | witness map[0 := 0, INT5 := INT5] 129 | 130 | // test for NegationExpression and UnaryOpExpr 131 | // should be cover by UnaryExp, but its included for completeness 132 | const NegInt1 := -1 133 | type SetNegationExpression = s : seq | |s| == - NegInt1 134 | witness [0] 135 | 136 | // test for ParensExpression 137 | // cover by other cases, only there for completeness 138 | const Int1 := 1 139 | type SetParensExpression = s : seq | |s| == (((Int1 + 0) - 0)+ 0) 140 | witness [0] 141 | 142 | // test for SeqConstructionExpr 143 | const Len := 1 144 | type SeqSeqConstructionExpr = s : seq | s == seq(Len,_ => Len) 145 | witness [1] 146 | 147 | // test for SeqDisplayExpr 148 | type SeqSeqDisplayExpr = x: seq | x == [INT5, INT5] 149 | witness [INT5, INT5] 150 | 151 | // test for SeqSelectExpr 152 | type SeqSeqSelectExpr = x: seq | |x| == 2 && x[Int1] == INT5 153 | witness [INT5, INT5] 154 | 155 | // test for SeqUpdateExpr 156 | type SeqSeqUpdateExpr = x: seq | |x| == 2 && x[Int1 := INT5] == x 157 | witness [INT5, INT5] 158 | 159 | // test for SetComprehension 160 | type SetSetComprehension = s: set | |s| == 1 && s == set x:int | x == INT5 161 | witness {INT5} 162 | 163 | // test for SetDisplayExpr 164 | type SetSetDisplayExpr = x: set | x == {INT5, Int1} 165 | witness {INT5, Int1} 166 | 167 | // test for StmtExpr 168 | // expressions in statements also need to be prefixed, not implemented for the time being 169 | type TypeStmtExpr = x: set | assert true; x == {INT5, Int1} 170 | witness {INT5, Int1} 171 | 172 | // test for TypeTestExpr 173 | type TypeTypeTestExpr = x: bool | x == INT5 is int 174 | witness true 175 | 176 | } 177 | -------------------------------------------------------------------------------- /TypeInjections/TypeInjections.Test/TestUtils.fs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT 3 | 4 | namespace TypeInjections.Test 5 | 6 | open System.IO 7 | open TypeInjections 8 | open Microsoft.Dafny 9 | 10 | module TestUtils = 11 | open NUnit.Framework 12 | open System 13 | module T = Typecart 14 | 15 | type TestFormat = string -> string -> unit 16 | 17 | let pwd : string = 18 | let wd = Environment.CurrentDirectory 19 | 20 | let pd = 21 | Directory 22 | .GetParent( 23 | wd 24 | ) 25 | .Parent 26 | .Parent 27 | .FullName 28 | 29 | Path.Combine( 30 | [| pd 31 | "Resources" 32 | TestContext.CurrentContext.Test.Name |] 33 | ) 34 | 35 | type DirectoryOutputWriter(outFolderPath: string) = 36 | 37 | let writeFile (prog: YIL.Program) (folder:string) (prefix:string) = 38 | let addSuffix (path: string) (ending: string): string = 39 | let endPos = path.IndexOf(".dfy") 40 | let newName = path.Insert( endPos, ("_" + ending )) 41 | newName.ToLower() 42 | let getName (x:YIL.Decl) = 43 | if x.meta.position.IsNone then 44 | match x with 45 | | YIL.Module (name, _, _) -> name + ".dfy" 46 | | _ -> ".dfy" 47 | else 48 | x.meta.position.Value.filename 49 | 50 | 51 | let mods = List.filter (fun (x:YIL.Decl) -> x.meta.position.IsSome) prog.decls 52 | // if YIL.decl is empty, don't write anything 53 | if mods.Length = 0 then 54 | 0 |> ignore 55 | else 56 | 57 | let outputProg = {YIL.name = getName mods.Head; YIL.decls = prog.decls; YIL.meta = YIL.emptyMeta} 58 | let endFileName = (addSuffix outputProg.name prefix) 59 | let s = YIL.printer().prog(outputProg, YIL.Context(outputProg)) 60 | let filePath = Path.Combine(folder, endFileName) 61 | IO.File.WriteAllText(filePath, s) 62 | 63 | let writeOut fileName prefix (prog:YIL.Program) = 64 | let folder = outFolderPath 65 | 66 | writeFile prog folder prefix 67 | 68 | interface Typecart.TypecartOutputProcessor with 69 | member this.processOld(oldYIL: YIL.Program) = writeOut "old.dfy" "Old" oldYIL 70 | member this.processNew(newYIL: YIL.Program) = writeOut "new.dfy" "New" newYIL 71 | member this.processJoint(jointYIL: YIL.Program) = writeOut "joint.dfy" "Joint" jointYIL 72 | member this.processProofs(translationsYIL: YIL.Program) = 73 | writeOut "proofs.dfy" "Proofs" translationsYIL 74 | 75 | let typeCartAPI (argv: string array) = 76 | 77 | Utils.log "***** Entering typecartAPI" 78 | 79 | if argv.Length < 3 then 80 | failwith "usage: program OLD NEW OUTPUTFOLDER" 81 | let argvList = argv |> Array.toList 82 | 83 | // get paths to input and outputs 84 | let oldFolderPath = argvList.Item(0) 85 | let newFolderPath = argvList.Item(1) 86 | let outFolderPath = argvList.Item(2) 87 | 88 | // path to the file that specifies filenames to ignore when processing change. 89 | let tcIgnore = 90 | if argv.Length >= 4 && (List.exists (fun s -> s = "-i") argvList[3..]) then 91 | Some(List.item ((List.findIndex (fun s -> s = "-i") argvList) + 1) argvList) 92 | else 93 | None 94 | 95 | // typecart config 96 | let config = 97 | if argv.Length >= 4 then 98 | Translation.parseConfig(argvList[3..]) 99 | else 100 | Translation.defaultConfig 101 | 102 | Directory.CreateDirectory(outFolderPath) |> ignore 103 | 104 | // error handling 105 | for a in [oldFolderPath;newFolderPath;outFolderPath] do 106 | if not (Directory.Exists(a)) then 107 | failwith("folder not found:" + a) 108 | 109 | let oldProj = T.TypecartProject(DirectoryInfo(oldFolderPath), tcIgnore) 110 | let newProj = T.TypecartProject(DirectoryInfo(newFolderPath), tcIgnore) 111 | 112 | let outputWriter = DirectoryOutputWriter(outFolderPath) 113 | 114 | T.Typecart(oldProj.toYILProgram("Old", Utils.initDafny), 115 | newProj.toYILProgram("New", Utils.initDafny)).go(config, outputWriter) 116 | 117 | let public testRunnerGen (directoryName: string) (outputDirectoryName: string) = 118 | let inputDirectory = Path.Combine([| pwd; directoryName |]) 119 | let inputDirectoryOld = Path.Combine([|inputDirectory; "Old"|]) 120 | let inputDirectoryNew = Path.Combine([|inputDirectory; "New"|]) 121 | let outputDirectory = Path.Combine([| pwd; outputDirectoryName |]) 122 | typeCartAPI [|inputDirectoryOld; inputDirectoryNew; outputDirectory|] 123 | 124 | (* 125 | let public testRunnerGen 126 | (testToRun: TestFormat) 127 | (inputFileName1: string) 128 | (inputFileName2: string) 129 | (outputFileName: string) 130 | (expectedFileName: string) 131 | = 132 | let inputFile1 = 133 | Path.Combine([| pwd; inputFileName1 |]) 134 | 135 | let inputFile2 = 136 | Path.Combine([| pwd; inputFileName2 |]) 137 | 138 | let outputFile = 139 | Path.Combine([| pwd; outputFileName |]) 140 | 141 | let expectedFile = 142 | Path.Combine([| pwd; expectedFileName |]) 143 | 144 | TypeInjections.Program.runTypeCart inputFile1 inputFile2 pwd extraFileName false outputFileName 145 | |> ignore 146 | 147 | testToRun outputFile expectedFile 148 | *) -------------------------------------------------------------------------------- /TypeInjections/TypeInjections/DiffAnalysis.fs: -------------------------------------------------------------------------------- 1 | namespace TypeInjections 2 | 3 | open TypeInjections.Diff 4 | open TypeInjections.YIL 5 | 6 | module DiffAnalysis = 7 | /// Returns the set of non-module decls (in Set) that are directly changed or deleted from the old program 8 | type GatherChangedOrDeleted() = 9 | inherit DiffTraverser.Identity() 10 | 11 | let mutable result : Set = Set.empty 12 | 13 | member this.addPath(ctxO: Context, p: Path) = 14 | if ctxO.lookupByPath(p).isModule () then 15 | // Do not add modules to the result. 16 | () 17 | else 18 | result <- result.Add(p) 19 | 20 | override this.name(ctxO: Context, ctxN: Context, name: Diff.Name) = 21 | match name with 22 | | Rename _ -> this.addPath (ctxO, ctxO.currentDecl) 23 | | SameName _ -> () 24 | 25 | name 26 | 27 | override this.tp(ctxO: Context, ctxN: Context, t: Diff.Type) = 28 | match t with 29 | | UpdateType _ -> this.addPath (ctxO, ctxO.currentDecl) 30 | | SameType _ -> () 31 | 32 | t 33 | 34 | override this.exprO(ctxO: Context, ctxN: Context, e: Diff.ExprO) = 35 | match e with 36 | | SameExprO _ -> () 37 | | AddExpr _ // adding an expr is a change (not addition) to the parent decl 38 | | DeleteExpr _ 39 | | UpdateExpr _ -> this.addPath (ctxO, ctxO.currentDecl) 40 | 41 | e 42 | 43 | override this.elem(ctxO: Context, ctxN: Context, dD: Diff.Elem<'y, 'd>, td: Context * Context * 'y * 'y * 'd -> 'd) = 44 | match dD with 45 | | Diff.Same _ 46 | | Diff.Add _ -> dD 47 | | Diff.Delete _ -> 48 | this.addPath (ctxO, ctxO.currentDecl) 49 | dD 50 | | Diff.Update (d, n, df) -> 51 | this.addPath (ctxO, ctxO.currentDecl) 52 | Diff.Update(d, n, td (ctxO, ctxN, d, n, df)) 53 | 54 | override this.declList(ctxO: Context, ctxN: Context, dsD: Diff.List) = 55 | // Ignore any changes on lemmas 56 | Diff.UpdateList( 57 | List.map 58 | (fun (dD: Diff.Elem) -> 59 | match dD with 60 | | Diff.Same d 61 | | Diff.Add d 62 | | Diff.Delete d when d.isLemma () -> dD 63 | | Diff.Update (d, n, _) when d.isLemma () && n.isLemma () -> dD 64 | | _ -> this.elem (ctxO, ctxN, dD, this.decl)) 65 | dsD.elements 66 | ) 67 | 68 | member this.gather(ctxO: Context, ctxN: Context, prog: Diff.Program) = 69 | this.prog (ctxO, ctxN, prog) |> ignore 70 | result 71 | 72 | /// Returns the set of decls (in Set) that are directly or indirectly changed or deleted from the old program 73 | let changedInOld (ctxO: Context, ctxN: Context, progD: Diff.Program) = 74 | let changedDirectly = 75 | GatherChangedOrDeleted() 76 | .gather (ctxO, ctxN, progD) 77 | 78 | System.Console.WriteLine("********* changed directly *****") 79 | Set.iter (fun (p: Path) -> System.Console.WriteLine(p.ToString())) changedDirectly 80 | System.Console.WriteLine("***** END changed directly *****") 81 | 82 | let closure = 83 | Analysis.dependencyClosureByGatherDeclsUsingPaths (ctxO.prog, changedDirectly) 84 | 85 | closure 86 | 87 | /// Gather all specialized lemmas to generate in the proof sketch. 88 | type GatherSpecializedLemmaToGenerate(generateLemmaOrAxiomFor: Path -> bool) = 89 | inherit DiffTraverser.Identity() 90 | 91 | let mutable specializedCalls: Map = Map.empty 92 | 93 | let generateLemmaOrAxiomForExpr (e: Expr) : bool = 94 | match e with 95 | | EMemberRef (_, m, _) -> generateLemmaOrAxiomFor m 96 | | _ -> false 97 | 98 | override this.elem 99 | ( 100 | ctxO: Context, 101 | ctxN: Context, 102 | dD: Diff.Elem<'y, 'd>, 103 | td: Context * Context * 'y * 'y * 'd -> 'd 104 | ) = 105 | match dD with 106 | | Diff.Same d -> 107 | match box (d, td) with 108 | | :? (Decl * (Context * Context * Decl * Decl * Diff.Decl -> Diff.Decl)) as (d, td) -> 109 | td (ctxO, ctxN, d, d, Diff.idDecl d) |> ignore // declSameOrUpdate 110 | dD 111 | | _ -> dD 112 | | Diff.Add _ 113 | | Diff.Delete _ -> dD 114 | | Diff.Update (d, n, df) -> Diff.Update(d, n, td (ctxO, ctxN, d, n, df)) 115 | override this.decl(ctxO: Context, ctxN: Context, declO: Decl, declN: Decl, d: Diff.Decl) = 116 | let n = declO.name 117 | let p = ctxO.currentDecl.child (n) 118 | match d with 119 | | Diff.Method (nameD, methodTypeD, tvsD, insD, outsD, bdD) when generateLemmaOrAxiomFor (p) -> 120 | if methodTypeD.getOld.bodyIsStatement() then 121 | () 122 | else 123 | match declO, declN with 124 | | Method (_, _, tvs_o, ins_o, outs_o, modifiesO, readsO, decreasesO, bodyO, _, isStatic, isOpaqueO, _), 125 | Method (_, _, tvs_n, ins_n, outs_n, modifiesN, readsN, decreasesN, _, _, _, isOpaqueN, _) -> 126 | let ctxOh = ctxO.enter(nameD.getOld).addTpvars (tvsD.getOld ()) 127 | let ctxOi = ctxOh.add (insD.decls.getOld ()) 128 | let ctxOb = ctxOi.add(outsD.namedDecls.getOld ()).enterBody () 129 | match bodyO with 130 | | Some bd -> 131 | specializedCalls <- Analysis.GatherSpecializedLemmaCalls(generateLemmaOrAxiomForExpr) 132 | .gather(ctxOb, bd, specializedCalls) 133 | () 134 | | _ -> () 135 | | _ -> () 136 | | _ -> () 137 | this.declDefault(ctxO, ctxN, declO, declN, d) 138 | 139 | member this.gather(ctxO: Context, ctxN: Context, prog: Diff.Program) = 140 | this.prog(ctxO, ctxN, prog) |> ignore 141 | specializedCalls 142 | -------------------------------------------------------------------------------- /TypeInjections/Tool.Test/Resources/Local/GoodInput/Expected/translations.dfy: -------------------------------------------------------------------------------- 1 | include "joint.dfy" 2 | include "old.dfy" 3 | include "new.dfy" 4 | /***** TYPECART PRELUDE START *****/ 5 | module Translations.RelateBuiltinTypes { 6 | 7 | function Seq(t: (o,n) -> bool, e: seq, f: seq) : (ret : bool) 8 | ensures (|e| == |f| && (forall i : int :: ((0 <= i < |f| && 0 <= i < |e|) ==> t(e[i],f[i])))) 9 | ==> ret == true 10 | { 11 | |e| == |f| && !(exists i : int :: 0 <= i < |e| && !t(e[i],f[i])) 12 | } 13 | 14 | function Set(t: (o,n) -> bool, e: set, f: set) : (ret : bool) 15 | { 16 | ((forall i : o :: i in e ==> exists j : n :: (j in f && t(i,j))) 17 | && (forall j : n :: j in f ==> exists i : o :: (i in e && t(i,j)))) 18 | } 19 | 20 | function Map(K : (K_O,K_N) -> bool, V : (V_O,V_N) -> bool, e : map, f: map) : (ret : bool) 21 | { 22 | (forall i : K_O :: i in e.Keys ==> (exists j : K_N :: j in f.Keys && K(i,j) && V(e[i],f[j]))) 23 | && (forall i : K_O :: i in e.Keys ==> exists j : K_N :: j in f.Keys && K(i,j) && V(e[i],f[j])) 24 | } 25 | 26 | /* 27 | function Array(t: (o,n) -> bool, e: array, f: array) : (ret : bool) 28 | reads e, f 29 | ensures (e.Length == f.Length && (forall i : int :: ((0 <= i < f.Length && 0 <= i < e.Length) ==> t(e[i],f[i])))) 30 | ==> ret == true 31 | { 32 | e.Length == f.Length && !(exists i : int :: 0 <= i < e.Length && !t(e[i],f[i])) 33 | } 34 | */ 35 | } 36 | 37 | module Translations.MapBuiltinTypes { 38 | export reveals Seq, Set, Map 39 | 40 | function Seq(t: o -> n, e: seq) : (f : seq) 41 | ensures |e| == |f| 42 | ensures forall i : int :: (0 <= i < |e| ==> f[i] == t(e[i])) 43 | { 44 | if |e| == 0 then [] else 45 | seq(|e|, i => if 0 <= i < |e| then t(e[i]) else t(e[0])) 46 | } 47 | function Set(t: o -> n, e: set) : (f : set) 48 | ensures forall x : o :: x in e ==> t(x) in f 49 | ensures forall x : n :: x in f ==> exists y : o :: y in e && t(y) == x 50 | { 51 | set x:o | x in e :: t(x) 52 | } 53 | function Map(K : K_O -> K_N, V : V_O -> V_N, e : map) : (f: map) 54 | { 55 | var fKeys := set x: K_O | x in e.Keys :: K(x); 56 | var fSet := set x:K_O | x in e.Keys :: (K(x),V(e[x])); 57 | map a | a in fKeys :: var b :| (a,b) in fSet; b 58 | } 59 | /* Map helper functions and lemmas -- for proving map size meets standard of map31 */ 60 | function fKeysFunc(T: T_O->T_N, m_O: set) : set 61 | { 62 | set x: T_O | x in m_O :: T(x) 63 | } 64 | function fSetFunc(T: T_O->T_N, U: U_O->U_N, m_O: map) : (res : set<(T_N,U_N)>) 65 | { 66 | var fKeys := fKeysFunc(T,m_O.Keys); 67 | set x:T_O | x in m_O.Keys :: (T(x),U(m_O[x])) 68 | } 69 | function fMapFunc(T: T_O->T_N, U: U_O->U_N, m_O: map) : (res : map) 70 | { 71 | var fKeys := fKeysFunc(T,m_O.Keys); 72 | var fSet := fSetFunc(T,U,m_O); 73 | map a | a in fKeys :: var b :| (a,b) in fSet; b 74 | } 75 | lemma testfKeysSize(T: T_O->T_N, m_O: set) 76 | requires forall x : T_O :: !exists y : T_O :: x != y && T(x) == T(y) 77 | ensures |fKeysFunc(T,m_O)| == |m_O| 78 | { 79 | var fKeys := fKeysFunc(T,m_O); 80 | if |m_O| == 0 { 81 | assert |fKeysFunc(T,m_O)| == |m_O|; 82 | } else { 83 | var x :| x in m_O; 84 | var smallKeys := m_O - {x}; 85 | var fSmallKeys := fKeysFunc(T,smallKeys); 86 | assert fKeys == fSmallKeys + {T(x)}; 87 | assert |fKeys| == |fSmallKeys| + 1; 88 | testfKeysSize(T,smallKeys); 89 | } 90 | } 91 | lemma testMapSize(T: T_O->T_N, U: U_O->U_N, m_O: map) 92 | requires forall x : T_O :: !exists y : T_O :: x != y && T(x) == T(y) 93 | ensures |fMapFunc(T,U,m_O).Keys| == |m_O.Keys|; 94 | { 95 | var fKeys := fKeysFunc(T,m_O.Keys); 96 | testfKeysSize(T,m_O.Keys); 97 | assert |fKeys| == |m_O.Keys|; 98 | var fSet := fSetFunc(T,U,m_O); 99 | var fMap := map a | a in fKeys :: var b :| (a,b) in fSet; b ; 100 | if |m_O| == 0 { 101 | assert |fMap.Keys| == |m_O.Keys|; 102 | } else { 103 | var x :| x in fKeys; 104 | var smallKeys := fKeys - {x}; 105 | var fMapSmall := map a | a in smallKeys :: var b :| (a,b) in fSet; b; 106 | assert fMap.Keys == fKeys; 107 | assert |fMap.Keys| == |fKeys|; 108 | } 109 | } 110 | function mapCreateGeneric(T: T_O->T_N, U: U_O->U_N, m_O: map) : (res : map) 111 | requires forall x : T_O :: !exists y : T_O :: x != y && T(x) == T(y) 112 | ensures forall x :: x in m_O.Keys ==> T(x) in res.Keys 113 | ensures forall y :: y in m_O.Values ==> U(y) in res.Values 114 | ensures |m_O.Keys| == |res.Keys| 115 | { 116 | testfKeysSize(T,m_O.Keys); 117 | var fKeys := fKeysFunc(T,m_O.Keys); 118 | var fSet := fSetFunc(T,U,m_O); 119 | testMapSize(T,U,m_O); 120 | map a | a in fKeys :: var b :| (a,b) in fSet; b 121 | } 122 | /* Set helper functions and lemmas -- for proving set size meets standard of set31 */ 123 | function setCreate2Generic(P : o -> n, S : set) : set 124 | { set x: o | x in S :: P(x) } 125 | lemma setCreateSizeGeneric(P : o -> n, S : set) 126 | ensures |setCreate2Generic(P,S)| <= |S| 127 | { 128 | if |S| == 0 { 129 | } else { 130 | var x: o :| x in S; 131 | assert setCreate2Generic(P,S) == setCreate2Generic(P,S - {x}) + {P(x)}; 132 | setCreateSizeGeneric(P,S - {x}); 133 | } 134 | } 135 | } 136 | /***** TYPECART PRELUDE END *****/ 137 | 138 | 139 | 140 | 141 | 142 | 143 | module Translations { 144 | import Joint 145 | 146 | import Old 147 | 148 | import New 149 | 150 | function TF_expr(e_O: Joint.ExprEval.expr, e_N: Joint.ExprEval.expr):bool 151 | { 152 | match (e_O, e_N) { 153 | case (Const(x_O), Const(x_N)) => 154 | /* unchanged constructor */ (x_O == x_N) 155 | case (Sub(e1_O, e2_O), Sub(e1_N, e2_N)) => 156 | /* unchanged constructor */ (ExprEval.TF_expr(e1_O, e1_N) && ExprEval.TF_expr(e2_O, e2_N)) 157 | case _ => 158 | false 159 | } 160 | 161 | } 162 | 163 | 164 | lemma eval(e_O: Joint.ExprEval.expr, e_N: Joint.ExprEval.expr) 165 | requires ExprEval.TF_expr(e_O, e_N) 166 | ensures (Old.ExprEval.eval(e_O) == New.ExprEval.eval(e_N)) 167 | 168 | 169 | 170 | } 171 | 172 | 173 | -------------------------------------------------------------------------------- /examples/command_exec/new.dfy: -------------------------------------------------------------------------------- 1 | module BigStep { 2 | type Ident = string 3 | 4 | datatype Aexp = 5 | | CONST(n: int) 6 | | VAR(x: Ident) 7 | | PLUS(a1: Aexp, a2: Aexp) 8 | | MINUS(a1: Aexp, a2: Aexp) 9 | 10 | predicate IdInAexp(id: Ident, a: Aexp) { 11 | match a 12 | case CONST(n) => false 13 | case VAR(id') => id == id' 14 | case PLUS(a1, a2) => IdInAexp(id,a1) || IdInAexp(id,a2) 15 | case MINUS(a1, a2) => IdInAexp(id,a1) || IdInAexp(id,a2) 16 | } 17 | 18 | type Store = map 19 | 20 | function Aeval(s: Store, a: Aexp): int 21 | requires forall id: Ident :: IdInAexp(id,a) ==> id in s 22 | { 23 | match a 24 | case CONST(n) => n 25 | case VAR(id) => s[id] 26 | case PLUS(a1, a2) => Aeval(s,a1) + Aeval(s,a2) 27 | case MINUS(a1, a2) => Aeval(s,a1) - Aeval(s,a2) 28 | } 29 | 30 | datatype Bexp = 31 | | TRUE 32 | | FALSE 33 | | EQUAL(a1: Aexp, a2: Aexp) 34 | | LESSEQUAL(a1: Aexp, a2: Aexp) 35 | | NOT(b1: Bexp) 36 | | AND(b1: Bexp, b2: Bexp) 37 | 38 | predicate IdInBexp(id: Ident, b: Bexp) { 39 | match b 40 | case TRUE => true 41 | case FALSE => false 42 | case EQUAL(a1,a2) => IdInAexp(id,a1) || IdInAexp(id,a2) 43 | case LESSEQUAL(a1,a2) => IdInAexp(id,a1) || IdInAexp(id,a2) 44 | case NOT(b) => IdInBexp(id,b) 45 | case AND(b1,b2) => IdInBexp(id,b1) || IdInBexp(id,b2) 46 | } 47 | 48 | function Beval(s: Store, b: Bexp): bool 49 | requires forall id: Ident :: IdInBexp(id,b) ==> id in s 50 | { 51 | match b 52 | case TRUE => true 53 | case FALSE => false 54 | case EQUAL(a1, a2) => Aeval(s,a1) == Aeval(s,a2) 55 | case LESSEQUAL(a1, a2) => Aeval(s,a1) <= Aeval(s,a2) 56 | case NOT(b) => ! Beval(s,b) 57 | case AND(b1,b2) => Beval(s,b1) && Beval(s,b2) 58 | } 59 | 60 | datatype Com = 61 | | SKIP 62 | | ASSIGN(x: Ident, a: Aexp) 63 | | SEQ(c1: Com, c2: Com) 64 | | IFTHENELSE(b: Bexp, c1: Com, c2: Com) 65 | | WHILE(b: Bexp, c1: Com) 66 | 67 | predicate IdInCom(id: Ident, c: Com) { 68 | match c 69 | case SKIP => false 70 | case ASSIGN(id',a) => IdInAexp(id,a) 71 | case SEQ(c1, c2) => IdInCom(id,c1) || IdInCom(id,c2) 72 | case IFTHENELSE(b,c1,c2) => IdInBexp(id,b) || IdInCom(id,c1) || IdInCom(id,c2) 73 | case WHILE(b,c) => IdInBexp(id,b) || IdInCom(id,c) 74 | } 75 | 76 | least predicate Cexec(s1: Store, c: Com, s2: Store) 77 | { 78 | match c 79 | case SKIP => s1 == s2 80 | case ASSIGN(id,a) => 81 | if (forall id: Ident :: IdInAexp(id,a) ==> id in s1) then s2 == s1[id := Aeval(s1,a)] else false 82 | case SEQ(c1, c2) => exists si: Store :: Cexec(s1,c1,si) && Cexec(si,c2,s2) 83 | case IFTHENELSE(b,c1,c2) => 84 | if (forall id: Ident :: IdInBexp(id,b) ==> id in s1) then 85 | (if Beval(s1,b) then Cexec(s1,c1,s2) else Cexec(s1,c2,s2)) 86 | else false 87 | case WHILE(b,c) => 88 | if (forall id: Ident :: IdInBexp(id,b) ==> id in s1) then 89 | if Beval(s1,b) then (exists si: Store :: Cexec(s1,c,si) && Cexec(si,WHILE(b,c),s2)) else s1 == s2 90 | else false 91 | } 92 | 93 | /* EXTENSION BELOW */ 94 | 95 | datatype Option = None | Some(x: T) 96 | 97 | ghost function CexecBounded(fuel: nat, s: Store, c: Com): Option { 98 | if (fuel == 0) then None else 99 | var fuel' := fuel -1; 100 | match c { 101 | case SKIP => Some(s) 102 | case ASSIGN(id, a) => 103 | if (forall id: Ident :: IdInAexp(id,a) ==> id in s) 104 | then Some(s[id := Aeval(s,a)]) else None 105 | case SEQ(c1, c2) => 106 | match CexecBounded(fuel', s, c1) { 107 | case None => None 108 | case Some(s') => CexecBounded(fuel', s', c2) 109 | } 110 | case IFTHENELSE(b, c1, c2) => 111 | if (forall id: Ident :: IdInBexp(id,b) ==> id in s) then 112 | if Beval(s,b) then CexecBounded(fuel', s, c1) else CexecBounded(fuel', s, c2) 113 | else None 114 | case WHILE(b, c1) => 115 | if (forall id: Ident :: IdInBexp(id,b) ==> id in s) then 116 | if Beval(s,b) then 117 | match CexecBounded(fuel', s, c1) { 118 | case None => None 119 | case Some(s') => CexecBounded(fuel', s', WHILE(b, c1)) 120 | } else Some(s) 121 | else None 122 | } 123 | } 124 | 125 | lemma CexecBoundedSound(fuel: nat, s: Store, c: Com, s': Store) 126 | ensures (CexecBounded(fuel, s, c) == Some(s')) ==> Cexec(s, c, s') 127 | { 128 | if (fuel == 0) {} else { 129 | match c { 130 | case SEQ(c1, c2) => { 131 | match CexecBounded(fuel-1, s, c1) { 132 | case Some(si) => { 133 | CexecBoundedSound(fuel-1, s, c1, si); 134 | CexecBoundedSound(fuel-1, si, c2, s'); 135 | } 136 | case _ => {} 137 | } 138 | } 139 | case IFTHENELSE(b, c1, c2) => { 140 | CexecBoundedSound(fuel-1, s, c1, s'); 141 | CexecBoundedSound(fuel-1, s, c2, s'); 142 | } 143 | case WHILE(b, c1) => { 144 | match CexecBounded(fuel-1, s, c1) { 145 | case Some(si) => { 146 | CexecBoundedSound(fuel-1, s, c1, si); 147 | CexecBoundedSound(fuel-1, si, WHILE(b, c1), s'); 148 | } 149 | case _ => {} 150 | } 151 | } 152 | case _ => {} 153 | } 154 | } 155 | } 156 | 157 | lemma CexecBoundedComplete(s: Store, c: Com, s': Store) 158 | ensures Cexec(s, c, s') ==> (exists fuel1 : nat :: forall fuel: nat :: 159 | (fuel >= fuel1) ==> CexecBounded(fuel, s, c) == Some(s')) 160 | { 161 | if (Cexec(s, c, s')) { assume false; 162 | match c { 163 | case SEQ(c1, c2) => { 164 | var si :| Cexec(s,c1,si) && Cexec(si,c2,s'); 165 | CexecBoundedComplete(s, c1, si); 166 | CexecBoundedComplete(si, c2, s'); 167 | } 168 | case IFTHENELSE(b,c1,c2) => { 169 | if (forall id: Ident :: IdInBexp(id,b) ==> id in s) { 170 | if Beval(s,b) { 171 | CexecBoundedComplete(s, c1, s'); 172 | } else { 173 | CexecBoundedComplete(s, c2, s'); 174 | } 175 | } 176 | } 177 | case WHILE(b,c) => { 178 | if (forall id: Ident :: IdInBexp(id,b) ==> id in s) { 179 | if Beval(s,b) { 180 | var si: Store :| Cexec(s,c,si) && Cexec(si,WHILE(b,c),s'); 181 | CexecBoundedComplete(s, c, si); 182 | CexecBoundedComplete(si, WHILE(b,c), s'); 183 | } 184 | } 185 | } 186 | case _ => {} 187 | } 188 | } else {} 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /TypeInjections/Tool/Tool.fs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT 3 | 4 | namespace Tool 5 | 6 | open System.Collections.Generic 7 | open LibGit2Sharp 8 | open System.IO 9 | open TypeInjections 10 | open CommandLine 11 | 12 | module Tool = 13 | 14 | //TODO add 'entryPoint' option to local and git 15 | [] 16 | type LocalOptions = 17 | { [] 18 | oldFolder: string 19 | [] 20 | newFolder: string 21 | [] 22 | outputFolder: string 23 | [] 24 | file: seq } 25 | 26 | [] 27 | type GitOptions = 28 | { [] 29 | oldHash: string 30 | [] 31 | newHash: string 32 | [] 33 | outputFolder: string 34 | [] 35 | url: string 36 | [] 37 | force: bool } 38 | 39 | let checkInputs (paths: string list) = 40 | for p in paths do 41 | // ensure folders are in computer 42 | if not (Directory.Exists(p)) then 43 | failwith $"{p} is not found" 44 | // make sure folder has at least 1 dafny file 45 | let dafnyFiles = Directory.GetFiles(p, "*.dfy") 46 | 47 | if dafnyFiles.Length = 0 then 48 | failwith $"{p} does not contain any dafny files" 49 | 50 | //NOTE 'location' refers to either the github url OR path to repo on local computer 51 | let checkoutCommit (hash: string) (location: string) (outputPath: string) : unit = 52 | try 53 | Repository.Clone(location, outputPath) |> ignore 54 | with :? LibGit2SharpException -> 55 | if Directory.Exists(location) then 56 | failwith "this location does not contain git repository" 57 | else 58 | failwith "bad URL, private or nonexistent repo" 59 | // get the Repository object of repository copied to output directory 60 | let newRepo = new Repository(outputPath) 61 | // try to find the commit that user inputted in git logs 62 | let commitRepo = newRepo.Lookup(hash) 63 | // checkout the repo at the given commit 64 | Commands.Checkout(newRepo, commitRepo) |> ignore 65 | // ensure that the checked out repo has dafny file(s) 66 | checkInputs [ outputPath ] 67 | 68 | let runGit (git: GitOptions) : unit = 69 | Utils.log "***** git commit" 70 | 71 | let commit1, commit2 = git.oldHash, git.newHash 72 | let path = git.outputFolder 73 | let pathOld = $"{path}/Old" 74 | let pathNew = $"{path}/New" 75 | 76 | let location = 77 | match git.url.Length with 78 | // empty git url means the user wants to run typeCart in current directory 79 | | 0 -> Directory.GetCurrentDirectory() 80 | // if there is a repo URL, use it 81 | | _ -> 82 | Utils.log $"Path to repo is {path}" 83 | git.url 84 | 85 | // lib2git needs an empty folder to store the repo clones 86 | for a in [ pathOld; pathNew ] do 87 | if Directory.Exists(a) then 88 | // if the user lets us delete existing folder 89 | if git.force then 90 | Directory.Delete(a, true) 91 | // else break program 92 | else 93 | failwith 94 | $"{a} already exists. We need an empty folder to place checkout git repo. 95 | Choose different output folder or choose --force to automatically overwrite folder data" 96 | 97 | checkoutCommit commit1 location pathOld 98 | checkoutCommit commit2 location pathNew 99 | 100 | checkInputs [ pathNew; pathOld ] 101 | 102 | Program.main [| pathOld 103 | pathNew 104 | $"{path}/Output" |] 105 | |> ignore 106 | 107 | Utils.log $"{pathOld}, {pathNew}, {path}/Output" 108 | 109 | let runLocal (local: LocalOptions) : unit = 110 | Utils.log "***** local project comparison" 111 | 112 | checkInputs [ local.oldFolder 113 | local.newFolder ] 114 | 115 | if not (Directory.Exists(local.outputFolder)) then 116 | failwith "Could not find directory" 117 | 118 | Utils.log $"{local.oldFolder}, {local.newFolder}, {local.outputFolder}" 119 | 120 | Program.main [| local.oldFolder 121 | local.newFolder 122 | local.outputFolder |] 123 | |> ignore 124 | 125 | [] 126 | let main (argv: string array) = 127 | // command line parser stores the error in IEnumerable, need to extract 128 | let getError (errors: IEnumerable) : Error = 129 | (errors |> Seq.cast |> Seq.toList).Head 130 | 131 | (* 132 | ParseArguments connects the user input to a "verb", 133 | returns a dictionary of option names and values, 134 | throws error message (and returns NotParsed) for two reasons: 135 | 1. unable to identify verb 136 | 2. if user asks for help, method prints help message (called helperErrorMessage) 137 | *) 138 | let result = 139 | Parser.Default.ParseArguments argv 140 | 141 | // if verb found, result = Parsed = map of options and values 142 | // if no verb found or help asked for, result = NotParsed = list of error messages 143 | match result with 144 | | :? (Parsed) as command -> 145 | match command.Value with 146 | | :? LocalOptions as opts -> runLocal opts 147 | | :? GitOptions as opts -> runGit opts 148 | | _ -> () 149 | // nothing to do if result=NotParsed because helpful error message 150 | // was already thrown by ParseArgument 151 | | :? (NotParsed) as errors -> 152 | // see what the error is 153 | match (getError errors.Errors) with 154 | | :? BadVerbSelectedError -> failwith $"bad verb" 155 | | :? HelpRequestedError -> failwith "user just wanted help!" 156 | | _ -> failwith "unknown error, need to investigate" 157 | | _ -> () 158 | 159 | 0 160 | --------------------------------------------------------------------------------