├── examples ├── FileIO │ ├── data.txt │ ├── ReadBytesFromFile.dfy │ └── WriteBytesToFile.dfy ├── LittleEndianNat │ ├── LittleEndianNatExample.dfy.expect │ ├── LittleEndianNatCustomExample.dfy.expect │ ├── LittleEndianNatCustomExample.dfy │ └── LittleEndianNatExample.dfy ├── BoundedIntsExamples.dfy ├── MathExamples.dfy ├── Collections │ ├── Sets │ │ └── Sets.dfy │ └── Arrays │ │ └── BinarySearch.dfy ├── Unicode │ ├── UnicodeExamples.dfy │ ├── Utf16EncodingFormExamples.dfy │ └── Utf8EncodingFormExamples.dfy ├── MutableMap │ ├── MutableMapTrait.dfy │ └── MutableMapExamples.dfy ├── RelationsExamples.dfy └── BinaryOperations.dfy ├── src ├── dafny │ ├── Unicode │ │ ├── Unicode.md │ │ ├── Unicode.dfy │ │ └── Utf8EncodingScheme.dfy │ ├── Collections │ │ ├── Collections.dfy │ │ ├── Collections.md │ │ ├── Arrays.dfy │ │ ├── Isets.dfy │ │ └── Imaps.dfy │ ├── NonlinearArithmetic │ │ ├── NonlinearArithmetic.dfy │ │ ├── Internals │ │ │ ├── Internals.dfy │ │ │ ├── GeneralInternals.dfy │ │ │ ├── DivInternalsNonlinear.dfy │ │ │ ├── ModInternalsNonlinear.dfy │ │ │ └── MulInternalsNonlinear.dfy │ │ ├── README.md │ │ ├── NonlinearArithmetic.md │ │ └── Power2.dfy │ ├── DafnyCore.dfy │ ├── Math.md │ ├── BinaryOperations.md │ ├── Math.dfy │ ├── FileIO │ │ ├── README.md │ │ ├── FileIO.dfy │ │ ├── FileIO.js │ │ ├── FileIO.cs │ │ └── FileIO.java │ ├── Relations.md │ ├── BoundedInts.dfy │ ├── BoundedInts.md │ ├── DafnyCore.md │ └── Relations.dfy ├── JSON │ ├── Tutorial.dfy.expect │ ├── Values.dfy │ ├── Utils │ │ ├── Seq.dfy │ │ ├── Lexers.dfy │ │ ├── Parsers.dfy │ │ ├── Views.Writers.dfy │ │ └── Views.dfy │ ├── API.dfy │ ├── ZeroCopy │ │ └── API.dfy │ ├── ConcreteSyntax.SpecProperties.dfy │ ├── Errors.dfy │ ├── ConcreteSyntax.Spec.dfy │ ├── Tests.dfy │ └── Grammar.dfy ├── NonlinearArithmetic │ ├── README.md │ ├── Internals │ │ ├── GeneralInternals.dfy │ │ ├── DivInternalsNonlinear.dfy │ │ ├── ModInternalsNonlinear.dfy │ │ └── MulInternalsNonlinear.dfy │ ├── Logarithm.dfy │ └── Power2.dfy ├── Math.dfy ├── Functions.dfy ├── MutableMap │ ├── README.md │ ├── MutableMap.java │ └── MutableMap.dfy ├── Collections │ ├── Arrays │ │ └── BinarySearch.dfy │ ├── Sets │ │ └── Isets.dfy │ ├── Sequences │ │ └── MergeSort.dfy │ └── Maps │ │ ├── Imaps.dfy │ │ └── Maps.dfy ├── FileIO │ ├── README.md │ ├── FileIO.js │ ├── FileIO.dfy │ ├── FileIO.cs │ └── FileIO.java ├── Unicode │ ├── UnicodeStringsWithoutUnicodeChar.dfy │ ├── UnicodeStrings.dfy │ ├── Unicode.dfy │ ├── Utf8EncodingScheme.dfy │ └── UnicodeStringsWithUnicodeChar.dfy ├── BoundedInts.dfy ├── Wrappers.dfy └── Relations.dfy ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── check-format │ ├── generate-test-report.yml │ ├── tests.yml │ ├── nightly.yml │ ├── check-format.yml │ ├── check-examples-in-docs.yml │ └── reusable-tests.yml ├── index.md ├── STYLE.md ├── .gitignore ├── Scripts ├── test-exit.py └── pydiff.py ├── README.md └── README-TESTING.md /examples/FileIO/data.txt: -------------------------------------------------------------------------------- 1 | Hello world 2 | Goodbye 3 | -------------------------------------------------------------------------------- /src/dafny/Unicode/Unicode.md: -------------------------------------------------------------------------------- 1 | 2 | ## The `Unicode` module {#sec-unicode} 3 | 4 | _Not yet written_ 5 | -------------------------------------------------------------------------------- /src/dafny/Collections/Collections.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" > "%t" 2 | 3 | module Collections { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /examples/LittleEndianNat/LittleEndianNatExample.dfy.expect: -------------------------------------------------------------------------------- 1 | 2 | Dafny program verifier finished with 1 verified, 0 errors 3 | -------------------------------------------------------------------------------- /examples/LittleEndianNat/LittleEndianNatCustomExample.dfy.expect: -------------------------------------------------------------------------------- 1 | 2 | Dafny program verifier finished with 76 verified, 0 errors 3 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license. 2 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | - [Dafny Core library](src/dafny/DafnyCore) --- basic reusable functionality 5 | -------------------------------------------------------------------------------- /src/JSON/Tutorial.dfy.expect: -------------------------------------------------------------------------------- 1 | 2 | Dafny program verifier finished with 3 verified, 0 errors 3 | JSON.Examples.AbstractSyntax.Test: PASSED 4 | JSON.Examples.ConcreteSyntax.Test: PASSED 5 | -------------------------------------------------------------------------------- /src/dafny/NonlinearArithmetic/NonlinearArithmetic.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | include "DivMod.dfy" 4 | include "Multiply.dfy" 5 | include "Power.dfy" 6 | include "Power2.dfy" 7 | include "Internals/Internals.dfy" 8 | -------------------------------------------------------------------------------- /.github/workflows/check-format: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | FAIL=0 4 | for f in `find $1 -name '*.dfy'`; do \ 5 | echo $f ;\ 6 | dafny format --check $f || ( dafny format --print $f | diff $f - ) ;\ 7 | if [ "$?" != 0 ] ; then FAIL=1 ; fi; \ 8 | done 9 | exit $FAIL 10 | -------------------------------------------------------------------------------- /STYLE.md: -------------------------------------------------------------------------------- 1 | The Dafny Style Guide is maintained as part of the Dafny project, 2 | [here](https://dafny.org/dafny/StyleGuide/Style-Guide). 3 | 4 | (The more or less duplicate copy that was part of the libraries repo has 5 | been combined with the project style guide to avoid duplication and divergence.) 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | **/Output/ 3 | **/TestResults/ 4 | examples/FileIO/ReadBytesFromFile.cs 5 | examples/FileIO/ReadBytesFromFile.jar 6 | examples/FileIO/ReadBytesFromFile.js 7 | examples/FileIO/WriteBytesToFile.cs 8 | examples/FileIO/WriteBytesToFile.jar 9 | examples/FileIO/WriteBytesToFile.js 10 | -------------------------------------------------------------------------------- /src/dafny/NonlinearArithmetic/Internals/Internals.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | include "DivInternals.dfy" 4 | include "DivInternalsNonlinear.dfy" 5 | include "GeneralInternals.dfy" 6 | include "ModInternals.dfy" 7 | include "ModInternalsNonlinear.dfy" 8 | include "MulInternals.dfy" 9 | include "MulInternalsNonlinear.dfy" 10 | -------------------------------------------------------------------------------- /examples/BoundedIntsExamples.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %run %s 2 | 3 | include "../src/dafny/BoundedInts.dfy" 4 | 5 | import opened Dafny.BoundedInts 6 | method m(x: uint32, z: nat16) { 7 | 8 | assert TWO_TO_THE_15 * 2 == TWO_TO_THE_16; 9 | 10 | assert 0 <= (x as int) < TWO_TO_THE_32; 11 | var y: uint64 := x as int as uint64; 12 | var int16 := z as int as int16; 13 | var uint16 := z as int as uint16; 14 | } 15 | -------------------------------------------------------------------------------- /src/dafny/DafnyCore.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" > "%t" 2 | 3 | include "./BoundedInts.dfy" 4 | include "./Wrappers.dfy" 5 | include "./Collections/Collections.dfy" 6 | include "./FileIO/FileIO.dfy" 7 | include "./Math.dfy" 8 | include "./NonlinearArithmetic/NonlinearArithmetic.dfy" 9 | include "./Relations.dfy" 10 | include "./Unicode/Unicode.dfy" 11 | include "./BinaryOperations.dfy" 12 | 13 | 14 | module Dafny { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/dafny/Math.md: -------------------------------------------------------------------------------- 1 | 2 | ## The `Math` module {#sec-math} 3 | 4 | The `Math` module currently contains a few integer-related math functions (e.g. Min, Max, Abs). 5 | - Functions over integers using multiply, divide and modulo are handled in the 6 | `NonlinearArith` module, along with lemmas about those functions. 7 | - Integer-related functions over collections of integers (e.g. minimum of a sequence) are 8 | available in the `Collections` module. -------------------------------------------------------------------------------- /.github/workflows/generate-test-report.yml: -------------------------------------------------------------------------------- 1 | name: 'Generate Test Report' 2 | on: 3 | workflow_run: 4 | workflows: ['tests'] # runs after CI workflow 5 | types: 6 | - completed 7 | jobs: 8 | report: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: dorny/test-reporter@v1 12 | with: 13 | artifact: verification-results 14 | name: Verification Results 15 | path: '*.trx' 16 | reporter: dotnet-trx -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Dafny Core verification 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: [ master ] 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | verification: 14 | strategy: 15 | matrix: 16 | version: [ 4.0.0, 4.1.0 ] 17 | 18 | uses: ./.github/workflows/reusable-tests.yml 19 | with: 20 | dafny-version: ${{ matrix.version }} 21 | -------------------------------------------------------------------------------- /src/JSON/Values.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | module {:options "-functionSyntax:4"} JSON.Values { 4 | datatype Decimal = 5 | Decimal(n: int, e10: int) // (n) * 10^(e10) 6 | 7 | function Int(n: int): Decimal { 8 | Decimal(n, 0) 9 | } 10 | 11 | datatype JSON = 12 | | Null 13 | | Bool(b: bool) 14 | | String(str: string) 15 | | Number(num: Decimal) 16 | | Object(obj: seq<(string, JSON)>) // Not a map to preserve order 17 | | Array(arr: seq) 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | name: Nightly Dafny Core verification 2 | 3 | on: 4 | # Scheduled to be run sometime after the nightly build of dafny 5 | schedule: 6 | - cron: "7 10 * * *" 7 | workflow_dispatch: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | verification: 15 | strategy: 16 | matrix: 17 | version: [ nightly-latest ] 18 | 19 | uses: ./.github/workflows/reusable-tests.yml 20 | with: 21 | dafny-version: ${{ matrix.version }} 22 | -------------------------------------------------------------------------------- /examples/MathExamples.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | include "../src/dafny/Math.dfy" 4 | include "../src/dafny/Relations.dfy" 5 | 6 | import Dafny.Math 7 | import Dafny.Relations 8 | 9 | method m() { 10 | assert Math.Max(-2, -3) == -2; 11 | assert Math.Min(-2, -3) == -3; 12 | assert Math.Abs(07) == Math.Abs(7) == 7; 13 | assert Relations.Associative(Math.Min); 14 | assert Relations.Commutative(Math.Max); 15 | } 16 | 17 | lemma MinAssociates(a: int, b: int, c: int) 18 | ensures Math.Min3(a,b,c) == Math.Min3(c,b,a) 19 | {} 20 | 21 | lemma MaxAssociates(a: int, b: int, c: int) 22 | ensures Math.Max3(a,b,c) == Math.Max3(c,b,a) 23 | {} 24 | -------------------------------------------------------------------------------- /src/JSON/Utils/Seq.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | module {:options "-functionSyntax:4"} JSON.Utils.Seq { 4 | lemma Neutral(l: seq) 5 | ensures l == l + [] 6 | {} 7 | 8 | lemma Assoc(a: seq, b: seq, c: seq) 9 | // `a + b + c` is parsed as `(a + b) + c` 10 | ensures a + b + c == a + (b + c) 11 | {} 12 | 13 | 14 | lemma Assoc'(a: seq, b: seq, c: seq) 15 | // `a + b + c` is parsed as `(a + b) + c` 16 | ensures a + (b + c) == a + b + c 17 | {} 18 | 19 | lemma Assoc2(a: seq, b: seq, c: seq, d: seq) 20 | // `a + b + c + d` is parsed as `((a + b) + c) + d` 21 | ensures a + b + c + d == a + (b + c + d) 22 | {} 23 | } 24 | -------------------------------------------------------------------------------- /src/NonlinearArithmetic/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Non-linear math is generally undecidable; hence Dafny (and Z3) rely on heuristics to prove such properties. 4 | While wonderful when they work, these heuristics can lead to unstable proofs. Hence, in many projects, 5 | it can be helpful to turn off these heuristics (via Dafny's `/noNLarith` flag) and instead explicitly 6 | invoke the lemmas in this library. All files in this portion of the library (except those in `Internals/*Nonlinear.dfy`) 7 | verify with the `/noNLarith` flag, which should keep the library itself stable. 8 | 9 | In general, it shouldn't be necessary to directly reference anything in `Internals`. 10 | -------------------------------------------------------------------------------- /src/dafny/NonlinearArithmetic/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Non-linear math is generally undecidable; hence Dafny (and Z3) rely on heuristics to prove such properties. 4 | While wonderful when they work, these heuristics can lead to unstable proofs. Hence, in many projects, 5 | it can be helpful to turn off these heuristics (via Dafny's `/noNLarith` flag) and instead explicitly 6 | invoke the lemmas in this library. All files in this portion of the library (except those in `Internals/*Nonlinear.dfy`) 7 | verify with the `/noNLarith` flag, which should keep the library itself stable. 8 | 9 | In general, it shouldn't be necessary to directly reference anything in `Internals`. 10 | -------------------------------------------------------------------------------- /src/Math.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | module {:options "-functionSyntax:4"} Math { 9 | function Min(a: int, b: int): int 10 | { 11 | if a < b 12 | then a 13 | else 14 | b 15 | } 16 | 17 | function Max(a: int, b: int): int 18 | { 19 | if a < b 20 | then b 21 | else 22 | a 23 | } 24 | 25 | function Abs(a: int): (a': int) 26 | ensures a' >= 0 27 | { 28 | if a >= 0 then a else -a 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/dafny/BinaryOperations.md: -------------------------------------------------------------------------------- 1 | 2 | ## The `Binary Operations` module {#sec-binaryop} 3 | 4 | The `Binary Operations` module defines a number of properties of binary operations. 5 | 6 | Properties of binary operations `bop`: 7 | - Associative: `bop(bop(x, y), z) == bop(x, bop(y, z))` 8 | - Unital: `bop(unit, x) == x == bop(x, unit)` 9 | - Abelian: `bop(x, y) == bop(y, x)` 10 | - Inverse: `bop(x, inverse(x)) == unit == bop(inverse(x), x)` 11 | - Distributive: `bop2(bop1(x, y), z) == bop1(bop2(x, z), bop2(y, z))` and `bop2(x, bop1(y, z)) == bop1(bop2(x, y), bop2(x, z))` 12 | - Monoid: Associative, Unital 13 | - Group: Monoid, Inverse 14 | - Abelian Group: Abelian, Group 15 | - Ring: AbelianGroup A, Monoid B, Distributive 16 | - Field: Abelian Group A, Abelian Group B (without 0), Distributive -------------------------------------------------------------------------------- /.github/workflows/check-format.yml: -------------------------------------------------------------------------------- 1 | # This workflow does static verification of the legacy modules 2 | name: Check formatting 3 | 4 | on: 5 | workflow_dispatch: 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | formatting: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - name: Install Dafny 16 | uses: dafny-lang/setup-dafny-action@v1.6.1 17 | with: 18 | dafny-version: "4.0.0" 19 | 20 | - name: Install lit 21 | run: pip install lit OutputCheck 22 | 23 | - name: Set up JS dependencies 24 | run: npm install bignumber.js 25 | 26 | - name: Check exe 27 | run: | 28 | dafny --version 29 | 30 | - name: Check formatting 31 | run: | 32 | chmod +x ./.github/workflows/check-format 33 | ./.github/workflows/check-format src 34 | -------------------------------------------------------------------------------- /src/Functions.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Original Copyright under the following: 5 | * Copyright 2018-2021 VMware, Inc., Microsoft Inc., Carnegie Mellon University, 6 | * ETH Zurich, and University of Washington 7 | * SPDX-License-Identifier: BSD-2-Clause 8 | * 9 | * Copyright (c) Microsoft Corporation 10 | * SPDX-License-Identifier: MIT 11 | * 12 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 13 | * SPDX-License-Identifier: MIT 14 | *******************************************************************************/ 15 | 16 | module {:options "-functionSyntax:4"} Functions { 17 | ghost predicate Injective(f: X-->Y) 18 | reads f.reads 19 | requires forall x :: f.requires(x) 20 | { 21 | forall x1, x2 :: f(x1) == f(x2) ==> x1 == x2 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/dafny/NonlinearArithmetic/NonlinearArithmetic.md: -------------------------------------------------------------------------------- 1 | 2 | ## The `NonlinearArith` module {#sec-nonlinear-arith} 3 | 4 | Non-linear math uses operations like multiplication, division and modulo (where neither argument is a literal integer). Reasoning problems using non-linear arithmetic are generally undecidable; hence Dafny (and Z3) rely on heuristics to prove such properties. 5 | While wonderful when they work, these heuristics can lead to unstable proofs. Hence, in many projects, 6 | it can be helpful to turn off these heuristics (via Dafny's `--disable-nonlinear-arithmetic` flag) and instead explicitly 7 | invoke the lemmas in this library. All files in this portion of the library (except those in `Internals/*Nonlinear.dfy`) 8 | verify with the `--disable-nonlinear-arithmetic` flag, which should keep the library itself stable. 9 | 10 | In general, it shouldn't be necessary to directly reference anything in `Internals`. 11 | 12 | -------------------------------------------------------------------------------- /examples/Collections/Sets/Sets.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../../../src/Collections/Sets/Sets.dfy" 9 | 10 | module {:options "-functionSyntax:4"} SetsExamples { 11 | 12 | import opened Sets 13 | 14 | method TestExtract() { 15 | var s: set := {0, 1}; 16 | var t: set := {0}; 17 | 18 | var x := ExtractFromNonEmptySet(s); 19 | assert x == 0 || x == 1; 20 | // assert x == 0; fails due to non-determinacy 21 | // assert x == 1; fails due to non-determinacy 22 | 23 | var y := ExtractFromSingleton(t); 24 | assert y == 0; 25 | 26 | // var z := ExtractFromSingleton(s); fails as s is not a singleton 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /examples/Unicode/UnicodeExamples.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../../src/Unicode/Unicode.dfy" 9 | 10 | module UnicodeExamples { 11 | import opened Unicode 12 | 13 | const TEST_ASSIGNED_PLANE_CODE_POINTS: set := { 14 | 0x000055, // Latin capital letter U 15 | 0x01F11D, // Parenthesized Latin capital letter N 16 | 0x02053C, // CJK unified ideograph 𠔼 17 | 0x030256, // CJK unified ideograph 𰉖 18 | 0x0E004F, // Tag Latin capital letter O 19 | 0x0FDDDD, // arbitrary code point in Private Use Area-A 20 | 0x10EEEE // arbitrary code point in Private Use Area-B 21 | } 22 | 23 | lemma LemmaAssignedCodePoints() 24 | ensures forall p | p in TEST_ASSIGNED_PLANE_CODE_POINTS :: IsInAssignedPlane(p) 25 | { 26 | reveal IsInAssignedPlane(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Scripts/test-exit.py: -------------------------------------------------------------------------------- 1 | ## This small script takes as first argument a test indicator 2 | ## and the rest of the arguments are a command to run, with its 3 | ## command-line arguments. 4 | ## The script runs the command and then if the first argument is 5 | ## (1) -any -- returns success, ignoring the exit code of the command 6 | ## (2) -z -- returns success iff the exit code is non-zero 7 | ## (3) some integer literal -- return success iff the exit code 8 | ## matches the given integer 9 | ## (4) -skip -- does not run the command and just exits with success 10 | 11 | import sys 12 | import subprocess 13 | 14 | arg = sys.argv[1] 15 | if arg == "-skip": 16 | print( 'Skipping' ) 17 | exit(0) 18 | 19 | p = subprocess.run( sys.argv[2:] ) 20 | 21 | if arg == "-any": 22 | exit(0) 23 | if arg == "-z": 24 | if p.returncode == 0 : 25 | print( 'Expected non-zero exit code' ) 26 | exit(1) 27 | else: 28 | exit(0) 29 | if p.returncode != int(arg) : 30 | print( 'Expected exit code', arg, ', actual result is', p.returncode ) 31 | exit(1) 32 | 33 | exit(0) 34 | -------------------------------------------------------------------------------- /.github/workflows/check-examples-in-docs.yml: -------------------------------------------------------------------------------- 1 | name: Checking examples in library documentation 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: [ master ] 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | check-examples-in-docs: 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | version: [ 4.0.0 ] 18 | os: [ ubuntu-latest ] 19 | 20 | runs-on: ${{ matrix.os }} 21 | steps: 22 | - uses: actions/checkout@v3 23 | 24 | - name: Install Dafny 25 | uses: dafny-lang/setup-dafny-action@v1.6.1 26 | with: 27 | dafny-version: ${{ matrix.version }} 28 | 29 | - name: Version information 30 | run: | 31 | dafny --version 32 | echo ${{ matrix.os }} ${{ runner.os }} ${{ matrix.version }} 33 | 34 | - name: Set up JS dependencies 35 | run: npm install bignumber.js 36 | 37 | - name: Verify Code and Examples 38 | run: | 39 | ./Scripts/check-examples `find . -name '*.md'` 40 | -------------------------------------------------------------------------------- /src/dafny/Math.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | /** Defines various integer-math functions */ 9 | module {:options "-functionSyntax:4"} Dafny.Math { 10 | 11 | /** Minimum of two integers */ 12 | function Min(a: int, b: int): int 13 | { 14 | if a < b 15 | then a 16 | else b 17 | } 18 | 19 | /** Minimum of three integers */ 20 | function Min3(a: int, b: int, c: int): int 21 | { 22 | Min(a, Min(b, c)) 23 | } 24 | 25 | /** Maximum of two integers */ 26 | function Max(a: int, b: int): int 27 | { 28 | if a < b 29 | then b 30 | else a 31 | } 32 | 33 | /** Maximum of three integers */ 34 | function Max3(a: int, b: int, c: int): int 35 | { 36 | Max(a, Max(b, c)) 37 | } 38 | 39 | /** Integer absolute value */ 40 | function Abs(a: int): int 41 | { 42 | if a < 0 43 | then -a 44 | else a 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/MutableMap/README.md: -------------------------------------------------------------------------------- 1 | # Mutable Map 2 | 3 | The `MutableMap` module introduces mutable maps for Dafny. At the moment, the API is intentionally limited in scope, and only supports compilation to Java. For the future, we plan to extend both the functionality and the range of supported languages. 4 | 5 | To use `MutableMap` in your code, you must: 6 | 7 | 1. `include` and `import` the `MutableMap` module as you would any other library module 8 | 2. incorporate the corresponding language-specific implementation file (that is, currently, `MutableMap.java`) when building or running your program 9 | 10 | For example, to run the `MutableMapExamples.dfy` file in the `examples/MutableMap` directory that depends on the `MutableMap` module, run the following. 11 | 12 | ```bash 13 | # Java 14 | $ dafny run MutableMapExamples.dfy --target:java --input ../../src/MutableMap/MutableMap.java 15 | ``` 16 | 17 | (If you aren't using `dafny run` to run your program, 18 | then you should instead integrate the appropriate language-specific implementation file in your build system.) 19 | 20 | The `examples/MutableMap` directory contains more detailed examples of how to use the `MutableMap` module. -------------------------------------------------------------------------------- /src/Collections/Arrays/BinarySearch.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../../Wrappers.dfy" 9 | include "../../Relations.dfy" 10 | 11 | module BinarySearch { 12 | import opened Wrappers 13 | import opened Relations 14 | 15 | method BinarySearch(a: array, key: T, less: (T, T) -> bool) returns (r: Option) 16 | requires SortedBy(a[..], (x, y) => less(x, y) || x == y) 17 | requires StrictTotalOrdering(less) 18 | ensures r.Some? ==> r.value < a.Length && a[r.value] == key 19 | ensures r.None? ==> key !in a[..] 20 | { 21 | var lo, hi : nat := 0, a.Length; 22 | while lo < hi 23 | invariant 0 <= lo <= hi <= a.Length 24 | invariant key !in a[..lo] && key !in a[hi..] 25 | invariant a[..] == old(a[..]) 26 | { 27 | var mid := (lo + hi) / 2; 28 | 29 | if less(key, a[mid]) { 30 | hi := mid; 31 | } else if less(a[mid], key) { 32 | lo:= mid + 1; 33 | } else { 34 | return Some(mid); 35 | } 36 | } 37 | 38 | return None; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/dafny/Collections/Collections.md: -------------------------------------------------------------------------------- 1 | 2 | ## The `Collections` module {#sec-collections} 3 | 4 | This module includes a variety of submodules that define properties of 5 | various collections: 6 | 7 | - Sets: functions and lemmas expressing properties of Dafny's `set` type, such as properties of subsets, unions, and intersections, filtering of sets using predicates, mapping sets to other sets using functions 8 | 9 | - Isets: function and lemmas to manipulate `iset`s 10 | 11 | - Seqs: functions and lemmas expressing properties of Dafny's `seq` type, 12 | such as properties of subsequences, filtering of sequences, 13 | finding elements (i.e., index-of, last-index-of), 14 | inserting or removing elements, 15 | reversing, repeating or combining (zipping or flattening) sequences, 16 | sorting, and 17 | conversion to sets. 18 | 19 | - Maps: functions and lemmas to manipulate `map`s 20 | 21 | - Imaps: functions and lemmas to manipulate `imap`s 22 | 23 | - Arrays: manipulations of arrays 24 | 25 | - LittleEndianNat: properties of sequences that represent natural numbers expressed as a given base, with the least significant digit at position 0 of the sequence and each element in the range 0 to the base. 26 | - LittleEndianNatConversions: conversions between two little-endian representations of nats in different bases 27 | -------------------------------------------------------------------------------- /src/JSON/API.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" --unicode-char:false ../Unicode/UnicodeStringsWithoutUnicodeChar.dfy 2 | // RUN: %verify "%s" --unicode-char:true ../Unicode/UnicodeStringsWithUnicodeChar.dfy 3 | 4 | include "Serializer.dfy" 5 | include "Deserializer.dfy" 6 | include "ZeroCopy/API.dfy" 7 | 8 | module {:options "-functionSyntax:4"} JSON.API { 9 | import opened BoundedInts 10 | import opened Errors 11 | import Values 12 | import Serializer 13 | import Deserializer 14 | import ZeroCopy = ZeroCopy.API 15 | 16 | function {:opaque} Serialize(js: Values.JSON) : (bs: SerializationResult>) 17 | { 18 | var js :- Serializer.JSON(js); 19 | ZeroCopy.Serialize(js) 20 | } 21 | 22 | method SerializeAlloc(js: Values.JSON) returns (bs: SerializationResult>) 23 | { 24 | var js :- Serializer.JSON(js); 25 | bs := ZeroCopy.SerializeAlloc(js); 26 | } 27 | 28 | method SerializeInto(js: Values.JSON, bs: array) returns (len: SerializationResult) 29 | modifies bs 30 | { 31 | var js :- Serializer.JSON(js); 32 | len := ZeroCopy.SerializeInto(js, bs); 33 | } 34 | 35 | function {:opaque} Deserialize(bs: seq) : (js: DeserializationResult) 36 | { 37 | var js :- ZeroCopy.Deserialize(bs); 38 | Deserializer.JSON(js) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/LittleEndianNat/LittleEndianNatCustomExample.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %run --disable-nonlinear-arithmetic "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../../src/Collections/Sequences/LittleEndianNatConversions.dfy" 9 | 10 | module {:options "--function-syntax:4"} Uint3_6 refines LittleEndianNatConversions { 11 | 12 | module Uint3Seq refines SmallSeq { 13 | function BITS(): nat { 3 } 14 | } 15 | module Uint6Seq refines LargeSeq { 16 | import Small = Uint3Seq 17 | function BITS(): nat { 6 } 18 | } 19 | import opened Large = Uint6Seq 20 | import Small = Large.Small 21 | 22 | } 23 | 24 | module {:options "--function-syntax:4"} LittleEndianNatCustomExample { 25 | 26 | import opened Uint3_6 27 | 28 | method Main() { 29 | var n := 165; 30 | 31 | // Convert n to uint3 and uint6 sequences 32 | var smallSeq, largeSeq := Small.FromNat(n), Large.FromNat(n); 33 | expect smallSeq == [5, 4, 2]; 34 | expect largeSeq == [37, 2]; 35 | 36 | Small.LemmaNatSeqNat(n); 37 | Large.LemmaNatSeqNat(n); 38 | assert Small.ToNatRight(smallSeq) == Large.ToNatRight(largeSeq) == n; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/dafny/Collections/Arrays.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../Wrappers.dfy" 9 | include "../Relations.dfy" 10 | include "Seqs.dfy" 11 | 12 | module Dafny.Collections.Arrays { 13 | 14 | import opened Wrappers 15 | import opened Relations 16 | import opened Seq 17 | 18 | method BinarySearch(a: array, key: T, less: (T, T) -> bool) returns (r: Option) 19 | requires SortedBy(a[..], (x, y) => less(x, y) || x == y) 20 | requires StrictTotalOrdering(less) 21 | ensures r.Some? ==> r.value < a.Length && a[r.value] == key 22 | ensures r.None? ==> key !in a[..] 23 | { 24 | var lo, hi : nat := 0, a.Length; 25 | while lo < hi 26 | invariant 0 <= lo <= hi <= a.Length 27 | invariant key !in a[..lo] && key !in a[hi..] 28 | invariant a[..] == old(a[..]) 29 | { 30 | var mid := (lo + hi) / 2; 31 | 32 | if less(key, a[mid]) { 33 | hi := mid; 34 | } else if less(a[mid], key) { 35 | lo:= mid + 1; 36 | } else { 37 | return Some(mid); 38 | } 39 | } 40 | 41 | return None; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /examples/Unicode/Utf16EncodingFormExamples.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../../src/Unicode/Utf16EncodingForm.dfy" 9 | 10 | module Utf16EncodingFormExamples { 11 | import Unicode 12 | import opened Utf16EncodingForm 13 | 14 | const TEST_SCALAR_VALUES: seq<(Unicode.ScalarValue, WellFormedCodeUnitSeq)> := [ 15 | // One code unit: dollar sign 16 | (0x0024, [0x0024]), 17 | // Two code units: money bag emoji 18 | (0x1F4B0, [0xD83D, 0xDCB0]) 19 | ] 20 | 21 | lemma TestEncodeScalarValue() 22 | ensures forall pair | pair in TEST_SCALAR_VALUES 23 | :: EncodeScalarValue(pair.0) == pair.1 24 | {} 25 | 26 | // Because surrogate code points are not Unicode scalar values, isolated UTF-16 code units in the range 27 | // D800_16 .. DFFF_16 are ill-formed. (Section 3.9 D91) 28 | const TEST_ILL_FORMED_SEQUENCES: seq := [ 29 | [0xD800], 30 | [0xDABC], 31 | [0xDFFF] 32 | ] 33 | 34 | lemma TestDecodeIllFormedSequence() 35 | ensures forall s | s in TEST_ILL_FORMED_SEQUENCES 36 | :: DecodeCodeUnitSequenceChecked(s).None? 37 | {} 38 | } 39 | -------------------------------------------------------------------------------- /src/NonlinearArithmetic/Internals/GeneralInternals.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify --disable-nonlinear-arithmetic "%s" 2 | 3 | /******************************************************************************* 4 | * Original: Copyright (c) Microsoft Corporation 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 8 | * SPDX-License-Identifier: MIT 9 | *******************************************************************************/ 10 | 11 | module {:options "-functionSyntax:4"} GeneralInternals { 12 | 13 | /* this predicate is primarily used as a trigger */ 14 | ghost predicate IsLe(x: int, y: int) 15 | { 16 | x <= y 17 | } 18 | 19 | /* aids in the process of induction for modulus */ 20 | lemma LemmaInductionHelper(n: int, f: int -> bool, x: int) 21 | requires n > 0 22 | requires forall i :: 0 <= i < n ==> f(i) 23 | requires forall i {:trigger f(i), f(i + n)} :: i >= 0 && f(i) ==> f(i + n) 24 | requires forall i {:trigger f(i), f(i - n)} :: i < n && f(i) ==> f(i - n) 25 | ensures f(x) 26 | decreases if x >= n then x else -x 27 | { 28 | if (x >= n) 29 | { 30 | LemmaInductionHelper(n, f, x - n); 31 | assert f((x - n) + n); 32 | } 33 | else if (x < 0) 34 | { 35 | LemmaInductionHelper(n, f, x + n); 36 | assert f((x + n) - n); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/dafny/NonlinearArithmetic/Internals/GeneralInternals.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify --disable-nonlinear-arithmetic "%s" 2 | 3 | /******************************************************************************* 4 | * Original: Copyright (c) Microsoft Corporation 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 8 | * SPDX-License-Identifier: MIT 9 | *******************************************************************************/ 10 | 11 | /** Declares helper lemmas and predicates for non-linear arithmetic */ 12 | module {:options "-functionSyntax:4"} Dafny.GeneralInternals 13 | { 14 | 15 | /* this predicate is primarily used as a trigger */ 16 | ghost predicate IsLe(x: int, y: int) 17 | { 18 | x <= y 19 | } 20 | 21 | /* aids in the process of induction for modulus */ 22 | lemma LemmaInductionHelper(n: int, f: int -> bool, x: int) 23 | requires n > 0 24 | requires forall i :: 0 <= i < n ==> f(i) 25 | requires forall i {:trigger f(i), f(i + n)} :: i >= 0 && f(i) ==> f(i + n) 26 | requires forall i {:trigger f(i), f(i - n)} :: i < n && f(i) ==> f(i - n) 27 | ensures f(x) 28 | decreases if x >= n then x else -x 29 | { 30 | if (x >= n) 31 | { 32 | LemmaInductionHelper(n, f, x - n); 33 | assert f((x - n) + n); 34 | } 35 | else if (x < 0) 36 | { 37 | LemmaInductionHelper(n, f, x + n); 38 | assert f((x + n) - n); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/Unicode/Utf8EncodingFormExamples.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../../src/Unicode/Utf8EncodingForm.dfy" 9 | 10 | module Utf8EncodingFormExamples { 11 | import Unicode 12 | import opened Utf8EncodingForm 13 | 14 | const TEST_SCALAR_VALUES: seq<(Unicode.ScalarValue, WellFormedCodeUnitSeq)> := [ 15 | // One byte: dollar sign 16 | (0x0024, [0x24]), 17 | // Two bytes: pound sign 18 | (0x00A3, [0xC2, 0xA3]), 19 | // Three bytes: euro sign 20 | (0x20AC, [0xE2, 0x82, 0xAC]), 21 | // Four bytes: money bag emoji 22 | (0x1F4B0, [0xF0, 0x9F, 0x92, 0xB0]) 23 | ] 24 | 25 | lemma TestEncodeScalarValue() 26 | ensures forall pair | pair in TEST_SCALAR_VALUES 27 | :: EncodeScalarValue(pair.0) == pair.1 28 | {} 29 | 30 | // Examples taken from description of Table 3-7. 31 | const TEST_ILL_FORMED_SEQUENCES: seq := [ 32 | // C0 is not well-formed as a first byte 33 | [0xC0, 0xAF], 34 | // 9F is not well-formed as a second byte when E0 is a well-formed first byte 35 | [0xE0, 0x9F, 0x80] 36 | ] 37 | 38 | lemma TestDecodeIllFormedSequence() 39 | ensures forall s | s in TEST_ILL_FORMED_SEQUENCES 40 | :: DecodeCodeUnitSequenceChecked(s).None? 41 | {} 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/NonlinearArithmetic/Internals/DivInternalsNonlinear.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Original: Copyright (c) Microsoft Corporation 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 8 | * SPDX-License-Identifier: MIT 9 | *******************************************************************************/ 10 | 11 | module {:options "-functionSyntax:4"} DivInternalsNonlinear { 12 | 13 | /* WARNING: Think three times before adding to this file, as nonlinear 14 | verification is highly unstable! */ 15 | 16 | /* zero divided by an integer besides 0 is 0 */ 17 | lemma LemmaDivOf0(d:int) 18 | requires d != 0 19 | ensures 0 / d == 0 20 | { 21 | } 22 | 23 | /* the quotient of an integer divided by itself is 1 */ 24 | lemma LemmaDivBySelf(d:int) 25 | requires d != 0 26 | ensures d / d == 1 27 | { 28 | } 29 | 30 | /* dividing a smaller integer by a larger integer results in a quotient of 0 */ 31 | lemma LemmaSmallDiv() 32 | ensures forall x, d {:trigger x / d} :: 0 <= x < d && d > 0 ==> x / d == 0 33 | { 34 | } 35 | 36 | /* the quotient of dividing a positive real number (not 0) by a smaller positive real number 37 | will be greater than 1 */ 38 | lemma LemmaRealDivGt(x:real, y:real) 39 | requires x > y 40 | requires x >= 0.0 41 | requires y > 0.0 42 | ensures x / y > 1 as real 43 | { 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/FileIO/README.md: -------------------------------------------------------------------------------- 1 | # File I/O 2 | 3 | The `FileIO` module provides basic file I/O operations. 4 | Right now, these are limited to reading bytes from a file, and writing bytes to a file. 5 | The API is intentionally limited in scope and will be expanded later. 6 | 7 | Unlike other modules in the `libraries` repo, 8 | the `FileIO` module will not compile or run correctly without a language-specific implementation file. 9 | Language-specific implementation files are provided for C#/.NET, Java, and Javascript. 10 | (Python and Golang support are planned.) 11 | Concretely, to use `FileIO` in your code, you must: 12 | 13 | 1. `include` and `import` the `FileIO` module as you would any other library module 14 | 2. incorporate the corresponding language-specific implementation file (e.g. `FileIO.cs`) when building or running your program 15 | 16 | For example, to run a `Program.dfy` file that depends on the `FileIO` module, run the following. 17 | (This assumes you have the `libraries` repository checked out within the working directory.) 18 | 19 | ```bash 20 | # C#/.NET 21 | $ dafny run Program.dfy --input libraries/src/FileIO/FileIO.cs 22 | 23 | # Java 24 | $ dafny run Program.dfy --target:java --input libraries/src/FileIO/FileIO.java 25 | 26 | # Javascript 27 | $ dafny run Program.dfy --target:js --input libraries/src/FileIO/FileIO.js 28 | ``` 29 | 30 | (If you aren't using `dafny run` to run your program, 31 | then you should instead integrate the appropriate language-specific implementation file in your build system.) 32 | 33 | The `examples/FileIO` directory contains more detailed examples of how to use the `FileIO` module. 34 | -------------------------------------------------------------------------------- /src/dafny/FileIO/README.md: -------------------------------------------------------------------------------- 1 | # File I/O 2 | 3 | The `FileIO` module provides basic file I/O operations. 4 | Right now, these are limited to reading bytes from a file, and writing bytes to a file. 5 | The API is intentionally limited in scope and will be expanded later. 6 | 7 | Unlike other modules in the `libraries` repo, 8 | the `FileIO` module will not compile or run correctly without a language-specific implementation file. 9 | Language-specific implementation files are provided for C#/.NET, Java, and Javascript. 10 | (Python and Golang support are planned.) 11 | Concretely, to use `FileIO` in your code, you must: 12 | 13 | 1. `include` and `import` the `FileIO` module as you would any other library module 14 | 2. incorporate the corresponding language-specific implementation file (e.g. `FileIO.cs`) when building or running your program 15 | 16 | For example, to run a `Program.dfy` file that depends on the `FileIO` module, run the following. 17 | (This assumes you have the `libraries` repository checked out within the working directory.) 18 | 19 | ```bash 20 | # C#/.NET 21 | $ dafny run Program.dfy --include libraries/src/FileIO/FileIO.cs 22 | 23 | # Java 24 | $ dafny run Program.dfy --target:java --include libraries/src/FileIO/FileIO.java 25 | 26 | # Javascript 27 | $ dafny run Program.dfy --target:js --include libraries/src/FileIO/FileIO.js 28 | ``` 29 | 30 | (If you aren't using `dafny run` to run your program, 31 | then you should instead integrate the appropriate language-specific implementation file in your build system.) 32 | 33 | The `examples/FileIO` directory contains more detailed examples of how to use the `FileIO` module. 34 | -------------------------------------------------------------------------------- /src/dafny/NonlinearArithmetic/Internals/DivInternalsNonlinear.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Original: Copyright (c) Microsoft Corporation 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 8 | * SPDX-License-Identifier: MIT 9 | *******************************************************************************/ 10 | 11 | /** Declares a few helper lemmas for internal use in non-linear arithmetic */ 12 | module {:options "-functionSyntax:4"} Dafny.DivInternalsNonlinear 13 | { 14 | 15 | /* WARNING: Think three times before adding to this file, as nonlinear 16 | verification is highly unstable! */ 17 | 18 | /* zero divided by an integer besides 0 is 0 */ 19 | lemma LemmaDivOf0(d:int) 20 | requires d != 0 21 | ensures 0 / d == 0 22 | { 23 | } 24 | 25 | /* the quotient of an integer divided by itself is 1 */ 26 | lemma LemmaDivBySelf(d:int) 27 | requires d != 0 28 | ensures d / d == 1 29 | { 30 | } 31 | 32 | /* dividing a smaller integer by a larger integer results in a quotient of 0 */ 33 | lemma LemmaSmallDiv() 34 | ensures forall x, d {:trigger x / d} :: 0 <= x < d && d > 0 ==> x / d == 0 35 | { 36 | } 37 | 38 | /* the quotient of dividing a positive real number (not 0) by a smaller positive real number 39 | will be greater than 1 */ 40 | lemma LemmaRealDivGt(x:real, y:real) 41 | requires x > y 42 | requires x >= 0.0 43 | requires y > 0.0 44 | ensures x / y > 1 as real 45 | { 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/dafny/Relations.md: -------------------------------------------------------------------------------- 1 | 2 | ## The `Relations` module {#sec-relations} 3 | 4 | The `Relations` module defines a number of properties of functions. 5 | 6 | Properties of binary predicates: 7 | - Reflexive: `R(x,x)` is true 8 | - Irreflexive: `R(x,x)` is false 9 | - AntiSymmetric: `R(x,y) && R(y,x) ==> x==y` 10 | - Transitive : `R(x,y) && R(y,z) ==> R(x,z)` 11 | - Connected : `x != y ==> R(x,y) || R(y,x)` 12 | - StronglyConnected : `R(x,y) || R(y,x)` 13 | - TotalOrdering : Reflexive, AntiSymmetric, Transitive, StronglyConnected (e.g., `<=` on integers) 14 | - StrictTotalOrdering : Irreflexive, AntiSymmetric, Transitive, Connected (e.g., `<` on integers) 15 | 16 | A property of unary functions: 17 | - Injective : `f(x) == f(y) ==> x == y` 18 | 19 | These properties are sometimes required for other functions. For example, 20 | if one wants to sort a sequence by some relation `R`, one must establish that `R` is a _Total Ordering_ 21 | or a _Strict Total Ordering_. 22 | In fact, that is part of the precondition of a sorting function. 23 | 24 | As a simple example, you might define a predicate like this: 25 | 26 | ```dafny 27 | const IntLT := ((i: int, j: int) => (i < j)) 28 | ``` 29 | 30 | and then need to prove this lemma to use it in a sorting routine: 31 | 32 | ```dafny 33 | import opened Dafny.Relations 34 | lemma IntLTisStrictTotalOrder() 35 | ensures StrictTotalOrdering(IntLT) {} 36 | ``` 37 | 38 | Fortunately, dafny proves this without aid. 39 | 40 | All these definitions are ghost predicates; they are used as part of proofs rather than in compiled code. 41 | -------------------------------------------------------------------------------- /examples/LittleEndianNat/LittleEndianNatExample.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %run --disable-nonlinear-arithmetic "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../../src/Collections/Sequences/LittleEndianNatConversions.dfy" 9 | 10 | module LittleEndianNatExample { 11 | 12 | import opened Uint8_32 13 | 14 | method Main() { 15 | var n := 49602234234; 16 | 17 | // Convert n to uint8 and uint32 sequences 18 | var smallSeq, largeSeq := Small.FromNat(n), Large.FromNat(n); 19 | expect smallSeq == [122, 7, 134, 140, 11]; 20 | expect largeSeq == [2357593978, 11]; 21 | 22 | Small.LemmaNatSeqNat(n); 23 | Large.LemmaNatSeqNat(n); 24 | assert Small.ToNatRight(smallSeq) == Large.ToNatRight(largeSeq) == n; 25 | 26 | // Extend smallSeq 27 | smallSeq := Small.SeqExtendMultiple(smallSeq, E()); 28 | assert Small.ToNatRight(smallSeq) == n; 29 | 30 | // Convert between smallSeqExtended and largeSeq 31 | LemmaToSmall(largeSeq); 32 | LemmaToLarge(smallSeq); 33 | assert Small.ToNatRight(ToSmall(largeSeq)) == n; 34 | assert Large.ToNatRight(ToLarge(smallSeq)) == n; 35 | assert |ToSmall(largeSeq)| == |largeSeq| * E(); 36 | assert |ToLarge(smallSeq)| == |smallSeq| / E(); 37 | 38 | LemmaSmallLargeSmall(smallSeq); 39 | LemmaLargeSmallLarge(largeSeq); 40 | assert ToSmall(ToLarge(smallSeq)) == smallSeq; 41 | assert ToLarge(ToSmall(largeSeq)) == largeSeq; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/NonlinearArithmetic/Internals/ModInternalsNonlinear.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Original: Copyright (c) Microsoft Corporation 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 8 | * SPDX-License-Identifier: MIT 9 | *******************************************************************************/ 10 | 11 | module {:options "-functionSyntax:4"} ModInternalsNonlinear { 12 | 13 | /* WARNING: Think three times before adding to this file, as nonlinear 14 | verification is highly unstable! */ 15 | 16 | /* the remainder of 0 divided by an integer is 0 */ 17 | lemma LemmaModOfZeroIsZero(m:int) 18 | requires 0 < m 19 | ensures 0 % m == 0 20 | { 21 | } 22 | 23 | /* describes fundementals of the modulus operator */ 24 | lemma LemmaFundamentalDivMod(x:int, d:int) 25 | requires d != 0 26 | ensures x == d * (x / d) + (x % d) 27 | { 28 | } 29 | 30 | /* the remained of 0 divided by any integer is always 0 */ 31 | lemma Lemma0ModAnything() 32 | ensures forall m: int {:trigger 0 % m} :: m > 0 ==> 0 % m == 0 33 | { 34 | } 35 | 36 | /* a natural number x divided by a larger natural number gives a remainder equal to x */ 37 | lemma LemmaSmallMod(x:nat, m:nat) 38 | requires x < m 39 | requires 0 < m 40 | ensures x % m == x 41 | { 42 | } 43 | 44 | /* the range of the modulus of any integer will be [0, m) where m is the divisor */ 45 | lemma LemmaModRange(x:int, m:int) 46 | requires m > 0 47 | ensures 0 <= x % m < m 48 | { 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/JSON/ZeroCopy/API.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" --unicode-char:false ../../Unicode/UnicodeStringsWithoutUnicodeChar.dfy 2 | // RUN: %verify "%s" --unicode-char:true ../../Unicode/UnicodeStringsWithUnicodeChar.dfy 3 | 4 | include "../Grammar.dfy" 5 | include "../ConcreteSyntax.Spec.dfy" 6 | include "Serializer.dfy" 7 | include "Deserializer.dfy" 8 | 9 | module {:options "-functionSyntax:4"} JSON.ZeroCopy.API { 10 | import opened BoundedInts 11 | import opened Wrappers 12 | 13 | import opened Errors 14 | import Grammar 15 | import ConcreteSyntax.Spec 16 | import Serializer 17 | import Deserializer 18 | 19 | function {:opaque} Serialize(js: Grammar.JSON) : (bs: SerializationResult>) 20 | ensures bs == Success(Spec.JSON(js)) 21 | { 22 | Success(Serializer.Text(js).Bytes()) 23 | } 24 | 25 | method SerializeAlloc(js: Grammar.JSON) returns (bs: SerializationResult>) 26 | ensures bs.Success? ==> fresh(bs.value) 27 | ensures bs.Success? ==> bs.value[..] == Spec.JSON(js) 28 | { 29 | bs := Serializer.Serialize(js); 30 | } 31 | 32 | method SerializeInto(js: Grammar.JSON, bs: array) returns (len: SerializationResult) 33 | modifies bs 34 | ensures len.Success? ==> len.value as int <= bs.Length 35 | ensures len.Success? ==> bs[..len.value] == Spec.JSON(js) 36 | ensures len.Success? ==> bs[len.value..] == old(bs[len.value..]) 37 | ensures len.Failure? ==> unchanged(bs) 38 | { 39 | len := Serializer.SerializeTo(js, bs); 40 | } 41 | 42 | function {:opaque} Deserialize(bs: seq) : (js: DeserializationResult) 43 | ensures js.Success? ==> bs == Spec.JSON(js.value) 44 | { 45 | Deserializer.API.OfBytes(bs) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/dafny/NonlinearArithmetic/Internals/ModInternalsNonlinear.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Original: Copyright (c) Microsoft Corporation 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 8 | * SPDX-License-Identifier: MIT 9 | *******************************************************************************/ 10 | 11 | /** Declares helper lemmas about the mod operation */ 12 | module {:options "-functionSyntax:4"} Dafny.ModInternalsNonlinear 13 | { 14 | 15 | /* WARNING: Think three times before adding to this file, as nonlinear 16 | verification is highly unstable! */ 17 | 18 | /* the remainder of 0 divided by an integer is 0 */ 19 | lemma LemmaModOfZeroIsZero(m:int) 20 | requires 0 < m 21 | ensures 0 % m == 0 22 | { 23 | } 24 | 25 | /* describes fundementals of the modulus operator */ 26 | lemma LemmaFundamentalDivMod(x:int, d:int) 27 | requires d != 0 28 | ensures x == d * (x / d) + (x % d) 29 | { 30 | } 31 | 32 | /* the remained of 0 divided by any integer is always 0 */ 33 | lemma Lemma0ModAnything() 34 | ensures forall m: int {:trigger 0 % m} :: m > 0 ==> 0 % m == 0 35 | { 36 | } 37 | 38 | /* a natural number x divided by a larger natural number gives a remainder equal to x */ 39 | lemma LemmaSmallMod(x:nat, m:nat) 40 | requires x < m 41 | requires 0 < m 42 | ensures x % m == x 43 | { 44 | } 45 | 46 | /* the range of the modulus of any integer will be [0, m) where m is the divisor */ 47 | lemma LemmaModRange(x:int, m:int) 48 | requires m > 0 49 | ensures 0 <= x % m < m 50 | { 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /examples/Collections/Arrays/BinarySearch.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %run "%s" > "%t" 2 | 3 | // OutputCheck is broken, see https://github.com/dafny-lang/libraries/issues/164 4 | // not working: %OutputCheck --file-to-check "%t" "%s" 5 | // CHECK-L: [-6, 0, 1, 3, 7, 7, 9] 6 | // CHECK-NEXT-L: 3 7 | 8 | /******************************************************************************* 9 | * Copyright by the contributors to the Dafny Project 10 | * SPDX-License-Identifier: MIT 11 | *******************************************************************************/ 12 | 13 | include "../../../src/Relations.dfy" 14 | include "../../../src/Collections/Arrays/BinarySearch.dfy" 15 | include "../../../src/Collections/Sequences/Seq.dfy" 16 | include "../../../src/Collections/Sequences/MergeSort.dfy" 17 | 18 | module BinarySearchExamples { 19 | 20 | import BinarySearch 21 | import opened Seq 22 | import opened Seq.MergeSort 23 | import opened Relations 24 | 25 | lemma SortedByLessThanOrEqualTo(s: seq) 26 | requires SortedBy(s, (x, y) => x <= y) 27 | ensures SortedBy(s, (x, y) => x < y || x == y) 28 | {} 29 | 30 | method {:vcs_split_on_every_assert} SortAndSearch() { 31 | var input := [1, 7, 7, 3, 9, 0, -6]; 32 | 33 | var sortedInput := MergeSortBy(input, (x, y) => x <= y); 34 | print sortedInput, "\n"; 35 | 36 | assert SortedBy(sortedInput, (x, y) => x <= y); 37 | var sortedArray := ToArray(sortedInput); 38 | SortedByLessThanOrEqualTo(sortedArray[..]); 39 | var indexOfThree := BinarySearch.BinarySearch(sortedArray, 3, (x, y) => x < y); 40 | if indexOfThree.Some? { 41 | print indexOfThree.value, "\n"; 42 | } else { 43 | print "Not found\n"; 44 | } 45 | } 46 | 47 | method Main() { 48 | SortAndSearch(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libraries 2 | 3 | Libraries useful for Dafny programs 4 | 5 | ## Status 6 | 7 | **This repository is now deprecated:** nearly all of this code is now available directly from the Dafny distribution using the flag `--standard-libraries`. See https://github.com/dafny-lang/dafny/tree/master/Source/DafnyStandardLibraries for details. 8 | 9 | At the moment, we're just collecting generally useful Dafny code. 10 | 11 | Once we have some amount of code which is used successfully by several projects, we might restructure this repo, informed by the concrete use cases. 12 | 13 | So, please do use this library, give feedback, and contribute code, but also expect breaking changes. 14 | 15 | ## Contributions 16 | 17 | Any contributions of generally useful code are welcome, just open a pull request! Please follow the [library style guidelines](STYLE.md). If the way to use your new code is not obvious, please add some examples in the `examples` directory to illustrate how to use the code. We use the [LLVM integrated tester (lit)](https://llvm.org/docs/CommandGuide/lit.html) to test the library files and ensure they all verify correctly. Please see Dafny's documentation on [installation](https://github.com/dafny-lang/dafny/wiki/INSTALL) and [testing](https://github.com/dafny-lang/dafny/wiki/Running-Dafny's-test-suite) for more details. 18 | 19 | ## Acknowledgements 20 | 21 | Much of this code came from or was inspired by code from the following projects: 22 | 23 | * [Ironclad Apps](https://github.com/microsoft/Ironclad/tree/main/ironclad-apps) 24 | * [IronFleet](https://github.com/microsoft/Ironclad/tree/main/ironfleet) 25 | * [Vale](https://github.com/project-everest/vale/tree/legacy_dafny) 26 | * [Verified BetrFS](https://github.com/vmware-labs/verified-betrfs) 27 | * [Verifying OpenTitan](https://github.com/secure-foundations/veri-titan) 28 | 29 | -------------------------------------------------------------------------------- /src/JSON/Utils/Lexers.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | include "../../Wrappers.dfy" 4 | include "../../BoundedInts.dfy" 5 | 6 | module {:options "-functionSyntax:4"} JSON.Utils.Lexers { 7 | module Core { 8 | import opened Wrappers 9 | import opened BoundedInts 10 | 11 | datatype LexerResult<+T, +R> = 12 | // A Lexer may return three results: 13 | | Accept // The input is valid. 14 | | Reject(err: R) // The input is not valid; `err` says why. 15 | | Partial(st: T) // More input is needed to finish lexing. 16 | 17 | type Lexer = (T, opt_byte) -> LexerResult 18 | } 19 | 20 | module Strings { 21 | import opened Core 22 | import opened BoundedInts 23 | 24 | type StringBodyLexerState = /* escaped: */ bool 25 | const StringBodyLexerStart: StringBodyLexerState := false 26 | 27 | function StringBody(escaped: StringBodyLexerState, byte: opt_byte) 28 | : LexerResult 29 | { 30 | if byte == '\\' as opt_byte then Partial(!escaped) 31 | else if byte == '\"' as opt_byte && !escaped then Accept 32 | else Partial(false) 33 | } 34 | 35 | datatype StringLexerState = Start | Body(escaped: bool) | End 36 | const StringLexerStart: StringLexerState := Start 37 | 38 | function String(st: StringLexerState, byte: opt_byte) 39 | : LexerResult 40 | { 41 | match st 42 | case Start() => 43 | if byte == '\"' as opt_byte then Partial(Body(false)) 44 | else Reject("String must start with double quote") 45 | case End() => 46 | Accept 47 | case Body(escaped) => 48 | if byte == '\\' as opt_byte then Partial(Body(!escaped)) 49 | else if byte == '\"' as opt_byte && !escaped then Partial(End) 50 | else Partial(Body(false)) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/NonlinearArithmetic/Internals/MulInternalsNonlinear.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Original: Copyright (c) Microsoft Corporation 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 8 | * SPDX-License-Identifier: MIT 9 | *******************************************************************************/ 10 | 11 | module {:options "-functionSyntax:4"} MulInternalsNonlinear { 12 | 13 | /* WARNING: Think three times before adding to this file, as nonlinear 14 | verification is highly unstable! */ 15 | 16 | /* multiplying two positive integers will result in a positive integer */ 17 | lemma LemmaMulStrictlyPositive(x: int, y: int) 18 | ensures (0 < x && 0 < y) ==> (0 < x * y) 19 | {} 20 | 21 | /* multiplying two nonzero integers will never result in 0 as the poduct */ 22 | lemma LemmaMulNonzero(x: int, y: int) 23 | ensures x * y != 0 <==> x != 0 && y != 0 24 | {} 25 | 26 | /* multiplication is associative */ 27 | lemma LemmaMulIsAssociative(x: int, y: int, z: int) 28 | ensures x * (y * z) == (x * y) * z 29 | {} 30 | 31 | /* multiplication is distributive */ 32 | lemma LemmaMulIsDistributiveAdd(x: int, y: int, z: int) 33 | ensures x * (y + z) == x * y + x * z 34 | {} 35 | 36 | /* the product of two integers is greater than the value of each individual integer */ 37 | lemma LemmaMulOrdering(x: int, y: int) 38 | requires x != 0 39 | requires y != 0 40 | requires 0 <= x * y 41 | ensures x * y >= x && x * y >= y 42 | {} 43 | 44 | /* multiplying by a positive integer preserves inequality */ 45 | lemma LemmaMulStrictInequality(x: int, y: int, z: int) 46 | requires x < y 47 | requires z > 0 48 | ensures x * z < y * z 49 | {} 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/Collections/Sets/Isets.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Original Copyright under the following: 5 | * Copyright 2018-2021 VMware, Inc., Microsoft Inc., Carnegie Mellon University, 6 | * ETH Zurich, and University of Washington 7 | * SPDX-License-Identifier: BSD-2-Clause 8 | * 9 | * Copyright (c) Microsoft Corporation 10 | * SPDX-License-Identifier: MIT 11 | * 12 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 13 | * SPDX-License-Identifier: MIT 14 | *******************************************************************************/ 15 | 16 | include "../../Functions.dfy" 17 | 18 | module {:options "-functionSyntax:4"} Isets { 19 | 20 | import opened Functions 21 | 22 | /* If all elements in iset x are in iset y, x is a subset of y. */ 23 | lemma LemmaSubset(x: iset, y: iset) 24 | requires forall e {:trigger e in y} :: e in x ==> e in y 25 | ensures x <= y 26 | { 27 | } 28 | 29 | /* Map an injective function to each element of an iset. */ 30 | ghost function {:opaque} Map(xs: iset, f: X --> Y): (ys: iset) 31 | requires forall x :: f.requires(x) 32 | requires Injective(f) 33 | reads f.reads 34 | ensures forall x {:trigger f(x)} :: x in xs <==> f(x) in ys 35 | { 36 | var ys := iset x | x in xs :: f(x); 37 | ys 38 | } 39 | 40 | /* Construct an iset using elements of another set for which a function 41 | returns true. */ 42 | ghost function {:opaque} Filter(xs: iset, f: X ~> bool): (ys: iset) 43 | requires forall x :: x in xs ==> f.requires(x) 44 | reads set x, o | x in xs && o in f.reads(x) :: o 45 | ensures forall y {:trigger f(y)}{:trigger y in xs} :: y in ys <==> y in xs && f(y) 46 | { 47 | var ys := iset x | x in xs && f(x); 48 | ys 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/dafny/Collections/Isets.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Original Copyright under the following: 5 | * Copyright 2018-2021 VMware, Inc., Microsoft Inc., Carnegie Mellon University, 6 | * ETH Zurich, and University of Washington 7 | * SPDX-License-Identifier: BSD-2-Clause 8 | * 9 | * Copyright (c) Microsoft Corporation 10 | * SPDX-License-Identifier: MIT 11 | * 12 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 13 | * SPDX-License-Identifier: MIT 14 | *******************************************************************************/ 15 | 16 | include "../Relations.dfy" 17 | 18 | module {:options "-functionSyntax:4"} Dafny.Collections.Isets { 19 | 20 | import opened Relations 21 | 22 | /* If all elements in iset x are in iset y, x is a subset of y. */ 23 | lemma LemmaSubset(x: iset, y: iset) 24 | requires forall e {:trigger e in y} :: e in x ==> e in y 25 | ensures x <= y 26 | { 27 | } 28 | 29 | /* Map an injective function to each element of an iset. */ 30 | ghost function {:opaque} Map(xs: iset, f: X-->Y): (ys: iset) 31 | reads f.reads 32 | requires forall x :: f.requires(x) 33 | requires Injective(f) 34 | ensures forall x {:trigger f(x)} :: x in xs <==> f(x) in ys 35 | { 36 | var ys := iset x | x in xs :: f(x); 37 | ys 38 | } 39 | 40 | /* Construct an iset using elements of another set for which a function 41 | returns true. */ 42 | ghost function {:opaque} Filter(xs: iset, f: X~>bool): (ys: iset) 43 | requires forall x :: x in xs ==> f.requires(x) 44 | reads set x, o | x in xs && o in f.reads(x) :: o 45 | ensures forall y {:trigger f(y)}{:trigger y in xs} :: y in ys <==> y in xs && f(y) 46 | { 47 | var ys := iset x | x in xs && f(x); 48 | ys 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /.github/workflows/reusable-tests.yml: -------------------------------------------------------------------------------- 1 | # This workflow does static verification of the DafnyCore library 2 | name: Dafny Core verification (Reusable) 3 | 4 | on: 5 | workflow_call: 6 | inputs: 7 | dafny-version: 8 | required: true 9 | type: string 10 | 11 | jobs: 12 | reusable_verification: 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: [ ubuntu-latest ] 17 | 18 | runs-on: ${{ matrix.os }} 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - name: Install Dafny 24 | uses: dafny-lang/setup-dafny-action@v1.6.1 25 | with: 26 | dafny-version: ${{ inputs.dafny-version }} 27 | 28 | - name: Version information 29 | run: | 30 | dafny --version 31 | 32 | - name: Upgrade outdated pip 33 | run: python -m pip install --upgrade pip 34 | 35 | - name: Setup .NET Core SDK ${{ matrix.dotnet-version }} 36 | uses: actions/setup-dotnet@v4 37 | with: 38 | dotnet-version: 8.0.x 39 | 40 | - name: Setup .NET Core SDK ${{ matrix.dotnet-version }} 41 | uses: actions/setup-dotnet@v4 42 | with: 43 | dotnet-version: 6.0.x 44 | 45 | - name: Install lit 46 | run: pip install lit OutputCheck 47 | 48 | - name: Set up JS dependencies 49 | run: npm install bignumber.js 50 | 51 | - name: Verify Code and Examples 52 | run: | 53 | lit --time-tests -v --param 'dafny_params=--log-format trx --log-format csv' . 54 | 55 | - name: Generate Report 56 | run: find . -name '*.csv' -print0 | xargs -0 --verbose dafny-reportgenerator summarize-csv-results --max-resource-count 40000000 57 | 58 | - uses: actions/upload-artifact@v4 # upload test results 59 | with: 60 | name: verification-results-${{ inputs.dafny-version }} 61 | path: '**/TestResults/*.trx' 62 | if-no-files-found: error 63 | -------------------------------------------------------------------------------- /src/dafny/NonlinearArithmetic/Internals/MulInternalsNonlinear.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Original: Copyright (c) Microsoft Corporation 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 8 | * SPDX-License-Identifier: MIT 9 | *******************************************************************************/ 10 | 11 | /** Declares some helper lemmas about multiply, for internal use */ 12 | module {:options "-functionSyntax:4"} Dafny.MulInternalsNonlinear 13 | { 14 | 15 | /* WARNING: Think three times before adding to this file, as nonlinear 16 | verification is highly unstable! */ 17 | 18 | /* multiplying two positive integers will result in a positive integer */ 19 | lemma LemmaMulStrictlyPositive(x: int, y: int) 20 | ensures (0 < x && 0 < y) ==> (0 < x * y) 21 | {} 22 | 23 | /* multiplying two nonzero integers will never result in 0 as the poduct */ 24 | lemma LemmaMulNonzero(x: int, y: int) 25 | ensures x * y != 0 <==> x != 0 && y != 0 26 | {} 27 | 28 | /* multiplication is associative */ 29 | lemma LemmaMulIsAssociative(x: int, y: int, z: int) 30 | ensures x * (y * z) == (x * y) * z 31 | {} 32 | 33 | /* multiplication is distributive */ 34 | lemma LemmaMulIsDistributiveAdd(x: int, y: int, z: int) 35 | ensures x * (y + z) == x * y + x * z 36 | {} 37 | 38 | /* the product of two integers is greater than the value of each individual integer */ 39 | lemma LemmaMulOrdering(x: int, y: int) 40 | requires x != 0 41 | requires y != 0 42 | requires 0 <= x * y 43 | ensures x * y >= x && x * y >= y 44 | {} 45 | 46 | /* multiplying by a positive integer preserves inequality */ 47 | lemma LemmaMulStrictInequality(x: int, y: int, z: int) 48 | requires x < y 49 | requires z > 0 50 | ensures x * z < y * z 51 | {} 52 | 53 | } 54 | -------------------------------------------------------------------------------- /README-TESTING.md: -------------------------------------------------------------------------------- 1 | # Testing the libraries 2 | 3 | The code in these libraries is tested both with deductive verification, using dafny, 4 | and with traditional runtime testing of compilations by dafny. 5 | The latter is important as a check that dafny compiled the (verified) dafny code correctly. 6 | Also, some functionality (e.g. FileIO) is partially implemented using 7 | custom code in the target programming language, which is not verified by dafny. 8 | All files are also checked that they conform to standard dafny formatting, 9 | using `dafny format`. 10 | 11 | The tests are run using `lit`. The configuration of the tests is controlled by 12 | (a) the lit.site.cfg file and (b) the yml files in .github/workflows. 13 | 14 | The tests are run on each push to a pull_request and are required to pass to enable merging. 15 | They are also run on a nightly schedule. 16 | 17 | To run the files manually: run `lit .` in this directory (`libraries`, at the top 18 | of the clone of the repo). You can also manually run individual files or directories, 19 | but `lit` must be invoked in the `libraries` directory. You can also trigger the workflow 20 | using `workflow_dispatch`. To do a manual check of formatting, use `dafny format --check `. 21 | 22 | The tests themselves are specially formatted lines (those beginning with `// RUN:`) in 23 | each .dfy file. The tests use dafny's new command-line syntax, using the lit 24 | macros `%verify` and `%run` and the like. 25 | 26 | The versions of dafny being run are set in the `tests.yml` file. The versions tested 27 | must be recent enough to support the new CLI syntax, so 3.11 and later. Also, if 28 | verification logging is desired, the version must be at least that of 3/18/2023 when 29 | the logging parameters were implemented in the new CLI. 30 | 31 | Testing can be run on older versions if (a) the lit macros like `%verify` are redefined using 32 | old CLI syntax and (b) the runtime testing tests are rewritten to not use `dafny run`. 33 | -------------------------------------------------------------------------------- /examples/FileIO/ReadBytesFromFile.dfy: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright by the contributors to the Dafny Project 3 | * SPDX-License-Identifier: MIT 4 | *******************************************************************************/ 5 | 6 | // RUN: %verify "%s" 7 | 8 | // #RUN: %run --no-verify --unicode-char:false --target:cs "%s" --input "%S/../../src/FileIO/FileIO.cs" -- "%S/data.txt" "System.ArgumentException:" 9 | // #RUN: %run --no-verify --unicode-char:false --target:java "%s" --input "%S/../../src/FileIO/FileIO.java" -- "%S/data.txt" "java.io.IOException:" 10 | // #RUN: %run --no-verify --unicode-char:false --target:js "%s" --input "%S/../../src/FileIO/FileIO.js" -- "%S/data.txt" "Error: ENOENT" 11 | 12 | include "../../src/dafny/FileIO/FileIO.dfy" 13 | 14 | module ReadBytesFromFile { 15 | import Dafny.FileIO 16 | 17 | method Main(args: seq) { 18 | expect |args| > 0; 19 | expect |args| == 3, "usage: " + args[0] + " DATA_PATH EXPECTED_ERROR_PREFIX"; 20 | var dataPath := args[1]; 21 | var expectedErrorPrefix := args[2]; 22 | 23 | // Happy path: read from the data file, and check that we see the expected content. 24 | { 25 | var expectedStr := "Hello world\nGoodbye\n"; 26 | // This conversion is safe only for ASCII values. For Unicode conversions, see the Unicode modules. 27 | var expectedBytes := seq(|expectedStr|, i requires 0 <= i < |expectedStr| => expectedStr[i] as int); 28 | 29 | var res := FileIO.ReadBytesFromFile(dataPath); 30 | expect res.Success?, "unexpected failure: " + res.error; 31 | 32 | var readBytes := seq(|res.value|, i requires 0 <= i < |res.value| => res.value[i] as int); 33 | expect readBytes == expectedBytes, "read unexpected byte sequence"; 34 | } 35 | 36 | // Failure path: attempting to read from a blank file path should never work. 37 | { 38 | var res := FileIO.ReadBytesFromFile(""); 39 | expect res.Failure?, "unexpected success"; 40 | expect expectedErrorPrefix <= res.error, "unexpected error message: " + res.error; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Unicode/UnicodeStringsWithoutUnicodeChar.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify --unicode-char:false %s 2 | 3 | /// Converting between strings and UTF-8/UTF-16 4 | /// ============================================= 5 | /// 6 | /// Implementation of `AbstractUnicodeStrings` for `--unicode-char:false`. 7 | /// See `UnicodeStrings.dfy` for details. 8 | 9 | include "UnicodeStrings.dfy" 10 | include "../Wrappers.dfy" 11 | include "../Collections/Sequences/Seq.dfy" 12 | 13 | module {:options "-functionSyntax:4"} UnicodeStrings refines AbstractUnicodeStrings { 14 | 15 | import Unicode 16 | import Utf8EncodingForm 17 | import Utf16EncodingForm 18 | 19 | predicate IsWellFormedString(s: string) 20 | ensures |s| == 0 ==> IsWellFormedString(s) 21 | { 22 | Utf16EncodingForm.IsWellFormedCodeUnitSequence(Seq.Map(c => c as Utf16EncodingForm.CodeUnit, s)) 23 | } 24 | 25 | function ToUTF8Checked(s: string): Option> { 26 | var asCodeUnits := Seq.Map(c => c as Utf16EncodingForm.CodeUnit, s); 27 | var utf32 :- Utf16EncodingForm.DecodeCodeUnitSequenceChecked(asCodeUnits); 28 | var asUtf8CodeUnits := Utf8EncodingForm.EncodeScalarSequence(utf32); 29 | Some(Seq.Map(c => c as byte, asUtf8CodeUnits)) 30 | } 31 | 32 | function {:vcs_split_on_every_assert} FromUTF8Checked(bs: seq): Option { 33 | var asCodeUnits := Seq.Map(c => c as Utf8EncodingForm.CodeUnit, bs); 34 | var utf32 :- Utf8EncodingForm.DecodeCodeUnitSequenceChecked(asCodeUnits); 35 | var asUtf16CodeUnits := Utf16EncodingForm.EncodeScalarSequence(utf32); 36 | Some(Seq.Map(cu => cu as char, asUtf16CodeUnits)) 37 | } 38 | 39 | function ToUTF16Checked(s: string): Option> { 40 | if Utf16EncodingForm.IsWellFormedCodeUnitSequence(Seq.Map(c => c as Utf16EncodingForm.CodeUnit, s)) then 41 | Some(Seq.Map(c => c as uint16, s)) 42 | else 43 | None 44 | } 45 | 46 | function FromUTF16Checked(bs: seq): Option { 47 | if Utf16EncodingForm.IsWellFormedCodeUnitSequence(Seq.Map(c => c as Utf16EncodingForm.CodeUnit, bs)) then 48 | Some(Seq.Map(c => c as char, bs)) 49 | else 50 | None 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/MutableMap/MutableMap.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright by the contributors to the Dafny Project 3 | * SPDX-License-Identifier: MIT 4 | *******************************************************************************/ 5 | package DafnyLibraries; 6 | 7 | import dafny.DafnySet; 8 | import dafny.DafnyMap; 9 | import dafny.Tuple2; 10 | import dafny.TypeDescriptor; 11 | import dafny.Helpers; 12 | 13 | import java.util.concurrent.*; 14 | import java.util.ArrayList; 15 | import java.util.Map; 16 | import java.math.BigInteger; 17 | 18 | public class MutableMap implements DafnyLibraries.MutableMapTrait { 19 | private ConcurrentHashMap m; 20 | 21 | // TODO: remove bytesKeys boolean https://github.com/dafny-lang/dafny/issues/6333 22 | public MutableMap( 23 | dafny.TypeDescriptor _td_K, 24 | dafny.TypeDescriptor _td_V, 25 | boolean bytesKeys) { 26 | m = new ConcurrentHashMap(); 27 | } 28 | 29 | public void __ctor(boolean bytesKeys) {} 30 | 31 | @Override 32 | public DafnyMap content() { 33 | return new DafnyMap(m); 34 | } 35 | 36 | @Override 37 | public void Put(K k, V v) { 38 | m.put(k, v); 39 | } 40 | 41 | @Override 42 | public DafnySet Keys() { 43 | return new DafnySet(m.keySet()); 44 | } 45 | 46 | @Override 47 | public boolean HasKey(K k) { 48 | return m.containsKey(k); 49 | } 50 | 51 | @Override 52 | public DafnySet Values() { 53 | return new DafnySet(m.values()); 54 | } 55 | 56 | @Override 57 | public DafnySet> Items() { 58 | ArrayList> list = new ArrayList>(); 59 | for (Map.Entry entry : m.entrySet()) { 60 | list.add(new Tuple2(entry.getKey(), entry.getValue())); 61 | } 62 | return new DafnySet>(list); 63 | } 64 | 65 | @Override 66 | public V Select(K k) { 67 | return m.get(k); 68 | } 69 | 70 | @Override 71 | public void Remove(K k) { 72 | m.remove(k); 73 | } 74 | 75 | @Override 76 | public BigInteger Size() { 77 | return BigInteger.valueOf(m.size()); 78 | } 79 | } -------------------------------------------------------------------------------- /src/JSON/ConcreteSyntax.SpecProperties.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" --unicode-char:false ../Unicode/UnicodeStringsWithoutUnicodeChar.dfy 2 | // RUN: %verify "%s" --unicode-char:true ../Unicode/UnicodeStringsWithUnicodeChar.dfy 3 | 4 | include "ConcreteSyntax.Spec.dfy" 5 | 6 | module {:options "-functionSyntax:4"} JSON.ConcreteSyntax.SpecProperties 7 | // Some useful properties about the functions used in `ConcreteSyntax.Spec`. 8 | { 9 | import opened BoundedInts 10 | 11 | import Vs = Utils.Views.Core 12 | import opened Grammar 13 | import Spec 14 | 15 | ghost predicate Bracketed_Morphism_Requires(bracketed: Bracketed, pd0: Suffixed --> bytes, pd1: Suffixed --> bytes) { 16 | && (forall d | d < bracketed :: pd0.requires(d)) 17 | && (forall d | d < bracketed :: pd1.requires(d)) 18 | && (forall d | d < bracketed :: pd0(d) == pd1(d)) 19 | } 20 | 21 | lemma Bracketed_Morphism(bracketed: Bracketed, pd0: Suffixed --> bytes, pd1: Suffixed --> bytes) 22 | requires Bracketed_Morphism_Requires(bracketed, pd0, pd1) 23 | ensures Spec.Bracketed(bracketed, pd0) == Spec.Bracketed(bracketed, pd1) 24 | { 25 | calc { 26 | Spec.Bracketed(bracketed, pd0); 27 | { ConcatBytes_Morphism(bracketed.data, pd0, pd1); } 28 | Spec.Bracketed(bracketed, pd1); 29 | } 30 | } 31 | 32 | lemma {:induction ts} ConcatBytes_Morphism(ts: seq, pt0: T --> bytes, pt1: T --> bytes) 33 | requires forall d | d in ts :: pt0.requires(d) 34 | requires forall d | d in ts :: pt1.requires(d) 35 | requires forall d | d in ts :: pt0(d) == pt1(d) 36 | ensures Spec.ConcatBytes(ts, pt0) == Spec.ConcatBytes(ts, pt1) 37 | {} 38 | 39 | lemma {:induction ts0} ConcatBytes_Linear(ts0: seq, ts1: seq, pt: T --> bytes) 40 | requires forall d | d in ts0 :: pt.requires(d) 41 | requires forall d | d in ts1 :: pt.requires(d) 42 | ensures Spec.ConcatBytes(ts0 + ts1, pt) == Spec.ConcatBytes(ts0, pt) + Spec.ConcatBytes(ts1, pt) 43 | { 44 | if |ts0| == 0 { 45 | assert [] + ts1 == ts1; 46 | } else { 47 | assert ts0 + ts1 == [ts0[0]] + (ts0[1..] + ts1); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Collections/Sequences/MergeSort.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../../Relations.dfy" 9 | 10 | module {:options "-functionSyntax:4"} Seq.MergeSort { 11 | 12 | import opened Relations 13 | 14 | //Splits a sequence in two, sorts the two subsequences using itself, and merge the two sorted sequences using `MergeSortedWith` 15 | function MergeSortBy(a: seq, lessThanOrEq: (T, T) -> bool): (result :seq) 16 | requires TotalOrdering(lessThanOrEq) 17 | ensures multiset(a) == multiset(result) 18 | ensures SortedBy(result, lessThanOrEq) 19 | { 20 | if |a| <= 1 then 21 | a 22 | else 23 | var splitIndex := |a| / 2; 24 | var left, right := a[..splitIndex], a[splitIndex..]; 25 | 26 | assert a == left + right; 27 | 28 | var leftSorted := MergeSortBy(left, lessThanOrEq); 29 | var rightSorted := MergeSortBy(right, lessThanOrEq); 30 | 31 | MergeSortedWith(leftSorted, rightSorted, lessThanOrEq) 32 | } 33 | 34 | function {:tailrecursion} MergeSortedWith(left: seq, right: seq, lessThanOrEq: (T, T) -> bool) : (result :seq) 35 | requires SortedBy(left, lessThanOrEq) 36 | requires SortedBy(right, lessThanOrEq) 37 | requires TotalOrdering(lessThanOrEq) 38 | ensures multiset(left + right) == multiset(result) 39 | ensures SortedBy(result, lessThanOrEq) 40 | { 41 | if |left| == 0 then 42 | right 43 | else if |right| == 0 then 44 | left 45 | else if lessThanOrEq(left[0], right[0]) then 46 | LemmaNewFirstElementStillSortedBy(left[0], MergeSortedWith(left[1..], right, lessThanOrEq), lessThanOrEq); 47 | assert left == [left[0]] + left[1..]; 48 | 49 | [left[0]] + MergeSortedWith(left[1..], right, lessThanOrEq) 50 | 51 | else 52 | LemmaNewFirstElementStillSortedBy(right[0], MergeSortedWith(left, right[1..], lessThanOrEq), lessThanOrEq); 53 | assert right == [right[0]] + right[1..]; 54 | 55 | [right[0]] + MergeSortedWith(left, right[1..], lessThanOrEq) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/JSON/Errors.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | include "../Wrappers.dfy" 4 | include "../BoundedInts.dfy" 5 | include "Utils/Str.dfy" 6 | 7 | module {:options "-functionSyntax:4"} JSON.Errors { 8 | import Wrappers 9 | import opened BoundedInts 10 | import Utils.Str 11 | 12 | datatype DeserializationError = 13 | | UnterminatedSequence 14 | | UnsupportedEscape(str: string) 15 | | EscapeAtEOS 16 | | EmptyNumber 17 | | ExpectingEOF 18 | | IntOverflow 19 | | ReachedEOF 20 | | ExpectingByte(expected: byte, b: opt_byte) 21 | | ExpectingAnyByte(expected_sq: seq, b: opt_byte) 22 | | InvalidUnicode 23 | { 24 | function ToString() : string { 25 | match this 26 | case UnterminatedSequence => "Unterminated sequence" 27 | case UnsupportedEscape(str) => "Unsupported escape sequence: " + str 28 | case EscapeAtEOS => "Escape character at end of string" 29 | case EmptyNumber => "Number must contain at least one digit" 30 | case ExpectingEOF => "Expecting EOF" 31 | case IntOverflow => "Input length does not fit in a 32-bit counter" 32 | case ReachedEOF => "Reached EOF" 33 | case ExpectingByte(b0, b) => 34 | var c := if b > 0 then "'" + [b as char] + "'" else "EOF"; 35 | "Expecting '" + [b0 as char] + "', read " + c 36 | case ExpectingAnyByte(bs0, b) => 37 | var c := if b > 0 then "'" + [b as char] + "'" else "EOF"; 38 | var c0s := seq(|bs0|, idx requires 0 <= idx < |bs0| => bs0[idx] as char); 39 | "Expecting one of '" + c0s + "', read " + c 40 | case InvalidUnicode => "Invalid Unicode sequence" 41 | } 42 | } 43 | 44 | datatype SerializationError = 45 | | OutOfMemory 46 | | IntTooLarge(i: int) 47 | | StringTooLong(s: string) 48 | | InvalidUnicode 49 | { 50 | function ToString() : string { 51 | match this 52 | case OutOfMemory => "Out of memory" 53 | case IntTooLarge(i: int) => "Integer too large: " + Str.OfInt(i) 54 | case StringTooLong(s: string) => "String too long: " + s 55 | case InvalidUnicode => "Invalid Unicode sequence" 56 | } 57 | } 58 | 59 | type SerializationResult<+T> = Wrappers.Result 60 | type DeserializationResult<+T> = Wrappers.Result 61 | } 62 | -------------------------------------------------------------------------------- /src/dafny/BoundedInts.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /** Defines symbolic names for various powers of 2 and newtypes for various common 4 | restricted-range int types. */ 5 | module {:options "-functionSyntax:4"} Dafny.BoundedInts { 6 | const TWO_TO_THE_0: int := 1 7 | 8 | const TWO_TO_THE_1: int := 2 9 | const TWO_TO_THE_2: int := 4 10 | const TWO_TO_THE_4: int := 16 11 | const TWO_TO_THE_5: int := 32 12 | const TWO_TO_THE_7: int := 0x80 13 | const TWO_TO_THE_8: int := 0x100 14 | const TWO_TO_THE_15: int := 0x8000 15 | const TWO_TO_THE_16: int := 0x1_0000 16 | const TWO_TO_THE_24: int := 0x100_0000 17 | const TWO_TO_THE_31: int := 0x8000_0000 18 | const TWO_TO_THE_32: int := 0x1_00000000 19 | const TWO_TO_THE_40: int := 0x100_00000000 20 | const TWO_TO_THE_48: int := 0x10000_00000000 21 | const TWO_TO_THE_56: int := 0x1000000_00000000 22 | const TWO_TO_THE_63: int := 0x80000000_00000000 23 | const TWO_TO_THE_64: int := 0x1_00000000_00000000 24 | const TWO_TO_THE_127: int := 0x80000000_00000000_00000000_00000000 25 | const TWO_TO_THE_128: int := 0x1_00000000_00000000_00000000_00000000 26 | const TWO_TO_THE_256: int := 0x1_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 27 | const TWO_TO_THE_512: int := 28 | 0x1_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 29 | 30 | newtype uint8 = x: int | 0 <= x < TWO_TO_THE_8 31 | newtype uint16 = x: int | 0 <= x < TWO_TO_THE_16 32 | newtype uint32 = x: int | 0 <= x < TWO_TO_THE_32 33 | newtype uint64 = x: int | 0 <= x < TWO_TO_THE_64 34 | newtype uint128 = x: int | 0 <= x < TWO_TO_THE_128 35 | 36 | newtype int8 = x: int | -TWO_TO_THE_7 <= x < TWO_TO_THE_7 37 | newtype int16 = x: int | -TWO_TO_THE_15 <= x < TWO_TO_THE_15 38 | newtype int32 = x: int | -TWO_TO_THE_31 <= x < TWO_TO_THE_31 39 | newtype int64 = x: int | -TWO_TO_THE_63 <= x < TWO_TO_THE_63 40 | newtype int128 = x: int | -TWO_TO_THE_127 <= x < TWO_TO_THE_127 41 | 42 | newtype nat8 = x: int | 0 <= x < TWO_TO_THE_7 43 | newtype nat16 = x: int | 0 <= x < TWO_TO_THE_15 44 | newtype nat32 = x: int | 0 <= x < TWO_TO_THE_31 45 | newtype nat64 = x: int | 0 <= x < TWO_TO_THE_63 46 | newtype nat128 = x: int | 0 <= x < TWO_TO_THE_127 47 | 48 | } 49 | -------------------------------------------------------------------------------- /examples/MutableMap/MutableMapTrait.dfy: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright by the contributors to the Dafny Project 3 | * SPDX-License-Identifier: MIT 4 | *******************************************************************************/ 5 | 6 | // RUN: %verify "%s" 7 | 8 | /** 9 | * Specifications that should be satisfied by any implementation of mutable maps. 10 | * Possible instantiations are given in "MutableMapDafny.dfy" (not meant for usage, 11 | * only exists to verify feasability) and "../../src/MutableMap/MutableMap.dfy" 12 | * (meant for usage; interfaces with external code, e.g. "../../src/MutableMap/ 13 | * MutableMap.java"). 14 | */ 15 | module {:options "-functionSyntax:4"} MutableMapTrait { 16 | trait {:termination false} MutableMapTrait { 17 | function content(): map 18 | reads this 19 | 20 | method Put(k: K, v: V) 21 | modifies this 22 | ensures this.content() == old(this.content())[k := v] 23 | ensures k in old(this.content()).Keys ==> this.content().Values + {old(this.content())[k]} == old(this.content()).Values + {v} 24 | ensures k !in old(this.content()).Keys ==> this.content().Values == old(this.content()).Values + {v} 25 | 26 | function Keys(): (keys: set) 27 | reads this 28 | ensures keys == this.content().Keys 29 | 30 | predicate HasKey(k: K) 31 | reads this 32 | ensures HasKey(k) <==> k in this.content().Keys 33 | 34 | function Values(): (values: set) 35 | reads this 36 | ensures values == this.content().Values 37 | 38 | function Items(): (items: set<(K,V)>) 39 | reads this 40 | ensures items == this.content().Items 41 | ensures items == set k | k in this.content().Keys :: (k, this.content()[k]) 42 | 43 | function Select(k: K): (v: V) 44 | reads this 45 | requires this.HasKey(k) 46 | ensures v in this.content().Values 47 | ensures this.content()[k] == v 48 | 49 | method Remove(k: K) 50 | modifies this 51 | ensures this.content() == old(this.content()) - {k} 52 | ensures k in old(this.content()).Keys ==> this.content().Values + {old(this.content())[k]} == old(this.content()).Values 53 | 54 | function Size(): (size: int) 55 | reads this 56 | ensures size == |this.content().Items| 57 | } 58 | } -------------------------------------------------------------------------------- /examples/RelationsExamples.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../src/dafny/Relations.dfy" 9 | 10 | module {:options "-functionSyntax:4"} RelationsExamples { 11 | 12 | import opened Dafny.Relations 13 | 14 | lemma BuiltInIntLTIsStrictTotalOrdering() 15 | ensures StrictTotalOrdering((x: int, y: int) => x < y) 16 | {} 17 | 18 | lemma BuiltInIntLEIsTotalOrdering() 19 | ensures TotalOrdering((x: int, y: int) => x <= y) 20 | {} 21 | 22 | function Modulo(n: nat): (R: (int, int) -> bool) 23 | requires n >= 1 24 | ensures EquivalenceRelation(R) 25 | { 26 | (x, y) => x % n == y % n 27 | } 28 | 29 | lemma BuiltInIntEqIsEquivalenceRelation() 30 | ensures EquivalenceRelation((x: int, y: int) => x == y) 31 | {} 32 | 33 | lemma BuiltInIntGeIsAntiSymmetricRelation() 34 | ensures AntiSymmetric((x: int, y: int) => x >= y) 35 | {} 36 | 37 | lemma BuiltInIntLtIsAsymmetricRelation() 38 | ensures Asymmetric((x: int, y: int) => x < y) 39 | { 40 | } 41 | 42 | lemma AlwaysTrueIsNotAntiSymmetric() 43 | ensures !AntiSymmetric((x: int, y: int) => true) 44 | { 45 | var f := (x: int, y: int) => true; 46 | assert f(2,3) && f(3,2) && 3 != 2; 47 | assert !AntiSymmetric(f); 48 | } 49 | 50 | lemma BuiltInIntLtIsNotReflexiveRelation() 51 | ensures !Reflexive((x: int, y: int) => x < y) 52 | { 53 | var f := (x: int, y: int) => x < y; 54 | assert !f(0, 0); 55 | assert !forall x: int :: f(x, x); 56 | assert !Reflexive(f); 57 | } 58 | 59 | lemma BuiltInIntLtIsIrreflexiveRelation() 60 | ensures Irreflexive((x: int, y: int) => x < y) 61 | {} 62 | 63 | lemma BuiltInIntEqIsNotIrreflexiveRelation() 64 | ensures !Irreflexive((x: int, y: int) => x == y) 65 | { 66 | var f := (x: int, y: int) => x == y; 67 | assert f(0, 0); 68 | assert !Irreflexive(f); 69 | } 70 | 71 | lemma AsymmetricIsAntiSymmetric(f: (T, T) -> bool) 72 | ensures Asymmetric(f) ==> AntiSymmetric(f) 73 | {} 74 | 75 | lemma AsymmetricIsIrreflexive(f: (T, T) -> bool) 76 | ensures Asymmetric(f) ==> Irreflexive(f) 77 | {} 78 | } 79 | -------------------------------------------------------------------------------- /src/Unicode/UnicodeStrings.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify %s 2 | 3 | /// Converting between strings and UTF-8/UTF-16 4 | /// ============================================= 5 | /// 6 | /// This abstract module defines an interface for converting 7 | /// between the Dafny `string` type and sequences of UTF-8 and UTF-16 8 | /// codepoints, where codepoints are represents as bounded ints 9 | /// (`uint8` and `uint16`). 10 | /// 11 | /// Because the `--unicode-char` option changes the meaning of the `char` 12 | /// type and hence the `string` type, there are two different concrete 13 | /// implementations in separate files. 14 | /// Only include the one that matches your value of that option! 15 | /// 16 | /// If you also want to maintain code that works for either mode, 17 | /// you have two options: 18 | /// 19 | /// 1. Implement your logic in an abstract module as well that 20 | /// imports this one, and define two different refining modules 21 | /// that import the appropriate UnicodeStrings module. 22 | /// 2. Do not `include` any of these three files in your source code, 23 | /// instead passing the appropriate file to Dafny when verifying and building, 24 | /// so that references to `UnicodeStrings` can be resolved. 25 | /// See the JSON modules as an example. 26 | /// 27 | /// Option 2. avoids needing to write boilerplate refining modules, 28 | /// but is less IDE-friendly until we have better project configuration support. 29 | 30 | include "../BoundedInts.dfy" 31 | include "../Wrappers.dfy" 32 | include "../Collections/Sequences/Seq.dfy" 33 | include "Utf8EncodingForm.dfy" 34 | include "Utf16EncodingForm.dfy" 35 | 36 | abstract module {:options "-functionSyntax:4"} AbstractUnicodeStrings { 37 | 38 | import Seq 39 | 40 | import opened Wrappers 41 | import opened BoundedInts 42 | 43 | function ToUTF8Checked(s: string): Option> 44 | 45 | function ASCIIToUTF8(s: string): seq 46 | requires forall i | 0 <= i < |s| :: 0 <= s[i] as int < 128 47 | { 48 | Seq.Map(c requires 0 <= c as int < 128 => c as uint8, s) 49 | } 50 | 51 | function FromUTF8Checked(bs: seq): Option 52 | 53 | function ToUTF16Checked(s: string): Option> 54 | 55 | function ASCIIToUTF16(s: string): seq 56 | requires forall i | 0 <= i < |s| :: 0 <= s[i] as int < 128 57 | { 58 | Seq.Map(c requires 0 <= c as int < 128 => c as uint16, s) 59 | } 60 | 61 | function FromUTF16Checked(bs: seq): Option 62 | } 63 | -------------------------------------------------------------------------------- /examples/MutableMap/MutableMapExamples.dfy: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright by the contributors to the Dafny Project 3 | * SPDX-License-Identifier: MIT 4 | *******************************************************************************/ 5 | 6 | // RUN: %run --target:java "%s" --input "%S/../../src/MutableMap/MutableMap.java" 7 | 8 | include "../../src/MutableMap/MutableMap.dfy" 9 | include "../../src/Wrappers.dfy" 10 | 11 | /** 12 | * Tests the Java interfacing implementation of mutable maps. 13 | */ 14 | module {:options "-functionSyntax:4"} MutableMapExamples { 15 | import opened DafnyLibraries 16 | import opened Wrappers 17 | 18 | method AssertAndExpect(p: bool, maybeMsg: Option := None) requires p { 19 | match maybeMsg { 20 | case None => { 21 | expect p; 22 | } 23 | case Some(msg) => { 24 | expect p, msg; 25 | } 26 | } 27 | } 28 | 29 | method Main() { 30 | var m := new MutableMap((k: string, v: string) => true, false); 31 | AssertAndExpect(m.Keys() == {}); 32 | AssertAndExpect(m.Values() == {}); 33 | AssertAndExpect(m.Items() == {}); 34 | AssertAndExpect(m.Size() == 0); 35 | 36 | m.Put("testkey", "testvalue"); 37 | AssertAndExpect(m.Select("testkey") == "testvalue"); 38 | AssertAndExpect(m.Keys() == {"testkey"}); 39 | AssertAndExpect(m.Values() == {"testvalue"}); 40 | AssertAndExpect(m.Items() == {("testkey", "testvalue")}); 41 | print m.Select("testkey"), "\n"; 42 | 43 | AssertAndExpect(m.Size() == 1); 44 | m.Put("testkey", "testvalue2"); 45 | AssertAndExpect(m.Keys() == {"testkey"}); 46 | AssertAndExpect(m.Values() == {"testvalue2"}); 47 | AssertAndExpect(m.Items() == {("testkey", "testvalue2")}); 48 | 49 | m.Put("testkey2", "testvalue2"); 50 | AssertAndExpect(m.Keys() == {"testkey", "testkey2"}); 51 | AssertAndExpect(m.Values() == {"testvalue2"}); 52 | AssertAndExpect(m.Items() == {("testkey", "testvalue2"), ("testkey2", "testvalue2")}); 53 | AssertAndExpect(m.Size() == 2); 54 | AssertAndExpect("testkey" in m.Keys()); 55 | AssertAndExpect("testkey2" in m.Keys()); 56 | print m.Select("testkey"), "\n"; 57 | print m.Select("testkey2"), "\n"; 58 | 59 | m.Remove("testkey"); 60 | AssertAndExpect(m.Keys() == {"testkey2"}); 61 | AssertAndExpect(m.Values() == {"testvalue2"}); 62 | AssertAndExpect(m.Items() == {("testkey2", "testvalue2")}); 63 | } 64 | } -------------------------------------------------------------------------------- /src/dafny/Unicode/Unicode.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../Collections/Seqs.dfy" 9 | 10 | // This module implements basic functionality of Unicode 14.0. 11 | module {:options "-functionSyntax:4"} Dafny.Unicode { 12 | 13 | /** 14 | * Any value in the Unicode codespace (a range of integers from 0 to 10FFFF_16). (Section 3.9 D9-D10) 15 | */ 16 | type CodePoint = i: bv24 | 0 <= i <= 0x10FFFF 17 | 18 | /** 19 | * A Unicode code point in the range U+D800 to U+DBFF. (Section 3.8 D71) 20 | */ 21 | type HighSurrogateCodePoint = p: CodePoint | HIGH_SURROGATE_MIN <= p <= HIGH_SURROGATE_MAX 22 | witness HIGH_SURROGATE_MIN 23 | const HIGH_SURROGATE_MIN: CodePoint := 0xD800 24 | const HIGH_SURROGATE_MAX: CodePoint := 0xDBFF 25 | 26 | /** 27 | * A Unicode code point in the range U+DC00 to U+DFFF. (Section 3.8 D73) 28 | */ 29 | type LowSurrogateCodePoint = p: CodePoint | LOW_SURROGATE_MIN <= p <= LOW_SURROGATE_MAX 30 | witness LOW_SURROGATE_MIN 31 | const LOW_SURROGATE_MIN: CodePoint := 0xDC00 32 | const LOW_SURROGATE_MAX: CodePoint := 0xDFFF 33 | 34 | /** 35 | * Any Unicode code point except high-surrogate and low-surrogate code points. (Section 3.9 D76) 36 | */ 37 | type ScalarValue = p: CodePoint | 38 | && (p < HIGH_SURROGATE_MIN || p > HIGH_SURROGATE_MAX) 39 | && (p < LOW_SURROGATE_MIN || p > LOW_SURROGATE_MAX) 40 | 41 | const ASSIGNED_PLANES: set := { 42 | 0, // Basic Multilingual Plane 43 | 1, // Supplementary Multilingual Plane 44 | 2, // Supplementary Ideographic Plane 45 | 3, // Tertiary Ideographic Plane 46 | 14, // Supplementary Special Purpose Plane 47 | 15, // Supplementary Private Use Area A 48 | 16 // Supplementary Private Use Area B 49 | } 50 | 51 | ghost predicate {:opaque} IsInAssignedPlane(i: CodePoint) { 52 | var plane := (i >> 16) as bv8; 53 | plane in ASSIGNED_PLANES 54 | } 55 | 56 | // These are actually supersets of the Unicode planes, 57 | // since not all code points in a plane are assigned. 58 | // 59 | // TODO: check against the list of assigned code points, instead of only checking their plane 60 | // (https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt) 61 | type AssignedCodePoint = p: CodePoint | IsInAssignedPlane(p) witness * 62 | } 63 | -------------------------------------------------------------------------------- /src/BoundedInts.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | module {:options "-functionSyntax:4"} BoundedInts { 4 | const TWO_TO_THE_0: int := 1 5 | const TWO_TO_THE_1: int := 2 6 | const TWO_TO_THE_2: int := 4 7 | const TWO_TO_THE_4: int := 16 8 | const TWO_TO_THE_5: int := 32 9 | const TWO_TO_THE_8: int := 0x100 10 | const TWO_TO_THE_16: int := 0x10000 11 | const TWO_TO_THE_24: int := 0x1000000 12 | const TWO_TO_THE_32: int := 0x1_00000000 13 | const TWO_TO_THE_40: int := 0x100_00000000 14 | const TWO_TO_THE_48: int := 0x10000_00000000 15 | const TWO_TO_THE_56: int := 0x1000000_00000000 16 | const TWO_TO_THE_64: int := 0x1_00000000_00000000 17 | const TWO_TO_THE_128: int := 0x1_00000000_00000000_00000000_00000000 18 | const TWO_TO_THE_256: int := 0x1_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 19 | const TWO_TO_THE_512: int := 20 | 0x1_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000 21 | 22 | newtype uint8 = x: int | 0 <= x < TWO_TO_THE_8 23 | newtype uint16 = x: int | 0 <= x < TWO_TO_THE_16 24 | newtype uint32 = x: int | 0 <= x < TWO_TO_THE_32 25 | newtype uint64 = x: int | 0 <= x < TWO_TO_THE_64 26 | 27 | newtype int8 = x: int | -0x80 <= x < 0x80 28 | newtype int16 = x: int | -0x8000 <= x < 0x8000 29 | newtype int32 = x: int | -0x8000_0000 <= x < 0x8000_0000 30 | newtype int64 = x: int | -0x8000_0000_0000_0000 <= x < 0x8000_0000_0000_0000 31 | 32 | newtype nat8 = x: int | 0 <= x < 0x80 33 | newtype nat16 = x: int | 0 <= x < 0x8000 34 | newtype nat32 = x: int | 0 <= x < 0x8000_0000 35 | newtype nat64 = x: int | 0 <= x < 0x8000_0000_0000_0000 36 | 37 | const UINT8_MAX: uint8 := 0xFF 38 | const UINT16_MAX: uint16 := 0xFFFF 39 | const UINT32_MAX: uint32 := 0xFFFF_FFFF 40 | const UINT64_MAX: uint64 := 0xFFFF_FFFF_FFFF_FFFF 41 | 42 | const INT8_MIN: int8 := -0x80 43 | const INT8_MAX: int8 := 0x7F 44 | const INT16_MIN: int16 := -0x8000 45 | const INT16_MAX: int16 := 0x7FFF 46 | const INT32_MIN: int32 := -0x8000_0000 47 | const INT32_MAX: int32 := 0x7FFFFFFF 48 | const INT64_MIN: int64 := -0x8000_0000_0000_0000 49 | const INT64_MAX: int64 := 0x7FFFFFFF_FFFFFFFF 50 | 51 | const NAT8_MAX: nat8 := 0x7F 52 | const NAT16_MAX: nat16 := 0x7FFF 53 | const NAT32_MAX: nat32 := 0x7FFFFFFF 54 | const NAT64_MAX: nat64 := 0x7FFFFFFF_FFFFFFFF 55 | 56 | type byte = uint8 57 | type bytes = seq 58 | newtype opt_byte = c: int | -1 <= c < TWO_TO_THE_8 59 | } 60 | -------------------------------------------------------------------------------- /src/JSON/Utils/Parsers.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | include "../../BoundedInts.dfy" 4 | include "../../Wrappers.dfy" 5 | include "Cursors.dfy" 6 | 7 | module {:options "-functionSyntax:4"} JSON.Utils.Parsers { 8 | import opened BoundedInts 9 | import opened Wrappers 10 | 11 | import opened Views.Core 12 | import opened Cursors 13 | 14 | type SplitResult<+T, +R> = Result, CursorError> 15 | 16 | type Parser = p: Parser_ | p.Valid?() 17 | // BUG(https://github.com/dafny-lang/dafny/issues/2103) 18 | witness ParserWitness() // BUG(https://github.com/dafny-lang/dafny/issues/2175) 19 | datatype Parser_ = Parser(fn: FreshCursor -> SplitResult, 20 | ghost spec: T -> bytes) { 21 | ghost predicate Valid?() { 22 | forall cs': FreshCursor :: fn(cs').Success? ==> fn(cs').value.StrictlySplitFrom?(cs', spec) 23 | } 24 | } 25 | 26 | function {:opaque} ParserWitness(): (p: Parser_) 27 | ensures p.Valid?() 28 | { 29 | Parser(_ => Failure(EOF), _ => []) 30 | } 31 | 32 | // BUG(https://github.com/dafny-lang/dafny/issues/2137): It would be much 33 | // nicer if `SubParser` was a special case of `Parser`, but that would require 34 | // making `fn` in parser a partial function `-->`. The problem with that is 35 | // that we would then have to restrict the `Valid?` clause of `Parser` on 36 | // `fn.requires()`, thus making it unprovable in the `SubParser` case (`fn` 37 | // for subparsers is typically a lambda, and the `requires` of lambdas are 38 | // essentially uninformative/opaque). 39 | datatype SubParser_ = SubParser( 40 | ghost cs: Cursor, 41 | ghost pre: FreshCursor -> bool, 42 | fn: FreshCursor --> SplitResult, 43 | ghost spec: T -> bytes) 44 | { 45 | ghost predicate Valid?() { 46 | && (forall cs': FreshCursor | pre(cs') :: fn.requires(cs')) 47 | && (forall cs': FreshCursor | cs'.StrictlySplitFrom?(cs) :: pre(cs')) 48 | && (forall cs': FreshCursor | pre(cs') :: fn(cs').Success? ==> fn(cs').value.StrictlySplitFrom?(cs', spec)) 49 | } 50 | } 51 | 52 | type SubParser = p: SubParser_ | p.Valid?() 53 | witness SubParserWitness() // BUG(https://github.com/dafny-lang/dafny/issues/2175) 54 | 55 | function {:opaque} SubParserWitness(): (subp: SubParser_) 56 | ensures subp.Valid?() 57 | { 58 | SubParser(Cursor([], 0, 0, 0), 59 | (cs: FreshCursor) => false, 60 | (cs: FreshCursor) => Failure(EOF), 61 | _ => []) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Unicode/Unicode.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../Wrappers.dfy" 9 | include "../Collections/Sequences/Seq.dfy" 10 | 11 | // This module implements basic functionality of Unicode 14.0. 12 | module {:options "-functionSyntax:4"} Unicode { 13 | import opened Wrappers 14 | 15 | import Seq 16 | 17 | /** 18 | * Any value in the Unicode codespace (a range of integers from 0 to 10FFFF_16). (Section 3.9 D9-D10) 19 | */ 20 | type CodePoint = i: bv24 | 0 <= i <= 0x10FFFF 21 | 22 | /** 23 | * A Unicode code point in the range U+D800 to U+DBFF. (Section 3.8 D71) 24 | */ 25 | type HighSurrogateCodePoint = p: CodePoint | HIGH_SURROGATE_MIN <= p <= HIGH_SURROGATE_MAX 26 | witness HIGH_SURROGATE_MIN 27 | const HIGH_SURROGATE_MIN: CodePoint := 0xD800 28 | const HIGH_SURROGATE_MAX: CodePoint := 0xDBFF 29 | 30 | /** 31 | * A Unicode code point in the range U+DC00 to U+DFFF. (Section 3.8 D73) 32 | */ 33 | type LowSurrogateCodePoint = p: CodePoint | LOW_SURROGATE_MIN <= p <= LOW_SURROGATE_MAX 34 | witness LOW_SURROGATE_MIN 35 | const LOW_SURROGATE_MIN: CodePoint := 0xDC00 36 | const LOW_SURROGATE_MAX: CodePoint := 0xDFFF 37 | 38 | /** 39 | * Any Unicode code point except high-surrogate and low-surrogate code points. (Section 3.9 D76) 40 | */ 41 | type ScalarValue = p: CodePoint | 42 | && (p < HIGH_SURROGATE_MIN || p > HIGH_SURROGATE_MAX) 43 | && (p < LOW_SURROGATE_MIN || p > LOW_SURROGATE_MAX) 44 | 45 | const ASSIGNED_PLANES: set := { 46 | 0, // Basic Multilingual Plane 47 | 1, // Supplementary Multilingual Plane 48 | 2, // Supplementary Ideographic Plane 49 | 3, // Tertiary Ideographic Plane 50 | 14, // Supplementary Special Purpose Plane 51 | 15, // Supplementary Private Use Area A 52 | 16 // Supplementary Private Use Area B 53 | } 54 | 55 | ghost predicate {:opaque} IsInAssignedPlane(i: CodePoint) { 56 | var plane := (i >> 16) as bv8; 57 | plane in ASSIGNED_PLANES 58 | } 59 | 60 | // These are actually supersets of the Unicode planes, 61 | // since not all code points in a plane are assigned. 62 | // 63 | // TODO: check against the list of assigned code points, instead of only checking their plane 64 | // (https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt) 65 | type AssignedCodePoint = p: CodePoint | IsInAssignedPlane(p) witness * 66 | } 67 | -------------------------------------------------------------------------------- /src/dafny/BoundedInts.md: -------------------------------------------------------------------------------- 1 | 2 | ## The `BoundedInts` module {#sec-boundedints} 3 | 4 | The `Dafny.BoundedInts` module contains definitions of types and constants for fixed-bit-width integers. 5 | 6 | Dafny itself generally uses and reasons about mathematical, unbounded integers and natural numbers. 7 | However compiled programs, for efficiency of computation and storage, will generally use fixed-bit-width 8 | numbers. So Dafny defines them here, for use when needed. 9 | 10 | An important implementation point about these type definitions is Dafny can determine which native type 11 | in the target compiled program best matches the Dafny type. For example, the Dafny type `int16` is 12 | a signed integer that fits in 16 bits. If a program using this type is compiled to, say Java, then 13 | variables of this Dafny type will be compiled to Java `short` variables. In some cases there is no 14 | natural match. For example Java does not have an unsigned 16-bit type while C# and C++ do. 15 | 16 | This module defines: 17 | - unsigned types of 8, 16, 32, 64, 128 bit widths (e.g. `uint32`) 18 | - signed types of 8, 16, 32, 64, 128 bit widths (e.g. `int16`) 19 | - unsigned types that are subsets of the corresponding signed type (e.g. `nat8` has values from 0 through 127) 20 | 21 | The `natN` series of types require some care. A `nat8` for example has non-negative values up through 127, 22 | that is, just 7-bits worth of values. But it can be directly converted to an `int8` and can be represented by a 23 | native signed 8-bit integer type. 24 | - if you need a general unsigned 8-bit type, with values running up to 256, use `uint8` 25 | - if you want natural numbers that interact well with signed numbers and do not mind the restriction in range, use `nat8` 26 | - if the target platform for compilation does not have native unsigned int types, then use nat types because of the smaller range 27 | 28 | In addition, the module defines a number of constants that are powers of two (not all of them, just those that are generally useful). 29 | They are useful in stating the ranges of fixed-bit-width integers. Examples are `TWO_TO_THE_15`, `TWO_TO_THE_32`. 30 | 31 | Here are two example methods. In the first, the module `Dafny.BoundedInts` is renamed to `BI`, which is then used as the prefix for type and constant names. 32 | In the second, the module is imported as opened, in which case the type and constant names can be used without qualification. 33 | 34 | ```dafny 35 | module M { 36 | import BI = Dafny.BoundedInts 37 | 38 | method m(k: BI.int16) { 39 | assert k as int < BI.TWO_TO_THE_15; 40 | } 41 | } 42 | 43 | module N { 44 | import opened Dafny.BoundedInts 45 | 46 | method m(k: int16) { 47 | assert (k/256) as int < TWO_TO_THE_8; 48 | } 49 | } 50 | ``` 51 | 52 | -------------------------------------------------------------------------------- /src/dafny/FileIO/FileIO.dfy: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright by the contributors to the Dafny Project 3 | * SPDX-License-Identifier: MIT 4 | *******************************************************************************/ 5 | 6 | // RUN: %verify "%s" 7 | 8 | include "../Wrappers.dfy" 9 | 10 | /** 11 | * This module provides basic file I/O operations: reading and writing bytes from/to a file. 12 | * The provided API is intentionally limited in scope and will be expanded later. 13 | * 14 | * Where the API accepts file paths as strings, there are some limitations. 15 | * File paths containing only ASCII characters work identically across languages and platforms; 16 | * non-ASCII Unicode codepoints may cause different language- or platform-specific behavior. 17 | * 18 | * File path symbols including . and .. are allowed. 19 | */ 20 | module {:options "-functionSyntax:4"} Dafny.FileIO { 21 | import opened Wrappers 22 | 23 | export provides ReadBytesFromFile, WriteBytesToFile, Wrappers 24 | 25 | /* 26 | * Public API 27 | */ 28 | 29 | /** 30 | * Attempts to read all bytes from the file at the given file path. 31 | * If an error occurs, a `Result.Failure` value is returned containing an implementation-specific 32 | * error message (which may also contain a stack trace). 33 | * 34 | * NOTE: See the module description for limitations on the path argument. 35 | */ 36 | method ReadBytesFromFile(path: string) returns (res: Result, string>) { 37 | var isError, bytesRead, errorMsg := INTERNAL_ReadBytesFromFile(path); 38 | return if isError then Failure(errorMsg) else Success(bytesRead); 39 | } 40 | 41 | /** 42 | * Attempts to write the given bytes to the file at the given file path, 43 | * creating nonexistent parent directories as necessary. 44 | * If an error occurs, a `Result.Failure` value is returned containing an implementation-specific 45 | * error message (which may also contain a stack trace). 46 | * 47 | * NOTE: See the module description for limitations on the path argument. 48 | */ 49 | method WriteBytesToFile(path: string, bytes: seq) returns (res: Result<(), string>) 50 | { 51 | var isError, errorMsg := INTERNAL_WriteBytesToFile(path, bytes); 52 | return if isError then Failure(errorMsg) else Success(()); 53 | } 54 | 55 | /* 56 | * Private API - these are intentionally not exported from the module and should not be used elsewhere 57 | */ 58 | 59 | method 60 | {:extern "DafnyLibraries.FileIO", "INTERNAL_ReadBytesFromFile"} 61 | INTERNAL_ReadBytesFromFile(path: string) 62 | returns (isError: bool, bytesRead: seq, errorMsg: string) 63 | 64 | method 65 | {:extern "DafnyLibraries.FileIO", "INTERNAL_WriteBytesToFile"} 66 | INTERNAL_WriteBytesToFile(path: string, bytes: seq) 67 | returns (isError: bool, errorMsg: string) 68 | } 69 | -------------------------------------------------------------------------------- /src/dafny/Unicode/Utf8EncodingScheme.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../Collections/Seqs.dfy" 9 | include "../BoundedInts.dfy" 10 | 11 | include "Unicode.dfy" 12 | include "Utf8EncodingForm.dfy" 13 | 14 | /** 15 | * The Unicode encoding scheme that serializes a UTF-8 code unit sequence in exactly the same order as the code unit 16 | * sequence itself. 17 | * 18 | * Because the UTF-8 encoding form deals in ordered byte sequences, the UTF-8 encoding scheme is trivial. 19 | * The byte ordering is completely defined by the UTF-8 code unit sequence itself. 20 | * We implement the encoding scheme here for completeness of the Unicode character encoding model, 21 | * and to perform the (trivial) conversion between `uint8`/`byte` and `bv8` values. 22 | * 23 | * (Section 3.10 D95) 24 | * 25 | * TODO: this should refine an abstract UnicodeEncodingScheme module 26 | * that states lemmas/conditions about Serialize and Deserialize 27 | * which refining modules would prove about their own implementations. 28 | * Proving those lemmas are easier to write using `calc`, 29 | * but that runs into . 30 | */ 31 | module {:options "-functionSyntax:4"} Dafny.Utf8EncodingScheme { 32 | 33 | import BoundedInts 34 | import Collections.Seq 35 | import Unicode 36 | import Utf8EncodingForm 37 | 38 | type byte = BoundedInts.uint8 39 | 40 | /** 41 | * Returns the byte serialization of the given code unit sequence. 42 | */ 43 | function Serialize(s: Utf8EncodingForm.CodeUnitSeq): (b: seq) 44 | { 45 | Seq.Map(c => c as byte, s) 46 | } 47 | 48 | /** 49 | * Returns the code unit sequence that serializes to the given byte sequence. 50 | */ 51 | function Deserialize(b: seq): (s: Utf8EncodingForm.CodeUnitSeq) 52 | { 53 | Seq.Map(b => b as Utf8EncodingForm.CodeUnit, b) 54 | } 55 | 56 | /** 57 | * Serializing a code unit sequence and then deserializing the result, yields the original code unit sequence. 58 | */ 59 | lemma LemmaSerializeDeserialize(s: Utf8EncodingForm.CodeUnitSeq) 60 | ensures Deserialize(Serialize(s)) == s 61 | {} 62 | 63 | /** 64 | * Deserializing a byte sequence and then serializing the result, yields the original byte sequence. 65 | */ 66 | lemma LemmaDeserializeSerialize(b: seq) 67 | ensures Serialize(Deserialize(b)) == b 68 | { 69 | calc { 70 | Serialize(Deserialize(b)); 71 | == // Definitions of Serialize, Deserialize 72 | Seq.Map(c => c as byte, Seq.Map(b => b as Utf8EncodingForm.CodeUnit, b)); 73 | == // Compositionality of Map 74 | Seq.Map(b => (b as Utf8EncodingForm.CodeUnit) as byte, b); 75 | == // Simplify map 76 | Seq.Map(b => b, b); 77 | == // Identity function 78 | b; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Unicode/Utf8EncodingScheme.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../Collections/Sequences/Seq.dfy" 9 | include "../BoundedInts.dfy" 10 | 11 | include "Unicode.dfy" 12 | include "Utf8EncodingForm.dfy" 13 | 14 | /** 15 | * The Unicode encoding scheme that serializes a UTF-8 code unit sequence in exactly the same order as the code unit 16 | * sequence itself. 17 | * 18 | * Because the UTF-8 encoding form deals in ordered byte sequences, the UTF-8 encoding scheme is trivial. 19 | * The byte ordering is completely defined by the UTF-8 code unit sequence itself. 20 | * We implement the encoding scheme here for completeness of the Unicode character encoding model, 21 | * and to perform the (trivial) conversion between `uint8`/`byte` and `bv8` values. 22 | * 23 | * (Section 3.10 D95) 24 | * 25 | * TODO: this should refine an abstract UnicodeEncodingScheme module 26 | * that states lemmas/conditions about Serialize and Deserialize 27 | * which refining modules would prove about their own implementations. 28 | * Proving those lemmas are easier to write using `calc`, 29 | * but that runs into . 30 | */ 31 | module {:options "-functionSyntax:4"} Utf8EncodingScheme { 32 | import opened Wrappers 33 | 34 | import BoundedInts 35 | import Seq 36 | import Unicode 37 | import Utf8EncodingForm 38 | 39 | type byte = BoundedInts.uint8 40 | 41 | /** 42 | * Returns the byte serialization of the given code unit sequence. 43 | */ 44 | function Serialize(s: Utf8EncodingForm.CodeUnitSeq): (b: seq) 45 | { 46 | Seq.Map(c => c as byte, s) 47 | } 48 | 49 | /** 50 | * Returns the code unit sequence that serializes to the given byte sequence. 51 | */ 52 | function Deserialize(b: seq): (s: Utf8EncodingForm.CodeUnitSeq) 53 | { 54 | Seq.Map(b => b as Utf8EncodingForm.CodeUnit, b) 55 | } 56 | 57 | /** 58 | * Serializing a code unit sequence and then deserializing the result, yields the original code unit sequence. 59 | */ 60 | lemma LemmaSerializeDeserialize(s: Utf8EncodingForm.CodeUnitSeq) 61 | ensures Deserialize(Serialize(s)) == s 62 | {} 63 | 64 | /** 65 | * Deserializing a byte sequence and then serializing the result, yields the original byte sequence. 66 | */ 67 | lemma LemmaDeserializeSerialize(b: seq) 68 | ensures Serialize(Deserialize(b)) == b 69 | { 70 | calc { 71 | Serialize(Deserialize(b)); 72 | == // Definitions of Serialize, Deserialize 73 | Seq.Map(c => c as byte, Seq.Map(b => b as Utf8EncodingForm.CodeUnit, b)); 74 | == // Compositionality of Map 75 | Seq.Map(b => (b as Utf8EncodingForm.CodeUnit) as byte, b); 76 | == // Simplify map 77 | Seq.Map(b => b, b); 78 | == // Identity function 79 | b; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/FileIO/FileIO.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright by the contributors to the Dafny Project 3 | * SPDX-License-Identifier: MIT 4 | *******************************************************************************/ 5 | 6 | var DafnyLibraries = DafnyLibraries || {}; 7 | DafnyLibraries.FileIO = (function() { 8 | const buffer = require("buffer"); 9 | const fs = require("fs"); 10 | const nodePath = require("path"); 11 | 12 | let $module = {}; 13 | 14 | /** 15 | * Attempts to read all bytes from the file at the given `path`, and returns an array of the following values: 16 | * 17 | * - `isError`: true iff an error was thrown during path string conversion or when reading the file 18 | * - `bytesRead`: the sequence of bytes from the file, or an empty sequence if `isError` is true 19 | * - `errorMsg`: the error message of the thrown error if `isError` is true, or an empty sequence otherwise 20 | * 21 | * We return these values individually because `Result` is not defined in the runtime but instead in library code. 22 | * It is the responsibility of library code to construct an equivalent `Result` value. 23 | */ 24 | $module.INTERNAL_ReadBytesFromFile = function(path) { 25 | const emptySeq = _dafny.Seq.of(); 26 | try { 27 | const readOpts = { encoding: null }; // read as buffer, not string 28 | const buf = fs.readFileSync(path, readOpts); 29 | const readBytes = _dafny.Seq.from(buf.valueOf(), byte => new BigNumber(byte)); 30 | return [false, readBytes, emptySeq]; 31 | } catch (e) { 32 | const errorMsg = _dafny.Seq.from(e.stack); 33 | return [true, emptySeq, errorMsg]; 34 | } 35 | } 36 | 37 | /** 38 | * Attempts to write all given `bytes` to the file at the given `path`, creating nonexistent parent directories as necessary, 39 | * and returns an array of the following values: 40 | * 41 | * - `isError`: true iff an error was thrown during path string conversion or when writing to the file 42 | * - `errorMsg`: the error message of the thrown error if `isError` is true, or an empty sequence otherwise 43 | * 44 | * We return these values individually because `Result` is not defined in the runtime but instead in library code. 45 | * It is the responsibility of library code to construct an equivalent `Result` value. 46 | */ 47 | $module.INTERNAL_WriteBytesToFile = function(path, bytes) { 48 | try { 49 | const buf = buffer.Buffer.from(bytes); 50 | createParentDirs(path); 51 | fs.writeFileSync(path, buf); // no need to specify encoding because data is a Buffer 52 | return [false, _dafny.Seq.of()]; 53 | } catch (e) { 54 | const errorMsg = _dafny.Seq.from(e.stack); 55 | return [true, errorMsg]; 56 | } 57 | } 58 | 59 | /** 60 | * Creates the nonexistent parent directory(-ies) of the given path. 61 | */ 62 | const createParentDirs = function(path) { 63 | const parentDir = nodePath.dirname(nodePath.normalize(path)); 64 | fs.mkdirSync(parentDir, { recursive: true }); 65 | }; 66 | 67 | return $module; 68 | })(); 69 | -------------------------------------------------------------------------------- /src/dafny/FileIO/FileIO.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright by the contributors to the Dafny Project 3 | * SPDX-License-Identifier: MIT 4 | *******************************************************************************/ 5 | 6 | var DafnyLibraries = DafnyLibraries || {}; 7 | DafnyLibraries.FileIO = (function() { 8 | const buffer = require("buffer"); 9 | const fs = require("fs"); 10 | const nodePath = require("path"); 11 | 12 | let $module = {}; 13 | 14 | /** 15 | * Attempts to read all bytes from the file at the given `path`, and returns an array of the following values: 16 | * 17 | * - `isError`: true iff an error was thrown during path string conversion or when reading the file 18 | * - `bytesRead`: the sequence of bytes from the file, or an empty sequence if `isError` is true 19 | * - `errorMsg`: the error message of the thrown error if `isError` is true, or an empty sequence otherwise 20 | * 21 | * We return these values individually because `Result` is not defined in the runtime but instead in library code. 22 | * It is the responsibility of library code to construct an equivalent `Result` value. 23 | */ 24 | $module.INTERNAL_ReadBytesFromFile = function(path) { 25 | const emptySeq = _dafny.Seq.of(); 26 | try { 27 | const readOpts = { encoding: null }; // read as buffer, not string 28 | const buf = fs.readFileSync(path, readOpts); 29 | const readBytes = _dafny.Seq.from(buf.valueOf(), byte => new BigNumber(byte)); 30 | return [false, readBytes, emptySeq]; 31 | } catch (e) { 32 | const errorMsg = _dafny.Seq.from(e.stack); 33 | return [true, emptySeq, errorMsg]; 34 | } 35 | } 36 | 37 | /** 38 | * Attempts to write all given `bytes` to the file at the given `path`, creating nonexistent parent directories as necessary, 39 | * and returns an array of the following values: 40 | * 41 | * - `isError`: true iff an error was thrown during path string conversion or when writing to the file 42 | * - `errorMsg`: the error message of the thrown error if `isError` is true, or an empty sequence otherwise 43 | * 44 | * We return these values individually because `Result` is not defined in the runtime but instead in library code. 45 | * It is the responsibility of library code to construct an equivalent `Result` value. 46 | */ 47 | $module.INTERNAL_WriteBytesToFile = function(path, bytes) { 48 | try { 49 | const buf = buffer.Buffer.from(bytes); 50 | createParentDirs(path); 51 | fs.writeFileSync(path, buf); // no need to specify encoding because data is a Buffer 52 | return [false, _dafny.Seq.of()]; 53 | } catch (e) { 54 | const errorMsg = _dafny.Seq.from(e.stack); 55 | return [true, errorMsg]; 56 | } 57 | } 58 | 59 | /** 60 | * Creates the nonexistent parent directory(-ies) of the given path. 61 | */ 62 | const createParentDirs = function(path) { 63 | const parentDir = nodePath.dirname(nodePath.normalize(path)); 64 | fs.mkdirSync(parentDir, { recursive: true }); 65 | }; 66 | 67 | return $module; 68 | })(); 69 | -------------------------------------------------------------------------------- /src/Wrappers.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | module {:options "-functionSyntax:4"} Wrappers { 9 | 10 | datatype Option<+T> = None | Some(value: T) { 11 | function ToResult(): Result { 12 | match this 13 | case Some(v) => Success(v) 14 | case None() => Failure("Option is None") 15 | } 16 | 17 | function ToResult'(error: R): Result { 18 | match this 19 | case Some(v) => Success(v) 20 | case None() => Failure(error) 21 | } 22 | 23 | function UnwrapOr(default: T): T { 24 | match this 25 | case Some(v) => v 26 | case None() => default 27 | } 28 | 29 | predicate IsFailure() { 30 | None? 31 | } 32 | 33 | function PropagateFailure(): Option 34 | requires None? 35 | { 36 | None 37 | } 38 | 39 | function Extract(): T 40 | requires Some? 41 | { 42 | value 43 | } 44 | } 45 | 46 | datatype Result<+T, +R> = | Success(value: T) | Failure(error: R) { 47 | function ToOption(): Option 48 | { 49 | match this 50 | case Success(s) => Some(s) 51 | case Failure(e) => None() 52 | } 53 | 54 | function UnwrapOr(default: T): T 55 | { 56 | match this 57 | case Success(s) => s 58 | case Failure(e) => default 59 | } 60 | 61 | predicate IsFailure() { 62 | Failure? 63 | } 64 | 65 | function PropagateFailure(): Result 66 | requires Failure? 67 | { 68 | Failure(this.error) 69 | } 70 | 71 | function MapFailure(reWrap: R -> NewR): Result 72 | { 73 | match this 74 | case Success(s) => Success(s) 75 | case Failure(e) => Failure(reWrap(e)) 76 | } 77 | 78 | function Extract(): T 79 | requires Success? 80 | { 81 | value 82 | } 83 | } 84 | 85 | datatype Outcome = Pass | Fail(error: E) 86 | { 87 | predicate IsFailure() { 88 | Fail? 89 | } 90 | // Note: PropagateFailure returns a Result, not an Outcome. 91 | function PropagateFailure(): Result 92 | requires Fail? 93 | { 94 | Failure(this.error) 95 | } 96 | // Note: no Extract method 97 | } 98 | 99 | // A helper function to ensure a requirement is true at runtime 100 | // :- Need(5 == |mySet|, "The set MUST have 5 elements.") 101 | function Need(condition: bool, error: E): (result: Outcome) 102 | { 103 | if condition then Pass else Fail(error) 104 | } 105 | 106 | // An improvement over Need if the error type is more complex than a static string 107 | // Replace 108 | // Need(condition, ErrorFunction()); 109 | // with 110 | // FNeed(condition, () => ErrorFunction()); 111 | function FNeed(condition: bool, error: () --> E): (result: Outcome) 112 | requires !condition ==> error.requires() 113 | { 114 | if condition then Pass else Fail(error()) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Unicode/UnicodeStringsWithUnicodeChar.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify --unicode-char:true %s 2 | 3 | /// Converting between strings and UTF-8/UTF-16 4 | /// ============================================= 5 | /// 6 | /// Implementation of `AbstractUnicodeStrings` for `--unicode-char:true`. 7 | /// See `UnicodeStrings.dfy` for details. 8 | 9 | include "UnicodeStrings.dfy" 10 | include "../Wrappers.dfy" 11 | include "../Collections/Sequences/Seq.dfy" 12 | 13 | module {:options "-functionSyntax:4"} UnicodeStrings refines AbstractUnicodeStrings { 14 | 15 | import Unicode 16 | import Utf8EncodingForm 17 | import Utf16EncodingForm 18 | 19 | lemma {:vcs_split_on_every_assert} CharIsUnicodeScalarValue(c: char) 20 | ensures 21 | && var asBits := c as bv24; 22 | && asBits <= 0x10_FFFF 23 | && (0 <= asBits < Unicode.HIGH_SURROGATE_MIN || Unicode.LOW_SURROGATE_MAX < asBits) 24 | { 25 | assert c as int < 0x11_0000; 26 | // This seems to be just too expensive to verify for such a wide bit-vector type, 27 | // but is clearly true given the above. 28 | assume {:axiom} c as bv24 < 0x11_0000 as bv24; 29 | var asBits := c as int as bv24; 30 | assert (asBits < Unicode.HIGH_SURROGATE_MIN || asBits > Unicode.LOW_SURROGATE_MAX); 31 | assert asBits <= 0x10_FFFF; 32 | } 33 | 34 | lemma UnicodeScalarValueIsChar(sv: Unicode.ScalarValue) 35 | ensures 36 | && var asInt := sv as int; 37 | && (0 <= asInt < 0xD800 || 0xE000 <= asInt < 0x11_0000) 38 | { 39 | var asInt := sv as int; 40 | assert (asInt < 0xD800 || asInt > 0xDFFF); 41 | assert (asInt < 0xDBFF || asInt > 0xDC00); 42 | } 43 | 44 | function CharAsUnicodeScalarValue(c: char): Unicode.ScalarValue { 45 | CharIsUnicodeScalarValue(c); 46 | c as Unicode.ScalarValue 47 | } 48 | 49 | function CharFromUnicodeScalarValue(sv: Unicode.ScalarValue): char { 50 | UnicodeScalarValueIsChar(sv); 51 | // TODO: Can we avoid the extra cast to int? 52 | sv as int as char 53 | } 54 | 55 | function ToUTF8Checked(s: string): Option> 56 | ensures ToUTF8Checked(s).Some? 57 | { 58 | var asCodeUnits := Seq.Map(CharAsUnicodeScalarValue, s); 59 | var asUtf8CodeUnits := Utf8EncodingForm.EncodeScalarSequence(asCodeUnits); 60 | var asBytes := Seq.Map(cu => cu as uint8, asUtf8CodeUnits); 61 | Some(asBytes) 62 | } 63 | 64 | function FromUTF8Checked(bs: seq): Option { 65 | var asCodeUnits := Seq.Map(c => c as Utf8EncodingForm.CodeUnit, bs); 66 | var utf32 :- Utf8EncodingForm.DecodeCodeUnitSequenceChecked(asCodeUnits); 67 | var asChars := Seq.Map(CharFromUnicodeScalarValue, utf32); 68 | Some(asChars) 69 | } 70 | 71 | function ToUTF16Checked(s: string): Option> 72 | ensures ToUTF16Checked(s).Some? 73 | { 74 | var asCodeUnits := Seq.Map(CharAsUnicodeScalarValue, s); 75 | var asUtf16CodeUnits := Utf16EncodingForm.EncodeScalarSequence(asCodeUnits); 76 | var asBytes := Seq.Map(cu => cu as uint16, asUtf16CodeUnits); 77 | Some(asBytes) 78 | } 79 | 80 | function FromUTF16Checked(bs: seq): Option { 81 | var asCodeUnits := Seq.Map(c => c as Utf16EncodingForm.CodeUnit, bs); 82 | var utf32 :- Utf16EncodingForm.DecodeCodeUnitSequenceChecked(asCodeUnits); 83 | var asChars := Seq.Map(CharFromUnicodeScalarValue, utf32); 84 | Some(asChars) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/NonlinearArithmetic/Logarithm.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify --disable-nonlinear-arithmetic "%s" 2 | 3 | include "Mul.dfy" 4 | include "DivMod.dfy" 5 | include "Power.dfy" 6 | 7 | module {:options "-functionSyntax:4"} Logarithm { 8 | import opened Mul 9 | import opened DivMod 10 | import opened Power 11 | 12 | function {:opaque} Log(base: nat, pow: nat): nat 13 | requires base > 1 14 | decreases pow 15 | { 16 | if pow < base then 0 17 | else 18 | LemmaDivPosIsPosAuto(); LemmaDivDecreasesAuto(); 19 | 1 + Log(base, pow / base) 20 | } 21 | 22 | lemma {:induction false} LemmaLog0(base: nat, pow: nat) 23 | requires base > 1 24 | requires pow < base 25 | ensures Log(base, pow) == 0 26 | { 27 | reveal Log(); 28 | } 29 | 30 | lemma {:induction false} LemmaLogS(base: nat, pow: nat) 31 | requires base > 1 32 | requires pow >= base 33 | ensures pow / base >= 0 34 | ensures Log(base, pow) == 1 + Log(base, pow / base) 35 | { 36 | LemmaDivPosIsPosAuto(); 37 | reveal Log(); 38 | } 39 | 40 | lemma {:induction false} LemmaLogSAuto() 41 | ensures forall base: nat, pow: nat 42 | {:trigger Log(base, pow / base)} 43 | | && base > 1 44 | && pow >= base 45 | :: && pow / base >= 0 46 | && Log(base, pow) == 1 + Log(base, pow / base) 47 | { 48 | forall base: nat, pow: nat | && base > 1 && pow >= base 49 | ensures && pow / base >= 0 50 | && Log(base, pow) == 1 + Log(base, pow / base) 51 | { 52 | LemmaLogS(base, pow); 53 | } 54 | } 55 | 56 | lemma {:induction false} LemmaLogIsOrdered(base: nat, pow: nat, pow': nat) 57 | requires base > 1 58 | requires pow <= pow' 59 | ensures Log(base, pow) <= Log(base, pow') 60 | decreases pow 61 | { 62 | reveal Log(); 63 | if pow' < base { 64 | assert Log(base, pow) == 0 == Log(base, pow'); 65 | } else if pow < base { 66 | assert Log(base, pow) == 0; 67 | } else { 68 | LemmaDivPosIsPosAuto(); LemmaDivDecreasesAuto(); LemmaDivIsOrderedAuto(); 69 | LemmaLogIsOrdered(base, pow / base, pow' / base); 70 | } 71 | } 72 | 73 | lemma {:induction false} LemmaLogPow(base: nat, n: nat) 74 | requires base > 1 75 | ensures (LemmaPowPositive(base, n); Log(base, Pow(base, n)) == n) 76 | { 77 | if n == 0 { 78 | reveal Pow(); 79 | reveal Log(); 80 | } else { 81 | LemmaPowPositive(base, n); 82 | calc { 83 | Log(base, Pow(base, n)); 84 | { reveal Pow(); } 85 | Log(base, base * Pow(base, n - 1)); 86 | { LemmaPowPositive(base, n - 1); 87 | LemmaMulIncreases(Pow(base, n - 1), base); 88 | LemmaMulIsCommutative(Pow(base, n - 1), base); 89 | LemmaLogS(base, base * Pow(base, n - 1)); } 90 | 1 + Log(base, (base * Pow(base, n - 1)) / base); 91 | { LemmaDivMultiplesVanish(Pow(base, n - 1), base); } 92 | 1 + Log(base, Pow(base, n - 1)); 93 | { LemmaLogPow(base, n - 1); } 94 | 1 + (n - 1); 95 | } 96 | } 97 | } 98 | 99 | // TODO 100 | // lemma {:induction false} Pow_Log(base: nat, pow: nat) 101 | // requires base > 1 102 | // requires pow > 0 103 | // ensures Pow(base, Log(base, pow)) <= pow < Pow(base, Log(base, pow) + 1) 104 | } 105 | -------------------------------------------------------------------------------- /src/JSON/Utils/Views.Writers.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | include "../../BoundedInts.dfy" 4 | include "../../Wrappers.dfy" 5 | include "Views.dfy" 6 | 7 | module {:options "-functionSyntax:4"} JSON.Utils.Views.Writers { 8 | import opened BoundedInts 9 | import opened Wrappers 10 | 11 | import opened Core 12 | 13 | datatype Chain = 14 | | Empty 15 | | Chain(previous: Chain, v: View) 16 | { 17 | function Length() : nat { 18 | if Empty? then 0 19 | else previous.Length() + v.Length() as int 20 | } 21 | 22 | function Count() : nat { 23 | if Empty? then 0 24 | else previous.Count() + 1 25 | } 26 | 27 | function Bytes() : (bs: bytes) 28 | ensures |bs| == Length() 29 | { 30 | if Empty? then [] 31 | else previous.Bytes() + v.Bytes() 32 | } 33 | 34 | function Append(v': View): (c: Chain) 35 | ensures c.Bytes() == Bytes() + v'.Bytes() 36 | { 37 | if Chain? && Adjacent(v, v') then 38 | Chain(previous, Merge(v, v')) 39 | else 40 | Chain(this, v') 41 | } 42 | 43 | method {:tailrecursion} CopyTo(dest: array, end: uint32) 44 | requires end as int == Length() <= dest.Length 45 | modifies dest 46 | ensures dest[..end] == Bytes() 47 | ensures dest[end..] == old(dest[end..]) 48 | { 49 | if Chain? { 50 | var end := end - v.Length(); 51 | v.CopyTo(dest, end); 52 | previous.CopyTo(dest, end); 53 | } 54 | } 55 | } 56 | 57 | type Writer = w: Writer_ | w.Valid? witness Writer(0, Chain.Empty) 58 | datatype Writer_ = Writer(length: uint32, chain: Chain) 59 | { 60 | static const Empty: Writer := Writer(0, Chain.Empty) 61 | 62 | const Empty? := chain.Empty? 63 | const Unsaturated? := length != UINT32_MAX 64 | 65 | ghost function Length() : nat { chain.Length() } 66 | 67 | ghost const Valid? := 68 | length == // length is a saturating counter 69 | if chain.Length() >= TWO_TO_THE_32 then UINT32_MAX 70 | else chain.Length() as uint32 71 | 72 | function Bytes() : (bs: bytes) 73 | ensures |bs| == Length() 74 | { 75 | chain.Bytes() 76 | } 77 | 78 | static function SaturatedAddU32(a: uint32, b: uint32): uint32 { 79 | if a <= UINT32_MAX - b then a + b 80 | else UINT32_MAX 81 | } 82 | 83 | function {:opaque} Append(v': View): (rw: Writer) 84 | requires Valid? 85 | ensures rw.Unsaturated? <==> v'.Length() < UINT32_MAX - length 86 | ensures rw.Bytes() == Bytes() + v'.Bytes() 87 | { 88 | Writer(SaturatedAddU32(length, v'.Length()), 89 | chain.Append(v')) 90 | } 91 | 92 | function Then(fn: Writer ~> Writer) : Writer 93 | requires Valid? 94 | requires fn.requires(this) 95 | reads fn.reads(this) 96 | { 97 | fn(this) 98 | } 99 | 100 | method {:tailrecursion} CopyTo(dest: array) 101 | requires Valid? 102 | requires Unsaturated? 103 | requires Length() <= dest.Length 104 | modifies dest 105 | ensures dest[..length] == Bytes() 106 | ensures dest[length..] == old(dest[length..]) 107 | { 108 | chain.CopyTo(dest, length); 109 | } 110 | 111 | method ToArray() returns (bs: array) 112 | requires Valid? 113 | requires Unsaturated? 114 | ensures fresh(bs) 115 | ensures bs[..] == Bytes() 116 | { 117 | bs := new byte[length]; 118 | CopyTo(bs); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Relations.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | module {:options "-functionSyntax:4"} Relations { 9 | 10 | ghost predicate Reflexive(R: (T, T) -> bool) { 11 | forall x :: R(x, x) 12 | } 13 | 14 | ghost predicate Irreflexive(R: (T, T) -> bool) { 15 | forall x :: !R(x, x) 16 | } 17 | 18 | ghost predicate AntiSymmetric(R: (T, T) -> bool) { 19 | forall x, y :: R(x, y) && R(y, x) ==> x == y 20 | } 21 | 22 | ghost predicate Symmetric(R: (T, T) -> bool) { 23 | forall x, y :: R(x, y) <==> R(y, x) 24 | } 25 | 26 | ghost predicate Connected(R: (T, T) -> bool) { 27 | forall x, y :: x != y ==> R(x, y) || R(y, x) 28 | } 29 | 30 | ghost predicate StronglyConnected(R: (T, T) -> bool) { 31 | forall x, y :: R(x, y) || R(y, x) 32 | } 33 | 34 | ghost predicate Transitive(R: (T, T) -> bool) { 35 | forall x, y, z :: R(x, y) && R(y, z) ==> R(x, z) 36 | } 37 | 38 | ghost predicate TotalOrdering(R: (T, T) -> bool) { 39 | && Reflexive(R) 40 | && AntiSymmetric(R) 41 | && Transitive(R) 42 | && StronglyConnected(R) 43 | } 44 | 45 | ghost predicate StrictTotalOrdering(R: (T, T) -> bool) { 46 | && Irreflexive(R) 47 | && AntiSymmetric(R) 48 | && Transitive(R) 49 | && Connected(R) 50 | } 51 | 52 | ghost predicate PreOrdering(R: (T, T) -> bool) { 53 | && Reflexive(R) 54 | && Transitive(R) 55 | } 56 | 57 | ghost predicate PartialOrdering(R: (T, T) -> bool) { 58 | && Reflexive(R) 59 | && Transitive(R) 60 | && AntiSymmetric(R) 61 | } 62 | 63 | ghost predicate EquivalenceRelation(R: (T, T) -> bool) { 64 | && Reflexive(R) 65 | && Symmetric(R) 66 | && Transitive(R) 67 | } 68 | 69 | ghost predicate SortedBy(a: seq, lessThan: (T, T) -> bool) { 70 | forall i, j | 0 <= i < j < |a| :: lessThan(a[i], a[j]) 71 | } 72 | 73 | /** An element in an ordered set is called a least element (or a minimum), if it is less than 74 | every other element of the set. */ 75 | ghost predicate IsLeast(R: (T, T) -> bool, min: T, s: set) { 76 | min in s && forall x | x in s :: R(min, x) 77 | } 78 | 79 | /** An element in an ordered set is called a minimal element, if no other element is less than it. */ 80 | ghost predicate IsMinimal(R: (T, T) -> bool, min: T, s: set) { 81 | min in s && forall x | x in s && R(x, min) :: R(min, x) 82 | } 83 | 84 | /** An element in an ordered set is called a greatest element (or a maximum), if it is greater than 85 | every other element of the set. */ 86 | ghost predicate IsGreatest(R: (T, T) -> bool, max: T, s: set) { 87 | max in s && forall x | x in s :: R(x, max) 88 | } 89 | 90 | /** An element in an ordered set is called a maximal element, if no other element is greater than it. */ 91 | ghost predicate IsMaximal(R: (T, T) -> bool, max: T, s: set) { 92 | max in s && forall x | x in s && R(max, x) :: R(x, max) 93 | } 94 | 95 | lemma LemmaNewFirstElementStillSortedBy(x: T, s: seq, lessThan: (T, T) -> bool) 96 | requires SortedBy(s, lessThan) 97 | requires |s| == 0 || lessThan(x, s[0]) 98 | requires TotalOrdering(lessThan) 99 | ensures SortedBy([x] + s, lessThan) 100 | {} 101 | } 102 | -------------------------------------------------------------------------------- /src/dafny/DafnyCore.md: -------------------------------------------------------------------------------- 1 | 2 | # Guide to the Dafny Core Library 3 | 4 | The Dafny programming language and tool set includes 5 | libraries that provide functionality in various domains. This document describes 6 | the _Dafny Core Library_. Dafny programs are composed of modules; to ensure 7 | verification soundness, Dafny imposes a strict non-circular dependency relationship 8 | among modules. This Core library contains functionality that may be needed 9 | by other libraries, but it itself is standalone. 10 | 11 | Our goal is that, eventually, all the supported, delivered libraries will 12 | - be stable --- a minimum of backwards incompatible changes 13 | - be verified --- proved to meet specifications and tested with expected use cases, 14 | with each release of the library and of the Dafny toolset 15 | - be efficient --- efficient in both proofs and executables that use the libraries 16 | - be compilable for every supported target language by default, with exceptions being documented and for exceptional reasons 17 | 18 | The Dafny code for the library is written to be understandable with a minimum of inline documentation. 19 | It is open source and available [on github](https://github.com/dafny-lang/libraries). 20 | This document is meant to give an overall introduction. Also helpful are the annotated examples 21 | in the github repository [here](https://github.com/dafny-lang/libraries/tree/master/examples). 22 | 23 | ## Content of the Core Library 24 | 25 | The sections below describe how to use each of the principal modules of the library: 26 | - [Dafny.Wrappers](Wrappers) -- simple datatypes to support common patterns, such as optional values or the result of operations that can fail 27 | - [Dafny.BoundedInts](BoundedInts) -- definitions of types and constants for bounded integer arithmetic 28 | - [Dafny.FileIO](FileIO/FileIO) -- basic input and output operations 29 | - [Dafny.Math](Math) --- basic mathematical operations and lemmas 30 | - [Dafny.Unicode](Unicode/Unicode) --- tools to manipulate unicode characters and strings 31 | - [Dafny.Collections](Collections/Collections) -- functions and lemmas about sequences, sets and maps 32 | - [Dafny.NonlinearArith](NonlinearArithmetic/NonlinearArithmetic) -- lemmas and functions for non-linear arithmetic 33 | - [Dafny.Relations](Relations) -- predicates and lemmas about properties of functions 34 | - [Dafny.BinaryOperations](BinaryOperations) -- properties of binary operations 35 | 36 | 37 | ## How to use the library 38 | 39 | One uses the library on the command-line by including a path to the library as an argument to the `--library` option. 40 | 41 | At present, you must have a copy of the library in your file system to use the library. Either 42 | - obtain a release zip file from [the library github release page](https://github.com/dafny-lang/libraries/releases) and unzip it in a folder of your choice, or 43 | - clone the library repository (`git clone https://github.com/dafny-lang/libraries.git`) to a location of your choice 44 | 45 | In either case, the relevant directory to point the `--library` option to is named `dafny`, either at the top of the zip file or at `libraries/src/dafny` in the clone, or to the `DafnyCore.dfy` file in that directory. 46 | 47 | Some functionality is implemented using native target language capabilities (e.g. FileIO). In these cases, a compiled program must also include the relevant target 48 | language files that are included in this Dafny Core distribution. See the FileIO examples and documentation 49 | for an instance of this requirement. 50 | 51 | The procedure for using library code directly with VSCode is not yet settled. For now make a copy of the library in your system and 'include' the `DafnyCore.dfy` file. 52 | 53 | -------------------------------------------------------------------------------- /src/JSON/ConcreteSyntax.Spec.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | include "Grammar.dfy" 4 | 5 | module {:options "-functionSyntax:4"} JSON.ConcreteSyntax.Spec { 6 | import opened BoundedInts 7 | 8 | import Vs = Utils.Views.Core 9 | import opened Grammar 10 | 11 | function View(v: Vs.View) : bytes { 12 | v.Bytes() 13 | } 14 | 15 | function Structural(self: Structural, fT: T -> bytes): bytes { 16 | View(self.before) + fT(self.t) + View(self.after) 17 | } 18 | 19 | function StructuralView(self: Structural): bytes { 20 | Structural(self, View) 21 | } 22 | 23 | function Maybe(self: Maybe, fT: T -> bytes): (bs: bytes) 24 | ensures self.Empty? ==> bs == [] 25 | ensures self.NonEmpty? ==> bs == fT(self.t) 26 | { 27 | if self.Empty? then [] else fT(self.t) 28 | } 29 | 30 | function ConcatBytes(ts: seq, fT: T --> bytes) : bytes 31 | requires forall d | d in ts :: fT.requires(d) 32 | { 33 | if |ts| == 0 then [] 34 | else fT(ts[0]) + ConcatBytes(ts[1..], fT) 35 | } 36 | 37 | function Bracketed(self: Bracketed, fDatum: Suffixed --> bytes): bytes 38 | requires forall d | d < self :: fDatum.requires(d) 39 | { 40 | StructuralView(self.l) + 41 | ConcatBytes(self.data, fDatum) + 42 | StructuralView(self.r) 43 | } 44 | 45 | function KeyValue(self: jKeyValue): bytes { 46 | String(self.k) + StructuralView(self.colon) + Value(self.v) 47 | } 48 | 49 | function Frac(self: jfrac): bytes { 50 | View(self.period) + View(self.num) 51 | } 52 | 53 | function Exp(self: jexp): bytes { 54 | View(self.e) + View(self.sign) + View(self.num) 55 | } 56 | 57 | function Number(self: jnumber): bytes { 58 | View(self.minus) + View(self.num) + Maybe(self.frac, Frac) + Maybe(self.exp, Exp) 59 | } 60 | 61 | function String(self: jstring): bytes { 62 | View(self.lq) + View(self.contents) + View(self.rq) 63 | } 64 | 65 | function CommaSuffix(c: Maybe>): bytes { 66 | // BUG(https://github.com/dafny-lang/dafny/issues/2179) 67 | Maybe>(c, StructuralView) 68 | } 69 | 70 | function Member(self: jmember) : bytes { 71 | KeyValue(self.t) + CommaSuffix(self.suffix) 72 | } 73 | 74 | function Item(self: jitem) : bytes { 75 | Value(self.t) + CommaSuffix(self.suffix) 76 | } 77 | 78 | function Object(obj: jobject) : bytes { 79 | Bracketed(obj, (d: jmember) requires d < obj => Member(d)) 80 | } 81 | 82 | function Array(arr: jarray) : bytes { 83 | Bracketed(arr, (d: jitem) requires d < arr => Item(d)) 84 | } 85 | 86 | function Value(self: Value) : bytes { 87 | match self { 88 | case Null(n) => View(n) 89 | case Bool(b) => View(b) 90 | case String(str) => String(str) 91 | case Number(num) => Number(num) 92 | case Object(obj) => Object(obj) 93 | case Array(arr) => Array(arr) 94 | } 95 | } 96 | 97 | lemma UnfoldValueNumber(v: Value) 98 | requires v.Number? 99 | ensures Value(v) == Number(v.num) 100 | { 101 | assert Value(v) == match v { case Number(num) => Number(num) case _ => []}; 102 | } 103 | 104 | lemma UnfoldValueObject(v: Value) 105 | requires v.Object? 106 | ensures Value(v) == Object(v.obj) 107 | { 108 | assert Value(v) == match v { case Object(obj) => Object(obj) case _ => []}; 109 | } 110 | 111 | lemma UnfoldValueArray(v: Value) 112 | requires v.Array? 113 | ensures Value(v) == Array(v.arr) 114 | { 115 | assert Value(v) == match v { case Array(arr) => Array(arr) case _ => []}; 116 | } 117 | 118 | function JSON(js: JSON) : bytes { 119 | Structural(js, Value) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /examples/FileIO/WriteBytesToFile.dfy: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright by the contributors to the Dafny Project 3 | * SPDX-License-Identifier: MIT 4 | *******************************************************************************/ 5 | 6 | // RUN: %verify "%s" 7 | 8 | // RUN: %run --no-verify --unicode-char:false --target:cs "%s" --input "%S/../../src/FileIO/FileIO.cs" -- "%t_cs" "System.ArgumentException:" 9 | // RUN: %run --no-verify --unicode-char:false --target:java "%s" --input "%S/../../src/FileIO/FileIO.java" -- "%t_java" "java.nio.file.FileSystemException:" 10 | // RUN: %run --no-verify --unicode-char:false --target:js "%s" --input "%S/../../src/FileIO/FileIO.js" -- "%t_js" "Error: ENOENT" 11 | 12 | //// Check that written files match expectations 13 | // RUN: %diff "%S/data.txt" "%t_cs/output_plain" 14 | // RUN: %diff "%S/data.txt" "%t_cs/foo/bar/output_nested" 15 | // RUN: %diff "%S/data.txt" "%t_cs/foo/output_up" 16 | // RUN: %diff "%S/data.txt" "%t_java/output_plain" 17 | // RUN: %diff "%S/data.txt" "%t_java/foo/bar/output_nested" 18 | // RUN: %diff "%S/data.txt" "%t_java/foo/output_up" 19 | // RUN: %diff "%S/data.txt" "%t_js/output_plain" 20 | // RUN: %diff "%S/data.txt" "%t_js/foo/bar/output_nested" 21 | // RUN: %diff "%S/data.txt" "%t_js/foo/output_up" 22 | 23 | include "../../src/dafny/FileIO/FileIO.dfy" 24 | 25 | module WriteBytesToFile { 26 | import Dafny.FileIO 27 | 28 | method Main(args: seq) { 29 | expect |args| > 0; 30 | expect |args| == 3, "usage: " + args[0] + " OUTPUT_DIR EXPECTED_ERROR_PREFIX"; 31 | var outputDir := args[1]; 32 | var expectedErrorPrefix := args[2]; 33 | 34 | // Happy paths: write files to the output dir. (The %diff LIT commands check that we wrote the correct content.) 35 | { 36 | // Ideally we would define `str` as a constant and compute `bytes` automatically. 37 | // To do so, we would need to convert each `char` in `str` to a `bv8` value, by using `as bv8`. 38 | // But that triggers a bug in the Java compiler: . 39 | // So for now we settle for manually writing out the desired `bytes`, 40 | // and statically verifying that these values match the characters of `str`. 41 | ghost var str := "Hello world\nGoodbye\n"; 42 | var bytes: seq := [ 43 | // "Hello world\n" 44 | 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0a, 45 | // "Goodbye\n" 46 | 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 0x65, 0x0a 47 | ]; 48 | assert forall i | 0 <= i < |bytes| :: bytes[i] as int == str[i] as int; 49 | 50 | // Write directly into the output directory 51 | { 52 | var res := FileIO.WriteBytesToFile(outputDir + "/output_plain", bytes); 53 | expect res.Success?, "unexpected failure writing to output_plain: " + res.error; 54 | } 55 | // Ensure that nonexistent parent directories are created as necessary 56 | { 57 | var res := FileIO.WriteBytesToFile(outputDir + "/foo/bar/output_nested", bytes); 58 | expect res.Success?, "unexpected failure writing to output_nested: " + res.error; 59 | } 60 | // Ensure that file paths with .. are handled correctly 61 | { 62 | var res := FileIO.WriteBytesToFile(outputDir + "/foo/bar/../output_up", bytes); 63 | expect res.Success?, "unexpected failure writing to output_up: " + res.error; 64 | } 65 | } 66 | 67 | // Failure path: attempting to write to a blank file path should never work. 68 | { 69 | var res := FileIO.WriteBytesToFile("", []); 70 | expect res.Failure?, "unexpected success"; 71 | expect expectedErrorPrefix <= res.error, "unexpected error message: " + res.error; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/FileIO/FileIO.dfy: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright by the contributors to the Dafny Project 3 | * SPDX-License-Identifier: MIT 4 | *******************************************************************************/ 5 | 6 | // RUN: %verify "%s" 7 | 8 | include "../Wrappers.dfy" 9 | include "../BoundedInts.dfy" 10 | 11 | /** 12 | * This module provides basic file I/O operations: reading and writing bytes from/to a file. 13 | * The provided API is intentionally limited in scope and will be expanded later. 14 | * 15 | * Where the API accepts file paths as strings, there are some limitations. 16 | * File paths containing only ASCII characters work identically across languages and platforms; 17 | * non-ASCII Unicode codepoints may cause different language- or platform-specific behavior. 18 | * 19 | * File path symbols including . and .. are allowed. 20 | */ 21 | module {:options "-functionSyntax:4"} FileIO { 22 | import opened Wrappers 23 | import opened BoundedInts 24 | 25 | export provides AppendBytesToFile, ReadBytesFromFile, WriteBytesToFile, Wrappers, BoundedInts 26 | 27 | /* 28 | * Public API 29 | */ 30 | 31 | /** 32 | * Attempts to read all bytes from the file at the given file path. 33 | * If an error occurs, a `Result.Failure` value is returned containing an implementation-specific 34 | * error message (which may also contain a stack trace). 35 | * 36 | * NOTE: See the module description for limitations on the path argument. 37 | */ 38 | method ReadBytesFromFile(path: string) returns (res: Result, string>) { 39 | var isError, bytesRead, errorMsg := INTERNAL_ReadBytesFromFile(path); 40 | return if isError then Failure(errorMsg) else Success(bytesRead); 41 | } 42 | 43 | /** 44 | * Attempts to write the given bytes to the file at the given file path, 45 | * creating nonexistent parent directories as necessary. 46 | * If an error occurs, a `Result.Failure` value is returned containing an implementation-specific 47 | * error message (which may also contain a stack trace). 48 | * 49 | * NOTE: See the module description for limitations on the path argument. 50 | */ 51 | method WriteBytesToFile(path: string, bytes: seq) returns (res: Result<(), string>) 52 | { 53 | var isError, errorMsg := INTERNAL_WriteBytesToFile(path, bytes); 54 | return if isError then Failure(errorMsg) else Success(()); 55 | } 56 | 57 | /** 58 | * Attempts to append the given bytes to the file at the given file path, 59 | * creating nonexistent parent directories as necessary. 60 | * If an error occurs, a `Result.Failure` value is returned containing an implementation-specific 61 | * error message (which may also contain a stack trace). 62 | * 63 | * NOTE: See the module description for limitations on the path argument. 64 | */ 65 | method AppendBytesToFile(path: string, bytes: seq) returns (res: Result<(), string>) 66 | { 67 | var isError, errorMsg := INTERNAL_AppendBytesToFile(path, bytes); 68 | return if isError then Failure(errorMsg) else Success(()); 69 | } 70 | 71 | /* 72 | * Private API - these are intentionally not exported from the module and should not be used elsewhere 73 | */ 74 | 75 | method 76 | {:extern "DafnyLibraries.FileIO", "INTERNAL_ReadBytesFromFile"} 77 | INTERNAL_ReadBytesFromFile(path: string) 78 | returns (isError: bool, bytesRead: seq, errorMsg: string) 79 | 80 | method 81 | {:extern "DafnyLibraries.FileIO", "INTERNAL_WriteBytesToFile"} 82 | INTERNAL_WriteBytesToFile(path: string, bytes: seq) 83 | returns (isError: bool, errorMsg: string) 84 | 85 | method 86 | {:extern "DafnyLibraries.FileIO", "INTERNAL_AppendBytesToFile"} 87 | INTERNAL_AppendBytesToFile(path: string, bytes: seq) 88 | returns (isError: bool, errorMsg: string) 89 | } 90 | -------------------------------------------------------------------------------- /src/JSON/Utils/Views.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | include "../../BoundedInts.dfy" 4 | 5 | module {:options "-functionSyntax:4"} JSON.Utils.Views.Core { 6 | import opened BoundedInts 7 | 8 | type View = v: View_ | v.Valid? witness View([], 0, 0) 9 | datatype View_ = View(s: bytes, beg: uint32, end: uint32) { 10 | ghost const Valid?: bool := 11 | 0 <= beg as int <= end as int <= |s| < TWO_TO_THE_32 12 | 13 | static const Empty: View := 14 | View([], 0, 0) 15 | 16 | const Empty? := 17 | beg == end 18 | 19 | function Length(): uint32 requires Valid? { 20 | end - beg 21 | } 22 | 23 | function Bytes(): bytes requires Valid? { 24 | s[beg..end] 25 | } 26 | 27 | static function OfBytes(bs: bytes) : (v: View) 28 | requires |bs| < TWO_TO_THE_32 29 | ensures v.Bytes() == bs 30 | { 31 | View(bs, 0 as uint32, |bs| as uint32) 32 | } 33 | 34 | static function OfString(s: string) : bytes 35 | requires forall c: char | c in s :: c as int < 256 36 | { 37 | seq(|s|, i requires 0 <= i < |s| => 38 | assert s[i] in s; s[i] as byte) 39 | } 40 | 41 | ghost predicate SliceOf?(v': View) { 42 | v'.s == s && v'.beg <= beg && end <= v'.end 43 | } 44 | 45 | ghost predicate StrictPrefixOf?(v': View) { 46 | v'.s == s && v'.beg == beg && end < v'.end 47 | } 48 | 49 | ghost predicate StrictSuffixOf?(v': View) { 50 | v'.s == s && v'.beg < beg && end == v'.end 51 | } 52 | 53 | predicate Byte?(c: byte) 54 | requires Valid? 55 | { 56 | Bytes() == [c] 57 | } by method { 58 | return Length() == 1 && At(0) == c; 59 | } 60 | 61 | predicate Char?(c: char) 62 | requires Valid? 63 | requires c as int < 256 64 | { 65 | Byte?(c as byte) 66 | } 67 | 68 | ghost predicate ValidIndex?(idx: uint32) { 69 | beg as int + idx as int < end as int 70 | } 71 | 72 | function At(idx: uint32) : byte 73 | requires Valid? 74 | requires ValidIndex?(idx) 75 | { 76 | s[beg + idx] 77 | } 78 | 79 | function Peek(): (r: opt_byte) 80 | requires Valid? 81 | ensures r < 0 <==> Empty? 82 | { 83 | if Empty? then -1 84 | else At(0) as opt_byte 85 | } 86 | 87 | method CopyTo(dest: array, start: uint32 := 0) 88 | requires Valid? 89 | requires start as int + Length() as int <= dest.Length 90 | requires start as int + Length() as int < TWO_TO_THE_32 91 | modifies dest 92 | ensures dest[start..start + Length()] == Bytes() 93 | ensures dest[start + Length()..] == old(dest[start + Length()..]) 94 | { 95 | for idx := 0 to Length() 96 | invariant dest[start..start + idx] == Bytes()[..idx] 97 | invariant dest[start + Length()..] == old(dest[start + Length()..]) 98 | { 99 | dest[start + idx] := s[beg + idx]; 100 | } 101 | } 102 | } 103 | 104 | predicate Adjacent(lv: View, rv: View) { 105 | // Compare endpoints first to short-circuit the potentially-costly string 106 | // comparison 107 | && lv.end == rv.beg 108 | // We would prefer to use reference equality here, but doing so in a sound 109 | // way is tricky (see chapter 9 of ‘Verasco: a Formally Verified C Static 110 | // Analyzer’ by Jacques-Henri Jourdan for details). The runtime optimizes 111 | // the common case of physical equality and otherwise performs a length 112 | // check, so the worst case (checking for adjacency in two slices that have 113 | // equal but not physically-equal contents) is hopefully not too common. 114 | && lv.s == rv.s 115 | } 116 | 117 | function Merge(lv: View, rv: View) : (v: View) 118 | requires Adjacent(lv, rv) 119 | ensures v.Bytes() == lv.Bytes() + rv.Bytes() 120 | { 121 | lv.(end := rv.end) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Scripts/pydiff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Simple driver around python's difflib that implements a basic ``diff`` like 4 | tool. This exists to provide a simple ``diff`` like tool that will run on 5 | Windows where only the ``fc`` tool is available. 6 | """ 7 | import argparse 8 | import difflib 9 | import os 10 | import sys 11 | 12 | def main(args): 13 | parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, 14 | description=__doc__ 15 | ) 16 | # Open in binary mode so python doesn't try and do 17 | # universal line endings for us. 18 | parser.add_argument('from-file', 19 | type=argparse.FileType('rb'), 20 | ) 21 | parser.add_argument('to-file', 22 | type=argparse.FileType('rb'), 23 | ) 24 | parser.add_argument('-U','--unified=', 25 | type=int, 26 | default=3, 27 | help='Number of context lines to show. ' 28 | 'Default %(default)s' 29 | ) 30 | parser.add_argument('--strip-trailing-cr', 31 | action='store_true', 32 | help='strip trailing carriage return when comparing' 33 | ) 34 | parser.add_argument('--ignore-all-space','-w', 35 | action='store_true', 36 | help='Ignore all whitespace characters when comparing' 37 | ) 38 | 39 | parsedArgs = parser.parse_args(args) 40 | fromFile, fromFileName = preProcess(getattr(parsedArgs,'from-file'), 41 | parsedArgs.strip_trailing_cr, 42 | parsedArgs.ignore_all_space 43 | ) 44 | toFile, toFileName = preProcess(getattr(parsedArgs,'to-file'), 45 | parsedArgs.strip_trailing_cr, 46 | parsedArgs.ignore_all_space 47 | ) 48 | result = difflib.unified_diff(fromFile, 49 | toFile, 50 | fromFileName, 51 | toFileName, 52 | n=getattr(parsedArgs,'unified='), 53 | ) 54 | # Force lazy computation to happen now 55 | result = list(result) 56 | 57 | if len(result) == 0: 58 | # Files are identical 59 | return 0 60 | else: 61 | for l in result: 62 | sys.stdout.write(l) 63 | return 1 64 | 65 | def preProcess(openFile, stripTrailingCR=False, ignoreAllSpace=False): 66 | """ 67 | Helper function to read lines in a file and do any necessary 68 | pre-processing 69 | """ 70 | original = openFile.readlines() 71 | 72 | # Translation table deletes white space characters Note we don't remove 73 | # newline characters because this will create a mess when outputting the 74 | # diff. Is this the right behaviour? 75 | deleteChars=' \t' 76 | if sys.version_info.major >= 3: 77 | translationTable = str.maketrans('','', deleteChars) 78 | 79 | copy = [ ] 80 | for line in original: 81 | newLine = str(line.decode()) 82 | 83 | if stripTrailingCR: 84 | if newLine[-2:] == '\r\n' or newLine[-2:] == '\n\r': 85 | newLine = newLine[:-2] + '\n' 86 | 87 | if ignoreAllSpace: 88 | newLine = newLine.translate(translationTable) 89 | 90 | copy.append(newLine) 91 | 92 | return (copy, openFile.name) 93 | 94 | def getFileName(openFile): 95 | return openFile.name 96 | 97 | if __name__ == '__main__': 98 | sys.exit(main(sys.argv[1:])) 99 | 100 | -------------------------------------------------------------------------------- /src/dafny/FileIO/FileIO.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright by the contributors to the Dafny Project 3 | * SPDX-License-Identifier: MIT 4 | *******************************************************************************/ 5 | 6 | namespace DafnyLibraries { 7 | using System; 8 | using System.IO; 9 | 10 | using Dafny; 11 | 12 | public class FileIO { 13 | /// 14 | /// Attempts to read all bytes from the file at the given path, and outputs the following values: 15 | /// 16 | /// 17 | /// isError 18 | /// 19 | /// true iff an exception was thrown during path string conversion or when reading the file 20 | /// 21 | /// 22 | /// 23 | /// bytesRead 24 | /// 25 | /// the sequence of bytes read from the file, or an empty sequence if isError is true 26 | /// 27 | /// 28 | /// 29 | /// errorMsg 30 | /// 31 | /// the error message of the thrown exception if isError is true, or an empty sequence otherwise 32 | /// 33 | /// 34 | /// 35 | /// 36 | /// We output these values individually because Result is not defined in the runtime but instead in library code. 37 | /// It is the responsibility of library code to construct an equivalent Result value. 38 | /// 39 | public static void INTERNAL_ReadBytesFromFile(ISequence path, out bool isError, out ISequence bytesRead, 40 | out ISequence errorMsg) { 41 | isError = true; 42 | bytesRead = Sequence.Empty; 43 | errorMsg = Sequence.Empty; 44 | try { 45 | bytesRead = Helpers.SeqFromArray(File.ReadAllBytes(path?.ToString())); 46 | isError = false; 47 | } catch (Exception e) { 48 | errorMsg = Helpers.SeqFromArray(e.ToString().ToCharArray()); 49 | } 50 | } 51 | 52 | /// 53 | /// Attempts to write all given bytes to the file at the given path, creating nonexistent parent directories as necessary, 54 | /// and outputs the following values: 55 | /// 56 | /// 57 | /// isError 58 | /// 59 | /// true iff an exception was thrown during path string conversion or when writing to the file 60 | /// 61 | /// 62 | /// 63 | /// errorMsg 64 | /// 65 | /// the error message of the thrown exception if isError is true, or an empty sequence otherwise 66 | /// 67 | /// 68 | /// 69 | /// 70 | /// We output these values individually because Result is not defined in the runtime but instead in library code. 71 | /// It is the responsibility of library code to construct an equivalent Result value. 72 | /// 73 | public static void INTERNAL_WriteBytesToFile(ISequence path, ISequence bytes, out bool isError, out ISequence errorMsg) { 74 | isError = true; 75 | errorMsg = Sequence.Empty; 76 | try { 77 | string pathStr = path?.ToString(); 78 | CreateParentDirs(pathStr); 79 | File.WriteAllBytes(pathStr, bytes.CloneAsArray()); 80 | isError = false; 81 | } catch (Exception e) { 82 | errorMsg = Helpers.SeqFromArray(e.ToString().ToCharArray()); 83 | } 84 | } 85 | 86 | /// 87 | /// Creates the nonexistent parent directory(-ies) of the given path. 88 | /// 89 | private static void CreateParentDirs(string path) { 90 | string parentDir = Path.GetDirectoryName(Path.GetFullPath(path)); 91 | Directory.CreateDirectory(parentDir); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/dafny/NonlinearArithmetic/Power2.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify --disable-nonlinear-arithmetic "%s" 2 | 3 | /******************************************************************************* 4 | * Original: Copyright (c) Microsoft Corporation 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 8 | * SPDX-License-Identifier: MIT 9 | *******************************************************************************/ 10 | 11 | /* Every lemma comes in 2 forms: 'LemmaProperty' and 'LemmaPropertyAuto'. The 12 | former takes arguments and may be more stable and less reliant on Z3 13 | heuristics. The latter includes automation and its use requires less effort */ 14 | 15 | include "Internals/GeneralInternals.dfy" 16 | include "Internals/MulInternals.dfy" 17 | include "Power.dfy" 18 | 19 | module {:options "-functionSyntax:4"} Dafny.Power2 { 20 | import opened GeneralInternals 21 | import opened MulInternals 22 | import opened Power 23 | 24 | function {:opaque} Pow2(e: nat): nat 25 | ensures Pow2(e) > 0 26 | { 27 | reveal Pow(); 28 | LemmaPowPositive(2, e); 29 | Pow(2, e) 30 | } 31 | 32 | /* Pow2() is equivalent to Pow() with base 2. */ 33 | lemma LemmaPow2(e: nat) 34 | ensures Pow2(e) == Pow(2, e) 35 | { 36 | reveal Pow(); 37 | reveal Pow2(); 38 | 39 | if e != 0 { 40 | LemmaPow2(e - 1); 41 | } 42 | } 43 | 44 | lemma LemmaPow2Auto() 45 | ensures forall e: nat {:trigger Pow2(e)} :: Pow2(e) == Pow(2, e) 46 | { 47 | reveal Pow(); 48 | reveal Pow2(); 49 | 50 | forall e: nat {:trigger Pow2(e)} 51 | ensures Pow2(e) == Pow(2, e) 52 | { 53 | LemmaPow2(e); 54 | } 55 | } 56 | 57 | /* (2^e - 1) / 2 = 2^(e - 1) - 1 */ 58 | // keep 59 | lemma LemmaPow2MaskDiv2(e: nat) 60 | requires 0 < e 61 | ensures (Pow2(e) - 1) / 2 == Pow2(e - 1) - 1 62 | { 63 | LemmaPow2Auto(); 64 | LemmaPowAuto(); 65 | var f := e => 0 < e ==> (Pow2(e) - 1) / 2 == Pow2(e - 1) - 1; 66 | assert forall i {:trigger IsLe(0, i)} :: IsLe(0, i) && f(i) ==> f(i + 1); 67 | assert forall i {:trigger IsLe(i, 0)} :: IsLe(i, 0) && f(i) ==> f(i - 1); 68 | LemmaMulInductionAuto(e, f); 69 | } 70 | 71 | lemma LemmaPow2MaskDiv2Auto() 72 | ensures forall e: nat {:trigger Pow2(e)} :: 73 | 0 < e ==> (Pow2(e) - 1) / 2 == Pow2(e - 1) - 1 74 | { 75 | reveal Pow2(); 76 | forall e: nat {:trigger Pow2(e)} | 0 < e 77 | ensures (Pow2(e) - 1) / 2 == Pow2(e - 1) - 1 78 | { 79 | LemmaPow2MaskDiv2(e); 80 | } 81 | } 82 | 83 | lemma Lemma2To64() 84 | ensures Pow2(0) == 0x1 85 | ensures Pow2(1) == 0x2 86 | ensures Pow2(2) == 0x4 87 | ensures Pow2(3) == 0x8 88 | ensures Pow2(4) == 0x10 89 | ensures Pow2(5) == 0x20 90 | ensures Pow2(6) == 0x40 91 | ensures Pow2(7) == 0x80 92 | ensures Pow2(8) == 0x100 93 | ensures Pow2(9) == 0x200 94 | ensures Pow2(10) == 0x400 95 | ensures Pow2(11) == 0x800 96 | ensures Pow2(12) == 0x1000 97 | ensures Pow2(13) == 0x2000 98 | ensures Pow2(14) == 0x4000 99 | ensures Pow2(15) == 0x8000 100 | ensures Pow2(16) == 0x10000 101 | ensures Pow2(17) == 0x20000 102 | ensures Pow2(18) == 0x40000 103 | ensures Pow2(19) == 0x80000 104 | ensures Pow2(20) == 0x100000 105 | ensures Pow2(21) == 0x200000 106 | ensures Pow2(22) == 0x400000 107 | ensures Pow2(23) == 0x800000 108 | ensures Pow2(24) == 0x1000000 109 | ensures Pow2(25) == 0x2000000 110 | ensures Pow2(26) == 0x4000000 111 | ensures Pow2(27) == 0x8000000 112 | ensures Pow2(28) == 0x10000000 113 | ensures Pow2(29) == 0x20000000 114 | ensures Pow2(30) == 0x40000000 115 | ensures Pow2(31) == 0x80000000 116 | ensures Pow2(32) == 0x100000000 117 | ensures Pow2(64) == 0x10000000000000000 118 | { 119 | reveal Pow(); 120 | reveal Pow2(); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/NonlinearArithmetic/Power2.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify --disable-nonlinear-arithmetic "%s" 2 | 3 | /******************************************************************************* 4 | * Original: Copyright (c) Microsoft Corporation 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 8 | * SPDX-License-Identifier: MIT 9 | *******************************************************************************/ 10 | 11 | /* Every lemma comes in 2 forms: 'LemmaProperty' and 'LemmaPropertyAuto'. The 12 | former takes arguments and may be more stable and less reliant on Z3 13 | heuristics. The latter includes automation and its use requires less effort */ 14 | 15 | include "Internals/GeneralInternals.dfy" 16 | include "Internals/MulInternals.dfy" 17 | include "Power.dfy" 18 | 19 | module {:options "-functionSyntax:4"} Power2 { 20 | import opened GeneralInternals 21 | import opened MulInternals 22 | import opened Power 23 | 24 | function {:opaque} Pow2(e: nat): nat 25 | ensures Pow2(e) > 0 26 | { 27 | reveal Pow(); 28 | LemmaPowPositive(2, e); 29 | Pow(2, e) 30 | } 31 | 32 | /* Pow2() is equivalent to Pow() with base 2. */ 33 | lemma LemmaPow2(e: nat) 34 | ensures Pow2(e) == Pow(2, e) 35 | { 36 | reveal Pow(); 37 | reveal Pow2(); 38 | 39 | if e != 0 { 40 | LemmaPow2(e - 1); 41 | } 42 | } 43 | 44 | lemma LemmaPow2Auto() 45 | ensures forall e: nat {:trigger Pow2(e)} :: Pow2(e) == Pow(2, e) 46 | { 47 | reveal Pow(); 48 | reveal Pow2(); 49 | 50 | forall e: nat {:trigger Pow2(e)} 51 | ensures Pow2(e) == Pow(2, e) 52 | { 53 | LemmaPow2(e); 54 | } 55 | } 56 | 57 | /* (2^e - 1) / 2 = 2^(e - 1) - 1 */ 58 | // keep 59 | lemma LemmaPow2MaskDiv2(e: nat) 60 | requires 0 < e 61 | ensures (Pow2(e) - 1) / 2 == Pow2(e - 1) - 1 62 | { 63 | LemmaPow2Auto(); 64 | LemmaPowAuto(); 65 | var f := e => 0 < e ==> (Pow2(e) - 1) / 2 == Pow2(e - 1) - 1; 66 | assert forall i {:trigger IsLe(0, i)} :: IsLe(0, i) && f(i) ==> f(i + 1); 67 | assert forall i {:trigger IsLe(i, 0)} :: IsLe(i, 0) && f(i) ==> f(i - 1); 68 | LemmaMulInductionAuto(e, f); 69 | } 70 | 71 | lemma LemmaPow2MaskDiv2Auto() 72 | ensures forall e: nat {:trigger Pow2(e)} :: 0 < e ==> 73 | (Pow2(e) - 1) / 2 == Pow2(e - 1) - 1 74 | { 75 | reveal Pow2(); 76 | forall e: nat {:trigger Pow2(e)} | 0 < e 77 | ensures (Pow2(e) - 1) / 2 == Pow2(e - 1) - 1 78 | { 79 | LemmaPow2MaskDiv2(e); 80 | } 81 | } 82 | 83 | lemma Lemma2To64() 84 | ensures Pow2(0) == 0x1 85 | ensures Pow2(1) == 0x2 86 | ensures Pow2(2) == 0x4 87 | ensures Pow2(3) == 0x8 88 | ensures Pow2(4) == 0x10 89 | ensures Pow2(5) == 0x20 90 | ensures Pow2(6) == 0x40 91 | ensures Pow2(7) == 0x80 92 | ensures Pow2(8) == 0x100 93 | ensures Pow2(9) == 0x200 94 | ensures Pow2(10) == 0x400 95 | ensures Pow2(11) == 0x800 96 | ensures Pow2(12) == 0x1000 97 | ensures Pow2(13) == 0x2000 98 | ensures Pow2(14) == 0x4000 99 | ensures Pow2(15) == 0x8000 100 | ensures Pow2(16) == 0x10000 101 | ensures Pow2(17) == 0x20000 102 | ensures Pow2(18) == 0x40000 103 | ensures Pow2(19) == 0x80000 104 | ensures Pow2(20) == 0x100000 105 | ensures Pow2(21) == 0x200000 106 | ensures Pow2(22) == 0x400000 107 | ensures Pow2(23) == 0x800000 108 | ensures Pow2(24) == 0x1000000 109 | ensures Pow2(25) == 0x2000000 110 | ensures Pow2(26) == 0x4000000 111 | ensures Pow2(27) == 0x8000000 112 | ensures Pow2(28) == 0x10000000 113 | ensures Pow2(29) == 0x20000000 114 | ensures Pow2(30) == 0x40000000 115 | ensures Pow2(31) == 0x80000000 116 | ensures Pow2(32) == 0x100000000 117 | ensures Pow2(64) == 0x10000000000000000 118 | { 119 | reveal Pow2(); 120 | reveal Pow(); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/dafny/Relations.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | /** Various properties of mathematical Relations (see also BinaryOperations) */ 9 | module {:options "-functionSyntax:4"} Dafny.Relations { 10 | 11 | ghost predicate Injective(f: X-->Y) 12 | reads f.reads 13 | requires forall x :: f.requires(x) 14 | { 15 | forall x1, x2 :: f(x1) == f(x2) ==> x1 == x2 16 | } 17 | 18 | ghost predicate Commutative(f: (T,T)->U) 19 | reads f.reads 20 | requires forall x,y :: f.requires(x,y) && f.requires(y,x) 21 | { 22 | forall x,y :: f(x,y) == f(y,x) 23 | } 24 | 25 | ghost predicate Associative(f: (T,T)->T) 26 | reads f.reads 27 | requires forall x, y, z :: f.requires(x,y) && f.requires(y,z) && f.requires(x,z) 28 | { 29 | forall x, y, z: T :: f(x,f(y,z)) == f(f(x,y),z) 30 | } 31 | 32 | ghost predicate Reflexive(R: (T, T) -> bool) { 33 | forall x :: R(x, x) 34 | } 35 | 36 | ghost predicate Irreflexive(R: (T, T) -> bool) { 37 | forall x :: !R(x, x) 38 | } 39 | 40 | ghost predicate AntiSymmetric(R: (T, T) -> bool) { 41 | forall x, y :: R(x, y) && R(y, x) ==> x == y 42 | } 43 | 44 | ghost predicate Asymmetric(R: (T, T) -> bool) { 45 | forall x, y :: R(x, y) ==> !R(y, x) 46 | } 47 | 48 | ghost predicate Symmetric(R: (T, T) -> bool) { 49 | forall x, y :: R(x, y) <==> R(y, x) 50 | } 51 | 52 | ghost predicate Connected(R: (T, T) -> bool) { 53 | forall x, y :: x != y ==> R(x, y) || R(y, x) 54 | } 55 | 56 | ghost predicate StronglyConnected(R: (T, T) -> bool) { 57 | forall x, y :: R(x, y) || R(y, x) 58 | } 59 | 60 | ghost predicate Transitive(R: (T, T) -> bool) { 61 | forall x, y, z :: R(x, y) && R(y, z) ==> R(x, z) 62 | } 63 | 64 | ghost predicate TotalOrdering(R: (T, T) -> bool) { 65 | && Reflexive(R) 66 | && AntiSymmetric(R) 67 | && Transitive(R) 68 | && StronglyConnected(R) 69 | } 70 | 71 | ghost predicate StrictTotalOrdering(R: (T, T) -> bool) { 72 | && Irreflexive(R) 73 | && AntiSymmetric(R) 74 | && Transitive(R) 75 | && Connected(R) 76 | } 77 | 78 | ghost predicate PreOrdering(R: (T, T) -> bool) { 79 | && Reflexive(R) 80 | && Transitive(R) 81 | } 82 | 83 | ghost predicate PartialOrdering(R: (T, T) -> bool) { 84 | && Reflexive(R) 85 | && Transitive(R) 86 | && AntiSymmetric(R) 87 | } 88 | 89 | ghost predicate EquivalenceRelation(R: (T, T) -> bool) { 90 | && Reflexive(R) 91 | && Symmetric(R) 92 | && Transitive(R) 93 | } 94 | 95 | /** An element in an ordered set is called a least element (or a minimum), if it is less than 96 | every other element of the set. */ 97 | ghost predicate IsLeast(R: (T, T) -> bool, min: T, s: set) { 98 | min in s && forall x | x in s :: R(min, x) 99 | } 100 | 101 | /** An element in an ordered set is called a minimal element, if no other element is less than it. */ 102 | ghost predicate IsMinimal(R: (T, T) -> bool, min: T, s: set) { 103 | min in s && forall x | x in s && R(x, min) :: R(min, x) 104 | } 105 | 106 | /** An element in an ordered set is called a greatest element (or a maximum), if it is greater than 107 | every other element of the set. */ 108 | ghost predicate IsGreatest(R: (T, T) -> bool, max: T, s: set) { 109 | max in s && forall x | x in s :: R(x, max) 110 | } 111 | 112 | /** An element in an ordered set is called a maximal element, if no other element is greater than it. */ 113 | ghost predicate IsMaximal(R: (T, T) -> bool, max: T, s: set) { 114 | max in s && forall x | x in s && R(max, x) :: R(x, max) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Collections/Maps/Imaps.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Original: Copyright 2018-2021 VMware, Inc., Microsoft Inc., Carnegie Mellon University, 5 | * ETH Zurich, and University of Washington 6 | * SPDX-License-Identifier: BSD-2-Clause 7 | * 8 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 9 | * SPDX-License-Identifier: MIT 10 | *******************************************************************************/ 11 | 12 | include "../../Wrappers.dfy" 13 | 14 | module {:options "-functionSyntax:4"} Imaps { 15 | import opened Wrappers 16 | 17 | function Get(m: imap, x: X): Option 18 | { 19 | if x in m then Some(m[x]) else None 20 | } 21 | 22 | /* Remove all key-value pairs corresponding to the iset of keys provided. */ 23 | ghost function {:opaque} RemoveKeys(m: imap, xs: iset): (m': imap) 24 | ensures forall x {:trigger m'[x]} :: x in m && x !in xs ==> x in m' && m'[x] == m[x] 25 | ensures forall x {:trigger x in m'} :: x in m' ==> x in m && x !in xs 26 | ensures m'.Keys == m.Keys - xs 27 | { 28 | imap x | x in m && x !in xs :: m[x] 29 | } 30 | 31 | /* Remove a key-value pair. Returns unmodified imap if key is not found. */ 32 | ghost function {:opaque} RemoveKey(m: imap, x: X): (m': imap) 33 | ensures m' == RemoveKeys(m, iset{x}) 34 | ensures forall x' {:trigger m'[x']} :: x' in m' ==> m'[x'] == m[x'] 35 | { 36 | imap i | i in m && i != x :: m[i] 37 | } 38 | 39 | /* Keep all key-value pairs corresponding to the iset of keys provided. */ 40 | ghost function {:opaque} Restrict(m: imap, xs: iset): (m': imap) 41 | ensures m' == RemoveKeys(m, m.Keys - xs) 42 | { 43 | imap x | x in xs && x in m :: m[x] 44 | } 45 | 46 | /* True iff x maps to the same value or does not exist in m and m'. */ 47 | ghost predicate EqualOnKey(m: imap, m': imap, x: X) 48 | { 49 | (x !in m && x !in m') || (x in m && x in m' && m[x] == m'[x]) 50 | } 51 | 52 | /* True iff m is a subset of m'. */ 53 | ghost predicate IsSubset(m: imap, m': imap) 54 | { 55 | && m.Keys <= m'.Keys 56 | && forall x {:trigger EqualOnKey(m, m', x)}{:trigger x in m} :: x in m ==> EqualOnKey(m, m', x) 57 | } 58 | 59 | /* Union of two imaps. Does not require disjoint domains; on the intersection, 60 | values from the second imap are chosen. */ 61 | ghost function {:opaque} Union(m: imap, m': imap): (r: imap) 62 | ensures r.Keys == m.Keys + m'.Keys 63 | ensures forall x {:trigger r[x]} :: x in m' ==> r[x] == m'[x] 64 | ensures forall x {:trigger r[x]} :: x in m && x !in m' ==> r[x] == m[x] 65 | { 66 | m + m' 67 | } 68 | 69 | /* True iff an imap is injective. */ 70 | ghost predicate {:opaque} Injective(m: imap) 71 | { 72 | forall x, x' {:trigger m[x], m[x']} :: x != x' && x in m && x' in m ==> m[x] != m[x'] 73 | } 74 | 75 | /* Swaps imap keys and values. Values are not required to be unique; no 76 | promises on which key is chosen on the intersection. */ 77 | ghost function {:opaque} Invert(m: imap): imap 78 | { 79 | imap y | y in m.Values :: var x :| x in m.Keys && m[x] == y; x 80 | } 81 | 82 | /* Inverted maps are injective. */ 83 | lemma LemmaInvertIsInjective(m: imap) 84 | ensures Injective(Invert(m)) 85 | { 86 | reveal Injective(); 87 | reveal Invert(); 88 | } 89 | 90 | /* True iff an imap contains all valid keys. */ 91 | ghost predicate {:opaque} Total(m: imap) 92 | { 93 | forall i {:trigger m[i]}{:trigger i in m} :: i in m 94 | } 95 | 96 | /* True iff an imap is monotonic. */ 97 | ghost predicate {:opaque} Monotonic(m: imap) 98 | { 99 | forall x, x' {:trigger m[x], m[x']} :: x in m && x' in m && x <= x' ==> m[x] <= m[x'] 100 | } 101 | 102 | /* True iff an imap is monotonic. Only considers keys greater than or 103 | equal to start. */ 104 | ghost predicate {:opaque} MonotonicFrom(m: imap, start: int) 105 | { 106 | forall x, x' {:trigger m[x], m[x']} :: x in m && x' in m && start <= x <= x' ==> m[x] <= m[x'] 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/dafny/Collections/Imaps.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Original: Copyright 2018-2021 VMware, Inc., Microsoft Inc., Carnegie Mellon University, 5 | * ETH Zurich, and University of Washington 6 | * SPDX-License-Identifier: BSD-2-Clause 7 | * 8 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 9 | * SPDX-License-Identifier: MIT 10 | *******************************************************************************/ 11 | 12 | include "../Wrappers.dfy" 13 | 14 | module {:options "-functionSyntax:4"} Dafny.Collections.Imaps { 15 | import opened Wrappers 16 | 17 | function Get(m: imap, x: X): Option 18 | { 19 | if x in m then Some(m[x]) else None 20 | } 21 | 22 | /* Remove all key-value pairs corresponding to the iset of keys provided. */ 23 | ghost function {:opaque} RemoveKeys(m: imap, xs: iset): (m': imap) 24 | ensures forall x {:trigger m'[x]} :: x in m && x !in xs ==> x in m' && m'[x] == m[x] 25 | ensures forall x {:trigger x in m'} :: x in m' ==> x in m && x !in xs 26 | ensures m'.Keys == m.Keys - xs 27 | { 28 | imap x | x in m && x !in xs :: m[x] 29 | } 30 | 31 | /* Remove a key-value pair. Returns unmodified imap if key is not found. */ 32 | ghost function {:opaque} RemoveKey(m: imap, x: X): (m': imap) 33 | ensures m' == RemoveKeys(m, iset{x}) 34 | ensures forall x' {:trigger m'[x']} :: x' in m' ==> m'[x'] == m[x'] 35 | { 36 | imap i | i in m && i != x :: m[i] 37 | } 38 | 39 | /* Keep all key-value pairs corresponding to the iset of keys provided. */ 40 | ghost function {:opaque} Restrict(m: imap, xs: iset): (m': imap) 41 | ensures m' == RemoveKeys(m, m.Keys - xs) 42 | { 43 | imap x | x in xs && x in m :: m[x] 44 | } 45 | 46 | /* True iff x maps to the same value or does not exist in m and m'. */ 47 | ghost predicate EqualOnKey(m: imap, m': imap, x: X) 48 | { 49 | (x !in m && x !in m') || (x in m && x in m' && m[x] == m'[x]) 50 | } 51 | 52 | /* True iff m is a subset of m'. */ 53 | ghost predicate IsSubset(m: imap, m': imap) 54 | { 55 | && m.Keys <= m'.Keys 56 | && forall x {:trigger EqualOnKey(m, m', x)}{:trigger x in m} :: x in m ==> EqualOnKey(m, m', x) 57 | } 58 | 59 | /* Union of two imaps. Does not require disjoint domains; on the intersection, 60 | values from the second imap are chosen. */ 61 | ghost function {:opaque} Union(m: imap, m': imap): (r: imap) 62 | ensures r.Keys == m.Keys + m'.Keys 63 | ensures forall x {:trigger r[x]} :: x in m' ==> r[x] == m'[x] 64 | ensures forall x {:trigger r[x]} :: x in m && x !in m' ==> r[x] == m[x] 65 | { 66 | m + m' 67 | } 68 | 69 | /* True iff an imap is injective. */ 70 | ghost predicate {:opaque} Injective(m: imap) 71 | { 72 | forall x, x' {:trigger m[x], m[x']} :: x != x' && x in m && x' in m ==> m[x] != m[x'] 73 | } 74 | 75 | /* Swaps imap keys and values. Values are not required to be unique; no 76 | promises on which key is chosen on the intersection. */ 77 | ghost function {:opaque} Invert(m: imap): imap 78 | { 79 | imap y | y in m.Values :: var x :| x in m.Keys && m[x] == y; x 80 | } 81 | 82 | /* Inverted maps are injective. */ 83 | lemma LemmaInvertIsInjective(m: imap) 84 | ensures Injective(Invert(m)) 85 | { 86 | reveal Injective(); 87 | reveal Invert(); 88 | } 89 | 90 | /* True iff an imap contains all valid keys. */ 91 | ghost predicate {:opaque} Total(m: imap) 92 | { 93 | forall i {:trigger m[i]}{:trigger i in m} :: i in m 94 | } 95 | 96 | /* True iff an imap is monotonic. */ 97 | ghost predicate {:opaque} Monotonic(m: imap) 98 | { 99 | forall x, x' {:trigger m[x], m[x']} :: x in m && x' in m && x <= x' ==> m[x] <= m[x'] 100 | } 101 | 102 | /* True iff an imap is monotonic. Only considers keys greater than or 103 | equal to start. */ 104 | ghost predicate {:opaque} MonotonicFrom(m: imap, start: int) 105 | { 106 | forall x, x' {:trigger m[x], m[x']} :: x in m && x' in m && start <= x <= x' ==> m[x] <= m[x'] 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/MutableMap/MutableMap.dfy: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright by the contributors to the Dafny Project 3 | * SPDX-License-Identifier: MIT 4 | *******************************************************************************/ 5 | 6 | // RUN: %verify "%s" 7 | 8 | include "../../src/Wrappers.dfy" 9 | 10 | /** 11 | * Implements mutable maps by interfacing with external code, e.g. "MutableMap.java". 12 | */ 13 | 14 | module {:extern "DafnyLibraries"} {:options "-functionSyntax:4"} DafnyLibraries { 15 | import opened Wrappers 16 | 17 | /** 18 | * NOTE: Only here because of #2500; once resolved import "../../examples/MutableMap/ 19 | * MutableMapTrait.dfy". 20 | */ 21 | trait {:termination false} MutableMapTrait { 22 | function content(): map 23 | reads this 24 | 25 | method Put(k: K, v: V) 26 | modifies this 27 | ensures this.content() == old(this.content())[k := v] 28 | ensures k in old(this.content()).Keys ==> this.content().Values + {old(this.content())[k]} == old(this.content()).Values + {v} 29 | ensures k !in old(this.content()).Keys ==> this.content().Values == old(this.content()).Values + {v} 30 | 31 | function Keys(): (keys: set) 32 | reads this 33 | ensures keys == this.content().Keys 34 | 35 | predicate HasKey(k: K) 36 | reads this 37 | ensures HasKey(k) <==> k in this.content().Keys 38 | 39 | function Values(): (values: set) 40 | reads this 41 | ensures values == this.content().Values 42 | 43 | function Items(): (items: set<(K,V)>) 44 | reads this 45 | ensures items == this.content().Items 46 | ensures items == set k | k in this.content().Keys :: (k, this.content()[k]) 47 | 48 | function Select(k: K): (v: V) 49 | reads this 50 | requires this.HasKey(k) 51 | ensures v in this.content().Values 52 | ensures this.content()[k] == v 53 | 54 | method Remove(k: K) 55 | modifies this 56 | ensures this.content() == old(this.content()) - {k} 57 | ensures k in old(this.content()).Keys ==> this.content().Values + {old(this.content())[k]} == old(this.content()).Values 58 | 59 | function Size(): (size: int) 60 | reads this 61 | ensures size == |this.content().Items| 62 | } 63 | 64 | class {:extern} MutableMap extends MutableMapTrait { 65 | 66 | // Invariant on key-value pairs this map may hold 67 | ghost const inv: (K, V) -> bool 68 | 69 | constructor {:extern} (ghost inv: (K, V) -> bool, bytesKeys: bool := false) 70 | ensures this.content() == map[] 71 | ensures this.inv == inv 72 | 73 | function {:extern} content(): map 74 | reads this 75 | 76 | method {:extern} Put(k: K, v: V) 77 | modifies this 78 | ensures this.content() == old(this.content())[k := v] 79 | ensures k in old(this.content()).Keys ==> this.content().Values + {old(this.content())[k]} == old(this.content()).Values + {v} 80 | ensures k !in old(this.content()).Keys ==> this.content().Values == old(this.content()).Values + {v} 81 | 82 | function {:extern} Keys(): (keys: set) 83 | reads this 84 | ensures keys == this.content().Keys 85 | 86 | predicate {:extern} HasKey(k: K) 87 | reads this 88 | ensures HasKey(k) <==> k in this.content().Keys 89 | 90 | function {:extern} Values(): (values: set) 91 | reads this 92 | ensures values == this.content().Values 93 | 94 | function {:extern} Items(): (items: set<(K,V)>) 95 | reads this 96 | ensures items == this.content().Items 97 | ensures items == set k | k in this.content().Keys :: (k, this.content()[k]) 98 | 99 | function {:extern} Select(k: K): (v: V) 100 | reads this 101 | requires this.HasKey(k) 102 | ensures v in this.content().Values 103 | ensures this.content()[k] == v 104 | 105 | method {:extern} Remove(k: K) 106 | modifies this 107 | ensures this.content() == old(this.content()) - {k} 108 | ensures k in old(this.content()).Keys ==> this.content().Values + {old(this.content())[k]} == old(this.content()).Values 109 | 110 | function {:extern} Size(): (size: int) 111 | reads this 112 | ensures size == |this.content().Items| 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/FileIO/FileIO.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright by the contributors to the Dafny Project 3 | * SPDX-License-Identifier: MIT 4 | *******************************************************************************/ 5 | 6 | namespace DafnyLibraries 7 | { 8 | using System; 9 | using System.IO; 10 | 11 | using Dafny; 12 | 13 | public class FileIO 14 | { 15 | /// 16 | /// Attempts to read all bytes from the file at the given path, and outputs the following values: 17 | /// 18 | /// 19 | /// isError 20 | /// 21 | /// true iff an exception was thrown during path string conversion or when reading the file 22 | /// 23 | /// 24 | /// 25 | /// bytesRead 26 | /// 27 | /// the sequence of bytes read from the file, or an empty sequence if isError is true 28 | /// 29 | /// 30 | /// 31 | /// errorMsg 32 | /// 33 | /// the error message of the thrown exception if isError is true, or an empty sequence otherwise 34 | /// 35 | /// 36 | /// 37 | /// 38 | /// We output these values individually because Result is not defined in the runtime but instead in library code. 39 | /// It is the responsibility of library code to construct an equivalent Result value. 40 | /// 41 | public static void INTERNAL_ReadBytesFromFile(ISequence path, out bool isError, out ISequence bytesRead, 42 | out ISequence errorMsg) 43 | { 44 | isError = true; 45 | bytesRead = Sequence.Empty; 46 | errorMsg = Sequence.Empty; 47 | try 48 | { 49 | bytesRead = Helpers.SeqFromArray(File.ReadAllBytes(path?.ToString())); 50 | isError = false; 51 | } 52 | catch (Exception e) 53 | { 54 | errorMsg = Helpers.SeqFromArray(e.ToString().ToCharArray()); 55 | } 56 | } 57 | 58 | /// 59 | /// Attempts to write all given bytes to the file at the given path, creating nonexistent parent directories as necessary, 60 | /// and outputs the following values: 61 | /// 62 | /// 63 | /// isError 64 | /// 65 | /// true iff an exception was thrown during path string conversion or when writing to the file 66 | /// 67 | /// 68 | /// 69 | /// errorMsg 70 | /// 71 | /// the error message of the thrown exception if isError is true, or an empty sequence otherwise 72 | /// 73 | /// 74 | /// 75 | /// 76 | /// We output these values individually because Result is not defined in the runtime but instead in library code. 77 | /// It is the responsibility of library code to construct an equivalent Result value. 78 | /// 79 | public static void INTERNAL_WriteBytesToFile(ISequence path, ISequence bytes, out bool isError, out ISequence errorMsg) 80 | { 81 | isError = true; 82 | errorMsg = Sequence.Empty; 83 | try 84 | { 85 | string pathStr = path?.ToString(); 86 | CreateParentDirs(pathStr); 87 | File.WriteAllBytes(pathStr, bytes.CloneAsArray()); 88 | isError = false; 89 | } 90 | catch (Exception e) 91 | { 92 | errorMsg = Helpers.SeqFromArray(e.ToString().ToCharArray()); 93 | } 94 | } 95 | 96 | /// 97 | /// Creates the nonexistent parent directory(-ies) of the given path. 98 | /// 99 | private static void CreateParentDirs(string path) 100 | { 101 | string parentDir = Path.GetDirectoryName(Path.GetFullPath(path)); 102 | Directory.CreateDirectory(parentDir); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/JSON/Tests.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %run "%s" --unicode-char:false --input ../Unicode/UnicodeStringsWithoutUnicodeChar.dfy 2 | // RUN: %run "%s" --unicode-char:true --input ../Unicode/UnicodeStringsWithUnicodeChar.dfy 3 | 4 | // TODO: Test for Java and other target languages too 5 | 6 | include "Errors.dfy" 7 | include "API.dfy" 8 | include "ZeroCopy/API.dfy" 9 | include "../Collections/Sequences/Seq.dfy" 10 | 11 | abstract module JSON.Tests.Wrapper { 12 | import Utils.Str 13 | import opened BoundedInts 14 | import opened UnicodeStrings 15 | import opened Errors 16 | 17 | type JSON 18 | method Deserialize(bs: bytes) returns (js: DeserializationResult) 19 | method SpecSerialize(js: JSON) returns (bs: SerializationResult) 20 | method Serialize(js: JSON) returns (bs: SerializationResult) 21 | method Check(bs: bytes, js: JSON, bs': bytes, sbs': bytes, js': JSON) 22 | 23 | method TestBytestring(bs: bytes, indent: string) { 24 | var js :- expect Deserialize(bs); 25 | // print indent, "=> ", js, "\n"; 26 | var bs' :- expect Serialize(js); 27 | print indent, "=> ", FromUTF8Checked(bs'), "\n"; 28 | var sbs' :- expect SpecSerialize(js); 29 | print indent, "=> ", FromUTF8Checked(sbs'), "\n"; 30 | var js' :- expect Deserialize(bs'); 31 | Check(bs, js, bs', sbs', js'); 32 | } 33 | 34 | method TestString(str: string, indent: string) { 35 | var bs :- expect ToUTF8Checked(str); 36 | TestBytestring(bs, indent); 37 | } 38 | 39 | method TestStrings(vectors: seq) { 40 | for i := 0 to |vectors| { 41 | var input := vectors[i]; 42 | var idx := Str.OfInt(i); 43 | var indent := seq(|idx| + 1, _ => ' '); 44 | print "[", idx, "]: ", input, "\n"; 45 | TestString(input, indent); 46 | print "\n"; 47 | } 48 | } 49 | } 50 | 51 | module JSON.Tests.ZeroCopyWrapper refines Wrapper { 52 | import opened Wrappers 53 | import Grammar 54 | import ZeroCopy.API 55 | import ConcreteSyntax.Spec 56 | 57 | type JSON = Grammar.JSON 58 | 59 | method Deserialize(bs: bytes) returns (js: DeserializationResult) { 60 | js := API.Deserialize(bs); 61 | } 62 | 63 | method Serialize(js: JSON) returns (bs: SerializationResult) { 64 | // print "Count: ", wr.chain.Count(), "\n"; 65 | bs := API.Serialize(js); 66 | } 67 | 68 | method SpecSerialize(js: JSON) returns (bs: SerializationResult) { 69 | bs := Success(Spec.JSON(js)); 70 | } 71 | 72 | method Check(bs: bytes, js: JSON, bs': bytes, sbs': bytes, js': JSON) { 73 | expect sbs' == bs' == bs; 74 | expect js' == js; // This doesn't hold in general, since the views could be different 75 | } 76 | } 77 | 78 | module JSON.Tests.AbstractSyntaxWrapper refines Wrapper { 79 | import opened Wrappers 80 | import Grammar 81 | import API 82 | import Values 83 | import Spec 84 | 85 | type JSON = Values.JSON 86 | 87 | method Deserialize(bs: bytes) returns (js: DeserializationResult) { 88 | js := API.Deserialize(bs); 89 | } 90 | 91 | method Serialize(js: JSON) returns (bs: SerializationResult) { 92 | bs := API.Serialize(js); 93 | } 94 | 95 | method SpecSerialize(js: JSON) returns (bs: SerializationResult) { 96 | bs := Spec.JSON(js); 97 | } 98 | 99 | method Check(bs: bytes, js: JSON, bs': bytes, sbs': bytes, js': JSON) { 100 | expect sbs' == bs'; // Serializing changes number representations, escapes, and spacing, so no == bs 101 | expect js' == js; 102 | } 103 | } 104 | 105 | module JSON.Tests { 106 | 107 | import opened Seq 108 | import Spec 109 | import opened BoundedInts 110 | 111 | const VECTORS := [ 112 | "true", 113 | "false", 114 | "null", 115 | "\"\"", 116 | "\"string\"", 117 | "[\"A\"]", 118 | "-123.456e-18", 119 | "[]", 120 | "[ ]", 121 | "[1]", 122 | "[1, 2]", 123 | "{}", 124 | "{ \"a\": 1 }", 125 | "{ \"a\": \"b\" }", 126 | "{ \"some\" : \"string\", \"and\": [ \"a number\", -123.456e-18 ] }", 127 | 128 | " true ", 129 | " { } ", 130 | "\"\\t\\r\\n\\f\"", 131 | "\"∀ABC // ABC\\u2200\"", // ∀ 132 | "\"🇫🇷 // \\u1f1eb\\u1f1EBABC\"", // 🇫🇷 133 | 134 | "[true, false , null, { \"some\" : \"string\", \"and\": [ \"a number\", -123.456e-18 ] } ] ", 135 | 136 | // Stress test - this used to cause stack overflow errors because of non-tail-recursive functions. 137 | // We should have these kinds of tests direclty in the Unicode module too. 138 | "\"" + Seq.Repeat('a', 100_000) + "\"" 139 | ] 140 | 141 | method Main() { 142 | ZeroCopyWrapper.TestStrings(VECTORS); 143 | AbstractSyntaxWrapper.TestStrings(VECTORS); 144 | expect Spec.EscapeUnicode(7) == ['0' as uint16, '0' as uint16, '0' as uint16, '7' as uint16]; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/JSON/Grammar.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /// ========================================== 4 | /// Low-level JSON grammar (Concrete syntax) 5 | /// ========================================== 6 | /// 7 | /// See ``JSON.Values`` for the high-level interface. 8 | 9 | include "../BoundedInts.dfy" 10 | include "Utils/Views.dfy" 11 | 12 | module {:options "-functionSyntax:4"} JSON.Grammar { 13 | import opened BoundedInts 14 | import opened Utils.Views.Core 15 | 16 | const EMPTY := View.OfBytes([]) 17 | const DOUBLEQUOTE := View.OfBytes(['\"' as byte]) 18 | const PERIOD := View.OfBytes(['.' as byte]) 19 | const E := View.OfBytes(['e' as byte]) 20 | const COLON := View.OfBytes([':' as byte]) 21 | const COMMA := View.OfBytes([',' as byte]) 22 | const LBRACE := View.OfBytes(['{' as byte]) 23 | const RBRACE := View.OfBytes(['}' as byte]) 24 | const LBRACKET := View.OfBytes(['[' as byte]) 25 | const RBRACKET := View.OfBytes([']' as byte]) 26 | const MINUS := View.OfBytes(['-' as byte]) 27 | 28 | type jchar = v: View | v.Length() == 1 witness View.OfBytes(['b' as byte]) 29 | type jquote = v: View | v.Char?('\"') witness DOUBLEQUOTE 30 | type jperiod = v: View | v.Char?('.') witness PERIOD 31 | type je = v: View | v.Char?('e') || v.Char?('E') witness E 32 | type jcolon = v: View | v.Char?(':') witness COLON 33 | type jcomma = v: View | v.Char?(',') witness COMMA 34 | type jlbrace = v: View | v.Char?('{') witness LBRACE 35 | type jrbrace = v: View | v.Char?('}') witness RBRACE 36 | type jlbracket = v: View | v.Char?('[') witness LBRACKET 37 | type jrbracket = v: View | v.Char?(']') witness RBRACKET 38 | type jminus = v: View | v.Char?('-') || v.Empty? witness MINUS 39 | type jsign = v: View | v.Char?('-') || v.Char?('+') || v.Empty? witness EMPTY 40 | 41 | predicate Blank?(b: byte) { b == 0x20 || b == 0x09 || b == 0x0A || b == 0x0D } 42 | ghost predicate Blanks?(v: View) { forall b | b in v.Bytes() :: Blank?(b) } 43 | type jblanks = v: View | Blanks?(v) witness View.OfBytes([]) 44 | 45 | datatype Structural<+T> = 46 | Structural(before: jblanks, t: T, after: jblanks) 47 | 48 | datatype Maybe<+T> = Empty() | NonEmpty(t: T) 49 | 50 | datatype Suffixed<+T, +S> = 51 | Suffixed(t: T, suffix: Maybe>) 52 | 53 | type SuffixedSequence<+D, +S> = s: seq> | NoTrailingSuffix(s) 54 | ghost predicate NoTrailingSuffix(s: seq>) { 55 | forall idx | 0 <= idx < |s| :: s[idx].suffix.Empty? <==> idx == |s| - 1 56 | } 57 | 58 | datatype Bracketed<+L, +D, +S, +R> = 59 | Bracketed(l: Structural, data: SuffixedSequence, r: Structural) 60 | 61 | const NULL: bytes := ['n' as byte, 'u' as byte, 'l' as byte, 'l' as byte] 62 | const TRUE: bytes := ['t' as byte, 'r' as byte, 'u' as byte, 'e' as byte] 63 | const FALSE: bytes := ['f' as byte, 'a' as byte, 'l' as byte, 's' as byte, 'e' as byte] 64 | 65 | ghost predicate Null?(v: View) { v.Bytes() == NULL } 66 | ghost predicate Bool?(v: View) { v.Bytes() in {TRUE, FALSE} } 67 | predicate Digit?(b: byte) { '0' as byte <= b <= '9' as byte } 68 | ghost predicate Digits?(v: View) { forall b | b in v.Bytes() :: Digit?(b) } 69 | ghost predicate Num?(v: View) { Digits?(v) && !v.Empty? } 70 | ghost predicate Int?(v: View) { v.Char?('0') || (Num?(v) && v.At(0) != '0' as byte) } 71 | 72 | type jnull = v: View | Null?(v) witness View.OfBytes(NULL) 73 | type jbool = v: View | Bool?(v) witness View.OfBytes(TRUE) 74 | type jdigits = v: View | Digits?(v) witness View.OfBytes([]) 75 | type jnum = v: View | Num?(v) witness View.OfBytes(['0' as byte]) 76 | type jint = v: View | Int?(v) witness View.OfBytes(['0' as byte]) 77 | type jstr = v: View | true witness View.OfBytes([]) // TODO: Enforce quoting and escaping 78 | datatype jstring = JString(lq: jquote, contents: jstr, rq: jquote) 79 | datatype jKeyValue = KeyValue(k: jstring, colon: Structural, v: Value) 80 | 81 | // TODO enforce no leading space before closing bracket to disambiguate WS { WS WS } WS 82 | type jobject = Bracketed 83 | type jarray = Bracketed 84 | type jmembers = SuffixedSequence 85 | type jmember = Suffixed 86 | type jitems = SuffixedSequence 87 | type jitem = Suffixed 88 | 89 | datatype jfrac = JFrac(period: jperiod, num: jnum) 90 | datatype jexp = JExp(e: je, sign: jsign, num: jnum) 91 | datatype jnumber = JNumber(minus: jminus, num: jnum, frac: Maybe, exp: Maybe) 92 | 93 | datatype Value = 94 | | Null(n: jnull) 95 | | Bool(b: jbool) 96 | | String(str: jstring) 97 | | Number(num: jnumber) 98 | | Object(obj: jobject) 99 | | Array(arr: jarray) 100 | 101 | type JSON = Structural 102 | } 103 | -------------------------------------------------------------------------------- /examples/BinaryOperations.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Copyright by the contributors to the Dafny Project 5 | * SPDX-License-Identifier: MIT 6 | *******************************************************************************/ 7 | 8 | include "../src/dafny/BinaryOperations.dfy" 9 | 10 | module {:options "-functionSyntax:4"} IntegersExample { 11 | 12 | import opened BinaryOperations 13 | 14 | ghost function add(x: int, y: int): int { 15 | x + y 16 | } 17 | 18 | ghost function minus(x: int): int { 19 | -x 20 | } 21 | 22 | ghost function mult(x: int, y: int): int { 23 | x * y 24 | } 25 | 26 | lemma IntegersAreAssociative() 27 | ensures IsAssociative(add) 28 | ensures IsAssociative(mult) 29 | {} 30 | 31 | lemma IntegersAreMonoid() 32 | ensures IsMonoid(add, 0) 33 | ensures IsMonoid(mult, 1) 34 | {} 35 | 36 | lemma IntegersHaveUnit() 37 | ensures IsLeftUnital(add, 0) 38 | ensures IsRightUnital(add, 0) 39 | ensures IsUnital(add, 0) 40 | ensures IsLeftUnital(mult, 1) 41 | ensures IsRightUnital(mult, 1) 42 | ensures IsUnital(mult, 1) 43 | {} 44 | 45 | lemma IntegersAreAbelian() 46 | ensures IsCommutative(add) 47 | ensures IsCommutative(mult) 48 | {} 49 | 50 | lemma IntegersAreAdditiveGroup() 51 | ensures IsGroup(add, minus, 0) 52 | {} 53 | 54 | lemma IntegersAreAdditiveAbelianGroup() 55 | ensures IsAbelianGroup(add, minus, 0) 56 | {} 57 | 58 | lemma IntegersHaveAdditiveInverse() 59 | ensures IsInverse(add, minus, 0) 60 | {} 61 | 62 | lemma IntegersAreDistributive() 63 | ensures IsLeftDistributive(add, mult) 64 | ensures IsRightDistributive(add, mult) 65 | ensures IsDistributive(add, mult) 66 | {} 67 | 68 | lemma IntegersAreRing() 69 | ensures IsRing(add, minus, 0, mult, 1) 70 | {} 71 | 72 | } 73 | 74 | module {:options "-functionSyntax:4"} RealsExample { 75 | 76 | import opened BinaryOperations 77 | 78 | ghost function add(x: real, y: real): real { 79 | x + y 80 | } 81 | 82 | ghost function minus(x: real): real { 83 | -x 84 | } 85 | 86 | ghost function mult(x: real, y: real): real { 87 | x * y 88 | } 89 | 90 | ghost function div(x: real): real 91 | requires x != 0.0 92 | { 93 | 1.0 / x 94 | } 95 | 96 | lemma RealsAreAssociative() 97 | ensures IsAssociative(add) 98 | ensures IsAssociative(mult) 99 | {} 100 | 101 | lemma RealsAreMonoid() 102 | ensures IsMonoid(add, 0.0) 103 | ensures IsMonoid(mult, 1.0) 104 | {} 105 | 106 | lemma RealsHaveUnit() 107 | ensures IsLeftUnital(add, 0.0) 108 | ensures IsRightUnital(add, 0.0) 109 | ensures IsUnital(add, 0.0) 110 | ensures IsLeftUnital(mult, 1.0) 111 | ensures IsRightUnital(mult, 1.0) 112 | ensures IsUnital(mult, 1.0) 113 | {} 114 | 115 | lemma RealsAreAbelian() 116 | ensures IsCommutative(add) 117 | ensures IsCommutative(mult) 118 | {} 119 | 120 | lemma RealsAreAdditiveGroup() 121 | ensures IsGroup(add, minus, 0.0) 122 | {} 123 | 124 | lemma RealsAreAdditiveAbelianGroup() 125 | ensures IsAbelianGroup(add, minus, 0.0) 126 | {} 127 | 128 | lemma NonZeroRealsAreMultiplicativeGroup() 129 | ensures IsGroup(mult, div, 1.0) 130 | {} 131 | 132 | lemma NonZeroRealsAreMultiplicativeAbelianGroup() 133 | ensures IsAbelianGroup(mult, div, 1.0) 134 | {} 135 | 136 | lemma RealsHaveAdditiveInverse() 137 | ensures IsInverse(add, minus, 0.0) 138 | {} 139 | 140 | lemma RealsAreDistributive() 141 | ensures IsLeftDistributive(add, mult) 142 | ensures IsRightDistributive(add, mult) 143 | ensures IsDistributive(add, mult) 144 | {} 145 | 146 | lemma RealsAreRing() 147 | ensures IsRing(add, minus, 0.0, mult, 1.0) 148 | { 149 | assert IsDistributive(add, mult); 150 | } 151 | 152 | lemma RealsAreField() 153 | ensures IsField(add, minus, 0.0, mult, div, 1.0) 154 | {} 155 | 156 | } 157 | 158 | module {:options "-functionSyntax:4"} HomomorphismExamples { 159 | 160 | import opened BinaryOperations 161 | import IntegersExample 162 | import RealsExample 163 | 164 | lemma IdentityIsHomomorphism(bop: (T, T) -> T) 165 | ensures IsHomomorphism(bop, bop, x => x) 166 | {} 167 | 168 | lemma IntRealEmbeddingIsHomomorphism() 169 | ensures IsHomomorphism(IntegersExample.add, RealsExample.add, (x: int) => x as real) 170 | {} 171 | 172 | lemma ConstUnitIsHomomorphism(bop1: (S, S) -> S, bop2: (T, T) -> T, unit: T) 173 | requires IsUnital(bop2, unit) 174 | ensures IsHomomorphism(bop1, bop2, x => unit) 175 | {} 176 | 177 | lemma ConstMultIsHomomorphism(n: int) 178 | ensures IsHomomorphism(IntegersExample.add, IntegersExample.add, x => n * x) 179 | {} 180 | 181 | } 182 | 183 | -------------------------------------------------------------------------------- /src/FileIO/FileIO.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright by the contributors to the Dafny Project 3 | * SPDX-License-Identifier: MIT 4 | *******************************************************************************/ 5 | 6 | package DafnyLibraries; 7 | 8 | import java.io.IOException; 9 | import java.io.PrintWriter; 10 | import java.io.StringWriter; 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | 15 | import dafny.DafnySequence; 16 | import dafny.Tuple2; 17 | import dafny.Tuple3; 18 | import dafny.TypeDescriptor; 19 | 20 | public class FileIO { 21 | /** 22 | * Attempts to read all bytes from the file at {@code path}, and returns a tuple of the following values: 23 | *
24 | *
{@code isError}
25 | *
true iff an exception was thrown during path string conversion or when reading the file
26 | *
{@code bytesRead}
27 | *
the sequence of bytes read from the file, or an empty sequence if {@code isError} is true
28 | *
{@code errorMsg}
29 | *
the error message of the thrown exception if {@code isError} is true, or an empty sequence otherwise
30 | *
31 | *

32 | * We return these values individually because {@code Result} is not defined in the runtime but instead in library code. 33 | * It is the responsibility of library code to construct an equivalent {@code Result} value. 34 | */ 35 | public static Tuple3, DafnySequence> 36 | INTERNAL_ReadBytesFromFile(DafnySequence path) { 37 | try { 38 | final Path pathObj = dafnyStringToPath(path); 39 | final DafnySequence readBytes = DafnySequence.fromBytes(Files.readAllBytes(pathObj)); 40 | return Tuple3.create(false, readBytes, DafnySequence.empty(TypeDescriptor.CHAR)); 41 | } catch (Exception ex) { 42 | return Tuple3.create(true, DafnySequence.empty(TypeDescriptor.BYTE), stackTraceToDafnyString(ex)); 43 | } 44 | } 45 | 46 | /** 47 | * Attempts to write {@code bytes} to the file at {@code path}, creating nonexistent parent directories as necessary, 48 | * and returns a tuple of the following values: 49 | *

50 | *
{@code isError}
51 | *
true iff an exception was thrown during path string conversion or when writing to the file
52 | *
{@code errorMsg}
53 | *
the error message of the thrown exception if {@code isError} is true, or an empty sequence otherwise
54 | *
55 | *

56 | * We return these values individually because {@code Result} is not defined in the runtime but instead in library code. 57 | * It is the responsibility of library code to construct an equivalent {@code Result} value. 58 | */ 59 | public static Tuple2> 60 | INTERNAL_WriteBytesToFile(DafnySequence path, DafnySequence bytes) { 61 | try { 62 | final Path pathObj = dafnyStringToPath(path); 63 | createParentDirs(pathObj); 64 | 65 | // It's safe to cast `bytes` to `DafnySequence` since the cast value is immediately consumed 66 | @SuppressWarnings("unchecked") 67 | final byte[] byteArr = DafnySequence.toByteArray((DafnySequence) bytes); 68 | 69 | Files.write(pathObj, byteArr); 70 | return Tuple2.create(false, DafnySequence.empty(TypeDescriptor.CHAR)); 71 | } catch (Exception ex) { 72 | return Tuple2.create(true, stackTraceToDafnyString(ex)); 73 | } 74 | } 75 | 76 | /** 77 | * Returns a Path constructed from the given Dafny string. 78 | */ 79 | private static final Path dafnyStringToPath(final DafnySequence path) { 80 | return Paths.get(new String((char[]) path.toArray().unwrap())); 81 | } 82 | 83 | /** 84 | * Creates the nonexistent parent directory(-ies) of the given path. 85 | */ 86 | private static final void createParentDirs(final Path path) throws IOException { 87 | final Path parentDir = path.toAbsolutePath().getParent(); 88 | if (parentDir == null) { 89 | return; 90 | } 91 | Files.createDirectories(parentDir); 92 | } 93 | 94 | private static final DafnySequence stackTraceToDafnyString(final Throwable t) { 95 | final StringWriter stringWriter = new StringWriter(); 96 | final PrintWriter printWriter = new PrintWriter(stringWriter); 97 | t.printStackTrace(printWriter); 98 | final String str = stringWriter.toString(); 99 | return DafnySequence.of(str.toCharArray()); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/dafny/FileIO/FileIO.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright by the contributors to the Dafny Project 3 | * SPDX-License-Identifier: MIT 4 | *******************************************************************************/ 5 | 6 | package DafnyLibraries; 7 | 8 | import java.io.IOException; 9 | import java.io.PrintWriter; 10 | import java.io.StringWriter; 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | 15 | import dafny.DafnySequence; 16 | import dafny.Tuple2; 17 | import dafny.Tuple3; 18 | import dafny.TypeDescriptor; 19 | 20 | public class FileIO { 21 | /** 22 | * Attempts to read all bytes from the file at {@code path}, and returns a tuple of the following values: 23 | *

24 | *
{@code isError}
25 | *
true iff an exception was thrown during path string conversion or when reading the file
26 | *
{@code bytesRead}
27 | *
the sequence of bytes read from the file, or an empty sequence if {@code isError} is true
28 | *
{@code errorMsg}
29 | *
the error message of the thrown exception if {@code isError} is true, or an empty sequence otherwise
30 | *
31 | *

32 | * We return these values individually because {@code Result} is not defined in the runtime but instead in library code. 33 | * It is the responsibility of library code to construct an equivalent {@code Result} value. 34 | */ 35 | public static Tuple3, DafnySequence> 36 | INTERNAL_ReadBytesFromFile(DafnySequence path) { 37 | try { 38 | final Path pathObj = dafnyStringToPath(path); 39 | final DafnySequence readBytes = DafnySequence.fromBytes(Files.readAllBytes(pathObj)); 40 | return Tuple3.create(false, readBytes, DafnySequence.empty(TypeDescriptor.CHAR)); 41 | } catch (Exception ex) { 42 | return Tuple3.create(true, DafnySequence.empty(TypeDescriptor.BYTE), stackTraceToDafnyString(ex)); 43 | } 44 | } 45 | 46 | /** 47 | * Attempts to write {@code bytes} to the file at {@code path}, creating nonexistent parent directories as necessary, 48 | * and returns a tuple of the following values: 49 | *

50 | *
{@code isError}
51 | *
true iff an exception was thrown during path string conversion or when writing to the file
52 | *
{@code errorMsg}
53 | *
the error message of the thrown exception if {@code isError} is true, or an empty sequence otherwise
54 | *
55 | *

56 | * We return these values individually because {@code Result} is not defined in the runtime but instead in library code. 57 | * It is the responsibility of library code to construct an equivalent {@code Result} value. 58 | */ 59 | public static Tuple2> 60 | INTERNAL_WriteBytesToFile(DafnySequence path, DafnySequence bytes) { 61 | try { 62 | final Path pathObj = dafnyStringToPath(path); 63 | createParentDirs(pathObj); 64 | 65 | // It's safe to cast `bytes` to `DafnySequence` since the cast value is immediately consumed 66 | @SuppressWarnings("unchecked") 67 | final byte[] byteArr = DafnySequence.toByteArray((DafnySequence) bytes); 68 | 69 | Files.write(pathObj, byteArr); 70 | return Tuple2.create(false, DafnySequence.empty(TypeDescriptor.CHAR)); 71 | } catch (Exception ex) { 72 | return Tuple2.create(true, stackTraceToDafnyString(ex)); 73 | } 74 | } 75 | 76 | /** 77 | * Returns a Path constructed from the given Dafny string. 78 | */ 79 | private static final Path dafnyStringToPath(final DafnySequence path) { 80 | return Paths.get(new String((char[]) path.toArray().unwrap())); 81 | } 82 | 83 | /** 84 | * Creates the nonexistent parent directory(-ies) of the given path. 85 | */ 86 | private static final void createParentDirs(final Path path) throws IOException { 87 | final Path parentDir = path.toAbsolutePath().getParent(); 88 | if (parentDir == null) { 89 | return; 90 | } 91 | Files.createDirectories(parentDir); 92 | } 93 | 94 | private static final DafnySequence stackTraceToDafnyString(final Throwable t) { 95 | final StringWriter stringWriter = new StringWriter(); 96 | final PrintWriter printWriter = new PrintWriter(stringWriter); 97 | t.printStackTrace(printWriter); 98 | final String str = stringWriter.toString(); 99 | return DafnySequence.of(str.toCharArray()); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Collections/Maps/Maps.dfy: -------------------------------------------------------------------------------- 1 | // RUN: %verify "%s" 2 | 3 | /******************************************************************************* 4 | * Original: Copyright 2018-2021 VMware, Inc., Microsoft Inc., Carnegie Mellon University, 5 | * ETH Zurich, and University of Washington 6 | * SPDX-License-Identifier: BSD-2-Clause 7 | * 8 | * Modifications and Extensions: Copyright by the contributors to the Dafny Project 9 | * SPDX-License-Identifier: MIT 10 | *******************************************************************************/ 11 | 12 | include "../../Wrappers.dfy" 13 | 14 | module {:options "-functionSyntax:4"} Maps { 15 | import opened Wrappers 16 | 17 | function Get(m: map, x: X): Option 18 | { 19 | if x in m then Some(m[x]) else None 20 | } 21 | 22 | function {:opaque} ToImap(m: map): (m': imap) 23 | ensures forall x {:trigger m'[x]} :: x in m ==> x in m' && m'[x] == m[x] 24 | ensures forall x {:trigger x in m'} :: x in m' ==> x in m 25 | { 26 | imap x | x in m :: m[x] 27 | } 28 | 29 | /* Remove all key-value pairs corresponding to the set of keys provided. */ 30 | function {:opaque} RemoveKeys(m: map, xs: set): (m': map) 31 | ensures forall x {:trigger m'[x]} :: x in m && x !in xs ==> x in m' && m'[x] == m[x] 32 | ensures forall x {:trigger x in m'} :: x in m' ==> x in m && x !in xs 33 | ensures m'.Keys == m.Keys - xs 34 | { 35 | m - xs 36 | } 37 | 38 | /* Remove a key-value pair. Returns unmodified map if key is not found. */ 39 | function {:opaque} Remove(m: map, x: X): (m': map) 40 | ensures m' == RemoveKeys(m, {x}) 41 | ensures |m'.Keys| <= |m.Keys| 42 | ensures x in m ==> |m'| == |m| - 1 43 | ensures x !in m ==> |m'| == |m| 44 | { 45 | var m' := map x' | x' in m && x' != x :: m[x']; 46 | assert m'.Keys == m.Keys - {x}; 47 | m' 48 | } 49 | 50 | /* Keep all key-value pairs corresponding to the set of keys provided. */ 51 | function {:opaque} Restrict(m: map, xs: set): (m': map) 52 | ensures m' == RemoveKeys(m, m.Keys - xs) 53 | { 54 | map x | x in xs && x in m :: m[x] 55 | } 56 | 57 | /* True iff x maps to the same value or does not exist in m and m'. */ 58 | ghost predicate EqualOnKey(m: map, m': map, x: X) 59 | { 60 | (x !in m && x !in m') || (x in m && x in m' && m[x] == m'[x]) 61 | } 62 | 63 | /* True iff m is a subset of m'. */ 64 | ghost predicate IsSubset(m: map, m': map) 65 | { 66 | && m.Keys <= m'.Keys 67 | && forall x {:trigger EqualOnKey(m, m', x)}{:trigger x in m} :: x in m ==> EqualOnKey(m, m', x) 68 | } 69 | 70 | /* Union of two maps. Does not require disjoint domains; on the intersection, 71 | values from the second map are chosen. */ 72 | function {:opaque} Union(m: map, m': map): (r: map) 73 | ensures r.Keys == m.Keys + m'.Keys 74 | ensures forall x {:trigger r[x]} :: x in m' ==> r[x] == m'[x] 75 | ensures forall x {:trigger r[x]} :: x in m && x !in m' ==> r[x] == m[x] 76 | { 77 | m + m' 78 | } 79 | 80 | /* The size of the disjoint union is equal to the sum of individual map 81 | sizes. */ 82 | lemma LemmaDisjointUnionSize(m: map, m': map) 83 | requires m.Keys !! m'.Keys 84 | ensures |Union(m, m')| == |m| + |m'| 85 | { 86 | var u := Union(m, m'); 87 | assert |u.Keys| == |m.Keys| + |m'.Keys|; 88 | } 89 | 90 | /* True iff a map is injective. */ 91 | ghost predicate {:opaque} Injective(m: map) 92 | { 93 | forall x, x' {:trigger m[x], m[x']} :: x != x' && x in m && x' in m ==> m[x] != m[x'] 94 | } 95 | 96 | /* Swaps map keys and values. Values are not required to be unique; no 97 | promises on which key is chosen on the intersection. */ 98 | ghost function {:opaque} Invert(m: map): map 99 | { 100 | map y | y in m.Values :: var x :| x in m.Keys && m[x] == y; x 101 | } 102 | 103 | /* Inverted maps are injective. */ 104 | lemma LemmaInvertIsInjective(m: map) 105 | ensures Injective(Invert(m)) 106 | { 107 | reveal Injective(); 108 | reveal Invert(); 109 | } 110 | 111 | /* True iff a map contains all valid keys. */ 112 | ghost predicate {:opaque} Total(m: map) 113 | { 114 | forall i {:trigger m[i]}{:trigger i in m} :: i in m 115 | } 116 | 117 | /* True iff a map is monotonic. */ 118 | ghost predicate {:opaque} Monotonic(m: map) 119 | { 120 | forall x, x' {:trigger m[x], m[x']} :: x in m && x' in m && x <= x' ==> m[x] <= m[x'] 121 | } 122 | 123 | /* True iff a map is monotonic. Only considers keys greater than or 124 | equal to start. */ 125 | ghost predicate {:opaque} MonotonicFrom(m: map, start: int) 126 | { 127 | forall x, x' {:trigger m[x], m[x']} :: x in m && x' in m && start <= x <= x' ==> m[x] <= m[x'] 128 | } 129 | 130 | } 131 | --------------------------------------------------------------------------------