summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Benjamin Barenblat <bbaren@mit.edu>2016-05-30 17:58:02 -0400
committerGravatar Benjamin Barenblat <bbaren@mit.edu>2016-05-30 17:58:02 -0400
commite67c951ad9c5c637e36a6f025ba3d6e3ad945416 (patch)
tree0cfb5c339602e4bdebf4bf97f3f0ccc3923c14d1
parent000aa762e1fee4b9bd83ec3d7c8b61fd203e2c9d (diff)
parentdf5c5f547990c1f80ab7594a1f9287ee03a61754 (diff)
Merge commit 'df5c5f5'
-rw-r--r--.hgignore13
-rw-r--r--Binaries/DafnyPrelude.bpl104
-rw-r--r--Binaries/DafnyRuntime.cs2238
-rw-r--r--Binaries/System.Collections.Immutable.dllbin0 -> 149752 bytes
-rwxr-xr-xBinaries/dafny16
-rwxr-xr-xBinaries/dafny-server16
-rw-r--r--Binaries/z3.exebin6343680 -> 10647552 bytes
-rw-r--r--Docs/DafnyRef/DafnyRef.bib48
-rw-r--r--Docs/DafnyRef/DafnyRef.mdk8331
-rw-r--r--Docs/DafnyRef/css.sty803
-rw-r--r--Docs/DafnyRef/dafnyx.json4
-rw-r--r--Docs/DafnyRef/ignores.dic83
-rw-r--r--Docs/DafnyRef/krml250.bib2026
-rw-r--r--Docs/DafnyRef/madoko.css471
-rw-r--r--Docs/DafnyRef/out/DafnyRef.html8287
-rw-r--r--Docs/DafnyRef/out/DafnyRef.pdfbin0 -> 563066 bytes
-rw-r--r--Docs/DafnyRef/paper-full.bib577
-rw-r--r--Docs/DafnyRef/poc.bib523
-rw-r--r--Docs/DafnyRef/references.bib1446
-rw-r--r--INSTALL57
-rw-r--r--LICENSE22
-rw-r--r--Source/Dafny.sln28
-rw-r--r--Source/Dafny/BigIntegerParser.cs27
-rw-r--r--Source/Dafny/Cloner.cs255
-rw-r--r--Source/Dafny/Compiler.cs1606
-rw-r--r--Source/Dafny/Dafny.atg781
-rw-r--r--Source/Dafny/DafnyAst.cs1313
-rw-r--r--Source/Dafny/DafnyMain.cs20
-rw-r--r--Source/Dafny/DafnyOptions.cs170
-rw-r--r--Source/Dafny/DafnyPipeline.csproj20
-rw-r--r--Source/Dafny/Makefile4
-rw-r--r--Source/Dafny/Parser.cs2992
-rw-r--r--Source/Dafny/Printer.cs3963
-rw-r--r--Source/Dafny/RefinementTransformer.cs489
-rw-r--r--Source/Dafny/Reporting.cs160
-rw-r--r--Source/Dafny/Resolver.cs5244
-rw-r--r--Source/Dafny/Rewriter.cs910
-rw-r--r--Source/Dafny/Scanner.cs494
-rw-r--r--Source/Dafny/SccGraph.cs10
-rw-r--r--Source/Dafny/Translator.cs3631
-rw-r--r--Source/Dafny/Triggers/QuantifierSplitter.cs138
-rw-r--r--Source/Dafny/Triggers/QuantifiersCollection.cs211
-rw-r--r--Source/Dafny/Triggers/QuantifiersCollector.cs57
-rw-r--r--Source/Dafny/Triggers/TriggerExtensions.cs509
-rw-r--r--Source/Dafny/Triggers/TriggerUtils.cs265
-rw-r--r--Source/Dafny/Triggers/TriggersCollector.cs325
-rw-r--r--Source/Dafny/Util.cs157
-rw-r--r--Source/DafnyDriver/DafnyDriver.cs208
-rw-r--r--Source/DafnyExtension/BufferIdleEventUtil.cs2
-rw-r--r--Source/DafnyExtension/DafnyDriver.cs135
-rw-r--r--Source/DafnyExtension/DafnyExtension.csproj79
-rw-r--r--Source/DafnyExtension/IdentifierTagger.cs167
-rw-r--r--Source/DafnyExtension/MenuProxy.cs25
-rw-r--r--Source/DafnyExtension/OutliningTagger.cs30
-rw-r--r--Source/DafnyExtension/ProgressMargin.cs46
-rw-r--r--Source/DafnyExtension/ResolverTagger.cs140
-rw-r--r--Source/DafnyExtension/TokenTagger.cs214
-rw-r--r--Source/DafnyExtension/packages.config29
-rw-r--r--Source/DafnyExtension/source.extension.vsixmanifest5
-rw-r--r--Source/DafnyMenu/DafnyMenu.csproj59
-rw-r--r--Source/DafnyMenu/DafnyMenu.vsct21
-rw-r--r--Source/DafnyMenu/DafnyMenuPackage.cs63
-rw-r--r--Source/DafnyMenu/PkgCmdID.cs2
-rw-r--r--Source/DafnyMenu/packages.config29
-rw-r--r--Source/DafnyMenu/source.extension.vsixmanifest3
-rw-r--r--Source/DafnyServer/App.config6
-rw-r--r--Source/DafnyServer/DafnyHelper.cs91
-rw-r--r--Source/DafnyServer/DafnyServer.csproj113
-rw-r--r--Source/DafnyServer/Properties/AssemblyInfo.cs36
-rw-r--r--Source/DafnyServer/Server.cs100
-rw-r--r--Source/DafnyServer/Utilities.cs61
-rw-r--r--Source/DafnyServer/VerificationTask.cs58
-rw-r--r--Source/version.cs6
-rw-r--r--Test/VSComp2010/Problem2-Invert.dfy2
-rw-r--r--Test/VSI-Benchmarks/b3.dfy10
-rw-r--r--Test/VSI-Benchmarks/b8.dfy2
-rw-r--r--Test/VerifyThis2015/Problem1.dfy10
-rw-r--r--Test/VerifyThis2015/Problem2.dfy4
-rw-r--r--Test/VerifyThis2015/Problem3.dfy20
-rw-r--r--Test/VerifyThis2015/Problem3.dfy.expect2
-rw-r--r--Test/cloudmake/CloudMake-CachedBuilds.dfy2
-rw-r--r--Test/cloudmake/CloudMake-ConsistentBuilds.dfy26
-rw-r--r--Test/cloudmake/CloudMake-ParallelBuilds.dfy2
-rw-r--r--Test/dafny0/AdvancedLHS.dfy.expect2
-rw-r--r--Test/dafny0/Array.dfy4
-rw-r--r--Test/dafny0/Array.dfy.expect82
-rw-r--r--Test/dafny0/AssumptionVariables0.dfy8
-rw-r--r--Test/dafny0/AssumptionVariables0.dfy.expect9
-rw-r--r--Test/dafny0/AutoReq.dfy7
-rw-r--r--Test/dafny0/AutoReq.dfy.expect53
-rw-r--r--Test/dafny0/Backticks.dfy.expect9
-rw-r--r--Test/dafny0/BadFunction.dfy.expect4
-rw-r--r--Test/dafny0/Basics.dfy4
-rw-r--r--Test/dafny0/Basics.dfy.expect36
-rw-r--r--Test/dafny0/BindingGuards.dfy159
-rw-r--r--Test/dafny0/BindingGuards.dfy.expect183
-rw-r--r--Test/dafny0/BindingGuardsResolution.dfy154
-rw-r--r--Test/dafny0/BindingGuardsResolution.dfy.expect165
-rw-r--r--Test/dafny0/Calculations.dfy3
-rw-r--r--Test/dafny0/Calculations.dfy.expect14
-rw-r--r--Test/dafny0/CallStmtTests.dfy34
-rw-r--r--Test/dafny0/CallStmtTests.dfy.expect4
-rw-r--r--Test/dafny0/Char.dfy.expect6
-rw-r--r--Test/dafny0/CoPrefix.dfy32
-rw-r--r--Test/dafny0/CoPrefix.dfy.expect47
-rw-r--r--Test/dafny0/CoinductiveProofs.dfy59
-rw-r--r--Test/dafny0/CoinductiveProofs.dfy.expect71
-rw-r--r--Test/dafny0/Compilation.dfy98
-rw-r--r--Test/dafny0/Compilation.dfy.expect17
-rw-r--r--Test/dafny0/Comprehensions.dfy10
-rw-r--r--Test/dafny0/Comprehensions.dfy.expect2
-rw-r--r--Test/dafny0/ComputationsLoop.dfy.expect6
-rw-r--r--Test/dafny0/ComputationsLoop2.dfy.expect10
-rw-r--r--Test/dafny0/ComputationsNeg.dfy2
-rw-r--r--Test/dafny0/ComputationsNeg.dfy.expect16
-rw-r--r--Test/dafny0/ContainerRanks.dfy33
-rw-r--r--Test/dafny0/ContainerRanks.dfy.expect2
-rw-r--r--Test/dafny0/ControlStructures.dfy.expect24
-rw-r--r--Test/dafny0/Corecursion.dfy.expect36
-rw-r--r--Test/dafny0/DTypes.dfy38
-rw-r--r--Test/dafny0/DTypes.dfy.expect24
-rw-r--r--Test/dafny0/DatatypeUpdate.dfy50
-rw-r--r--Test/dafny0/DatatypeUpdate.dfy.expect13
-rw-r--r--Test/dafny0/DatatypeUpdateResolution.dfy20
-rw-r--r--Test/dafny0/DatatypeUpdateResolution.dfy.expect5
-rw-r--r--Test/dafny0/Datatypes.dfy.expect46
-rw-r--r--Test/dafny0/Definedness.dfy.expect110
-rw-r--r--Test/dafny0/DeterministicPick.dfy1
-rw-r--r--Test/dafny0/DeterministicPick.dfy.expect4
-rw-r--r--Test/dafny0/DiamondImports.dfy.expect10
-rw-r--r--Test/dafny0/DirtyLoops.dfy3
-rw-r--r--Test/dafny0/DiscoverBounds.dfy.expect6
-rw-r--r--Test/dafny0/EqualityTypes.dfy112
-rw-r--r--Test/dafny0/EqualityTypes.dfy.expect24
-rw-r--r--Test/dafny0/Extern.dfy27
-rw-r--r--Test/dafny0/Extern.dfy.expect4
-rw-r--r--Test/dafny0/Extern2.cs14
-rw-r--r--Test/dafny0/ExternHelloLibrary.cs15
-rw-r--r--Test/dafny0/ExternHelloLibrary.dllbin0 -> 3072 bytes
-rw-r--r--Test/dafny0/ExternNegative.dfy26
-rw-r--r--Test/dafny0/ExternNegative.dfy.expect3
-rw-r--r--Test/dafny0/ExternNegative2.dfy26
-rw-r--r--Test/dafny0/ExternNegative2.dfy.expect2
-rw-r--r--Test/dafny0/ForallCompilation.dfy2
-rw-r--r--Test/dafny0/Fuel.dfy462
-rw-r--r--Test/dafny0/Fuel.dfy.expect113
-rw-r--r--Test/dafny0/FunctionSpecifications.dfy.expect72
-rw-r--r--Test/dafny0/IMaps.dfy.expect2
-rw-r--r--Test/dafny0/ISets.dfy43
-rw-r--r--Test/dafny0/ISets.dfy.expect2
-rw-r--r--Test/dafny0/Include.dfy.expect12
-rw-r--r--Test/dafny0/Includee.dfy.expect10
-rw-r--r--Test/dafny0/IndexIntoUpdate.dfy9
-rw-r--r--Test/dafny0/IndexIntoUpdate.dfy.expect6
-rw-r--r--Test/dafny0/InductivePredicates.dfy45
-rw-r--r--Test/dafny0/InductivePredicates.dfy.expect6
-rw-r--r--Test/dafny0/Inverses.dfy5
-rw-r--r--Test/dafny0/Inverses.dfy.expect10
-rw-r--r--Test/dafny0/Iterators.dfy.expect44
-rw-r--r--Test/dafny0/JustWarnings.dfy19
-rw-r--r--Test/dafny0/JustWarnings.dfy.expect4
-rw-r--r--Test/dafny0/LetExpr.dfy3
-rw-r--r--Test/dafny0/LetExpr.dfy.expect23
-rw-r--r--Test/dafny0/LhsDuplicates.dfy2
-rw-r--r--Test/dafny0/LhsDuplicates.dfy.expect12
-rw-r--r--Test/dafny0/LitTriggers.dfy39
-rw-r--r--Test/dafny0/LitTriggers.dfy.expect2
-rw-r--r--Test/dafny0/LoopModifies.dfy.expect18
-rw-r--r--Test/dafny0/Maps.dfy.expect4
-rw-r--r--Test/dafny0/Matrix-OOB.dfy13
-rw-r--r--Test/dafny0/Matrix-OOB.dfy.expect14
-rw-r--r--Test/dafny0/ModifyStmt.dfy.expect22
-rw-r--r--Test/dafny0/ModuleExport.dfy105
-rw-r--r--Test/dafny0/ModuleExport.dfy.expect13
-rw-r--r--Test/dafny0/Modules0.dfy26
-rw-r--r--Test/dafny0/Modules0.dfy.expect38
-rw-r--r--Test/dafny0/Modules1.dfy4
-rw-r--r--Test/dafny0/Modules1.dfy.expect12
-rw-r--r--Test/dafny0/Modules2.dfy2
-rw-r--r--Test/dafny0/MultiDimArray.dfy.expect4
-rw-r--r--Test/dafny0/MultiSets.dfy5
-rw-r--r--Test/dafny0/MultiSets.dfy.expect14
-rw-r--r--Test/dafny0/NatTypes.dfy.expect26
-rw-r--r--Test/dafny0/NestedMatch.dfy59
-rw-r--r--Test/dafny0/NestedMatch.dfy.expect2
-rw-r--r--Test/dafny0/NestedPatterns.dfy124
-rw-r--r--Test/dafny0/NestedPatterns.dfy.expect9
-rw-r--r--Test/dafny0/Newtypes.dfy.expect26
-rw-r--r--Test/dafny0/NonGhostQuantifiers.dfy7
-rw-r--r--Test/dafny0/NonGhostQuantifiers.dfy.expect20
-rw-r--r--Test/dafny0/OpaqueFunctions.dfy4
-rw-r--r--Test/dafny0/OpaqueFunctions.dfy.expect52
-rw-r--r--Test/dafny0/Parallel.dfy150
-rw-r--r--Test/dafny0/Parallel.dfy.expect20
-rw-r--r--Test/dafny0/ParallelResolveErrors.dfy17
-rw-r--r--Test/dafny0/ParallelResolveErrors.dfy.expect39
-rw-r--r--Test/dafny0/ParseErrors.dfy.expect32
-rw-r--r--Test/dafny0/PredExpr.dfy.expect14
-rw-r--r--Test/dafny0/Predicates.dfy2
-rw-r--r--Test/dafny0/Predicates.dfy.expect28
-rw-r--r--Test/dafny0/Protected.dfy.expect10
-rw-r--r--Test/dafny0/RangeCompilation.dfy25
-rw-r--r--Test/dafny0/RangeCompilation.dfy.expect6
-rw-r--r--Test/dafny0/RankNeg.dfy.expect24
-rw-r--r--Test/dafny0/Reads.dfy81
-rw-r--r--Test/dafny0/Reads.dfy.expect35
-rw-r--r--Test/dafny0/RealCompare.dfy.expect10
-rw-r--r--Test/dafny0/RealTypes.dfy.expect10
-rw-r--r--Test/dafny0/Refinement.dfy.expect38
-rw-r--r--Test/dafny0/RefinementErrors.dfy37
-rw-r--r--Test/dafny0/RefinementErrors.dfy.expect3
-rw-r--r--Test/dafny0/ResolutionErrors.dfy769
-rw-r--r--Test/dafny0/ResolutionErrors.dfy.expect373
-rw-r--r--Test/dafny0/SeqFromArray.dfy18
-rw-r--r--Test/dafny0/Shadows.dfy42
-rw-r--r--Test/dafny0/Shadows.dfy.expect12
-rw-r--r--Test/dafny0/Simple.dfy27
-rw-r--r--Test/dafny0/Simple.dfy.expect29
-rw-r--r--Test/dafny0/Skeletons.dfy.expect4
-rw-r--r--Test/dafny0/SmallTests.dfy92
-rw-r--r--Test/dafny0/SmallTests.dfy.expect109
-rw-r--r--Test/dafny0/SplitExpr.dfy.expect4
-rw-r--r--Test/dafny0/StatementExpressions.dfy.expect14
-rw-r--r--Test/dafny0/Superposition.dfy.expect22
-rw-r--r--Test/dafny0/Termination.dfy.expect26
-rw-r--r--Test/dafny0/Trait/TraitBasix.dfy.expect2
-rw-r--r--Test/dafny0/Trait/TraitExample.dfy113
-rw-r--r--Test/dafny0/Trait/TraitExample.dfy.expect7
-rw-r--r--Test/dafny0/Trait/TraitUsingParentMembers.dfy.expect2
-rw-r--r--Test/dafny0/Trait/TraitsDecreases.dfy46
-rw-r--r--Test/dafny0/Trait/TraitsDecreases.dfy.expect30
-rw-r--r--Test/dafny0/TriggerInPredicate.dfy19
-rw-r--r--Test/dafny0/TriggerInPredicate.dfy.expect7
-rw-r--r--Test/dafny0/Tuples.dfy.expect4
-rw-r--r--Test/dafny0/TypeAntecedents.dfy.expect8
-rw-r--r--Test/dafny0/TypeParameters.dfy.expect30
-rw-r--r--Test/dafny0/TypeTests.dfy50
-rw-r--r--Test/dafny0/TypeTests.dfy.expect14
-rw-r--r--Test/dafny0/UnfoldingPerformance.dfy61
-rw-r--r--Test/dafny0/UnfoldingPerformance.dfy.expect11
-rw-r--r--Test/dafny0/UserSpecifiedTypeParameters.dfy.expect4
-rw-r--r--Test/dafny0/columns.dfy12
-rw-r--r--Test/dafny0/columns.dfy.expect12
-rw-r--r--Test/dafny0/fun-with-slices.dfy19
-rw-r--r--Test/dafny0/fun-with-slices.dfy.expect2
-rw-r--r--Test/dafny0/one-message-per-failed-precondition.dfy20
-rw-r--r--Test/dafny0/one-message-per-failed-precondition.dfy.expect20
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots0.v0.dfy (renamed from Test/dafny0/snapshots/Snapshots0.v0.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots0.v1.dfy (renamed from Test/dafny0/snapshots/Snapshots0.v1.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots1.v0.dfy (renamed from Test/dafny0/snapshots/Snapshots1.v0.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots1.v1.dfy (renamed from Test/dafny0/snapshots/Snapshots1.v1.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots2.v0.dfy (renamed from Test/dafny0/snapshots/Snapshots2.v0.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots2.v1.dfy (renamed from Test/dafny0/snapshots/Snapshots2.v1.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots3.v0.dfy (renamed from Test/dafny0/snapshots/Snapshots3.v0.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots3.v1.dfy (renamed from Test/dafny0/snapshots/Snapshots3.v1.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots4.v0.dfy (renamed from Test/dafny0/snapshots/Snapshots4.v0.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots4.v1.dfy (renamed from Test/dafny0/snapshots/Snapshots4.v1.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots5.v0.dfy (renamed from Test/dafny0/snapshots/Snapshots5.v0.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots5.v1.dfy (renamed from Test/dafny0/snapshots/Snapshots5.v1.dfy)4
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots6.v0.dfy (renamed from Test/dafny0/snapshots/Snapshots6.v0.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots6.v1.dfy (renamed from Test/dafny0/snapshots/Snapshots6.v1.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots7.v0.dfy (renamed from Test/dafny0/snapshots/Snapshots7.v0.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots7.v1.dfy (renamed from Test/dafny0/snapshots/Snapshots7.v1.dfy)0
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots8.v0.dfy29
-rw-r--r--Test/dafny0/snapshots/Inputs/Snapshots8.v1.dfy33
-rw-r--r--Test/dafny0/snapshots/Snapshots0.run.dfy2
-rw-r--r--Test/dafny0/snapshots/Snapshots0.run.dfy.expect25
-rw-r--r--Test/dafny0/snapshots/Snapshots1.run.dfy2
-rw-r--r--Test/dafny0/snapshots/Snapshots1.run.dfy.expect21
-rw-r--r--Test/dafny0/snapshots/Snapshots2.run.dfy2
-rw-r--r--Test/dafny0/snapshots/Snapshots2.run.dfy.expect41
-rw-r--r--Test/dafny0/snapshots/Snapshots3.run.dfy2
-rw-r--r--Test/dafny0/snapshots/Snapshots3.run.dfy.expect18
-rw-r--r--Test/dafny0/snapshots/Snapshots4.run.dfy2
-rw-r--r--Test/dafny0/snapshots/Snapshots4.run.dfy.expect20
-rw-r--r--Test/dafny0/snapshots/Snapshots5.run.dfy2
-rw-r--r--Test/dafny0/snapshots/Snapshots5.run.dfy.expect35
-rw-r--r--Test/dafny0/snapshots/Snapshots6.run.dfy2
-rw-r--r--Test/dafny0/snapshots/Snapshots6.run.dfy.expect11
-rw-r--r--Test/dafny0/snapshots/Snapshots7.run.dfy2
-rw-r--r--Test/dafny0/snapshots/Snapshots7.run.dfy.expect31
-rw-r--r--Test/dafny0/snapshots/Snapshots8.run.dfy2
-rw-r--r--Test/dafny0/snapshots/Snapshots8.run.dfy.expect55
-rw-r--r--Test/dafny0/snapshots/lit.local.cfg5
-rw-r--r--Test/dafny0/snapshots/runtest.snapshot2
-rw-r--r--Test/dafny0/snapshots/runtest.snapshot.expect180
-rw-r--r--Test/dafny1/BDD.dfy1
-rw-r--r--Test/dafny1/ExtensibleArrayAuto.dfy36
-rw-r--r--Test/dafny1/FindZero.dfy10
-rw-r--r--Test/dafny1/Induction.dfy32
-rw-r--r--Test/dafny1/MoreInduction.dfy18
-rw-r--r--Test/dafny1/MoreInduction.dfy.expect16
-rw-r--r--Test/dafny1/PriorityQueue.dfy32
-rw-r--r--Test/dafny1/Rippling.dfy114
-rw-r--r--Test/dafny1/SchorrWaite-stages.dfy2
-rw-r--r--Test/dafny1/SchorrWaite.dfy8
-rw-r--r--Test/dafny1/Substitution.dfy2
-rw-r--r--Test/dafny1/UltraFilter.dfy2
-rw-r--r--Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy9
-rw-r--r--Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy.expect2
-rw-r--r--Test/dafny2/COST-verif-comp-2011-3-TwoDuplicates.dfy4
-rw-r--r--Test/dafny2/COST-verif-comp-2011-4-FloydCycleDetect.dfy4
-rw-r--r--Test/dafny2/Calculations.dfy24
-rw-r--r--Test/dafny2/MajorityVote.dfy4
-rw-r--r--Test/dafny2/SnapshotableTrees.dfy2
-rw-r--r--Test/dafny2/SnapshotableTrees.dfy.expect13
-rw-r--r--Test/dafny3/CachedContainer.dfy.expect1
-rw-r--r--Test/dafny3/CalcExample.dfy6
-rw-r--r--Test/dafny3/Dijkstra.dfy4
-rw-r--r--Test/dafny3/Filter.dfy240
-rw-r--r--Test/dafny3/Filter.dfy.expect2
-rw-r--r--Test/dafny3/GenericSort.dfy60
-rw-r--r--Test/dafny3/InductionVsCoinduction.dfy2
-rw-r--r--Test/dafny3/InfiniteTrees.dfy44
-rw-r--r--Test/dafny3/SimpleInduction.dfy14
-rw-r--r--Test/dafny4/ACL2-extractor.dfy8
-rw-r--r--Test/dafny4/Ackermann.dfy235
-rw-r--r--Test/dafny4/Ackermann.dfy.expect6
-rw-r--r--Test/dafny4/BinarySearch.dfy.expect2
-rw-r--r--Test/dafny4/Bug100.dfy23
-rw-r--r--Test/dafny4/Bug100.dfy.expect2
-rw-r--r--Test/dafny4/Bug101.dfy19
-rw-r--r--Test/dafny4/Bug101.dfy.expect8
-rw-r--r--Test/dafny4/Bug103.dfy20
-rw-r--r--Test/dafny4/Bug103.dfy.expect2
-rw-r--r--Test/dafny4/Bug104.dfy12
-rw-r--r--Test/dafny4/Bug104.dfy.expect3
-rw-r--r--Test/dafny4/Bug107.dfy16
-rw-r--r--Test/dafny4/Bug107.dfy.expect6
-rw-r--r--Test/dafny4/Bug108.dfy11
-rw-r--r--Test/dafny4/Bug108.dfy.expect7
-rw-r--r--Test/dafny4/Bug110.dfy31
-rw-r--r--Test/dafny4/Bug110.dfy.expect2
-rw-r--r--Test/dafny4/Bug111.dfy18
-rw-r--r--Test/dafny4/Bug111.dfy.expect2
-rw-r--r--Test/dafny4/Bug113.dfy10
-rw-r--r--Test/dafny4/Bug113.dfy.expect6
-rw-r--r--Test/dafny4/Bug114.dfy10
-rw-r--r--Test/dafny4/Bug114.dfy.expect7
-rw-r--r--Test/dafny4/Bug116.dfy15
-rw-r--r--Test/dafny4/Bug116.dfy.expect3
-rw-r--r--Test/dafny4/Bug117.dfy37
-rw-r--r--Test/dafny4/Bug117.dfy.expect3
-rw-r--r--Test/dafny4/Bug118.dfy12
-rw-r--r--Test/dafny4/Bug118.dfy.expect2
-rw-r--r--Test/dafny4/Bug120.dfy11
-rw-r--r--Test/dafny4/Bug120.dfy.expect2
-rw-r--r--Test/dafny4/Bug121.dfy18
-rw-r--r--Test/dafny4/Bug121.dfy.expect2
-rw-r--r--Test/dafny4/Bug122.dfy17
-rw-r--r--Test/dafny4/Bug122.dfy.expect2
-rw-r--r--Test/dafny4/Bug124.dfy14
-rw-r--r--Test/dafny4/Bug124.dfy.expect2
-rw-r--r--Test/dafny4/Bug125.dfy58
-rw-r--r--Test/dafny4/Bug125.dfy.expect2
-rw-r--r--Test/dafny4/Bug128.dfy13
-rw-r--r--Test/dafny4/Bug128.dfy.expect2
-rw-r--r--Test/dafny4/Bug129.dfy12
-rw-r--r--Test/dafny4/Bug129.dfy.expect2
-rw-r--r--Test/dafny4/Bug131.dfy11
-rw-r--r--Test/dafny4/Bug131.dfy.expect2
-rw-r--r--Test/dafny4/Bug132.dfy45
-rw-r--r--Test/dafny4/Bug132.dfy.expect18
-rw-r--r--Test/dafny4/Bug133.dfy18
-rw-r--r--Test/dafny4/Bug133.dfy.expect2
-rw-r--r--Test/dafny4/Bug134.dfy23
-rw-r--r--Test/dafny4/Bug134.dfy.expect4
-rw-r--r--Test/dafny4/Bug136.dfy12
-rw-r--r--Test/dafny4/Bug136.dfy.expect2
-rw-r--r--Test/dafny4/Bug138.dfy22
-rw-r--r--Test/dafny4/Bug138.dfy.expect2
-rw-r--r--Test/dafny4/Bug139.dfy25
-rw-r--r--Test/dafny4/Bug139.dfy.expect2
-rw-r--r--Test/dafny4/Bug140.dfy67
-rw-r--r--Test/dafny4/Bug140.dfy.expect6
-rw-r--r--Test/dafny4/Bug148.dfy25
-rw-r--r--Test/dafny4/Bug148.dfy.expect17
-rw-r--r--Test/dafny4/Bug49.dfy74
-rw-r--r--Test/dafny4/Bug49.dfy.expect8
-rw-r--r--Test/dafny4/Bug60.dfy4
-rw-r--r--Test/dafny4/Bug63.dfy4
-rw-r--r--Test/dafny4/Bug73.dfy.expect4
-rw-r--r--Test/dafny4/Bug75.dfy50
-rw-r--r--Test/dafny4/Bug75.dfy.expect2
-rw-r--r--Test/dafny4/Bug79.dfy10
-rw-r--r--Test/dafny4/Bug79.dfy.expect2
-rw-r--r--Test/dafny4/Bug81.dfy9
-rw-r--r--Test/dafny4/Bug81.dfy.expect2
-rw-r--r--Test/dafny4/Bug82.dfy11
-rw-r--r--Test/dafny4/Bug82.dfy.expect2
-rw-r--r--Test/dafny4/Bug88.dfy18
-rw-r--r--Test/dafny4/Bug88.dfy.expect11
-rw-r--r--Test/dafny4/Bug89.dfy15
-rw-r--r--Test/dafny4/Bug89.dfy.expect6
-rw-r--r--Test/dafny4/Bug91.dfy40
-rw-r--r--Test/dafny4/Bug91.dfy.expect2
-rw-r--r--Test/dafny4/Bug93.dfy37
-rw-r--r--Test/dafny4/Bug93.dfy.expect8
-rw-r--r--Test/dafny4/Bug94.dfy35
-rw-r--r--Test/dafny4/Bug94.dfy.expect6
-rw-r--r--Test/dafny4/Bug99.dfy11
-rw-r--r--Test/dafny4/Bug99.dfy.expect2
-rw-r--r--Test/dafny4/Circ.dfy2
-rw-r--r--Test/dafny4/CoqArt-InsertionSort.dfy1
-rw-r--r--Test/dafny4/FlyingRobots.dfy285
-rw-r--r--Test/dafny4/FlyingRobots.dfy.expect5
-rw-r--r--Test/dafny4/Fstar-QuickSort.dfy40
-rw-r--r--Test/dafny4/GHC-MergeSort.dfy9
-rw-r--r--Test/dafny4/KozenSilva.dfy80
-rw-r--r--Test/dafny4/KozenSilva.dfy.expect2
-rw-r--r--Test/dafny4/LargeConstants.dfy14
-rw-r--r--Test/dafny4/LargeConstants.dfy.expect2
-rw-r--r--Test/dafny4/Leq.dfy174
-rw-r--r--Test/dafny4/Leq.dfy.expect3
-rw-r--r--Test/dafny4/McCarthy91.dfy86
-rw-r--r--Test/dafny4/McCarthy91.dfy.expect25
-rw-r--r--Test/dafny4/MonadicLaws.dfy100
-rw-r--r--Test/dafny4/MonadicLaws.dfy.expect2
-rw-r--r--Test/dafny4/NipkowKlein-chapter3.dfy17
-rw-r--r--Test/dafny4/NipkowKlein-chapter3.dfy.expect2
-rw-r--r--Test/dafny4/NipkowKlein-chapter7.dfy164
-rw-r--r--Test/dafny4/NipkowKlein-chapter7.dfy.expect2
-rw-r--r--Test/dafny4/NumberRepresentations.dfy19
-rw-r--r--Test/dafny4/Primes.dfy17
-rw-r--r--Test/dafny4/Regression0.dfy13
-rw-r--r--Test/dafny4/Regression0.dfy.expect3
-rw-r--r--Test/dafny4/Regression1.dfy9
-rw-r--r--Test/dafny4/Regression1.dfy.expect2
-rw-r--r--Test/dafny4/SoftwareFoundations-Basics.dfy.expect2
-rw-r--r--Test/dafny4/UnionFind.dfy332
-rw-r--r--Test/dafny4/UnionFind.dfy.expect3
-rw-r--r--Test/dafny4/set-compr.dfy54
-rw-r--r--Test/dafny4/set-compr.dfy.expect17
-rw-r--r--Test/hofs/Apply.dfy.expect2
-rw-r--r--Test/hofs/Classes.dfy26
-rw-r--r--Test/hofs/Classes.dfy.expect14
-rw-r--r--Test/hofs/Examples.dfy14
-rw-r--r--Test/hofs/Field.dfy.expect8
-rw-r--r--Test/hofs/FnRef.dfy.expect8
-rw-r--r--Test/hofs/Fold.dfy2
-rw-r--r--Test/hofs/Frame.dfy.expect14
-rw-r--r--Test/hofs/Lambda.dfy.expect2
-rw-r--r--Test/hofs/LambdaParsefail.dfy.expect10
-rw-r--r--Test/hofs/LambdaParsefail2.dfy.expect2
-rw-r--r--Test/hofs/Monads.dfy34
-rw-r--r--Test/hofs/Naked.dfy10
-rw-r--r--Test/hofs/Naked.dfy.expect48
-rw-r--r--Test/hofs/OneShot.dfy9
-rw-r--r--Test/hofs/OneShot.dfy.expect6
-rw-r--r--Test/hofs/ReadsReads.dfy52
-rw-r--r--Test/hofs/ReadsReads.dfy.expect24
-rw-r--r--Test/hofs/Requires.dfy82
-rw-r--r--Test/hofs/Requires.dfy.expect5
-rw-r--r--Test/hofs/ResolveError.dfy34
-rw-r--r--Test/hofs/ResolveError.dfy.expect6
-rw-r--r--Test/hofs/Simple.dfy20
-rw-r--r--Test/hofs/Simple.dfy.expect25
-rw-r--r--Test/hofs/TreeMapSimple.dfy24
-rw-r--r--Test/hofs/Twice.dfy4
-rw-r--r--Test/hofs/Twice.dfy.expect8
-rw-r--r--Test/hofs/VectorUpdate.dfy67
-rw-r--r--Test/hofs/VectorUpdate.dfy.expect2
-rw-r--r--Test/hofs/WhileLoop.dfy10
-rw-r--r--Test/irondafny0/FIFO.dfy43
-rw-r--r--Test/irondafny0/FIFO.dfy.expect8
-rw-r--r--Test/irondafny0/LIFO.dfy43
-rw-r--r--Test/irondafny0/LIFO.dfy.expect8
-rw-r--r--Test/irondafny0/Queue.dfyi22
-rw-r--r--Test/irondafny0/inheritreqs0.dfy22
-rw-r--r--Test/irondafny0/inheritreqs0.dfy.expect6
-rw-r--r--Test/irondafny0/inheritreqs1.dfy22
-rw-r--r--Test/irondafny0/inheritreqs1.dfy.expect6
-rw-r--r--Test/irondafny0/opened_workaround.dfy21
-rw-r--r--Test/irondafny0/opened_workaround.dfy.expect3
-rw-r--r--Test/irondafny0/optimize0.dfy6
-rw-r--r--Test/irondafny0/optimize0.dfy.expect6
-rw-r--r--Test/irondafny0/xrefine0.dfy6
-rw-r--r--Test/irondafny0/xrefine0.dfy.expect2
-rw-r--r--Test/irondafny0/xrefine1.dfy77
-rw-r--r--Test/irondafny0/xrefine1.dfy.expect6
-rw-r--r--Test/irondafny0/xrefine2.dfy77
-rw-r--r--Test/irondafny0/xrefine2.dfy.expect9
-rw-r--r--Test/irondafny0/xrefine3.dfy72
-rw-r--r--Test/irondafny0/xrefine3.dfy.expect6
-rw-r--r--Test/lit.site.cfg12
-rw-r--r--Test/pydiff.py3
-rw-r--r--Test/runTests.bat2
-rw-r--r--Test/runTests.py567
-rw-r--r--Test/server/minimal.transcript8
-rw-r--r--Test/server/minimal.transcript.expect14
-rw-r--r--Test/server/simple-session.transcript637
-rw-r--r--Test/server/simple-session.transcript.expect992
-rw-r--r--Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy23
-rw-r--r--Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy.expect4
-rw-r--r--Test/triggers/constructors-cause-matching-loops.dfy11
-rw-r--r--Test/triggers/constructors-cause-matching-loops.dfy.expect6
-rw-r--r--Test/triggers/function-applications-are-triggers.dfy15
-rw-r--r--Test/triggers/function-applications-are-triggers.dfy.expect13
-rw-r--r--Test/triggers/large-quantifiers-dont-break-dafny.dfy61
-rw-r--r--Test/triggers/large-quantifiers-dont-break-dafny.dfy.expect4
-rw-r--r--Test/triggers/loop-detection-is-not-too-strict.dfy40
-rw-r--r--Test/triggers/loop-detection-is-not-too-strict.dfy.expect17
-rw-r--r--Test/triggers/loop-detection-looks-at-ranges-too.dfy14
-rw-r--r--Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect6
-rw-r--r--Test/triggers/loop-detection-messages--unit-tests.dfy29
-rw-r--r--Test/triggers/loop-detection-messages--unit-tests.dfy.expect37
-rw-r--r--Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy32
-rw-r--r--Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect10
-rw-r--r--Test/triggers/matrix-accesses-are-triggers.dfy9
-rw-r--r--Test/triggers/matrix-accesses-are-triggers.dfy.expect12
-rw-r--r--Test/triggers/nested-quantifiers-all-get-triggers.dfy9
-rw-r--r--Test/triggers/nested-quantifiers-all-get-triggers.dfy.expect4
-rw-r--r--Test/triggers/old-is-a-special-case-for-triggers.dfy32
-rw-r--r--Test/triggers/old-is-a-special-case-for-triggers.dfy.expect22
-rw-r--r--Test/triggers/redundancy-detection-is-bidirectional.dfy29
-rw-r--r--Test/triggers/redundancy-detection-is-bidirectional.dfy.expect12
-rw-r--r--Test/triggers/regression-tests.dfy20
-rw-r--r--Test/triggers/regression-tests.dfy.expect3
-rw-r--r--Test/triggers/set-construction-is-a-good-trigger.dfy12
-rw-r--r--Test/triggers/set-construction-is-a-good-trigger.dfy.expect5
-rw-r--r--Test/triggers/some-proofs-only-work-without-autoTriggers.dfy48
-rw-r--r--Test/triggers/some-proofs-only-work-without-autoTriggers.dfy.expect31
-rw-r--r--Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy16
-rw-r--r--Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect10
-rw-r--r--Test/triggers/splitting-picks-the-right-tokens.dfy24
-rw-r--r--Test/triggers/splitting-picks-the-right-tokens.dfy.expect38
-rw-r--r--Test/triggers/splitting-triggers-recovers-expressivity.dfy61
-rw-r--r--Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect39
-rw-r--r--Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy21
-rw-r--r--Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy.expect32
-rw-r--r--Test/triggers/suppressing-warnings-behaves-properly.dfy21
-rw-r--r--Test/triggers/suppressing-warnings-behaves-properly.dfy.expect14
-rw-r--r--Test/triggers/triggers-prevent-some-inlining.dfy26
-rw-r--r--Test/triggers/triggers-prevent-some-inlining.dfy.expect9
-rw-r--r--Test/triggers/useless-triggers-are-removed.dfy25
-rw-r--r--Test/triggers/useless-triggers-are-removed.dfy.expect17
-rw-r--r--Test/triggers/wf-checks-use-the-original-quantifier.dfy28
-rw-r--r--Test/triggers/wf-checks-use-the-original-quantifier.dfy.expect17
-rw-r--r--Test/tutorial/maximum.dfy32
-rw-r--r--Test/tutorial/maximum.dfy.expect7
-rw-r--r--Test/vacid0/Composite.dfy2
-rw-r--r--Test/vstte2012/BreadthFirstSearch.dfy2
-rw-r--r--Test/vstte2012/Combinators.dfy12
-rw-r--r--Test/vstte2012/Tree.dfy71
-rw-r--r--Test/wishlist/calc.dfy17
-rw-r--r--Test/wishlist/calc.dfy.expect11
-rw-r--r--Test/wishlist/exists-b-exists-not-b.dfy10
-rw-r--r--Test/wishlist/exists-b-exists-not-b.dfy.expect8
-rw-r--r--Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy9
-rw-r--r--Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy.expect3
-rw-r--r--Test/wishlist/naked-function-in-recursive-setting.dfy13
-rw-r--r--Test/wishlist/naked-function-in-recursive-setting.dfy.expect8
-rw-r--r--Test/wishlist/sequences-literals.dfy58
-rw-r--r--Test/wishlist/sequences-literals.dfy.expect20
-rw-r--r--Test/wishlist/sequences-s0-in-s.dfy25
-rw-r--r--Test/wishlist/sequences-s0-in-s.dfy.expect6
-rw-r--r--Test/wishlist/strings.dfy6
-rw-r--r--Test/wishlist/strings.dfy.expect5
-rw-r--r--Test/wishlist/we-should-always-print-tooltips.dfy4
-rw-r--r--Test/wishlist/we-should-always-print-tooltips.dfy.expect2
-rw-r--r--Util/Emacs/README2
-rw-r--r--Util/Emacs/dafny-mode.el121
-rw-r--r--Util/Emacs/jennisys-mode.el113
-rw-r--r--Util/latex/dafny.sty2
-rw-r--r--Util/vim/dafny.vim2
-rwxr-xr-xpackage.py219
566 files changed, 53543 insertions, 12411 deletions
diff --git a/.hgignore b/.hgignore
index ecf4ed7b..a52bee3a 100644
--- a/.hgignore
+++ b/.hgignore
@@ -1,11 +1,19 @@
syntax: regexp
^Source/.*\.(user|suo|cache|vs10x)$
-^Binaries/.*\.(dll|pdb|exe|manifest|config|smt2|vsix|vsixmanifest|bpl|pkgdef)$
+^Binaries/.*\.(dll|pdb|exe|manifest|config|smt2|vsix|vsixmanifest|bpl|pkgdef|xml)$
^Source/.*\.(smt2|bpl)$
^.*(bin|obj)/([^/]*/)?(Debug|Release|Checked|Debug All|DEBUG ALL)/.*$
Test/.*/Output
Test/desktop/.*
Test/([^/]*)/([^/]*)\.sx
+^Test/sandbox/.*
+^Test/.*\.csv
+Package/.*
+Test/.*/flycheck_.*
+.*\.orig
+Test/.*\.bpl
+Test/.*/axiom-profiler.html
+Test/.*/z3.log
syntax: glob
*.exe
*.pdb
@@ -13,3 +21,6 @@ syntax: glob
*.tmp
*.tmp.dfy
Source/DafnyExtension/DafnyRuntime.cs
+Source/DafnyExtension/Z3-LICENSE.txt
+Test/failing.lst
+packages/*
diff --git a/Binaries/DafnyPrelude.bpl b/Binaries/DafnyPrelude.bpl
index 74bdb63e..2ca10f73 100644
--- a/Binaries/DafnyPrelude.bpl
+++ b/Binaries/DafnyPrelude.bpl
@@ -20,6 +20,7 @@ const unique TInt : Ty;
const unique TNat : Ty;
const unique TReal : Ty;
function TSet(Ty) : Ty;
+function TISet(Ty) : Ty;
function TMultiSet(Ty) : Ty;
function TSeq(Ty) : Ty;
function TMap(Ty, Ty) : Ty;
@@ -27,6 +28,8 @@ function TIMap(Ty, Ty) : Ty;
function Inv0_TSet(Ty) : Ty;
axiom (forall t: Ty :: { TSet(t) } Inv0_TSet(TSet(t)) == t);
+function Inv0_TISet(Ty) : Ty;
+axiom (forall t: Ty :: { TISet(t) } Inv0_TISet(TISet(t)) == t);
function Inv0_TSeq(Ty) : Ty;
axiom (forall t: Ty :: { TSeq(t) } Inv0_TSeq(TSeq(t)) == t);
function Inv0_TMultiSet(Ty) : Ty;
@@ -52,6 +55,7 @@ const unique TagInt : TyTag;
const unique TagNat : TyTag;
const unique TagReal : TyTag;
const unique TagSet : TyTag;
+const unique TagISet : TyTag;
const unique TagMultiSet : TyTag;
const unique TagSeq : TyTag;
const unique TagMap : TyTag;
@@ -64,6 +68,7 @@ axiom Tag(TInt) == TagInt;
axiom Tag(TNat) == TagNat;
axiom Tag(TReal) == TagReal;
axiom (forall t: Ty :: { TSet(t) } Tag(TSet(t)) == TagSet);
+axiom (forall t: Ty :: { TISet(t) } Tag(TISet(t)) == TagISet);
axiom (forall t: Ty :: { TMultiSet(t) } Tag(TMultiSet(t)) == TagMultiSet);
axiom (forall t: Ty :: { TSeq(t) } Tag(TSeq(t)) == TagSeq);
axiom (forall t, u: Ty :: { TMap(t,u) } Tag(TMap(t,u)) == TagMap);
@@ -138,6 +143,9 @@ axiom (forall bx : Box, t : Ty ::
{ $IsBox(bx, TSet(t)) }
( $IsBox(bx, TSet(t)) ==> $Box($Unbox(bx) : Set Box) == bx && $Is($Unbox(bx) : Set Box, TSet(t))));
axiom (forall bx : Box, t : Ty ::
+ { $IsBox(bx, TISet(t)) }
+ ( $IsBox(bx, TISet(t)) ==> $Box($Unbox(bx) : ISet Box) == bx && $Is($Unbox(bx) : ISet Box, TISet(t))));
+axiom (forall bx : Box, t : Ty ::
{ $IsBox(bx, TMultiSet(t)) }
( $IsBox(bx, TMultiSet(t)) ==> $Box($Unbox(bx) : MultiSet Box) == bx && $Is($Unbox(bx) : MultiSet Box, TMultiSet(t))));
axiom (forall bx : Box, t : Ty ::
@@ -187,6 +195,10 @@ axiom (forall v: Set Box, t0: Ty :: { $Is(v, TSet(t0)) }
$Is(v, TSet(t0)) <==>
(forall bx: Box :: { v[bx] }
v[bx] ==> $IsBox(bx, t0)));
+axiom (forall v: ISet Box, t0: Ty :: { $Is(v, TISet(t0)) }
+ $Is(v, TISet(t0)) <==>
+ (forall bx: Box :: { v[bx] }
+ v[bx] ==> $IsBox(bx, t0)));
axiom (forall v: MultiSet Box, t0: Ty :: { $Is(v, TMultiSet(t0)) }
$Is(v, TMultiSet(t0)) <==>
(forall bx: Box :: { v[bx] }
@@ -202,6 +214,10 @@ axiom (forall v: Set Box, t0: Ty, h: Heap :: { $IsAlloc(v, TSet(t0), h) }
$IsAlloc(v, TSet(t0), h) <==>
(forall bx: Box :: { v[bx] }
v[bx] ==> $IsAllocBox(bx, t0, h)));
+axiom (forall v: ISet Box, t0: Ty, h: Heap :: { $IsAlloc(v, TISet(t0), h) }
+ $IsAlloc(v, TISet(t0), h) <==>
+ (forall bx: Box :: { v[bx] }
+ v[bx] ==> $IsAllocBox(bx, t0, h)));
axiom (forall v: MultiSet Box, t0: Ty, h: Heap :: { $IsAlloc(v, TMultiSet(t0), h) }
$IsAlloc(v, TMultiSet(t0), h) <==>
(forall bx: Box :: { v[bx] }
@@ -257,6 +273,8 @@ const unique class._System.set: ClassName;
const unique class._System.seq: ClassName;
const unique class._System.multiset: ClassName;
+function Tclass._System.object(): Ty;
+
function /*{:never_pattern true}*/ dtype(ref): Ty; // changed from ClassName to Ty
function TypeTuple(a: ClassName, b: ClassName): ClassName;
@@ -271,6 +289,12 @@ axiom (forall a: ClassName, b: ClassName :: { TypeTuple(a,b) }
type HandleType;
+function SetRef_to_SetBox(s: [ref]bool): Set Box;
+axiom (forall s: [ref]bool, bx: Box :: { SetRef_to_SetBox(s)[bx] }
+ SetRef_to_SetBox(s)[bx] == s[$Unbox(bx): ref]);
+axiom (forall s: [ref]bool :: { SetRef_to_SetBox(s) }
+ $Is(SetRef_to_SetBox(s), TSet(Tclass._System.object())));
+
// ---------------------------------------------------------------
// -- Datatypes --------------------------------------------------
// ---------------------------------------------------------------
@@ -382,7 +406,8 @@ function {:inline true} read<alpha>(H:Heap, r:ref, f:Field alpha): alpha { H[r,
function {:inline true} update<alpha>(H:Heap, r:ref, f:Field alpha, v:alpha): Heap { H[r,f := v] }
function $IsGoodHeap(Heap): bool;
-var $Heap: Heap where $IsGoodHeap($Heap);
+function $IsHeapAnchor(Heap): bool;
+var $Heap: Heap where $IsGoodHeap($Heap) && $IsHeapAnchor($Heap);
function $HeapSucc(Heap, Heap): bool;
axiom (forall<alpha> h: Heap, r: ref, f: Field alpha, x: alpha :: { update(h, r, f, x) }
@@ -545,6 +570,81 @@ axiom (forall<T> a: Set T, b: Set T :: { Set#Disjoint(a,b) }
Set#Disjoint(a,b) <==> (forall o: T :: {a[o]} {b[o]} !a[o] || !b[o]));
// ---------------------------------------------------------------
+// -- Axiomatization of isets -------------------------------------
+// ---------------------------------------------------------------
+
+type ISet T = [T]bool;
+
+function ISet#Empty<T>(): Set T;
+axiom (forall<T> o: T :: { ISet#Empty()[o] } !ISet#Empty()[o]);
+
+// the empty set could be of anything
+//axiom (forall<T> t: Ty :: { $Is(ISet#Empty() : [T]bool, TISet(t)) } $Is(ISet#Empty() : [T]bool, TISet(t)));
+
+
+function ISet#UnionOne<T>(ISet T, T): ISet T;
+axiom (forall<T> a: ISet T, x: T, o: T :: { ISet#UnionOne(a,x)[o] }
+ ISet#UnionOne(a,x)[o] <==> o == x || a[o]);
+axiom (forall<T> a: ISet T, x: T :: { ISet#UnionOne(a, x) }
+ ISet#UnionOne(a, x)[x]);
+axiom (forall<T> a: ISet T, x: T, y: T :: { ISet#UnionOne(a, x), a[y] }
+ a[y] ==> ISet#UnionOne(a, x)[y]);
+
+function ISet#Union<T>(ISet T, ISet T): ISet T;
+axiom (forall<T> a: ISet T, b: ISet T, o: T :: { ISet#Union(a,b)[o] }
+ ISet#Union(a,b)[o] <==> a[o] || b[o]);
+axiom (forall<T> a, b: ISet T, y: T :: { ISet#Union(a, b), a[y] }
+ a[y] ==> ISet#Union(a, b)[y]);
+axiom (forall<T> a, b: Set T, y: T :: { ISet#Union(a, b), b[y] }
+ b[y] ==> ISet#Union(a, b)[y]);
+axiom (forall<T> a, b: ISet T :: { ISet#Union(a, b) }
+ ISet#Disjoint(a, b) ==>
+ ISet#Difference(ISet#Union(a, b), a) == b &&
+ ISet#Difference(ISet#Union(a, b), b) == a);
+// Follows from the general union axiom, but might be still worth including, because disjoint union is a common case:
+// axiom (forall<T> a, b: ISet T :: { ISet#Card(ISet#Union(a, b)) }
+// ISet#Disjoint(a, b) ==>
+// ISet#Card(ISet#Union(a, b)) == ISet#Card(a) + ISet#Card(b));
+
+function ISet#Intersection<T>(ISet T, ISet T): ISet T;
+axiom (forall<T> a: ISet T, b: ISet T, o: T :: { ISet#Intersection(a,b)[o] }
+ ISet#Intersection(a,b)[o] <==> a[o] && b[o]);
+
+axiom (forall<T> a, b: ISet T :: { ISet#Union(ISet#Union(a, b), b) }
+ ISet#Union(ISet#Union(a, b), b) == ISet#Union(a, b));
+axiom (forall<T> a, b: Set T :: { ISet#Union(a, ISet#Union(a, b)) }
+ ISet#Union(a, ISet#Union(a, b)) == ISet#Union(a, b));
+axiom (forall<T> a, b: ISet T :: { ISet#Intersection(ISet#Intersection(a, b), b) }
+ ISet#Intersection(ISet#Intersection(a, b), b) == ISet#Intersection(a, b));
+axiom (forall<T> a, b: ISet T :: { ISet#Intersection(a, ISet#Intersection(a, b)) }
+ ISet#Intersection(a, ISet#Intersection(a, b)) == ISet#Intersection(a, b));
+
+
+function ISet#Difference<T>(ISet T, ISet T): ISet T;
+axiom (forall<T> a: ISet T, b: ISet T, o: T :: { ISet#Difference(a,b)[o] }
+ ISet#Difference(a,b)[o] <==> a[o] && !b[o]);
+axiom (forall<T> a, b: ISet T, y: T :: { ISet#Difference(a, b), b[y] }
+ b[y] ==> !ISet#Difference(a, b)[y] );
+
+function ISet#Subset<T>(ISet T, ISet T): bool;
+axiom(forall<T> a: ISet T, b: ISet T :: { ISet#Subset(a,b) }
+ ISet#Subset(a,b) <==> (forall o: T :: {a[o]} {b[o]} a[o] ==> b[o]));
+// axiom(forall<T> a: ISet T, b: ISet T ::
+// { ISet#Subset(a,b), ISet#Card(a), ISet#Card(b) } // very restrictive trigger
+// ISet#Subset(a,b) ==> ISet#Card(a) <= ISet#Card(b));
+
+
+function ISet#Equal<T>(ISet T, ISet T): bool;
+axiom(forall<T> a: ISet T, b: ISet T :: { ISet#Equal(a,b) }
+ ISet#Equal(a,b) <==> (forall o: T :: {a[o]} {b[o]} a[o] <==> b[o]));
+axiom(forall<T> a: ISet T, b: ISet T :: { ISet#Equal(a,b) } // extensionality axiom for sets
+ ISet#Equal(a,b) ==> a == b);
+
+function ISet#Disjoint<T>(ISet T, ISet T): bool;
+axiom (forall<T> a: ISet T, b: ISet T :: { ISet#Disjoint(a,b) }
+ ISet#Disjoint(a,b) <==> (forall o: T :: {a[o]} {b[o]} !a[o] || !b[o]));
+
+// ---------------------------------------------------------------
// -- Axiomatization of multisets --------------------------------
// ---------------------------------------------------------------
@@ -745,7 +845,7 @@ function Seq#Contains<T>(Seq T, T): bool;
axiom (forall<T> s: Seq T, x: T :: { Seq#Contains(s,x) }
Seq#Contains(s,x) <==>
(exists i: int :: { Seq#Index(s,i) } 0 <= i && i < Seq#Length(s) && Seq#Index(s,i) == x));
-axiom (forall x: ref ::
+axiom (forall<T> x: T ::
{ Seq#Contains(Seq#Empty(), x) }
!Seq#Contains(Seq#Empty(), x));
diff --git a/Binaries/DafnyRuntime.cs b/Binaries/DafnyRuntime.cs
index fd680a0b..587d5159 100644
--- a/Binaries/DafnyRuntime.cs
+++ b/Binaries/DafnyRuntime.cs
@@ -1,914 +1,1324 @@
-using System; // for Func
-using System.Numerics;
-
-namespace Dafny
-{
- using System.Collections.Generic;
-
- public class Set<T>
- {
- Dictionary<T, bool> dict;
- Set(Dictionary<T, bool> d) {
- dict = d;
- }
- public static Set<T> Empty {
- get {
- return new Set<T>(new Dictionary<T, bool>(0));
- }
- }
- public static Set<T> FromElements(params T[] values) {
- Dictionary<T, bool> d = new Dictionary<T, bool>(values.Length);
- foreach (T t in values)
- d[t] = true;
- return new Set<T>(d);
- }
- public static Set<T> FromCollection(ICollection<T> values) {
- Dictionary<T, bool> d = new Dictionary<T, bool>();
- foreach (T t in values)
- d[t] = true;
- return new Set<T>(d);
- }
- public int Length {
- get { return dict.Count; }
- }
- public long LongLength {
- get { return dict.Count; }
- }
- public IEnumerable<T> Elements {
- get {
- return dict.Keys;
- }
- }
- /// <summary>
- /// This is an inefficient iterator for producing all subsets of "this". Each set returned is the same
- /// Set<T> object (but this Set<T> object is fresh; in particular, it is not "this").
- /// </summary>
- public IEnumerable<Set<T>> AllSubsets {
- get {
- // Start by putting all set elements into a list
- var elmts = new List<T>();
- elmts.AddRange(dict.Keys);
- var n = elmts.Count;
- var which = new bool[n];
- var s = new Set<T>(new Dictionary<T, bool>(0));
- while (true) {
- yield return s;
- // "add 1" to "which", as if doing a carry chain. For every digit changed, change the membership of the corresponding element in "s".
- int i = 0;
- for (; i < n && which[i]; i++) {
- which[i] = false;
- s.dict.Remove(elmts[i]);
- }
- if (i == n) {
- // we have cycled through all the subsets
- break;
- }
- which[i] = true;
- s.dict.Add(elmts[i], true);
- }
- }
- }
- public bool Equals(Set<T> other) {
- return dict.Count == other.dict.Count && IsSubsetOf(other);
- }
- public override bool Equals(object other) {
- return other is Set<T> && Equals((Set<T>)other);
- }
- public override int GetHashCode() {
- var hashCode = 1;
- foreach (var t in dict.Keys) {
- hashCode = hashCode * (t.GetHashCode()+3);
- }
- return hashCode;
- }
- public override string ToString() {
- var s = "{";
- var sep = "";
- foreach (var t in dict.Keys) {
- s += sep + t.ToString();
- sep = ", ";
- }
- return s + "}";
- }
- public bool IsProperSubsetOf(Set<T> other) {
- return dict.Count < other.dict.Count && IsSubsetOf(other);
- }
- public bool IsSubsetOf(Set<T> other) {
- if (other.dict.Count < dict.Count)
- return false;
- foreach (T t in dict.Keys) {
- if (!other.dict.ContainsKey(t))
- return false;
- }
- return true;
- }
- public bool IsSupersetOf(Set<T> other) {
- return other.IsSubsetOf(this);
- }
- public bool IsProperSupersetOf(Set<T> other) {
- return other.IsProperSubsetOf(this);
- }
- public bool IsDisjointFrom(Set<T> other) {
- Dictionary<T, bool> a, b;
- if (dict.Count < other.dict.Count) {
- a = dict; b = other.dict;
- } else {
- a = other.dict; b = dict;
- }
- foreach (T t in a.Keys) {
- if (b.ContainsKey(t))
- return false;
- }
- return true;
- }
- public bool Contains(T t) {
- return dict.ContainsKey(t);
- }
- public Set<T> Union(Set<T> other) {
- if (dict.Count == 0)
- return other;
- else if (other.dict.Count == 0)
- return this;
- Dictionary<T, bool> a, b;
- if (dict.Count < other.dict.Count) {
- a = dict; b = other.dict;
- } else {
- a = other.dict; b = dict;
- }
- Dictionary<T, bool> r = new Dictionary<T, bool>();
- foreach (T t in b.Keys)
- r[t] = true;
- foreach (T t in a.Keys)
- r[t] = true;
- return new Set<T>(r);
- }
- public Set<T> Intersect(Set<T> other) {
- if (dict.Count == 0)
- return this;
- else if (other.dict.Count == 0)
- return other;
- Dictionary<T, bool> a, b;
- if (dict.Count < other.dict.Count) {
- a = dict; b = other.dict;
- } else {
- a = other.dict; b = dict;
- }
- var r = new Dictionary<T, bool>();
- foreach (T t in a.Keys) {
- if (b.ContainsKey(t))
- r.Add(t, true);
- }
- return new Set<T>(r);
- }
- public Set<T> Difference(Set<T> other) {
- if (dict.Count == 0)
- return this;
- else if (other.dict.Count == 0)
- return this;
- var r = new Dictionary<T, bool>();
- foreach (T t in dict.Keys) {
- if (!other.dict.ContainsKey(t))
- r.Add(t, true);
- }
- return new Set<T>(r);
- }
- }
- public class MultiSet<T>
- {
- Dictionary<T, int> dict;
- MultiSet(Dictionary<T, int> d) {
- dict = d;
- }
- public static MultiSet<T> Empty {
- get {
- return new MultiSet<T>(new Dictionary<T, int>(0));
- }
- }
- public static MultiSet<T> FromElements(params T[] values) {
- Dictionary<T, int> d = new Dictionary<T, int>(values.Length);
- foreach (T t in values) {
- var i = 0;
- if (!d.TryGetValue(t, out i)) {
- i = 0;
- }
- d[t] = i + 1;
- }
- return new MultiSet<T>(d);
- }
- public static MultiSet<T> FromCollection(ICollection<T> values) {
- Dictionary<T, int> d = new Dictionary<T, int>();
- foreach (T t in values) {
- var i = 0;
- if (!d.TryGetValue(t, out i)) {
- i = 0;
- }
- d[t] = i + 1;
- }
- return new MultiSet<T>(d);
- }
- public static MultiSet<T> FromSeq(Sequence<T> values) {
- Dictionary<T, int> d = new Dictionary<T, int>();
- foreach (T t in values.Elements) {
- var i = 0;
- if (!d.TryGetValue(t, out i)) {
- i = 0;
- }
- d[t] = i + 1;
- }
- return new MultiSet<T>(d);
- }
- public static MultiSet<T> FromSet(Set<T> values) {
- Dictionary<T, int> d = new Dictionary<T, int>();
- foreach (T t in values.Elements) {
- d[t] = 1;
- }
- return new MultiSet<T>(d);
- }
-
- public bool Equals(MultiSet<T> other) {
- return other.IsSubsetOf(this) && this.IsSubsetOf(other);
- }
- public override bool Equals(object other) {
- return other is MultiSet<T> && Equals((MultiSet<T>)other);
- }
- public override int GetHashCode() {
- var hashCode = 1;
- foreach (var kv in dict) {
- var key = kv.Key.GetHashCode();
- key = (key << 3) | (key >> 29) ^ kv.Value.GetHashCode();
- hashCode = hashCode * (key + 3);
- }
- return hashCode;
- }
- public override string ToString() {
- var s = "multiset{";
- var sep = "";
- foreach (var kv in dict) {
- var t = kv.Key.ToString();
- for (int i = 0; i < kv.Value; i++) {
- s += sep + t.ToString();
- sep = ", ";
- }
- }
- return s + "}";
- }
- public bool IsProperSubsetOf(MultiSet<T> other) {
- return !Equals(other) && IsSubsetOf(other);
- }
- public bool IsSubsetOf(MultiSet<T> other) {
- foreach (T t in dict.Keys) {
- if (!other.dict.ContainsKey(t) || other.dict[t] < dict[t])
- return false;
- }
- return true;
- }
- public bool IsSupersetOf(MultiSet<T> other) {
- return other.IsSubsetOf(this);
- }
- public bool IsProperSupersetOf(MultiSet<T> other) {
- return other.IsProperSubsetOf(this);
- }
- public bool IsDisjointFrom(MultiSet<T> other) {
- foreach (T t in dict.Keys) {
- if (other.dict.ContainsKey(t))
- return false;
- }
- foreach (T t in other.dict.Keys) {
- if (dict.ContainsKey(t))
- return false;
- }
- return true;
- }
- public bool Contains(T t) {
- return dict.ContainsKey(t);
- }
- public MultiSet<T> Union(MultiSet<T> other) {
- if (dict.Count == 0)
- return other;
- else if (other.dict.Count == 0)
- return this;
- var r = new Dictionary<T, int>();
- foreach (T t in dict.Keys) {
- var i = 0;
- if (!r.TryGetValue(t, out i)) {
- i = 0;
- }
- r[t] = i + dict[t];
- }
- foreach (T t in other.dict.Keys) {
- var i = 0;
- if (!r.TryGetValue(t, out i)) {
- i = 0;
- }
- r[t] = i + other.dict[t];
- }
- return new MultiSet<T>(r);
- }
- public MultiSet<T> Intersect(MultiSet<T> other) {
- if (dict.Count == 0)
- return this;
- else if (other.dict.Count == 0)
- return other;
- var r = new Dictionary<T, int>();
- foreach (T t in dict.Keys) {
- if (other.dict.ContainsKey(t)) {
- r.Add(t, other.dict[t] < dict[t] ? other.dict[t] : dict[t]);
- }
- }
- return new MultiSet<T>(r);
- }
- public MultiSet<T> Difference(MultiSet<T> other) { // \result == this - other
- if (dict.Count == 0)
- return this;
- else if (other.dict.Count == 0)
- return this;
- var r = new Dictionary<T, int>();
- foreach (T t in dict.Keys) {
- if (!other.dict.ContainsKey(t)) {
- r.Add(t, dict[t]);
- } else if (other.dict[t] < dict[t]) {
- r.Add(t, dict[t] - other.dict[t]);
- }
- }
- return new MultiSet<T>(r);
- }
- public IEnumerable<T> Elements {
- get {
- List<T> l = new List<T>();
- foreach (T t in dict.Keys) {
- int n;
- dict.TryGetValue(t, out n);
- for (int i = 0; i < n; i ++) {
- l.Add(t);
- }
- }
- return l;
- }
- }
- }
-
- public class Map<U, V>
- {
- Dictionary<U, V> dict;
- Map(Dictionary<U, V> d) {
- dict = d;
- }
- public static Map<U, V> Empty {
- get {
- return new Map<U, V>(new Dictionary<U,V>());
- }
- }
- public static Map<U, V> FromElements(params Pair<U, V>[] values) {
- Dictionary<U, V> d = new Dictionary<U, V>(values.Length);
- foreach (Pair<U, V> p in values) {
- d[p.Car] = p.Cdr;
- }
- return new Map<U, V>(d);
- }
- public static Map<U, V> FromCollection(List<Pair<U, V>> values) {
- Dictionary<U, V> d = new Dictionary<U, V>(values.Count);
- foreach (Pair<U, V> p in values) {
- d[p.Car] = p.Cdr;
- }
- return new Map<U, V>(d);
- }
- public int Length {
- get { return dict.Count; }
- }
- public long LongLength {
- get { return dict.Count; }
- }
- public bool Equals(Map<U, V> other) {
- foreach (U u in dict.Keys) {
- V v1, v2;
- if (!dict.TryGetValue(u, out v1)) {
- return false; // this shouldn't happen
- }
- if (!other.dict.TryGetValue(u, out v2)) {
- return false; // other dictionary does not contain this element
- }
- if (!v1.Equals(v2)) {
- return false;
- }
- }
- foreach (U u in other.dict.Keys) {
- if (!dict.ContainsKey(u)) {
- return false; // this shouldn't happen
- }
- }
- return true;
- }
- public override bool Equals(object other) {
- return other is Map<U, V> && Equals((Map<U, V>)other);
- }
- public override int GetHashCode() {
- var hashCode = 1;
- foreach (var kv in dict) {
- var key = kv.Key.GetHashCode();
- key = (key << 3) | (key >> 29) ^ kv.Value.GetHashCode();
- hashCode = hashCode * (key + 3);
- }
- return hashCode;
- }
- public override string ToString() {
- var s = "map[";
- var sep = "";
- foreach (var kv in dict) {
- s += sep + kv.Key.ToString() + " := " + kv.Value.ToString();
- sep = ", ";
- }
- return s + "]";
- }
- public bool IsDisjointFrom(Map<U, V> other) {
- foreach (U u in dict.Keys) {
- if (other.dict.ContainsKey(u))
- return false;
- }
- foreach (U u in other.dict.Keys) {
- if (dict.ContainsKey(u))
- return false;
- }
- return true;
- }
- public bool Contains(U u) {
- return dict.ContainsKey(u);
- }
- public V Select(U index) {
- return dict[index];
- }
- public Map<U, V> Update(U index, V val) {
- Dictionary<U, V> d = new Dictionary<U, V>(dict);
- d[index] = val;
- return new Map<U, V>(d);
- }
- public IEnumerable<U> Domain {
- get {
- return dict.Keys;
- }
- }
- }
- public class Sequence<T>
- {
- T[] elmts;
- public Sequence(T[] ee) {
- elmts = ee;
- }
- public static Sequence<T> Empty {
- get {
- return new Sequence<T>(new T[0]);
- }
- }
- public static Sequence<T> FromElements(params T[] values) {
- return new Sequence<T>(values);
- }
- public static Sequence<char> FromString(string s) {
- return new Sequence<char>(s.ToCharArray());
- }
- public int Length {
- get { return elmts.Length; }
- }
- public long LongLength {
- get { return elmts.LongLength; }
- }
- public T[] Elements {
- get {
- return elmts;
- }
- }
- public IEnumerable<T> UniqueElements {
- get {
- var st = Set<T>.FromElements(elmts);
- return st.Elements;
- }
- }
- public T Select(ulong index) {
- return elmts[index];
- }
- public T Select(long index) {
- return elmts[index];
- }
- public T Select(uint index) {
- return elmts[index];
- }
- public T Select(int index) {
- return elmts[index];
- }
- public T Select(BigInteger index) {
- return elmts[(int)index];
- }
- public Sequence<T> Update(long index, T t) {
- T[] a = (T[])elmts.Clone();
- a[index] = t;
- return new Sequence<T>(a);
- }
- public Sequence<T> Update(ulong index, T t) {
- return Update((long)index, t);
- }
- public Sequence<T> Update(BigInteger index, T t) {
- return Update((long)index, t);
- }
- public bool Equals(Sequence<T> other) {
- int n = elmts.Length;
- return n == other.elmts.Length && EqualUntil(other, n);
- }
- public override bool Equals(object other) {
- return other is Sequence<T> && Equals((Sequence<T>)other);
- }
- public override int GetHashCode() {
- if (elmts == null || elmts.Length == 0)
- return 0;
- var hashCode = 0;
- for (var i = 0; i < elmts.Length; i++) {
- hashCode = (hashCode << 3) | (hashCode >> 29) ^ elmts[i].GetHashCode();
- }
- return hashCode;
- }
- public override string ToString() {
- if (elmts is char[]) {
- var s = "";
- foreach (var t in elmts) {
- s += t.ToString();
- }
- return s;
- } else {
- var s = "[";
- var sep = "";
- foreach (var t in elmts) {
- s += sep + t.ToString();
- sep = ", ";
- }
- return s + "]";
- }
- }
- bool EqualUntil(Sequence<T> other, int n) {
- for (int i = 0; i < n; i++) {
- if (!elmts[i].Equals(other.elmts[i]))
- return false;
- }
- return true;
- }
- public bool IsProperPrefixOf(Sequence<T> other) {
- int n = elmts.Length;
- return n < other.elmts.Length && EqualUntil(other, n);
- }
- public bool IsPrefixOf(Sequence<T> other) {
- int n = elmts.Length;
- return n <= other.elmts.Length && EqualUntil(other, n);
- }
- public Sequence<T> Concat(Sequence<T> other) {
- if (elmts.Length == 0)
- return other;
- else if (other.elmts.Length == 0)
- return this;
- T[] a = new T[elmts.Length + other.elmts.Length];
- System.Array.Copy(elmts, 0, a, 0, elmts.Length);
- System.Array.Copy(other.elmts, 0, a, elmts.Length, other.elmts.Length);
- return new Sequence<T>(a);
- }
- public bool Contains(T t) {
- int n = elmts.Length;
- for (int i = 0; i < n; i++) {
- if (t.Equals(elmts[i]))
- return true;
- }
- return false;
- }
- public Sequence<T> Take(long m) {
- if (elmts.LongLength == m)
- return this;
- T[] a = new T[m];
- System.Array.Copy(elmts, a, m);
- return new Sequence<T>(a);
- }
- public Sequence<T> Take(ulong n) {
- return Take((long)n);
- }
- public Sequence<T> Take(BigInteger n) {
- return Take((long)n);
- }
- public Sequence<T> Drop(long m) {
- if (m == 0)
- return this;
- T[] a = new T[elmts.Length - m];
- System.Array.Copy(elmts, m, a, 0, elmts.Length - m);
- return new Sequence<T>(a);
- }
- public Sequence<T> Drop(ulong n) {
- return Drop((long)n);
- }
- public Sequence<T> Drop(BigInteger n) {
- if (n.IsZero)
- return this;
- return Drop((long)n);
- }
- }
- public struct Pair<A, B>
- {
- public readonly A Car;
- public readonly B Cdr;
- public Pair(A a, B b) {
- this.Car = a;
- this.Cdr = b;
- }
- }
- public partial class Helpers {
- // Computing forall/exists quantifiers
- public static bool QuantBool(bool frall, System.Predicate<bool> pred) {
- if (frall) {
- return pred(false) && pred(true);
- } else {
- return pred(false) || pred(true);
- }
- }
- public static bool QuantInt(BigInteger lo, BigInteger hi, bool frall, System.Predicate<BigInteger> pred) {
- for (BigInteger i = lo; i < hi; i++) {
- if (pred(i) != frall) { return !frall; }
- }
- return frall;
- }
- public static bool QuantSet<U>(Dafny.Set<U> set, bool frall, System.Predicate<U> pred) {
- foreach (var u in set.Elements) {
- if (pred(u) != frall) { return !frall; }
- }
- return frall;
- }
- public static bool QuantMap<U,V>(Dafny.Map<U,V> map, bool frall, System.Predicate<U> pred) {
- foreach (var u in map.Domain) {
- if (pred(u) != frall) { return !frall; }
- }
- return frall;
- }
- public static bool QuantSeq<U>(Dafny.Sequence<U> seq, bool frall, System.Predicate<U> pred) {
- foreach (var u in seq.Elements) {
- if (pred(u) != frall) { return !frall; }
- }
- return frall;
- }
- public static bool QuantDatatype<U>(IEnumerable<U> set, bool frall, System.Predicate<U> pred) {
- foreach (var u in set) {
- if (pred(u) != frall) { return !frall; }
- }
- return frall;
- }
- // Enumerating other collections
- public delegate Dafny.Set<T> ComprehensionDelegate<T>();
- public delegate Dafny.Map<U, V> MapComprehensionDelegate<U, V>();
- public static IEnumerable<bool> AllBooleans {
- get {
- yield return false;
- yield return true;
- }
- }
- public static IEnumerable<BigInteger> AllIntegers {
- get {
- yield return new BigInteger(0);
- for (var j = new BigInteger(1);; j++) {
- yield return j;
- yield return -j;
- }
- }
- }
- // pre: b != 0
- // post: result == a/b, as defined by Euclidean Division (http://en.wikipedia.org/wiki/Modulo_operation)
- public static sbyte EuclideanDivision_sbyte(sbyte a, sbyte b) {
- return (sbyte)EuclideanDivision_int(a, b);
- }
- public static short EuclideanDivision_short(short a, short b) {
- return (short)EuclideanDivision_int(a, b);
- }
- public static int EuclideanDivision_int(int a, int b) {
- if (0 <= a) {
- if (0 <= b) {
- // +a +b: a/b
- return (int)(((uint)(a)) / ((uint)(b)));
- } else {
- // +a -b: -(a/(-b))
- return -((int)(((uint)(a)) / ((uint)(unchecked(-b)))));
- }
- } else {
- if (0 <= b) {
- // -a +b: -((-a-1)/b) - 1
- return -((int)(((uint)(-(a + 1))) / ((uint)(b)))) - 1;
- } else {
- // -a -b: ((-a-1)/(-b)) + 1
- return ((int)(((uint)(-(a + 1))) / ((uint)(unchecked(-b))))) + 1;
- }
- }
- }
- public static long EuclideanDivision_long(long a, long b) {
- if (0 <= a) {
- if (0 <= b) {
- // +a +b: a/b
- return (long)(((ulong)(a)) / ((ulong)(b)));
- } else {
- // +a -b: -(a/(-b))
- return -((long)(((ulong)(a)) / ((ulong)(unchecked(-b)))));
- }
- } else {
- if (0 <= b) {
- // -a +b: -((-a-1)/b) - 1
- return -((long)(((ulong)(-(a + 1))) / ((ulong)(b)))) - 1;
- } else {
- // -a -b: ((-a-1)/(-b)) + 1
- return ((long)(((ulong)(-(a + 1))) / ((ulong)(unchecked(-b))))) + 1;
- }
- }
- }
- public static BigInteger EuclideanDivision(BigInteger a, BigInteger b) {
- if (0 <= a.Sign) {
- if (0 <= b.Sign) {
- // +a +b: a/b
- return BigInteger.Divide(a, b);
- } else {
- // +a -b: -(a/(-b))
- return BigInteger.Negate(BigInteger.Divide(a, BigInteger.Negate(b)));
- }
- } else {
- if (0 <= b.Sign) {
- // -a +b: -((-a-1)/b) - 1
- return BigInteger.Negate(BigInteger.Divide(BigInteger.Negate(a) - 1, b)) - 1;
- } else {
- // -a -b: ((-a-1)/(-b)) + 1
- return BigInteger.Divide(BigInteger.Negate(a) - 1, BigInteger.Negate(b)) + 1;
- }
- }
- }
- // pre: b != 0
- // post: result == a%b, as defined by Euclidean Division (http://en.wikipedia.org/wiki/Modulo_operation)
- public static sbyte EuclideanModulus_sbyte(sbyte a, sbyte b) {
- return (sbyte)EuclideanModulus_int(a, b);
- }
- public static short EuclideanModulus_short(short a, short b) {
- return (short)EuclideanModulus_int(a, b);
- }
- public static int EuclideanModulus_int(int a, int b) {
- uint bp = (0 <= b) ? (uint)b : (uint)(unchecked(-b));
- if (0 <= a) {
- // +a: a % b'
- return (int)(((uint)a) % bp);
- } else {
- // c = ((-a) % b')
- // -a: b' - c if c > 0
- // -a: 0 if c == 0
- uint c = ((uint)(unchecked(-a))) % bp;
- return (int)(c == 0 ? c : bp - c);
- }
- }
- public static long EuclideanModulus_long(long a, long b) {
- ulong bp = (0 <= b) ? (ulong)b : (ulong)(unchecked(-b));
- if (0 <= a) {
- // +a: a % b'
- return (long)(((ulong)a) % bp);
- } else {
- // c = ((-a) % b')
- // -a: b' - c if c > 0
- // -a: 0 if c == 0
- ulong c = ((ulong)(unchecked(-a))) % bp;
- return (long)(c == 0 ? c : bp - c);
- }
- }
- public static BigInteger EuclideanModulus(BigInteger a, BigInteger b) {
- var bp = BigInteger.Abs(b);
- if (0 <= a.Sign) {
- // +a: a % b'
- return BigInteger.Remainder(a, bp);
- } else {
- // c = ((-a) % b')
- // -a: b' - c if c > 0
- // -a: 0 if c == 0
- var c = BigInteger.Remainder(BigInteger.Negate(a), bp);
- return c.IsZero ? c : BigInteger.Subtract(bp, c);
- }
- }
- public static Sequence<T> SeqFromArray<T>(T[] array) {
- return new Sequence<T>(array);
- }
- // In .NET version 4.5, it it possible to mark a method with "AggressiveInlining", which says to inline the
- // method if possible. Method "ExpressionSequence" would be a good candidate for it:
- // [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
- public static U ExpressionSequence<T, U>(T t, U u)
- {
- return u;
- }
-
- public static U Let<T, U>(T t, Func<T,U> f) {
- return f(t);
- }
-
- public delegate Result Function<Input,Result>(Input input);
-
- public static A Id<A>(A a) {
- return a;
- }
- }
-
- public struct BigRational
- {
- public static readonly BigRational ZERO = new BigRational(0);
-
- BigInteger num, den; // invariant 1 <= den
- public override string ToString() {
- return string.Format("({0}.0 / {1}.0)", num, den);
- }
- public BigRational(int n) {
- num = new BigInteger(n);
- den = BigInteger.One;
- }
- public BigRational(BigInteger n, BigInteger d) {
- // requires 1 <= d
- num = n;
- den = d;
- }
- public BigInteger ToBigInteger() {
- if (0 <= num) {
- return num / den;
- } else {
- return (num - den + 1) / den;
- }
- }
- /// <summary>
- /// Returns values such that aa/dd == a and bb/dd == b.
- /// </summary>
- private static void Normalize(BigRational a, BigRational b, out BigInteger aa, out BigInteger bb, out BigInteger dd) {
- var gcd = BigInteger.GreatestCommonDivisor(a.den, b.den);
- var xx = a.den / gcd;
- var yy = b.den / gcd;
- // We now have a == a.num / (xx * gcd) and b == b.num / (yy * gcd).
- aa = a.num * yy;
- bb = b.num * xx;
- dd = a.den * yy;
- }
- public int CompareTo(BigRational that) {
- // simple things first
- int asign = this.num.Sign;
- int bsign = that.num.Sign;
- if (asign < 0 && 0 <= bsign) {
- return 1;
- } else if (asign <= 0 && 0 < bsign) {
- return 1;
- } else if (bsign < 0 && 0 <= asign) {
- return -1;
- } else if (bsign <= 0 && 0 < asign) {
- return -1;
- }
- BigInteger aa, bb, dd;
- Normalize(this, that, out aa, out bb, out dd);
- return aa.CompareTo(bb);
- }
- public override int GetHashCode() {
- return num.GetHashCode() + 29 * den.GetHashCode();
- }
- public override bool Equals(object obj) {
- if (obj is BigRational) {
- return this == (BigRational)obj;
- } else {
- return false;
- }
- }
- public static bool operator ==(BigRational a, BigRational b) {
- return a.CompareTo(b) == 0;
- }
- public static bool operator !=(BigRational a, BigRational b) {
- return a.CompareTo(b) != 0;
- }
- public static bool operator >(BigRational a, BigRational b) {
- return 0 < a.CompareTo(b);
- }
- public static bool operator >=(BigRational a, BigRational b) {
- return 0 <= a.CompareTo(b);
- }
- public static bool operator <(BigRational a, BigRational b) {
- return a.CompareTo(b) < 0;
- }
- public static bool operator <=(BigRational a, BigRational b) {
- return a.CompareTo(b) <= 0;
- }
- public static BigRational operator +(BigRational a, BigRational b) {
- BigInteger aa, bb, dd;
- Normalize(a, b, out aa, out bb, out dd);
- return new BigRational(aa + bb, dd);
- }
- public static BigRational operator -(BigRational a, BigRational b) {
- BigInteger aa, bb, dd;
- Normalize(a, b, out aa, out bb, out dd);
- return new BigRational(aa - bb, dd);
- }
- public static BigRational operator -(BigRational a) {
- return new BigRational(-a.num, a.den);
- }
- public static BigRational operator *(BigRational a, BigRational b) {
- return new BigRational(a.num * b.num, a.den * b.den);
- }
- public static BigRational operator /(BigRational a, BigRational b) {
- // Compute the reciprocal of b
- BigRational bReciprocal;
- if (0 < b.num) {
- bReciprocal = new BigRational(b.den, b.num);
- } else {
- // this is the case b.num < 0
- bReciprocal = new BigRational(-b.den, -b.num);
- }
- return a * bReciprocal;
- }
- }
-}
+using System; // for Func
+using System.Numerics;
+
+namespace Dafny
+{
+ using System.Collections.Generic;
+// set this option if you want to use System.Collections.Immutable and if you know what you're doing.
+#if DAFNY_USE_SYSTEM_COLLECTIONS_IMMUTABLE
+ using System.Collections.Immutable;
+ using System.Linq;
+
+ public class Set<T>
+ {
+ readonly ImmutableHashSet<T> setImpl;
+ Set(ImmutableHashSet<T> d) {
+ this.setImpl = d;
+ }
+ public static readonly Set<T> Empty = new Set<T>(ImmutableHashSet<T>.Empty);
+ public static Set<T> FromElements(params T[] values) {
+ return FromElements((IEnumerable<T>)values);
+ }
+ public static Set<T> FromElements(IEnumerable<T> values) {
+ var d = ImmutableHashSet<T>.Empty.ToBuilder();
+ foreach (T t in values)
+ d.Add(t);
+ return new Set<T>(d.ToImmutable());
+ }
+ public static Set<T> FromCollection(ICollection<T> values) {
+ var d = ImmutableHashSet<T>.Empty.ToBuilder();
+ foreach (T t in values)
+ d.Add(t);
+ return new Set<T>(d.ToImmutable());
+ }
+ public int Length {
+ get { return this.setImpl.Count; }
+ }
+ public long LongLength {
+ get { return this.setImpl.Count; }
+ }
+ public IEnumerable<T> Elements {
+ get {
+ return this.setImpl;
+ }
+ }
+ /// <summary>
+ /// This is an inefficient iterator for producing all subsets of "this". Each set returned is the same
+ /// Set<T> object (but this Set<T> object is fresh; in particular, it is not "this").
+ /// </summary>
+ public IEnumerable<Set<T>> AllSubsets {
+ get {
+ // Start by putting all set elements into a list
+ var elmts = new List<T>();
+ elmts.AddRange(this.setImpl);
+ var n = elmts.Count;
+ var which = new bool[n];
+ var s = ImmutableHashSet<T>.Empty.ToBuilder();
+ while (true) {
+ yield return new Set<T>(s.ToImmutable());
+ // "add 1" to "which", as if doing a carry chain. For every digit changed, change the membership of the corresponding element in "s".
+ int i = 0;
+ for (; i < n && which[i]; i++) {
+ which[i] = false;
+ s.Remove(elmts[i]);
+ }
+ if (i == n) {
+ // we have cycled through all the subsets
+ break;
+ }
+ which[i] = true;
+ s.Add(elmts[i]);
+ }
+ }
+ }
+ public bool Equals(Set<T> other) {
+ return this.setImpl.SetEquals(other.setImpl);
+ }
+ public override bool Equals(object other) {
+ var otherSet = other as Set<T>;
+ return otherSet != null && this.Equals(otherSet);
+ }
+ public override int GetHashCode() {
+ var hashCode = 1;
+ foreach (var t in this.setImpl) {
+ hashCode = hashCode * (t.GetHashCode()+3);
+ }
+ return hashCode;
+ }
+ public override string ToString() {
+ var s = "{";
+ var sep = "";
+ foreach (var t in this.setImpl) {
+ s += sep + t.ToString();
+ sep = ", ";
+ }
+ return s + "}";
+ }
+ public bool IsProperSubsetOf(Set<T> other) {
+ return IsProperSubsetOf(other);
+ }
+ public bool IsSubsetOf(Set<T> other) {
+ return IsSubsetOf(other);
+ }
+ public bool IsSupersetOf(Set<T> other) {
+ return other.IsSupersetOf(this);
+ }
+ public bool IsProperSupersetOf(Set<T> other) {
+ return other.IsProperSupersetOf(this);
+ }
+ public bool IsDisjointFrom(Set<T> other) {
+ ImmutableHashSet<T> a, b;
+ if (this.setImpl.Count < other.setImpl.Count) {
+ a = this.setImpl; b = other.setImpl;
+ } else {
+ a = other.setImpl; b = this.setImpl;
+ }
+ foreach (T t in a) {
+ if (b.Contains(t))
+ return false;
+ }
+ return true;
+ }
+ public bool Contains(T t) {
+ return this.setImpl.Contains(t);
+ }
+ public Set<T> Union(Set<T> other) {
+ return new Set<T>(this.setImpl.Union(other.setImpl));
+ }
+ public Set<T> Intersect(Set<T> other) {
+ return new Set<T>(this.setImpl.Intersect(other.setImpl));
+ }
+ public Set<T> Difference(Set<T> other) {
+ return new Set<T>(this.setImpl.Except(other.setImpl));
+ }
+ }
+ public partial class MultiSet<T>
+ {
+
+ readonly ImmutableDictionary<T, int> dict;
+ MultiSet(ImmutableDictionary<T, int> d) {
+ dict = d;
+ }
+ public static readonly MultiSet<T> Empty = new MultiSet<T>(ImmutableDictionary<T, int>.Empty);
+ public static MultiSet<T> FromElements(params T[] values) {
+ var d = ImmutableDictionary<T, int>.Empty.ToBuilder();
+ foreach (T t in values) {
+ var i = 0;
+ if (!d.TryGetValue(t, out i)) {
+ i = 0;
+ }
+ d[t] = i + 1;
+ }
+ return new MultiSet<T>(d.ToImmutable());
+ }
+ public static MultiSet<T> FromCollection(ICollection<T> values) {
+ var d = ImmutableDictionary<T, int>.Empty.ToBuilder();
+ foreach (T t in values) {
+ var i = 0;
+ if (!d.TryGetValue(t, out i)) {
+ i = 0;
+ }
+ d[t] = i + 1;
+ }
+ return new MultiSet<T>(d.ToImmutable());
+ }
+ public static MultiSet<T> FromSeq(Sequence<T> values) {
+ var d = ImmutableDictionary<T, int>.Empty.ToBuilder();
+ foreach (T t in values.Elements) {
+ var i = 0;
+ if (!d.TryGetValue(t, out i)) {
+ i = 0;
+ }
+ d[t] = i + 1;
+ }
+ return new MultiSet<T>(d.ToImmutable());
+ }
+ public static MultiSet<T> FromSet(Set<T> values) {
+ var d = ImmutableDictionary<T, int>.Empty.ToBuilder();
+ foreach (T t in values.Elements) {
+ d[t] = 1;
+ }
+ return new MultiSet<T>(d.ToImmutable());
+ }
+
+ public bool Equals(MultiSet<T> other) {
+ return other.IsSubsetOf(this) && this.IsSubsetOf(other);
+ }
+ public override bool Equals(object other) {
+ return other is MultiSet<T> && Equals((MultiSet<T>)other);
+ }
+ public override int GetHashCode() {
+ var hashCode = 1;
+ foreach (var kv in dict) {
+ var key = kv.Key.GetHashCode();
+ key = (key << 3) | (key >> 29) ^ kv.Value.GetHashCode();
+ hashCode = hashCode * (key + 3);
+ }
+ return hashCode;
+ }
+ public override string ToString() {
+ var s = "multiset{";
+ var sep = "";
+ foreach (var kv in dict) {
+ var t = kv.Key.ToString();
+ for (int i = 0; i < kv.Value; i++) {
+ s += sep + t.ToString();
+ sep = ", ";
+ }
+ }
+ return s + "}";
+ }
+ public bool IsProperSubsetOf(MultiSet<T> other) {
+ return !Equals(other) && IsSubsetOf(other);
+ }
+ public bool IsSubsetOf(MultiSet<T> other) {
+ foreach (T t in dict.Keys) {
+ if (!other.dict.ContainsKey(t) || other.dict[t] < dict[t])
+ return false;
+ }
+ return true;
+ }
+ public bool IsSupersetOf(MultiSet<T> other) {
+ return other.IsSubsetOf(this);
+ }
+ public bool IsProperSupersetOf(MultiSet<T> other) {
+ return other.IsProperSubsetOf(this);
+ }
+ public bool IsDisjointFrom(MultiSet<T> other) {
+ foreach (T t in dict.Keys) {
+ if (other.dict.ContainsKey(t))
+ return false;
+ }
+ foreach (T t in other.dict.Keys) {
+ if (dict.ContainsKey(t))
+ return false;
+ }
+ return true;
+ }
+ public bool Contains(T t) {
+ return dict.ContainsKey(t);
+ }
+ public MultiSet<T> Union(MultiSet<T> other) {
+ if (dict.Count == 0)
+ return other;
+ else if (other.dict.Count == 0)
+ return this;
+ var r = ImmutableDictionary<T, int>.Empty.ToBuilder();
+ foreach (T t in dict.Keys) {
+ var i = 0;
+ if (!r.TryGetValue(t, out i)) {
+ i = 0;
+ }
+ r[t] = i + dict[t];
+ }
+ foreach (T t in other.dict.Keys) {
+ var i = 0;
+ if (!r.TryGetValue(t, out i)) {
+ i = 0;
+ }
+ r[t] = i + other.dict[t];
+ }
+ return new MultiSet<T>(r.ToImmutable());
+ }
+ public MultiSet<T> Intersect(MultiSet<T> other) {
+ if (dict.Count == 0)
+ return this;
+ else if (other.dict.Count == 0)
+ return other;
+ var r = ImmutableDictionary<T, int>.Empty.ToBuilder();
+ foreach (T t in dict.Keys) {
+ if (other.dict.ContainsKey(t)) {
+ r[t] = other.dict[t] < dict[t] ? other.dict[t] : dict[t];
+ }
+ }
+ return new MultiSet<T>(r.ToImmutable());
+ }
+ public MultiSet<T> Difference(MultiSet<T> other) { // \result == this - other
+ if (dict.Count == 0)
+ return this;
+ else if (other.dict.Count == 0)
+ return this;
+ var r = ImmutableDictionary<T, int>.Empty.ToBuilder();
+ foreach (T t in dict.Keys) {
+ if (!other.dict.ContainsKey(t)) {
+ r[t] = dict[t];
+ } else if (other.dict[t] < dict[t]) {
+ r[t] = dict[t] - other.dict[t];
+ }
+ }
+ return new MultiSet<T>(r.ToImmutable());
+ }
+ public IEnumerable<T> Elements {
+ get {
+ foreach (T t in dict.Keys) {
+ int n;
+ dict.TryGetValue(t, out n);
+ for (int i = 0; i < n; i ++) {
+ yield return t;
+ }
+ }
+ }
+ }
+ }
+
+ public partial class Map<U, V>
+ {
+ readonly ImmutableDictionary<U, V> dict;
+ Map(ImmutableDictionary<U, V> d) {
+ dict = d;
+ }
+ public static readonly Map<U, V> Empty = new Map<U, V>(ImmutableDictionary<U, V>.Empty);
+ public static Map<U, V> FromElements(params Pair<U, V>[] values) {
+ var d = ImmutableDictionary<U, V>.Empty.ToBuilder();
+ foreach (Pair<U, V> p in values) {
+ d[p.Car] = p.Cdr;
+ }
+ return new Map<U, V>(d.ToImmutable());
+ }
+ public static Map<U, V> FromCollection(List<Pair<U, V>> values) {
+ var d = ImmutableDictionary<U, V>.Empty.ToBuilder();
+ foreach (Pair<U, V> p in values) {
+ d[p.Car] = p.Cdr;
+ }
+ return new Map<U, V>(d.ToImmutable());
+ }
+ public int Length {
+ get { return dict.Count; }
+ }
+ public long LongLength {
+ get { return dict.Count; }
+ }
+ public bool Equals(Map<U, V> other) {
+ foreach (U u in dict.Keys) {
+ V v1, v2;
+ if (!dict.TryGetValue(u, out v1)) {
+ return false; // this shouldn't happen
+ }
+ if (!other.dict.TryGetValue(u, out v2)) {
+ return false; // other dictionary does not contain this element
+ }
+ if (!v1.Equals(v2)) {
+ return false;
+ }
+ }
+ foreach (U u in other.dict.Keys) {
+ if (!dict.ContainsKey(u)) {
+ return false; // this shouldn't happen
+ }
+ }
+ return true;
+ }
+ public override bool Equals(object other) {
+ return other is Map<U, V> && Equals((Map<U, V>)other);
+ }
+ public override int GetHashCode() {
+ var hashCode = 1;
+ foreach (var kv in dict) {
+ var key = kv.Key.GetHashCode();
+ key = (key << 3) | (key >> 29) ^ kv.Value.GetHashCode();
+ hashCode = hashCode * (key + 3);
+ }
+ return hashCode;
+ }
+ public override string ToString() {
+ var s = "map[";
+ var sep = "";
+ foreach (var kv in dict) {
+ s += sep + kv.Key.ToString() + " := " + kv.Value.ToString();
+ sep = ", ";
+ }
+ return s + "]";
+ }
+ public bool IsDisjointFrom(Map<U, V> other) {
+ foreach (U u in dict.Keys) {
+ if (other.dict.ContainsKey(u))
+ return false;
+ }
+ foreach (U u in other.dict.Keys) {
+ if (dict.ContainsKey(u))
+ return false;
+ }
+ return true;
+ }
+ public bool Contains(U u) {
+ return dict.ContainsKey(u);
+ }
+ public V Select(U index) {
+ return dict[index];
+ }
+ public Map<U, V> Update(U index, V val) {
+ return new Map<U, V>(dict.SetItem(index, val));
+ }
+ public IEnumerable<U> Domain {
+ get {
+ return dict.Keys;
+ }
+ }
+ }
+#else // !def DAFNY_USE_SYSTEM_COLLECTIONS_IMMUTABLE
+ public class Set<T>
+ {
+ HashSet<T> set;
+ Set(HashSet<T> s) {
+ this.set = s;
+ }
+ public static Set<T> Empty {
+ get {
+ return new Set<T>(new HashSet<T>());
+ }
+ }
+ public static Set<T> FromElements(params T[] values) {
+ var s = new HashSet<T>();
+ foreach (T t in values)
+ s.Add(t);
+ return new Set<T>(s);
+ }
+ public static Set<T> FromCollection(ICollection<T> values) {
+ HashSet<T> s = new HashSet<T>();
+ foreach (T t in values)
+ s.Add(t);
+ return new Set<T>(s);
+ }
+ public int Length {
+ get { return this.set.Count; }
+ }
+ public long LongLength {
+ get { return this.set.Count; }
+ }
+ public IEnumerable<T> Elements {
+ get {
+ return this.set;
+ }
+ }
+ /// <summary>
+ /// This is an inefficient iterator for producing all subsets of "this". Each set returned is the same
+ /// Set<T> object (but this Set<T> object is fresh; in particular, it is not "this").
+ /// </summary>
+ public IEnumerable<Set<T>> AllSubsets {
+ get {
+ // Start by putting all set elements into a list
+ var elmts = new List<T>();
+ elmts.AddRange(this.set);
+ var n = elmts.Count;
+ var which = new bool[n];
+ var s = new Set<T>(new HashSet<T>());
+ while (true) {
+ yield return s;
+ // "add 1" to "which", as if doing a carry chain. For every digit changed, change the membership of the corresponding element in "s".
+ int i = 0;
+ for (; i < n && which[i]; i++) {
+ which[i] = false;
+ s.set.Remove(elmts[i]);
+ }
+ if (i == n) {
+ // we have cycled through all the subsets
+ break;
+ }
+ which[i] = true;
+ s.set.Add(elmts[i]);
+ }
+ }
+ }
+ public bool Equals(Set<T> other) {
+ return this.set.Count == other.set.Count && IsSubsetOf(other);
+ }
+ public override bool Equals(object other) {
+ return other is Set<T> && Equals((Set<T>)other);
+ }
+ public override int GetHashCode() {
+ var hashCode = 1;
+ foreach (var t in this.set) {
+ hashCode = hashCode * (t.GetHashCode()+3);
+ }
+ return hashCode;
+ }
+ public override string ToString() {
+ var s = "{";
+ var sep = "";
+ foreach (var t in this.set) {
+ s += sep + t.ToString();
+ sep = ", ";
+ }
+ return s + "}";
+ }
+ public bool IsProperSubsetOf(Set<T> other) {
+ return this.set.Count < other.set.Count && IsSubsetOf(other);
+ }
+ public bool IsSubsetOf(Set<T> other) {
+ if (other.set.Count < this.set.Count)
+ return false;
+ foreach (T t in this.set) {
+ if (!other.set.Contains(t))
+ return false;
+ }
+ return true;
+ }
+ public bool IsSupersetOf(Set<T> other) {
+ return other.IsSubsetOf(this);
+ }
+ public bool IsProperSupersetOf(Set<T> other) {
+ return other.IsProperSubsetOf(this);
+ }
+ public bool IsDisjointFrom(Set<T> other) {
+ HashSet<T> a, b;
+ if (this.set.Count < other.set.Count) {
+ a = this.set; b = other.set;
+ } else {
+ a = other.set; b = this.set;
+ }
+ foreach (T t in a) {
+ if (b.Contains(t))
+ return false;
+ }
+ return true;
+ }
+ public bool Contains(T t) {
+ return this.set.Contains(t);
+ }
+ public Set<T> Union(Set<T> other) {
+ if (this.set.Count == 0)
+ return other;
+ else if (other.set.Count == 0)
+ return this;
+ HashSet<T> a, b;
+ if (this.set.Count < other.set.Count) {
+ a = this.set; b = other.set;
+ } else {
+ a = other.set; b = this.set;
+ }
+ var r = new HashSet<T>();
+ foreach (T t in b)
+ r.Add(t);
+ foreach (T t in a)
+ r.Add(t);
+ return new Set<T>(r);
+ }
+ public Set<T> Intersect(Set<T> other) {
+ if (this.set.Count == 0)
+ return this;
+ else if (other.set.Count == 0)
+ return other;
+ HashSet<T> a, b;
+ if (this.set.Count < other.set.Count) {
+ a = this.set; b = other.set;
+ } else {
+ a = other.set; b = this.set;
+ }
+ var r = new HashSet<T>();
+ foreach (T t in a) {
+ if (b.Contains(t))
+ r.Add(t);
+ }
+ return new Set<T>(r);
+ }
+ public Set<T> Difference(Set<T> other) {
+ if (this.set.Count == 0)
+ return this;
+ else if (other.set.Count == 0)
+ return this;
+ var r = new HashSet<T>();
+ foreach (T t in this.set) {
+ if (!other.set.Contains(t))
+ r.Add(t);
+ }
+ return new Set<T>(r);
+ }
+ }
+ public class MultiSet<T>
+ {
+ Dictionary<T, int> dict;
+ MultiSet(Dictionary<T, int> d) {
+ dict = d;
+ }
+ public static MultiSet<T> Empty {
+ get {
+ return new MultiSet<T>(new Dictionary<T, int>(0));
+ }
+ }
+ public static MultiSet<T> FromElements(params T[] values) {
+ Dictionary<T, int> d = new Dictionary<T, int>(values.Length);
+ foreach (T t in values) {
+ var i = 0;
+ if (!d.TryGetValue(t, out i)) {
+ i = 0;
+ }
+ d[t] = i + 1;
+ }
+ return new MultiSet<T>(d);
+ }
+ public static MultiSet<T> FromCollection(ICollection<T> values) {
+ Dictionary<T, int> d = new Dictionary<T, int>();
+ foreach (T t in values) {
+ var i = 0;
+ if (!d.TryGetValue(t, out i)) {
+ i = 0;
+ }
+ d[t] = i + 1;
+ }
+ return new MultiSet<T>(d);
+ }
+ public static MultiSet<T> FromSeq(Sequence<T> values) {
+ Dictionary<T, int> d = new Dictionary<T, int>();
+ foreach (T t in values.Elements) {
+ var i = 0;
+ if (!d.TryGetValue(t, out i)) {
+ i = 0;
+ }
+ d[t] = i + 1;
+ }
+ return new MultiSet<T>(d);
+ }
+ public static MultiSet<T> FromSet(Set<T> values) {
+ Dictionary<T, int> d = new Dictionary<T, int>();
+ foreach (T t in values.Elements) {
+ d[t] = 1;
+ }
+ return new MultiSet<T>(d);
+ }
+
+ public bool Equals(MultiSet<T> other) {
+ return other.IsSubsetOf(this) && this.IsSubsetOf(other);
+ }
+ public override bool Equals(object other) {
+ return other is MultiSet<T> && Equals((MultiSet<T>)other);
+ }
+ public override int GetHashCode() {
+ var hashCode = 1;
+ foreach (var kv in dict) {
+ var key = kv.Key.GetHashCode();
+ key = (key << 3) | (key >> 29) ^ kv.Value.GetHashCode();
+ hashCode = hashCode * (key + 3);
+ }
+ return hashCode;
+ }
+ public override string ToString() {
+ var s = "multiset{";
+ var sep = "";
+ foreach (var kv in dict) {
+ var t = kv.Key.ToString();
+ for (int i = 0; i < kv.Value; i++) {
+ s += sep + t.ToString();
+ sep = ", ";
+ }
+ }
+ return s + "}";
+ }
+ public bool IsProperSubsetOf(MultiSet<T> other) {
+ return !Equals(other) && IsSubsetOf(other);
+ }
+ public bool IsSubsetOf(MultiSet<T> other) {
+ foreach (T t in dict.Keys) {
+ if (!other.dict.ContainsKey(t) || other.dict[t] < dict[t])
+ return false;
+ }
+ return true;
+ }
+ public bool IsSupersetOf(MultiSet<T> other) {
+ return other.IsSubsetOf(this);
+ }
+ public bool IsProperSupersetOf(MultiSet<T> other) {
+ return other.IsProperSubsetOf(this);
+ }
+ public bool IsDisjointFrom(MultiSet<T> other) {
+ foreach (T t in dict.Keys) {
+ if (other.dict.ContainsKey(t))
+ return false;
+ }
+ foreach (T t in other.dict.Keys) {
+ if (dict.ContainsKey(t))
+ return false;
+ }
+ return true;
+ }
+ public bool Contains(T t) {
+ return dict.ContainsKey(t);
+ }
+ public MultiSet<T> Union(MultiSet<T> other) {
+ if (dict.Count == 0)
+ return other;
+ else if (other.dict.Count == 0)
+ return this;
+ var r = new Dictionary<T, int>();
+ foreach (T t in dict.Keys) {
+ var i = 0;
+ if (!r.TryGetValue(t, out i)) {
+ i = 0;
+ }
+ r[t] = i + dict[t];
+ }
+ foreach (T t in other.dict.Keys) {
+ var i = 0;
+ if (!r.TryGetValue(t, out i)) {
+ i = 0;
+ }
+ r[t] = i + other.dict[t];
+ }
+ return new MultiSet<T>(r);
+ }
+ public MultiSet<T> Intersect(MultiSet<T> other) {
+ if (dict.Count == 0)
+ return this;
+ else if (other.dict.Count == 0)
+ return other;
+ var r = new Dictionary<T, int>();
+ foreach (T t in dict.Keys) {
+ if (other.dict.ContainsKey(t)) {
+ r.Add(t, other.dict[t] < dict[t] ? other.dict[t] : dict[t]);
+ }
+ }
+ return new MultiSet<T>(r);
+ }
+ public MultiSet<T> Difference(MultiSet<T> other) { // \result == this - other
+ if (dict.Count == 0)
+ return this;
+ else if (other.dict.Count == 0)
+ return this;
+ var r = new Dictionary<T, int>();
+ foreach (T t in dict.Keys) {
+ if (!other.dict.ContainsKey(t)) {
+ r.Add(t, dict[t]);
+ } else if (other.dict[t] < dict[t]) {
+ r.Add(t, dict[t] - other.dict[t]);
+ }
+ }
+ return new MultiSet<T>(r);
+ }
+ public IEnumerable<T> Elements {
+ get {
+ List<T> l = new List<T>();
+ foreach (T t in dict.Keys) {
+ int n;
+ dict.TryGetValue(t, out n);
+ for (int i = 0; i < n; i ++) {
+ l.Add(t);
+ }
+ }
+ return l;
+ }
+ }
+ }
+
+ public class Map<U, V>
+ {
+ Dictionary<U, V> dict;
+ Map(Dictionary<U, V> d) {
+ dict = d;
+ }
+ public static Map<U, V> Empty {
+ get {
+ return new Map<U, V>(new Dictionary<U,V>());
+ }
+ }
+ public static Map<U, V> FromElements(params Pair<U, V>[] values) {
+ Dictionary<U, V> d = new Dictionary<U, V>(values.Length);
+ foreach (Pair<U, V> p in values) {
+ d[p.Car] = p.Cdr;
+ }
+ return new Map<U, V>(d);
+ }
+ public static Map<U, V> FromCollection(List<Pair<U, V>> values) {
+ Dictionary<U, V> d = new Dictionary<U, V>(values.Count);
+ foreach (Pair<U, V> p in values) {
+ d[p.Car] = p.Cdr;
+ }
+ return new Map<U, V>(d);
+ }
+ public int Length {
+ get { return dict.Count; }
+ }
+ public long LongLength {
+ get { return dict.Count; }
+ }
+ public bool Equals(Map<U, V> other) {
+ foreach (U u in dict.Keys) {
+ V v1, v2;
+ if (!dict.TryGetValue(u, out v1)) {
+ return false; // this shouldn't happen
+ }
+ if (!other.dict.TryGetValue(u, out v2)) {
+ return false; // other dictionary does not contain this element
+ }
+ if (!v1.Equals(v2)) {
+ return false;
+ }
+ }
+ foreach (U u in other.dict.Keys) {
+ if (!dict.ContainsKey(u)) {
+ return false; // this shouldn't happen
+ }
+ }
+ return true;
+ }
+ public override bool Equals(object other) {
+ return other is Map<U, V> && Equals((Map<U, V>)other);
+ }
+ public override int GetHashCode() {
+ var hashCode = 1;
+ foreach (var kv in dict) {
+ var key = kv.Key.GetHashCode();
+ key = (key << 3) | (key >> 29) ^ kv.Value.GetHashCode();
+ hashCode = hashCode * (key + 3);
+ }
+ return hashCode;
+ }
+ public override string ToString() {
+ var s = "map[";
+ var sep = "";
+ foreach (var kv in dict) {
+ s += sep + kv.Key.ToString() + " := " + kv.Value.ToString();
+ sep = ", ";
+ }
+ return s + "]";
+ }
+ public bool IsDisjointFrom(Map<U, V> other) {
+ foreach (U u in dict.Keys) {
+ if (other.dict.ContainsKey(u))
+ return false;
+ }
+ foreach (U u in other.dict.Keys) {
+ if (dict.ContainsKey(u))
+ return false;
+ }
+ return true;
+ }
+ public bool Contains(U u) {
+ return dict.ContainsKey(u);
+ }
+ public V Select(U index) {
+ return dict[index];
+ }
+ public Map<U, V> Update(U index, V val) {
+ Dictionary<U, V> d = new Dictionary<U, V>(dict);
+ d[index] = val;
+ return new Map<U, V>(d);
+ }
+ public IEnumerable<U> Domain {
+ get {
+ return dict.Keys;
+ }
+ }
+ }
+#endif
+ public class Sequence<T>
+ {
+ T[] elmts;
+ public Sequence(T[] ee) {
+ elmts = ee;
+ }
+ public static Sequence<T> Empty {
+ get {
+ return new Sequence<T>(new T[0]);
+ }
+ }
+ public static Sequence<T> FromElements(params T[] values) {
+ return new Sequence<T>(values);
+ }
+ public static Sequence<char> FromString(string s) {
+ return new Sequence<char>(s.ToCharArray());
+ }
+ public int Length {
+ get { return elmts.Length; }
+ }
+ public long LongLength {
+ get { return elmts.LongLength; }
+ }
+ public T[] Elements {
+ get {
+ return elmts;
+ }
+ }
+ public IEnumerable<T> UniqueElements {
+ get {
+ var st = Set<T>.FromElements(elmts);
+ return st.Elements;
+ }
+ }
+ public T Select(ulong index) {
+ return elmts[index];
+ }
+ public T Select(long index) {
+ return elmts[index];
+ }
+ public T Select(uint index) {
+ return elmts[index];
+ }
+ public T Select(int index) {
+ return elmts[index];
+ }
+ public T Select(BigInteger index) {
+ return elmts[(int)index];
+ }
+ public Sequence<T> Update(long index, T t) {
+ T[] a = (T[])elmts.Clone();
+ a[index] = t;
+ return new Sequence<T>(a);
+ }
+ public Sequence<T> Update(ulong index, T t) {
+ return Update((long)index, t);
+ }
+ public Sequence<T> Update(BigInteger index, T t) {
+ return Update((long)index, t);
+ }
+ public bool Equals(Sequence<T> other) {
+ int n = elmts.Length;
+ return n == other.elmts.Length && EqualUntil(other, n);
+ }
+ public override bool Equals(object other) {
+ return other is Sequence<T> && Equals((Sequence<T>)other);
+ }
+ public override int GetHashCode() {
+ if (elmts == null || elmts.Length == 0)
+ return 0;
+ var hashCode = 0;
+ for (var i = 0; i < elmts.Length; i++) {
+ hashCode = (hashCode << 3) | (hashCode >> 29) ^ elmts[i].GetHashCode();
+ }
+ return hashCode;
+ }
+ public override string ToString() {
+ if (elmts is char[]) {
+ var s = "";
+ foreach (var t in elmts) {
+ s += t.ToString();
+ }
+ return s;
+ } else {
+ var s = "[";
+ var sep = "";
+ foreach (var t in elmts) {
+ s += sep + t.ToString();
+ sep = ", ";
+ }
+ return s + "]";
+ }
+ }
+ bool EqualUntil(Sequence<T> other, int n) {
+ for (int i = 0; i < n; i++) {
+ if (!elmts[i].Equals(other.elmts[i]))
+ return false;
+ }
+ return true;
+ }
+ public bool IsProperPrefixOf(Sequence<T> other) {
+ int n = elmts.Length;
+ return n < other.elmts.Length && EqualUntil(other, n);
+ }
+ public bool IsPrefixOf(Sequence<T> other) {
+ int n = elmts.Length;
+ return n <= other.elmts.Length && EqualUntil(other, n);
+ }
+ public Sequence<T> Concat(Sequence<T> other) {
+ if (elmts.Length == 0)
+ return other;
+ else if (other.elmts.Length == 0)
+ return this;
+ T[] a = new T[elmts.Length + other.elmts.Length];
+ System.Array.Copy(elmts, 0, a, 0, elmts.Length);
+ System.Array.Copy(other.elmts, 0, a, elmts.Length, other.elmts.Length);
+ return new Sequence<T>(a);
+ }
+ public bool Contains(T t) {
+ int n = elmts.Length;
+ for (int i = 0; i < n; i++) {
+ if (t.Equals(elmts[i]))
+ return true;
+ }
+ return false;
+ }
+ public Sequence<T> Take(long m) {
+ if (elmts.LongLength == m)
+ return this;
+ T[] a = new T[m];
+ System.Array.Copy(elmts, a, m);
+ return new Sequence<T>(a);
+ }
+ public Sequence<T> Take(ulong n) {
+ return Take((long)n);
+ }
+ public Sequence<T> Take(BigInteger n) {
+ return Take((long)n);
+ }
+ public Sequence<T> Drop(long m) {
+ if (m == 0)
+ return this;
+ T[] a = new T[elmts.Length - m];
+ System.Array.Copy(elmts, m, a, 0, elmts.Length - m);
+ return new Sequence<T>(a);
+ }
+ public Sequence<T> Drop(ulong n) {
+ return Drop((long)n);
+ }
+ public Sequence<T> Drop(BigInteger n) {
+ if (n.IsZero)
+ return this;
+ return Drop((long)n);
+ }
+ }
+ public struct Pair<A, B>
+ {
+ public readonly A Car;
+ public readonly B Cdr;
+ public Pair(A a, B b) {
+ this.Car = a;
+ this.Cdr = b;
+ }
+ }
+ public partial class Helpers {
+ // Computing forall/exists quantifiers
+ public static bool QuantBool(bool frall, System.Predicate<bool> pred) {
+ if (frall) {
+ return pred(false) && pred(true);
+ } else {
+ return pred(false) || pred(true);
+ }
+ }
+ public static bool QuantChar(bool frall, System.Predicate<char> pred) {
+ for (int i = 0; i < 0x10000; i++) {
+ if (pred((char)i) != frall) { return !frall; }
+ }
+ return frall;
+ }
+ public static bool QuantInt(BigInteger lo, BigInteger hi, bool frall, System.Predicate<BigInteger> pred) {
+ for (BigInteger i = lo; i < hi; i++) {
+ if (pred(i) != frall) { return !frall; }
+ }
+ return frall;
+ }
+ public static bool QuantSet<U>(Dafny.Set<U> set, bool frall, System.Predicate<U> pred) {
+ foreach (var u in set.Elements) {
+ if (pred(u) != frall) { return !frall; }
+ }
+ return frall;
+ }
+ public static bool QuantMap<U,V>(Dafny.Map<U,V> map, bool frall, System.Predicate<U> pred) {
+ foreach (var u in map.Domain) {
+ if (pred(u) != frall) { return !frall; }
+ }
+ return frall;
+ }
+ public static bool QuantSeq<U>(Dafny.Sequence<U> seq, bool frall, System.Predicate<U> pred) {
+ foreach (var u in seq.Elements) {
+ if (pred(u) != frall) { return !frall; }
+ }
+ return frall;
+ }
+ public static bool QuantDatatype<U>(IEnumerable<U> set, bool frall, System.Predicate<U> pred) {
+ foreach (var u in set) {
+ if (pred(u) != frall) { return !frall; }
+ }
+ return frall;
+ }
+ // Enumerating other collections
+ public delegate Dafny.Set<T> ComprehensionDelegate<T>();
+ public delegate Dafny.Map<U, V> MapComprehensionDelegate<U, V>();
+ public static IEnumerable<bool> AllBooleans {
+ get {
+ yield return false;
+ yield return true;
+ }
+ }
+ public static IEnumerable<char> AllChars {
+ get {
+ for (int i = 0; i < 0x10000; i++) {
+ yield return (char)i;
+ }
+ }
+ }
+ public static IEnumerable<BigInteger> AllIntegers {
+ get {
+ yield return new BigInteger(0);
+ for (var j = new BigInteger(1);; j++) {
+ yield return j;
+ yield return -j;
+ }
+ }
+ }
+ public static IEnumerable<BigInteger> IntegerRange(BigInteger lo, BigInteger hi) {
+ for (var j = lo; j < hi; j++) {
+ yield return j;
+ }
+ }
+ // pre: b != 0
+ // post: result == a/b, as defined by Euclidean Division (http://en.wikipedia.org/wiki/Modulo_operation)
+ public static sbyte EuclideanDivision_sbyte(sbyte a, sbyte b) {
+ return (sbyte)EuclideanDivision_int(a, b);
+ }
+ public static short EuclideanDivision_short(short a, short b) {
+ return (short)EuclideanDivision_int(a, b);
+ }
+ public static int EuclideanDivision_int(int a, int b) {
+ if (0 <= a) {
+ if (0 <= b) {
+ // +a +b: a/b
+ return (int)(((uint)(a)) / ((uint)(b)));
+ } else {
+ // +a -b: -(a/(-b))
+ return -((int)(((uint)(a)) / ((uint)(unchecked(-b)))));
+ }
+ } else {
+ if (0 <= b) {
+ // -a +b: -((-a-1)/b) - 1
+ return -((int)(((uint)(-(a + 1))) / ((uint)(b)))) - 1;
+ } else {
+ // -a -b: ((-a-1)/(-b)) + 1
+ return ((int)(((uint)(-(a + 1))) / ((uint)(unchecked(-b))))) + 1;
+ }
+ }
+ }
+ public static long EuclideanDivision_long(long a, long b) {
+ if (0 <= a) {
+ if (0 <= b) {
+ // +a +b: a/b
+ return (long)(((ulong)(a)) / ((ulong)(b)));
+ } else {
+ // +a -b: -(a/(-b))
+ return -((long)(((ulong)(a)) / ((ulong)(unchecked(-b)))));
+ }
+ } else {
+ if (0 <= b) {
+ // -a +b: -((-a-1)/b) - 1
+ return -((long)(((ulong)(-(a + 1))) / ((ulong)(b)))) - 1;
+ } else {
+ // -a -b: ((-a-1)/(-b)) + 1
+ return ((long)(((ulong)(-(a + 1))) / ((ulong)(unchecked(-b))))) + 1;
+ }
+ }
+ }
+ public static BigInteger EuclideanDivision(BigInteger a, BigInteger b) {
+ if (0 <= a.Sign) {
+ if (0 <= b.Sign) {
+ // +a +b: a/b
+ return BigInteger.Divide(a, b);
+ } else {
+ // +a -b: -(a/(-b))
+ return BigInteger.Negate(BigInteger.Divide(a, BigInteger.Negate(b)));
+ }
+ } else {
+ if (0 <= b.Sign) {
+ // -a +b: -((-a-1)/b) - 1
+ return BigInteger.Negate(BigInteger.Divide(BigInteger.Negate(a) - 1, b)) - 1;
+ } else {
+ // -a -b: ((-a-1)/(-b)) + 1
+ return BigInteger.Divide(BigInteger.Negate(a) - 1, BigInteger.Negate(b)) + 1;
+ }
+ }
+ }
+ // pre: b != 0
+ // post: result == a%b, as defined by Euclidean Division (http://en.wikipedia.org/wiki/Modulo_operation)
+ public static sbyte EuclideanModulus_sbyte(sbyte a, sbyte b) {
+ return (sbyte)EuclideanModulus_int(a, b);
+ }
+ public static short EuclideanModulus_short(short a, short b) {
+ return (short)EuclideanModulus_int(a, b);
+ }
+ public static int EuclideanModulus_int(int a, int b) {
+ uint bp = (0 <= b) ? (uint)b : (uint)(unchecked(-b));
+ if (0 <= a) {
+ // +a: a % b'
+ return (int)(((uint)a) % bp);
+ } else {
+ // c = ((-a) % b')
+ // -a: b' - c if c > 0
+ // -a: 0 if c == 0
+ uint c = ((uint)(unchecked(-a))) % bp;
+ return (int)(c == 0 ? c : bp - c);
+ }
+ }
+ public static long EuclideanModulus_long(long a, long b) {
+ ulong bp = (0 <= b) ? (ulong)b : (ulong)(unchecked(-b));
+ if (0 <= a) {
+ // +a: a % b'
+ return (long)(((ulong)a) % bp);
+ } else {
+ // c = ((-a) % b')
+ // -a: b' - c if c > 0
+ // -a: 0 if c == 0
+ ulong c = ((ulong)(unchecked(-a))) % bp;
+ return (long)(c == 0 ? c : bp - c);
+ }
+ }
+ public static BigInteger EuclideanModulus(BigInteger a, BigInteger b) {
+ var bp = BigInteger.Abs(b);
+ if (0 <= a.Sign) {
+ // +a: a % b'
+ return BigInteger.Remainder(a, bp);
+ } else {
+ // c = ((-a) % b')
+ // -a: b' - c if c > 0
+ // -a: 0 if c == 0
+ var c = BigInteger.Remainder(BigInteger.Negate(a), bp);
+ return c.IsZero ? c : BigInteger.Subtract(bp, c);
+ }
+ }
+ public static Sequence<T> SeqFromArray<T>(T[] array) {
+ return new Sequence<T>((T[])array.Clone());
+ }
+ // In .NET version 4.5, it it possible to mark a method with "AggressiveInlining", which says to inline the
+ // method if possible. Method "ExpressionSequence" would be a good candidate for it:
+ // [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ public static U ExpressionSequence<T, U>(T t, U u)
+ {
+ return u;
+ }
+
+ public static U Let<T, U>(T t, Func<T,U> f) {
+ return f(t);
+ }
+
+ public delegate Result Function<Input,Result>(Input input);
+
+ public static A Id<A>(A a) {
+ return a;
+ }
+ }
+
+ public struct BigRational
+ {
+ public static readonly BigRational ZERO = new BigRational(0);
+
+ BigInteger num, den; // invariant 1 <= den
+ public override string ToString() {
+ return string.Format("({0}.0 / {1}.0)", num, den);
+ }
+ public BigRational(int n) {
+ num = new BigInteger(n);
+ den = BigInteger.One;
+ }
+ public BigRational(BigInteger n, BigInteger d) {
+ // requires 1 <= d
+ num = n;
+ den = d;
+ }
+ public BigInteger ToBigInteger() {
+ if (0 <= num) {
+ return num / den;
+ } else {
+ return (num - den + 1) / den;
+ }
+ }
+ /// <summary>
+ /// Returns values such that aa/dd == a and bb/dd == b.
+ /// </summary>
+ private static void Normalize(BigRational a, BigRational b, out BigInteger aa, out BigInteger bb, out BigInteger dd) {
+ var gcd = BigInteger.GreatestCommonDivisor(a.den, b.den);
+ var xx = a.den / gcd;
+ var yy = b.den / gcd;
+ // We now have a == a.num / (xx * gcd) and b == b.num / (yy * gcd).
+ aa = a.num * yy;
+ bb = b.num * xx;
+ dd = a.den * yy;
+ }
+ public int CompareTo(BigRational that) {
+ // simple things first
+ int asign = this.num.Sign;
+ int bsign = that.num.Sign;
+ if (asign < 0 && 0 <= bsign) {
+ return -1;
+ } else if (asign <= 0 && 0 < bsign) {
+ return -1;
+ } else if (bsign < 0 && 0 <= asign) {
+ return 1;
+ } else if (bsign <= 0 && 0 < asign) {
+ return 1;
+ }
+ BigInteger aa, bb, dd;
+ Normalize(this, that, out aa, out bb, out dd);
+ return aa.CompareTo(bb);
+ }
+ public override int GetHashCode() {
+ return num.GetHashCode() + 29 * den.GetHashCode();
+ }
+ public override bool Equals(object obj) {
+ if (obj is BigRational) {
+ return this == (BigRational)obj;
+ } else {
+ return false;
+ }
+ }
+ public static bool operator ==(BigRational a, BigRational b) {
+ return a.CompareTo(b) == 0;
+ }
+ public static bool operator !=(BigRational a, BigRational b) {
+ return a.CompareTo(b) != 0;
+ }
+ public static bool operator >(BigRational a, BigRational b) {
+ return 0 < a.CompareTo(b);
+ }
+ public static bool operator >=(BigRational a, BigRational b) {
+ return 0 <= a.CompareTo(b);
+ }
+ public static bool operator <(BigRational a, BigRational b) {
+ return a.CompareTo(b) < 0;
+ }
+ public static bool operator <=(BigRational a, BigRational b) {
+ return a.CompareTo(b) <= 0;
+ }
+ public static BigRational operator +(BigRational a, BigRational b) {
+ BigInteger aa, bb, dd;
+ Normalize(a, b, out aa, out bb, out dd);
+ return new BigRational(aa + bb, dd);
+ }
+ public static BigRational operator -(BigRational a, BigRational b) {
+ BigInteger aa, bb, dd;
+ Normalize(a, b, out aa, out bb, out dd);
+ return new BigRational(aa - bb, dd);
+ }
+ public static BigRational operator -(BigRational a) {
+ return new BigRational(-a.num, a.den);
+ }
+ public static BigRational operator *(BigRational a, BigRational b) {
+ return new BigRational(a.num * b.num, a.den * b.den);
+ }
+ public static BigRational operator /(BigRational a, BigRational b) {
+ // Compute the reciprocal of b
+ BigRational bReciprocal;
+ if (0 < b.num) {
+ bReciprocal = new BigRational(b.den, b.num);
+ } else {
+ // this is the case b.num < 0
+ bReciprocal = new BigRational(-b.den, -b.num);
+ }
+ return a * bReciprocal;
+ }
+ }
+}
diff --git a/Binaries/System.Collections.Immutable.dll b/Binaries/System.Collections.Immutable.dll
new file mode 100644
index 00000000..bcf23fe5
--- /dev/null
+++ b/Binaries/System.Collections.Immutable.dll
Binary files differ
diff --git a/Binaries/dafny b/Binaries/dafny
new file mode 100755
index 00000000..889ebc81
--- /dev/null
+++ b/Binaries/dafny
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+MONO=$(which mono)
+DAFNY=$(dirname "${BASH_SOURCE[0]}")/Dafny.exe
+
+if [[ ! -x "$MONO" ]]; then
+ echo "Error: Dafny requires Mono to run on non-Windows systems."
+ exit 1
+fi
+
+if [[ ! -x "$DAFNY" ]]; then
+ echo "Error: Dafny.exe not found at $DAFNY."
+ exit 1
+fi
+
+"$MONO" "$DAFNY" "$@"
diff --git a/Binaries/dafny-server b/Binaries/dafny-server
new file mode 100755
index 00000000..2fd0d202
--- /dev/null
+++ b/Binaries/dafny-server
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+MONO=$(which mono)
+DAFNYSERVER=$(dirname "${BASH_SOURCE[0]}")/DafnyServer.exe
+
+if [[ ! -x "$MONO" ]]; then
+ echo "Error: Dafny requires Mono to run on non-Windows systems."
+ exit 1
+fi
+
+if [[ ! -x "$DAFNYSERVER" ]]; then
+ echo "Error: DafnyServer.exe not found at $DAFNYSERVER."
+ exit 1
+fi
+
+"$MONO" "$DAFNYSERVER" "$@"
diff --git a/Binaries/z3.exe b/Binaries/z3.exe
index 72aa6ccd..21c6d85b 100644
--- a/Binaries/z3.exe
+++ b/Binaries/z3.exe
Binary files differ
diff --git a/Docs/DafnyRef/DafnyRef.bib b/Docs/DafnyRef/DafnyRef.bib
new file mode 100644
index 00000000..9a2a5a5b
--- /dev/null
+++ b/Docs/DafnyRef/DafnyRef.bib
@@ -0,0 +1,48 @@
+@Misc{Rise4fun:dafny,
+ author = {K. Rustan M. Leino},
+ title = {Try Dafny In Your Browser},
+ note = "Available at \url{http://rise4fun.com/Dafny}"
+}
+@Misc{MSR:dafny:main,
+ author = {K. Rustan M. Leino},
+ title = {Main Microsoft Research Dafny Web page},
+ note = "Available at \url{http://research.microsoft.com/en-us/projects/dafny}"
+}
+@Misc{MSR:dafny:source,
+ author = {K. Rustan M. Leino et al},
+ title = {Dafny Source Code},
+ note = "Available at \url{http://dafny.codeplex.com}"
+}
+@Misc{MSR:dafny:quickref,
+ author = {K. Rustan M. Leino},
+ title = {Dafny Quick Reference},
+ note = "Available at \url{http://research.microsoft.com/en-us/projects/dafny/reference.aspx}"
+}
+@Misc{Linz:Coco,
+ author = {Hanspeter M{\"{o}}ssenb{\"{o}}ck and Markus L{\"{o}}berbauer and Albrecht W{\"{o}}{\ss}},
+ title = {The Compiler Generator Coco/R},
+ howpublished = {Open source from University of Linz},
+ year = 2013,
+ note = "Available at \url{http://www.ssw.uni-linz.ac.at/Research/Projects/Coco/}"
+}
+@Misc{LEINO:Dafny:Calc,
+ author = {K. Rustan M. Leino and Nadia Polikarpova},
+ title = {Verified Calculations},
+ howpublished = {Manuscript KRML 231},
+ year = 2013,
+ note = "Available at \url{http://research.microsoft.com/en-us/um/people/leino/papers/krml231.pdf}"
+}
+@Misc{LEINO:Dafny:Coinduction,
+ author = {K. Rustan M. Leino and Michal Moskal},
+ title = {Co-induction Simply: Automatic Co-inductive Proofs in a Program Verifier},
+ howpublished = {Manuscript KRML 230},
+ year = 2014,
+ note = "Available at \url{http://research.microsoft.com/en-us/um/people/leino/papers/krml230.pdf}"
+}
+@Misc{LEINO:Dafny:DynamicFrames,
+ author = {K. Rustan M. Leino},
+ title = {Dynamic-frame specifications in Dafny},
+ howpublished = {JML seminar, Dagstuhl, Germany},
+ year = 2009,
+ note = "Available at \url{http://research.microsoft.com/en-us/um/people/leino/papers/dafny-jml-dagstuhl-2009.pptx}"
+}
diff --git a/Docs/DafnyRef/DafnyRef.mdk b/Docs/DafnyRef/DafnyRef.mdk
new file mode 100644
index 00000000..59db10ab
--- /dev/null
+++ b/Docs/DafnyRef/DafnyRef.mdk
@@ -0,0 +1,8331 @@
+<!--madoko
+Title : Draft Dafny Reference Manual
+Title Note : Manuscript Dafny Reference&br;&date; &time;
+Author : Richard L. Ford
+Email : richford@microsoft.com
+Author : K. Rustan M. Leino
+Email : leino@microsoft.com
+Doc Class : [12pt,twoside]article
+Package : [left=1.0in, right=0.75in]geometry
+Heading Base: 2
+Toc Depth : 5
+Heading Depth: 5
+xMath Mode : dynamic
+
+Bibliography: DafnyRef.bib
+Bibliography: references.bib
+Bibliography: paper-full.bib
+Bibliography: poc.bib
+Bibliography: krml250.bib
+Cite All: false
+
+[INCLUDE=zero]
+Tex Header : \setcounter{footnote}{-1}
+xPackage : times
+xTex Header : \IfFileExists{luximono.sty}{\usepackage[scaled=0.81]{luximono}}{\usepackage[scaled=0.81]{beramono}}
+Colorizer: dafny
+Colorizer: dafnyx
+~Pre,.code1 : replace="//\\forall\s/\(&forall;[&thinsp;]{font-family=serif}\)\
+ //\bexists\s/\(&exist;[&thinsp;]{font-family=serif}\)\
+ //g"
+ language=dafnyx
+ font-size=small
+~Pre : margin-left=1em
+
+~Exercise : .block @Exercise=arabic0 label='[@Exercise]{.Exercise-label}'
+ before='[**Exercise\ &label;.** ]{.Exercise-before}'
+ after=' &box;'
+
+Comment : We use four backticks (````) for grammar productions. We use the Java
+ colorizer as that seems good enough for grammars.
+Colorizer : java
+
+.pre-fenced4: language=java .grammar-def
+.code2 : language=java .grammar-ref
+
+.grammar-def: replace='//(^|\n)([A-Z]\w+)/\1\([\2]{#1_\L\2\E .ntdef}\)\
+ //(^|\n)([a-z]\w+)/\1\([\2]{#2_\L\2\E .ntdef}\)\
+ //\/\/.*/\0\
+ //\/\*[\s\S]*?\*\//\0\
+ //"([^"|\s]*)"/\([\"\1\"]{.terminal}\)\
+ //([^\[\(\{\w#.&])([A-Z]\w+)\b/\1\([\2](#1_\L\2\E){.ntref}\)\
+ //([^\[\(\{\w#.&])([a-z]\w+)\b/\1\([\2](#2_\L\2\E){.ntref}\)\
+ //cg'
+
+.grammar-ref: replace='//\/\/.*/\0\
+ //\/\*[\s\S]*?\*\//\0\
+ //"([^"\s]*)"/\([\"\1\"]{.terminal}\)\
+ //([^\[\(\{\w#.&])([A-Z]\w+)\b/\1\([\2](#1_\L\2\E){.ntref}\)\
+ //([^\[\(\{\w#.&])([a-z]\w+)\b/\1\([\2](#2_\L\2\E){.ntref}\)\
+ //^([A-Z]\w+)$/\([\1](#1_\L\1\E){.ntref}\)\
+ //^([a-z]\w+)$/\([\1](#2_\L\1\E){.ntref}\)\
+ //cg'
+.terminal : font-weight=bold color=black
+.ntref : color=maroon
+.ntdef : color=olive
+
+.AuthorNote : before="**&author;: **"
+~Daan : .AuthorNote color=purple author="Daan"
+~Rich : .AuthorNote color=teal author="Rich"
+~Rustan : .AuthorNote color=darkgreen author="Rustan"
+
+.grammar : .framed padding=1ex margin-left=0ex language=java
+
+Css Header : body { text-rendering=optimizeLegibility }
+
+
+<style>
+.code-escaped .comment-color { color: darkgreen }
+
+.token.java.string { color: black; font-weight: bold }
+.token.java.type { color: black; font-weight: normal }
+.token.java.operator,
+.token.java.delimiter { color: blue; }
+</style>
+
+~ MathDefs
+\newcommand{\F}{\mathcal{F}}
+\newcommand{\Equal}{\;\;\;=\;\;\;}
+\newcommand{\Equiv}{\;\;\equiv\;\;}
+\newcommand{\ES}{\;\;}
+\newcommand{\ite}[3]{\textrm{if}\ES #1 \ES\textrm{then}\ES #2 \ES\textrm{else}\ES #3}
+\newcommand{\Less}{\ll}
+\newcommand{\Imp}{\;\Longrightarrow\;}
+\renewcommand{\And}{\;\wedge\;}
+\newcommand{\Or}{\;\vee\;}
+\newcommand{\FBelow}{\ES\dot{\Rightarrow}\ES}
+\newcommand{\least}{^{\downarrow}}
+\newcommand{\greatest}{^{\uparrow}}
+\newcommand{\Dfrac}[2]{%
+ \ooalign{%
+ $\genfrac{}{}{1.8pt}0{#1}{#2}$\cr%
+ $\color{white}\genfrac{}{}{1pt}0{\phantom{#1}}{\phantom{#2}}$}%
+}
+\newcommand{\DfracA}[2]{%
+ \ooalign{%
+ $\genfrac{}{}{1.2pt}0{#1}{#2}$\cr%
+ $\color{white}\genfrac{}{}{0.6pt}0{\phantom{#1}}{\phantom{#2}}$}%
+}
+\newcommand{\false}{\mathit{false}}
+\newcommand{\true}{\mathit{true}}
+\newcommand{\fib}{\mathit{fib}}
+\newcommand{\PDownward}{\mathit{PDownward}}
+\newcommand{\iterX}[1]{\hat{#1}}
+\newcommand{\IterX}[1]{\check{#1}}
+\renewcommand\vec[1]{\overrightarrow{#1}}
+\newcommand\cev[1]{\overleftarrow{#1}}
+\newcommand{\iterY}[1]{\vec{#1}}
+\newcommand{\IterY}[1]{\cev{#1}}
+\newcommand{\iter}[1]{{{}^{\flat}\!#1}}
+\newcommand{\Iter}[1]{{{}^{\sharp}\!#1}}
+
+% daan's fractions
+\newcommand\xstrut{\vrule height 9.4pt depth 4.6pt width 0pt\relax}
+\newcommand\xupstrut{\vrule height 9.4pt depth 0pt width 0pt\relax}
+
+\renewcommand{\Dfrac}[2]{%
+ \ooalign{%
+ % first a thick fraction line
+ $\genfrac{}{}{1.4pt}1{\displaystyle #1\strut}{\displaystyle #2\strut}$\cr%
+ % and then a thinner white fraction line on top of it
+ $\color{white}\genfrac{}{}{0.6pt}1{\phantom{\displaystyle #1\strut}}{\phantom{\displaystyle #2\strut}}$}%
+}
+\renewcommand{\dfrac}[2]{%
+ \displaystyle\genfrac{}{}{0.4pt}1{\displaystyle #1}{\displaystyle #2}%
+}
+~
+
+-->
+
+[TITLE]
+
+~ Abstract
+This is the Dafny reference manual which describes the Dafny programming
+language and how to use the Dafny verification system.
+Parts of this manual are more tutorial in nature in order to help the
+user understand how to do proofs with Dafny.
+~
+
+[TOC]
+
+
+# Introduction
+
+Dafny [@Leino:Dafny:LPAR16] is a programming language with built-in specification constructs.
+The Dafny static program verifier can be used to verify the functional
+correctness of programs.
+
+The Dafny programming language is designed to support the static
+verification of programs. It is imperative, sequential, supports generic
+classes, methods and functions, dynamic allocation, inductive and
+co-inductive datatypes, and specification constructs. The
+specifications include pre- and postconditions, frame specifications
+(read and write sets), and termination metrics. To further support
+specifications, the language also offers updatable ghost variables,
+recursive functions, and types like sets and sequences. Specifications
+and ghost constructs are used only during verification; the compiler
+omits them from the executable code.
+
+The Dafny verifier is run as part of the compiler. As such, a programmer
+interacts with it much in the same way as with the static type
+checkerâ€â€when the tool produces errors, the programmer responds by
+changing the program’s type declarations, specifications, and statements.
+
+The easiest way to try out [Dafny is in your web browser at
+rise4fun](http://rise4fun.com/Dafny)[@Rise4fun:dafny]. Once you get a bit
+more serious, you may prefer to [download](http://dafny.codeplex.com/) it
+to run it on your machine. Although Dafny can be run from the command
+line (on Windows or other platforms), the preferred way to run it is in
+Microsoft Visual Studio 2012 (or newer) or using emacs, where the Dafny
+verifier runs in the background while the programmer is editing the
+program.
+
+The Dafny verifier is powered
+by [Boogie](http://research.microsoft.com/boogie)
+[@Boogie:Architecture;@Leino:Boogie2-RefMan;@LeinoRuemmer:Boogie2]
+and [Z3](https://github.com/z3prover)[@deMouraBjorner:Z3:overview].
+
+From verified programs, the Dafny compiler produces code (`.dll` or
+`.exe`) for the .NET platform via intermediate C# files. However, the
+facilities for interfacing with other .NET code are minimal.
+
+This is the reference manual for the Dafny verification system. It is
+based on the following references:
+[@Leino:Dafny:LPAR16;@MSR:dafny:main;
+@MSR:dafny:source;@MSR:dafny:quickref; @LEINO:Dafny:Calc;
+@LEINO:Dafny:Coinduction;
+and the tutorials at @Rise4fun:dafny]
+
+The main part of the reference manual is in top down order except for an
+initial section that deals with the lowest level constructs.
+
+[Co-induction Simply]: http://research.microsoft.com/en-us/um/people/leino/papers/krml230.pdf "Co-induction Simply: Automatic Co-inductive Proofs in a Program Verifier"
+
+## Dafny Example
+To give a flavor of Dafny, here is the solution to a competition problem.
+
+```
+// VSComp 2010, problem 3, find a 0 in a linked list and return how many
+// nodes were skipped until the first 0 (or end-of-list) was found.
+// Rustan Leino, 18 August 2010.
+//
+// The difficulty in this problem lies in specifying what the return
+// value 'r' denotes and in proving that the program terminates. Both of
+// these are addressed by declaring a ghost field 'List' in each
+// linked-list node, abstractly representing the linked-list elements
+// from the node to the end of the linked list. The specification can
+// now talk about that sequence of elements and can use 'r' as an index
+// into the sequence, and termination can be proved from the fact that
+// all sequences in Dafny are finite.
+//
+// We only want to deal with linked lists whose 'List' field is properly
+// filled in (which can only happen in an acyclic list, for example). To
+// that avail, the standard idiom in Dafny is to declare a predicate
+// 'Valid()' that is true of an object when the data structure
+// representing object's abstract value is properly formed. The
+// definition of 'Valid()' is what one intuitively would think of as the
+// ''object invariant'', and it is mentioned explicitly in method pre-
+// and postconditions. As part of this standard idiom, one also declared
+// a ghost variable 'Repr' that is maintained as the set of objects that
+// make up the representation of the aggregate object--in this case, the
+// Node itself and all its successors.
+
+class Node {
+ ghost var List: seq<int>
+ ghost var Repr: set<Node>
+ var head: int
+ var next: Node
+
+ predicate Valid()
+ reads this, Repr
+ {
+ this in Repr &&
+ 1 <= |List| && List[0] == head &&
+ (next == null ==> |List| == 1) &&
+ (next != null ==>
+ next in Repr && next.Repr <= Repr && this !in next.Repr &&
+ next.Valid() && next.List == List[1..])
+ }
+
+ static method Cons(x: int, tail: Node) returns (n: Node)
+ requires tail == null || tail.Valid()
+ ensures n != null && n.Valid()
+ ensures if tail == null then n.List == [x]
+ else n.List == [x] + tail.List
+ {
+ n := new Node;
+ n.head, n.next := x, tail;
+ if (tail == null) {
+ n.List := [x];
+ n.Repr := {n};
+ } else {
+ n.List := [x] + tail.List;
+ n.Repr := {n} + tail.Repr;
+ }
+ }
+}
+
+method Search(ll: Node) returns (r: int)
+ requires ll == null || ll.Valid()
+ ensures ll == null ==> r == 0
+ ensures ll != null ==>
+ 0 <= r && r <= |ll.List| &&
+ (r < |ll.List| ==> ll.List[r] == 0 && 0 !in ll.List[..r]) &&
+ (r == |ll.List| ==> 0 !in ll.List)
+{
+ if (ll == null) {
+ r := 0;
+ } else {
+ var jj,i := ll,0;
+ while (jj != null && jj.head != 0)
+ invariant jj != null ==> jj.Valid() && i + |jj.List| == |ll.List| &&
+ ll.List[i..] == jj.List
+ invariant jj == null ==> i == |ll.List|
+ invariant 0 !in ll.List[..i]
+ decreases |ll.List| - i
+ {
+ jj := jj.next;
+ i := i + 1;
+ }
+ r := i;
+ }
+}
+
+method Main()
+{
+ var list: Node := null;
+ list := list.Cons(0, list);
+ list := list.Cons(5, list);
+ list := list.Cons(0, list);
+ list := list.Cons(8, list);
+ var r := Search(list);
+ print "Search returns ", r, "\n";
+ assert r == 1;
+}
+```
+
+
+# Lexical and Low Level Grammar
+Dafny uses the Coco/R lexer and parser generator for its lexer and parser
+(<http://www.ssw.uni-linz.ac.at/Research/Projects/Coco>)[@Linz:Coco].
+The Dafny input file to Coco/R is the `Dafny.atg` file in the source tree.
+A Coco/R input file consists of code written in the target language
+(&eg; C#) intermixed with these special sections:
+
+0. The Characters section which defines classes of characters that are used
+ in defining the lexer (Section [#sec-character-classes]).
+1. The Tokens section which defines the lexical tokens (Section [#sec-tokens]).
+2. The Productions section which defines the grammar. The grammar productions
+are distributed in the later parts of this document in the parts where
+those constructs are explained.
+
+The grammar presented in this document was derived from the `Dafny.atg`
+file but has been simplified by removing details that, though needed by
+the parser, are not needed to understand the grammar. In particular, the
+following transformation have been performed.
+
+* The semantics actions, enclosed by "(." and ".)", where removed.
+* There are some elements in the grammar used for error recovery
+ ("SYNC"). These were removed.
+* There are some elements in the grammar for resolving conflicts
+ ("IF(b)"). These have been removed.
+* Some comments related to Coco/R parsing details have been removed.
+* A Coco/R grammar is an attributed grammar where the attributes enable
+ the productions to have input and output parameters. These attributes
+ were removed except that boolean input parameters that affect
+ the parsing are kept.
+ * In our representation we represent these
+ in a definition by giving the names of the parameters following
+ the non-terminal name. For example `entity1(allowsX)`.
+ * In the case of uses of the parameter, the common case is that the
+ parameter is just passed to a lower-level non-terminal. In that
+ case we just give the name, e.g. `entity2(allowsX)`.
+ * If we want to given an explicit value to a parameter, we specify it in
+ a keyword notation like this: `entity2(allowsX: true)`.
+ * In some cases the value to be passed depends on the grammatical context.
+ In such cases we give a description of the conditions under which the
+ parameter is true, enclosed in parenthesis. For example:
+
+ `FunctionSignatureOrEllipsis_(allowGhostKeyword: ("method" present))`
+
+ means that the `allowGhostKeyword` parameter is true if the
+ "method" keyword was given in the associated ``FunctionDecl``.
+ * Where a parameter affects the parsing of a non-terminal we will
+ explain the effect of the parameter.
+
+
+The names of character sets and tokens start with a lower case
+letter but the names of grammar non-terminals start with
+an upper-case letter.
+
+The grammar uses Extended BNF notation. See the [Coco/R Referenced
+manual](http://www.ssw.uni-linz.ac.at/Research/Projects/Coco/Doc/UserManual.pdf)
+for details. But in summary:
+
+* identifiers starting with a lower case letter denote
+terminal symbols,
+* identifiers starting with an upper case letter denote nonterminal
+symbols.
+* Strings denote themselves.
+* `=` separates the sides of a production, &eg; `A = a b c`
+* In the Coco grammars "." terminates a production, but for readability
+ in this document a production starts with the defined identifier in
+ the left margin and may be continued on subsequent lines if they
+ are indented.
+* `|` separates alternatives, &eg; `a b | c | d e` means `a b` or `c or d e`
+* `(` `)` groups alternatives, &eg; (a | b) c means a c or b c
+* `[ ]` option, &eg; `[a] b` means `a b` or `b`
+* `{ }` iteration (0 or more times), &eg; `{a} b` means `b` or `a b` or `a a b` or ...
+* We allow `|` inside `[ ]` and `{ }`. So `[a | b]` is short for `[(a | b)]`
+ and `{a | b}` is short for `{(a | b)}`.
+* The first production defines the name of the grammar, in this case `Dafny`.
+
+In addition to the Coco rules, for the sake of readability we have adopted
+these additional conventions.
+
+* We allow `-` to be used. `a - b` means it matches if it matches `a` but not `b`.
+* To aid in explaining the grammar we have added some additional productions
+that are not present in the original grammar. We name these with a trailing
+underscore. If you inline these where they are referenced, the result should
+let you reconstruct the original grammar.
+
+**For the convenience of the reader, any references to character sets,
+tokens, or grammar non-terminals in this document are hyper-links that
+will link to the definition of the entity.**
+
+## Character Classes
+This section defines character classes used later in the token definitions.
+In this section backslash is used to start an escape sequence, so for example
+'\n' denotes the single linefeed character.
+
+````
+letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+````
+At present, a letter is an ASCII upper or lowercase letter. Other Unicode letters
+are not supported.
+
+````
+digit = "0123456789"
+````
+A digit is just one of the base-10 digits.
+
+````
+posDigit = "123456789"
+````
+A ``posDigit`` is a digit, excluding 0.
+
+````
+hexdigit = "0123456789ABCDEFabcdef"
+````
+A ``hexdigit`` character is a digit or one of the letters from 'A' to 'F' in either case.
+
+````
+special = "'_?"
+````
+The _special_ characters are the characters in addition to alphanumeric characters
+that are allowed to appear in a Dafny identifier. These are
+
+* `"'"` because mathematicians like to put primes on identifiers and some ML
+ programmers like to start names of type parameters with a "'".
+* "_" because computer scientists expect to be able to have underscores in identifiers.
+* "?" because it is useful to have "?" at the end of names of predicates,
+ e.g. "Cons?".
+
+````
+cr = '\r'
+````
+A carriage return character.
+
+````
+lf = '\n'
+````
+A line feed character.
+
+````
+tab = '\t'
+````
+A tab character.
+
+````
+space = ' '
+````
+A space character.
+
+````
+nondigitIdChar = letter + special
+````
+The characters that can be used in an identifier minus the digits.
+
+````
+idchar = nondigitIdChar + digit
+````
+The characters that can be used in an identifier.
+
+````
+nonidchar = ANY - idchar
+````
+Any character except those that can be used in an identifier.
+
+````
+charChar = ANY - '\'' - '\\' - cr - lf
+````
+Characters that can appear in a character constant.
+
+````
+stringChar = ANY - '"' - '\\' - cr - lf
+````
+Characters that can appear in a string constant.
+
+````
+verbatimStringChar = ANY - '"'
+````
+Characters that can appear in a verbatim string.
+
+### Comments
+Comments are in two forms.
+
+* They may go from "/*" to "*/" and be nested.
+* They may go from "//" to the end of the line.
+
+## Tokens
+As with most languages, Dafny syntax is defined in two levels. First the stream
+of input characters is broken up into _tokens_. Then these tokens are parsed
+using the Dafny grammar. The Dafny tokens are defined in this section.
+
+### Reserved Words
+The following reserved words appear in the Dafny grammar and may not be used
+as identifiers of user-defined entities:
+
+````
+reservedword =
+ "abstract" | "array" | "as" | "assert" | "assume" | "bool" | "break" |
+ "calc" | "case" | "char" | "class" | "codatatype" | "colemma" |
+ "constructor" | "copredicate" | "datatype" | "decreases" |
+ "default" | "else" | "ensures" | "exists" | "extends" | "false" |
+ "forall" | "free" | "fresh" | "function" | "ghost" | "if" | "imap" | "import" |
+ "in" | "include" | "inductive" | "int" | "invariant" | "iset" | "iterator" | "label" |
+ "lemma" | "map" | "match" | "method" | "modifies" | "modify" |
+ "module" | "multiset" | "nat" | "new" | "newtype" | "null" | "object" |
+ "old" | "opened" | "predicate" | "print" | "protected" |
+ "reads" | "real" | "refines" | "requires" | "return" | "returns" | "seq" |
+ "set" | "static" | "string" | "then" | "this" | "trait" | "true" | "type" |
+ "var" | "where" | "while" | "yield" | "yields" | arrayToken
+
+arrayToken = "array" [ posDigit { digit }]
+````
+
+An ``arrayToken`` is a reserved word that denotes an array type of
+given rank. `array` is an array type of rank 1 (aka a vector). `array2`
+is the type of two-dimensional arrays, etc.
+
+TODO: Is "_" is reserved word?
+
+### Identifiers
+
+````
+ident = nondigitIdChar { idchar } - arraytoken - chartoken - reservedword
+````
+In general Dafny identifiers are sequences of ``idChar`` characters where
+the first character is a ``nondigitIdChar``. However tokens that fit this pattern
+are not identifiers if they look like an array type token, a character literal,
+or a reserved work.
+
+### Digits
+````
+digits = digit {['_'] digit}
+````
+
+A sequence of decimal digits, possibly interspersed with underscores for readability. Example: `1_234_567`.
+````
+hexdigits = "0x" hexdigit {['_'] hexdigit}
+````
+
+A hexadecimal constant, possibly interspersed with underscores for readability.
+Example: `0xffff_ffff`.
+
+````
+decimaldigits = digit {['_'] digit} '.' digit {['_'] digit}
+````
+A decimal fraction constant, possibly interspersed with underscores for readability.
+Example: `123_456.789_123`.
+
+### Escaped Character
+In this section the "\\" characters are literal.
+````
+escapedChar =
+ ( "\\\'" | "\\&quot;" | "\\\\" | "\\0" | "\\n" | "\\r" | "\\t"
+ | "\\u" hexdigit hexdigit hexdigit hexdigit
+ )
+````
+
+In Dafny character or string literals escaped characters may be used
+to specify the presence of the delimiting quote, or back slash,
+or null, or new line, or carriage return or tab, or the
+Unicode character with given hexadecimal representation.
+
+### Character Constant Token
+````
+charToken = "'" ( charChar | escapedChar ) "'"
+````
+
+A character constant is enclosed by "'" and includes either a character
+from the ``charChar`` set, or an escaped character. Note that although Unicode
+letters are not allowed in Dafny identifiers, Dafny does support Unicode
+in its character and string constants and in its data. A character
+constant has type `char`.
+
+
+### String Constant Token
+````
+stringToken =
+ '"' { stringChar | escapedChar } '"'
+ | '@' '"' { verbatimStringChar | '"' '"' } '"'
+````
+
+A string constant is either a normal string constant or a verbatim string constant.
+A normal string constant is enclosed by '"' and can contain characters from the
+``stringChar`` set and escapes.
+
+A verbatim string constant is enclosed between '@"' and '"' and can
+consists of any characters (including newline characters) except that two
+successive double quotes give a way to escape one quote character inside
+the string.
+
+## Low Level Grammar Productions
+
+### Identifier Variations
+
+````
+Ident = ident
+````
+The ``Ident`` non-terminal is just an ``ident`` token and represents an ordinary
+identifier.
+
+````
+DotSuffix =
+ ( ident | digits | "requires" | "reads" )
+````
+When using the _dot_ notation to denote a component of a compound entity
+the token following the ".", in addition to being an identifier,
+can also be a natural number, or one of the keywords `requires` or `reads`.
+
+* Digits can be used to name fields of classes and destructors of
+ datatypes. For example, the built-in tuple datatypes have destructors
+ named 0, 1, 2, etc. Note that as a field or destructor name, internal
+ underscores matter, so 10 is different from 1_0.
+* `m.requires` is used to denote the precondition for method m.
+* `m.reads` is used to denote the things that method m may read.
+
+````
+NoUSIdent = ident - "_" { idChar }
+````
+A ``NoUSIdent`` is an identifier except that identifiers with a **leading**
+underscore are not allowed. The names of user-defined entities are
+required to be ``NoUSIdent``s. We introduce more mnemonic names
+for these below (e.g. ``ClassName``).
+
+````
+WildIdent = NoUSIdent | "_"
+````
+Identifier, disallowing leading underscores, except the "wildcard"
+identifier "_". When "_" appears it is replaced by a unique generated
+identifier distinct from user identifiers.
+
+### NoUSIdent Synonyms
+In the productions for the declaration of user-defined entities the name of the
+user-defined entity is required to be an identifier that does not start
+with an underscore, i.e., a ``NoUSIdent``. To make the productions more
+mnemonic, we introduce the following synonyms for ``NoUSIdent``.
+
+````
+ModuleName = NoUSIdent
+ClassName = NoUSIdent
+TraitName = NoUSIdent
+DatatypeName = NoUSIdent
+DatatypeMemberName = NoUSIdent
+NewtypeName = NoUSIdent
+NumericTypeName = NoUSIdent
+SynonymTypeName = NoUSIdent
+IteratorName = NoUSIdent
+TypeVariableName = NoUSIdent
+MethodName = NoUSIdent
+FunctionName = NoUSIdent
+PredicateName = NoUSIdent
+CopredicateName = NoUSIdent
+LabelName = NoUSIdent
+AttributeName = NoUSIdent
+FieldIdent = NoUSIdent
+````
+A ``FieldIdent`` is one of the ways to identify a field. The other is
+using digits.
+
+### Qualified Names
+A qualified name starts with the name of the top-level entity and then is followed by
+zero or more ``DotSuffix``s which denote a component. Examples:
+
+* `Module.MyType1`
+* `MyTuple.1`
+* `MyMethod.requires`
+
+The grammar does not actually have a production for qualified names
+except in the special case of a qualified name that is known to be
+a module name, i.e. a ``QualifiedModuleName``.
+
+### Identifier-Type Combinations
+In this section, we describe some nonterminals that combine an identifier and a type.
+
+````
+IdentType = WildIdent ":" Type
+````
+In Dafny, a variable or field is typically declared by giving its name followed by
+a ``colon`` and its type. An ``IdentType`` is such a construct.
+
+````
+GIdentType(allowGhostKeyword) = [ "ghost" ] IdentType
+````
+A ``GIdentType`` is a typed entity declaration optionally preceded by "ghost". The _ghost_
+qualifier means the entity is only used during verification but not in the generated code.
+Ghost variables are useful for abstractly representing internal state in specifications.
+If `allowGhostKeyword` is false then "ghost" is not allowed.
+
+````
+LocalIdentTypeOptional = WildIdent [ ":" Type ]
+````
+A ``LocalIdentTypeOptional`` is used when declaring local variables. In
+such a case a value may be specified for the variable in which case the
+type may be omitted because it can be inferred from the initial value.
+The initial value value may also be omitted.
+
+````
+IdentTypeOptional = WildIdent [ ":" Type ]
+````
+A ``IdentTypeOptional`` is typically used in a context where the type of the identifier
+may be inferred from the context. Examples are in pattern matching or quantifiers.
+
+````
+TypeIdentOptional = [ "ghost" ] ( NoUSIdent | digits ) ":" ] Type
+````
+``TypeIdentOptional``s are used in ``FormalsOptionalIds``. This represents situations
+where a type is given but there may not be an identifier.
+
+````
+FormalsOptionalIds = "(" [TypeIdentOptional { "," TypeIdentOptional } ] ")"
+````
+A ``FormalsOptionalIds`` is a formal parameter list in which the types are required
+but the names of the parameters is optional. This is used in algebraic
+datatype definitions.
+
+### Numeric Literals
+````
+Nat = ( digits | hexdigits )
+````
+A ``Nat`` represents a natural number expressed in either decimal or hexadecimal.
+
+````
+Dec = (decimaldigits )
+````
+A ``Dec`` represents a decimal fraction literal.
+
+# Programs
+````
+Dafny = { IncludeDirective_ } { TopDecl } EOF
+````
+At the top level, a Dafny program (stored as files with extension `.dfy`)
+is a set of declarations. The declarations introduce (module-level)
+methods and functions, as well as types (classes, traits, inductive and
+co-inductive datatypes, new_types, type synonyms, opaque types, and
+iterators) and modules, where the order of introduction is irrelevant. A
+class also contains a set of declarations, introducing fields, methods,
+and functions.
+
+When asked to compile a program, Dafny looks for the existence of a
+Main() method. If a legal Main() method is found, the compiler will emit
+a `.EXE`; otherwise, it will emit a `.DLL`.
+
+ (If there is more than one Main(), Dafny will try to emit an .EXE, but
+ this may cause the C# compiler to complain. One could imagine improving
+ this functionality so that Dafny will produce a polite error message in
+ this case.)
+
+In order to be a legal Main() method, the following must be true:
+
+* The method takes no parameters
+* The method is not a ghost method
+* The method has no requires clause
+* The method has no modifies clause
+* If the method is an instance (that is, non-static) method in a class,
+ then the enclosing class must not declare any constructor
+
+Note, however, that the following are allowed:
+
+* The method is allowed to be an instance method as long as the enclosing
+ class does not declare any constructor. In this case, the runtime
+ system will allocate an object of the enclosing class and will invoke
+ Main() on it.
+* The method is allowed to have `ensures` clauses
+* The method is allowed to have `decreases` clauses, including a
+ `decreases *`. (If Main() has a `decreases *`, then its execution may
+ go on forever, but in the absence of a `decreases *` on Main(), Dafny
+ will have verified that the entire execution will eventually
+ terminate.)
+
+An invocation of Dafny may specify a number of source files.
+Each Dafny file follows the grammar of the ``Dafny`` non-terminal.
+
+It consists of a sequence of optional _include_ directives followed by top
+level declarations followed by the end of the file.
+
+## Include Directives
+````
+IncludeDirective_ = "include" stringToken
+````
+
+Include directives have the form ``"include" stringToken`` where
+the string token is either a normal string token or a
+verbatim string token. The ``stringToken`` is interpreted as the name of
+a file that will be included in the Dafny source. These included
+files also obey the ``Dafny`` grammar. Dafny parses and processes the
+transitive closure of the original source files and all the included files,
+but will not invoke the verifier on these unless they have been listed
+explicitly on the command line.
+
+## Top Level Declarations
+````
+TopDecl = { { DeclModifier }
+ ( SubModuleDecl
+ | ClassDecl
+ | DatatypeDecl
+ | NewtypeDecl
+ | SynonymTypeDecl
+ | IteratorDecl
+ | TraitDecl
+ | ClassMemberDecl(moduleLevelDecl: true)
+ }
+````
+Top-level declarations may appear either at the top level of a Dafny file,
+or within a ``SubModuleDecl``. A top-level declaration is one of the following
+types of declarations which are described later.
+
+The ``ClassDecl``, ``DatatypeDecl``, ``NewtypeDecl``,
+``SynonymTypeDecl``, ``IteratorDecl``, and ``TraitDecl`` declarations are
+type declarations and are describe in Section [#sec-types]. Ordinarily
+``ClassMemberDecl``s appear in class declarations but they can also
+appear at the top level. In that case they are included as part of an
+implicit top-level class and are implicitly `static` (but cannot be
+declared as static). In addition a ``ClassMemberDecl`` that appears at
+the top level cannot be a ``FieldDecl``.
+
+## Declaration Modifiers
+````
+DeclModifier =
+ ( "abstract" | "ghost" | "static" | "protected"
+ | "extern" [ stringToken]
+ )
+````
+
+Top level declarations may be preceded by zero or more declaration
+modifiers. Not all of these are allowed in all contexts.
+
+The "abstract" modifiers may only be used for module declarations.
+An abstract module can leave some entities underspecified.
+Abstract modules are not compiled to C#.
+
+The ghost modifier is used to mark entities as being used for
+specification only, not for compilation to code.
+
+The static modifier is used for class members that that
+are associated with the class as a whole rather than with
+an instance of the class.
+
+The protected modifier is used to control the visibility of the
+body of functions.
+
+The extern modifier is used to alter the CompileName of
+entities. The CompileName is the name for the entity
+when translating to Boogie or C#.
+
+The following table shows modifiers that are available
+for each of the kinds of declaration. In the table
+we use already-ghost to denote that the item is not
+allowed to have the ghost modifier because it is already
+implicitly ghost.
+
++--------------------------+---------------------------------------+
+| Declaration | allowed modifiers |
++--------------------------+---------------------------------------+
+| module | abstract |
+| class | extern |
+| trait | - |
+| datatype or codatatype | - |
+| field | ghost |
+| newtype | - |
+| synonym types | - |
+| iterators | - |
+| method | ghost static extern |
+| lemma, colemma, comethod | already-ghost static protected |
+| inductive lemma | already-ghost static |
+| constructor | - |
+| function (non-method) | already-ghost static protected |
+| function method | already-ghost static protected extern |
+| predicate (non-method) | already-ghost static protected |
+| predicate method | already-ghost static protected extern |
+| inductive predicate | already-ghost static protected |
+| copredicate | already-ghost static protected |
++--------------------------+---------------------------------------+
+
+
+# Modules
+
+````
+SubModuleDecl = ( ModuleDefinition_ | ModuleImport_ )
+````
+
+Structuring a program by breaking it into parts is an important part of
+creating large programs. In Dafny, this is accomplished via _modules_.
+Modules provide a way to group together related types, classes, methods,
+functions, and other modules together, as well as control the scope of
+declarations. Modules may import each other for code reuse, and it is
+possible to abstract over modules to separate an implementation from an
+interface.
+
+## Declaring New Modules
+````
+ModuleDefinition_ = "module" { Attribute } ModuleName
+ [ [ "exclusively" ] "refines" QualifiedModuleName ]
+ "{" { TopDecl } "}"
+QualifiedModuleName = Ident { "." Ident }
+````
+A qualified name that is known to refer to a module.
+
+A new module is declared with the `module` keyword, followed by the name
+of the new module, and a pair of curly braces ({}) enclosing the body
+of the module:
+
+```
+module Mod {
+ ...
+}
+```
+
+A module body can consist of anything that you could put at the top
+level. This includes classes, datatypes, types, methods, functions, etc.
+
+```
+module Mod {
+ class C {
+ var f: int
+ method m()
+ }
+ datatype Option = A(int) | B(int)
+ type T
+ method m()
+ function f(): int
+}
+```
+
+You can also put a module inside another, in a nested fashion:
+
+```
+module Mod {
+ module Helpers {
+ class C {
+ method doIt()
+ var f: int
+ }
+ }
+}
+```
+
+Then you can refer to the members of the `Helpers` module within the
+`Mod` module by prefixing them with "Helpers.". For example:
+
+```
+module Mod {
+ module Helpers { ... }
+ method m() {
+ var x := new Helpers.C;
+ x.doIt();
+ x.f := 4;
+ }
+}
+```
+
+Methods and functions defined at the module level are available like
+classes, with just the module name prefixing them. They are also
+available in the methods and functions of the classes in the same
+module.
+
+```
+module Mod {
+ module Helpers {
+ function method addOne(n: nat): nat {
+ n + 1
+ }
+ }
+ method m() {
+ var x := 5;
+ x := Helpers.addOne(x); // x is now 6
+ }
+}
+```
+
+## Importing Modules
+````
+ModuleImport_ = "import" ["opened" ] ModuleName
+ [ "=" QualifiedModuleName
+ | "as" QualifiedModuleName ["default" QualifiedModuleName ]
+ ]
+ [ ";" ]
+````
+
+Declaring new submodules is useful, but sometimes you want to refer to
+things from an existing module, such as a library. In this case, you
+can _import_ one module into another. This is done via the `import`
+keyword, and there are a few different forms, each of which has a
+different meaning. The simplest kind is the concrete import, and has
+the form `import A = B`. This declaration creates a reference to the
+module `B` (which must already exist), and binds it to the new name
+`A`. Note this new name, i.e. `A`, is only bound in the module containing
+the import declaration; it does not create a global alias. For
+example, if `Helpers` was defined outside of `Mod`, then we could import
+it:
+
+```
+module Helpers {
+ ...
+}
+module Mod {
+ import A = Helpers
+ method m() {
+ assert A.addOne(5) == 6;
+ }
+}
+```
+
+Note that inside `m()`, we have to use `A` instead of `Helpers`, as we bound
+it to a different name. The name `Helpers` is not available inside `m()`,
+as only names that have been bound inside `Mod` are available. In order
+to use the members from another module, it either has to be declared
+there with `module` or imported with `import`.
+
+We don't have to give `Helpers` a new name, though, if we don't want
+to. We can write `import Helpers = Helpers` if we want to, and Dafny
+even provides the shorthand `import Helpers` for this behavior. You
+can't bind two modules with the same name at the same time, so
+sometimes you have to use the = version to ensure the names do not
+clash.
+
+The ``QualifiedModuleName`` in the ``ModuleImport_`` starts with a
+sibling module of the importing module, or with a submodule of the
+importing module. There is no wya to refer to the parent module, only
+sibling modules (and their submodules).
+
+## Opening Modules
+
+Sometimes, prefixing the members of the module you imported with the
+name is tedious and ugly, even if you select a short name when
+importing it. In this case, you can import the module as `opened`,
+which causes all of its members to be available without adding the
+module name. The `opened` keyword must immediately follow `import`, if it
+is present. For example, we could write the previous example as:
+
+```
+module Mod {
+ import opened Helpers
+ method m() {
+ assert addOne(5) == 6;
+ }
+}
+```
+
+When opening modules, the newly bound members will have low priority,
+so they will be hidden by local definitions. This means if you define
+a local function called `addOne`, the function from `Helpers` will no
+longer be available under that name. When modules are opened, the
+original name binding is still present however, so you can always use
+the name that was bound to get to anything that is hidden.
+
+```
+module Mod {
+ import opened Helpers
+ function addOne(n: nat): nat {
+ n - 1
+ }
+ method m() {
+ assert addOne(5) == 6; // this is now false,
+ // as this is the function just defined
+ assert Helpers.addOne(5) == 6; // this is still true
+ }
+}
+```
+
+If you open two modules that both declare members with the same name,
+then neither member can be referred to without a module prefix, as it
+would be ambiguous which one was meant. Just opening the two modules
+is not an error, however, as long as you don't attempt to use members
+with common names. The `opened` keyword can be used with any kind of
+`import` declaration, including the module abstraction form.
+
+## Module Abstraction
+
+Sometimes, using a specific implementation is unnecessary; instead,
+all that is needed is a module that implements some interface. In
+that case, you can use an _abstract_ module import. In Dafny, this is
+written `import A as B`. This means bind the name `A` as before, but
+instead of getting the exact module `B`, you get any module which is a
+_adheres_ of `B`. Typically, the module `B` may have abstract type
+definitions, classes with bodyless methods, or otherwise be unsuitable
+to use directly. Because of the way refinement is defined, any
+refinement of `B` can be used safely. For example, if we start with:
+
+```
+module Interface {
+ function method addSome(n: nat): nat
+ ensures addSome(n) > n
+}
+module Mod {
+ import A as Interface
+ method m() {
+ assert 6 <= A.addSome(5);
+ }
+}
+```
+
+then we can be more precise if we know that `addSome` actually adds
+exactly one. The following module has this behavior. Further, the
+postcondition is stronger, so this is actually a refinement of the
+Interface module.
+
+```
+module Implementation {
+ function method addSome(n: nat): nat
+ ensures addSome(n) == n + 1
+ {
+ n + 1
+ }
+}
+```
+
+We can then substitute `Implementation` for `A` in a new module, by
+declaring a refinement of `Mod` which defines `A` to be `Implementation`.
+
+```
+module Mod2 refines Mod {
+ import A = Implementation
+ ...
+}
+```
+
+You can also give an implementation directly, without introducing a
+refinement, by giving a default to the abstract import:
+
+```
+module Interface {
+ function method addSome(n: nat): nat
+ ensures addSome(n) > n
+}
+module Mod {
+ import A as Interface default Implementation
+ method m() {
+ assert 6 <= A.addSome(5);
+ }
+}
+module Implementation {
+ function method addSome(n: nat): nat
+ ensures addSome(n) == n + 1
+ {
+ n + 1
+ }
+}
+module Mod2 refines Mod {
+ import A as Interface default Implementation
+ ...
+}
+```
+
+Regardless of whether there is a default, the only things known about
+`A` in this example is that it has a function `addSome` that returns a
+strictly bigger result, so even with the default we still can't prove
+that `A.addSome(5) == 6`, only that `6 <= A.addSome(5)`.
+
+When you refine an abstract import into a concrete one, or giving a
+default, Dafny checkes that the concrete module is a
+refinement of the abstract one. This means that the methods must have
+compatible signatures, all the classes and datatypes with their
+constructors and fields in the abstract one must be present in the
+concrete one, the specifications must be compatible, etc.
+
+## Module Ordering and Dependencies
+
+Dafny isn't particular about which order the modules appear in, but
+they must follow some rules to be well formed. As a rule of thumb,
+there should be a way to order the modules in a program such that each
+only refers to things defined **before** it in the source text. That
+doesn't mean the modules have to be given in that order. Dafny will
+figure out that order for you, assuming you haven't made any circular
+references. For example, this is pretty clearly meaningless:
+
+```
+import A = B
+import B = A
+```
+
+You can have import statements at the toplevel, and you can import
+modules defined at the same level:
+
+```
+import A = B
+method m() {
+ A.whatever();
+}
+module B { ... }
+```
+
+In this case, everything is well defined because we can put `B` first,
+followed by the `A` import, and then finally `m()`. If there is no
+ordering, then Dafny will give an error, complaining about a cyclic
+dependency.
+
+Note that when rearranging modules and imports, they have to be kept
+in the same containing module, which disallows some pathological
+module structures. Also, the imports and submodules are always
+considered to be first, even at the toplevel. This means that the
+following is not well formed:
+
+```
+method doIt() { }
+module M {
+ method m() {
+ doIt();
+ }
+}
+```
+
+because the module `M` must come before any other kind of members, such
+as methods. To define global functions like this, you can put them in
+a module (called `Globals`, say) and open it into any module that needs
+its functionality. Finally, if you import via a path, such as `import A
+= B.C`, then this creates a dependency of `A` on `B`, as we need to know
+what `B` is (is it abstract or concrete, or a refinement?).
+
+## Name Resolution
+
+When Dafny sees something like `A<T>.B<U>.C<V>`, how does it know what each part
+refers to? The process Dafny uses to determine what identifier
+sequences like this refer to is name resolution. Though the rules may
+seem complex, usually they do what you would expect. Dafny first looks
+up the initial identifier. Depending on what the first identifier
+refers to, the rest of the identifier is looked up in the appropriate
+context.
+
+In terms of the grammar, sequences like the above are represented as
+a ``NameSegment`` followed by 0 or more ``Suffix``es. A ``Suffix`` is
+more general and the form shown above would be for when the
+``Suffix`` is an ``AugmentedDotSuffix_``.
+
+The resolution is different depending on whether it is in
+an expression context or a type context.
+
+### Expression Context Name Resolution
+
+The leading ``NameSegment`` is resolved using the first following
+rule that succeeds.
+
+0. Local variables, parameters and bound variables. These are things like
+ `x`, `y`, and `i` in `var x;, ... returns (y: int)`, and
+ `forall i :: ....` The declaration chosen is the match from the
+ innermost matching scope.
+
+1. If in a class, try to match a member of the class. If the member that
+ is found is not static an implicit `this` is inserted. This works for
+ fields, functions, and methods of the current class (if in a static
+ context, then only static methods and functions are allowed). You can
+ refer to fields of the current class either as `this.f` or `f`,
+ assuming of course that `f` hasn't be hidden by one of the above. You
+ can always prefix this if needed, which cannot be hidden. (Note, a
+ field whose name is a string of digits must always have some prefix.)
+
+2. If there is no ``Suffix``, then look for a datatype constructor, if
+ unambiguous. Any datatypes that don't need qualification (so the
+ datatype name itself doesn't need a prefix), and also have a uniquely
+ named constructor, can be referred to just by its name. So if
+ `datatype List = Cons(List) | Nil` is the only datatype that declares
+ `Cons` and `Nil` constructors, then you can write `Cons(Cons(Nil))`.
+ If the constructor name is not unique, then you need to prefix it with
+ the name of the datatype (for example `List.Cons(List.Nil)))`. This is
+ done per constructor, not per datatype.
+
+3. Look for a member of the enclosing module.
+
+4. Module-level (static) functions and methods
+
+TODO: Not sure about the following paragraph.
+Opened modules are treated at each level, after the declarations in the
+current module. Opened modules only affect steps 2, 3 and 5. If a
+ambiguous name is found, an error is generated, rather than continuing
+down the list. After the first identifier, the rules are basically the
+same, except in the new context. For example, if the first identifier is
+a module, then the next identifier looks into that module. Opened modules
+only apply within the module it is opened into. When looking up into
+another module, only things explicitly declared in that module are
+considered.
+
+To resolve expression `E.id`:
+
+First resolve expression E and any type arguments.
+
+* If `E` resolved to a module `M`:
+ 0. If `E.id<T>` is not followed by any further suffixes, look for
+ unambiguous datatype constructor.
+ 1. Member of module M: a sub-module (including submodules of imports),
+ class, datatype, etc.
+ 2. Static function or method.
+* If `E` denotes a type:
+ 3. Look up id as a member of that type
+* If `E` denotes an expression:
+ 4. Let T be the type of E. Look up id in T.
+
+### Type Context Name Resolution
+
+In a type context the priority of ``NameSegment`` resolution is:
+
+1. Type parameters.
+
+2. Member of enclosing module (type name or the name of a module).
+
+To resolve expression `E.id`:
+
+* If `E` resolved to a module `M`:
+ 0. Member of module M: a sub-module (including submodules of imports),
+ class, datatype, etc.
+* If `E` denotes a type:
+ 1. If `allowDanglingDotName`: Return the type of `E` and the given `E.id`,
+ letting the caller try to make sense of the final dot-name.
+ TODO: I don't under this sentence. What is `allowDanglingDotName`?
+
+# Specifications
+Specifications describe logical properties of Dafny methods, functions,
+lambdas, iterators and loops. They specify preconditions, postconditions,
+invariants, what memory locations may be read or modified, and
+termination information by means of _specification clauses_.
+For each kind of specification zero or more specification
+clauses (of the type accepted for that type of specification)
+may be given, in any order.
+
+We document specifications at these levels:
+
+- At the lowest level are the various kinds of specification clauses,
+ e.g. a ``RequiresClause_``.
+- Next are the specifications for entities that need them,
+ e.g. a ``MethodSpec``.
+- At the top level are the entity declarations that include
+ the specifications, e.g. ``MethodDecl``.
+
+This section documents the first two of these in a bottom-up manner.
+We first document the clauses and then the specifications
+that use them.
+
+## Specification Clauses
+
+### Requires Clause
+
+````
+RequiresClause_ =
+ "requires" Expression(allowLemma: false, allowLambda: false)
+````
+
+The **requires** clauses specify preconditions for methods,
+functions, lambda expressions and iterators. Dafny checks
+that the preconditions are met at all call sites. The
+callee may then assume the preconditions hold on entry.
+
+If no **requires** clause is specified it is taken to be `true`.
+
+If more than one **requires** clause is given, then the
+precondition is the conjunction of all of the expressions
+from all of the **requires** clauses.
+
+### Ensures Clause
+
+````
+EnsuresClause_ =
+ "ensures" { Attribute } Expression(allowLemma: false, allowLambda: false)
+ForAllEnsuresClause_ =
+ "ensures" Expression(allowLemma: false, allowLambda: true)
+FunctionEnsuresClause_ =
+ "ensures" Expression(allowLemma: false, allowLambda: false)
+````
+
+An **ensures** clause specifies the post condition for a
+method, function or iterator.
+
+If no **ensures** clause is specified it is taken to be `true`.
+
+If more than one **ensures** clause is given, then the
+postcondition is the conjunction of all of the expressions
+from all of the **ensures** clauses.
+
+TODO: In the present sources ``FunctionEnsuresClause_`` differs from
+``EnsuresClause_`` only in that it is not allowed to specify
+``Attribute``s. This seems like a bug and will likely
+be fixed in a future version.
+
+### Decreases Clause
+````
+DecreasesClause_(allowWildcard, allowLambda) =
+ "decreases" { Attribute } DecreasesList(allowWildcard, allowLambda)
+FunctionDecreasesClause_(allowWildcard, allowLambda) =
+ "decreases" DecreasesList(allowWildcard, allowLambda)
+````
+
+````
+DecreasesList(allowWildcard, allowLambda) =
+ PossiblyWildExpression(allowLambda)
+ { "," PossiblyWildExpression(allowLambda) }
+````
+If `allowWildcard` is false but one of the
+``PossiblyWildExpression``s is a wild-card, an error is
+reported.
+
+TODO: A ``FunctionDecreasesClause_`` is not allowed to specify
+``Attribute``s. this will be fixed in a future version.
+
+**Decreases** clauses are used to prove termination in the
+presence of recursion. if more than one **decreases** clause is given
+it is as if a single **decreases** clause had been given with the
+collected list of arguments. That is,
+
+```
+decreases A, B
+decreases C, D
+```
+
+is equivalent to
+
+```
+decreases A, B, C, D
+```
+
+If any of the expressions in the **decreases** clause are wild (i.e. "*")
+then proof of termination will be skipped.
+
+Termination metrics in Dafny, which are declared by **decreases** clauses,
+are lexicographic tuples of expressions. At each recursive (or mutually
+recursive) call to a function or method, Dafny checks that the effective
+**decreases** clause of the callee is strictly smaller than the effective
+**decreases** clause of the caller.
+
+ What does "strictly smaller" mean? Dafny provides a built-in
+ well-founded order for every type and, in some cases, between types. For
+ example, the Boolean "false" is strictly smaller than "true", the
+ integer 78 is strictly smaller than 102, the set `{2,5}` is strictly
+ smaller than the set `{2,3,5}`, and for "s" of type `seq<Color>` where
+ `Color` is some inductive datatype, the color `s[0]` is strictly less than
+ `s` (provided `s` is nonempty).
+
+What does "effective decreases clause" mean? Dafny always appends a
+"top" element to the lexicographic tuple given by the user. This top
+element cannot be syntactically denoted in a Dafny program and it never
+occurs as a run-time value either. Rather, it is a fictitious value,
+which here we will denote \top, such that each value that can ever occur
+in a Dafny program is strictly less than \top. Dafny sometimes also
+prepends expressions to the lexicographic tuple given by the user. The
+effective decreases clause is any such prefix, followed by the
+user-provided decreases clause, followed by \top. We said "user-provided
+decreases clause", but if the user completely omits a "decreases" clause,
+then Dafny will usually make a guess at one, in which case the effective
+decreases clause is any prefix followed by the guess followed by \top.
+(If you're using the Dafny IDE in Visual Studio, you can hover the mouse
+over the name of a recursive function or method, or the "while" keyword
+for a loop, to see the "decreases" clause that Dafny guessed, if any.)
+
+Here is a simple but interesting example: the Fibonacci function.
+
+```
+function Fib(n: nat) : nat
+{
+ if n < 2 then n else Fib(n-2) + Fib(n-1)
+}
+
+```
+
+In this example, if you hover your mouse over the function name
+you will see that Dafny has supplied a `**decreases** n` clause.
+
+Let's take a look at the kind of example where a mysterious-looking
+decreases clause like "Rank, 0" is useful.
+
+Consider two mutually recursive methods, `A` and `B`:
+```
+method A(x: nat)
+{
+ B(x);
+}
+
+method B(x: nat)
+{
+ if x != 0 { A(x-1); }
+}
+```
+
+To prove termination of `A` and `B`, Dafny needs to have effective
+decreases clauses for A and B such that:
+
+* the measure for the callee `B(x)` is strictly smaller than the measure
+ for the caller `A(x)`, and
+
+* the measure for the callee `A(x-1)` is strictly smaller than the measure
+ for the caller `B(x)`.
+
+Satisfying the second of these conditions is easy, but what about the
+first? Note, for example, that declaring both `A` and `B` with "decreases x"
+does not work, because that won't prove a strict decrease for the call
+from `A(x)` to `B(x)`.
+
+Here's one possibility (for brevity, we will omit the method bodies):
+```
+method A(x: nat)
+ decreases x, 1
+
+method B(x: nat)
+ decreases x, 0
+```
+
+For the call from `A(x)` to `B(x)`, the lexicographic tuple `"x, 0"` is
+strictly smaller than `"x, 1"`, and for the call from `B(x)` to `A(x-1)`, the
+lexicographic tuple `"x-1, 1"` is strictly smaller than `"x, 0"`.
+
+ Two things to note: First, the choice of "0" and "1" as the second
+ components of these lexicographic tuples is rather arbitrary. It could
+ just as well have been "false" and "true", respectively, or the sets
+ `{2,5}` and `{2,3,5}`. Second, the keyword **decreases** often gives rise to
+ an intuitive English reading of the declaration. For example, you might
+ say that the recursive calls in the definition of the familiar Fibonacci
+ function `Fib(n)` "decreases n". But when the lexicographic tuple contains
+ constants, the English reading of the declaration becomes mysterious and
+ may give rise to questions like "how can you decrease the constant 0?".
+ The keyword is just that---a keyword. It says "here comes a list of
+ expressions that make up the lexicographic tuple we want to use for the
+ termination measure". What is important is that one effective decreases
+ clause is compared against another one, and it certainly makes sense to
+ compare something to a constant (and to compare one constant to
+ another).
+
+ We can simplify things a little bit by remembering that Dafny appends
+ \top to the user-supplied decreases clause. For the A-and-B example,
+ this lets us drop the constant from the **decreases** clause of A:
+
+```
+ method A(x: nat)
+ decreases x
+
+method B(x: nat)
+ decreases x, 0
+```
+
+The effective decreases clause of `A` is `"x, \top"` and the effective
+decreases clause of `B` is `"x, 0, \top"`. These tuples still satisfy the two
+conditions `(x, 0, \top) < (x, \top)` and `(x-1, \top) < (x, 0, \top)`. And
+as before, the constant "0" is arbitrary; anything less than \top (which
+is any Dafny expression) would work.
+
+Let's take a look at one more example that better illustrates the utility
+of `\top`. Consider again two mutually recursive methods, call them `Outer`
+and `Inner`, representing the recursive counterparts of what iteratively
+might be two nested loops:
+```
+method Outer(x: nat)
+{
+ // set y to an arbitrary non-negative integer
+ var y :| 0 <= y;
+ Inner(x, y);
+}
+
+method Inner(x: nat, y: nat)
+{
+ if y != 0 {
+ Inner(x, y-1);
+ } else if x != 0 {
+ Outer(x-1);
+ }
+}
+```
+The body of `Outer` uses an assign-such-that statement to represent some
+computation that takes place before `Inner` is called. It sets "y" to some
+arbitrary non-negative value. In a more concrete example, `Inner` would do
+some work for each "y" and then continue as `Outer` on the next smaller
+"x".
+
+Using a **decreases** clause `"x, y"` for `Inner` seems natural, but if
+we don't have any bound on the size of the `"y"` computed by `Outer`,
+there is no expression we can write in **decreases** clause of `Outer`
+that is sure to lead to a strictly smaller value for `"y"` when `Inner`
+is called. `\top` to the rescue. If we arrange for the effective
+decreases clause of `Outer` to be `"x, \top"` and the effective decreases
+clause for `Inner` to be `"x, y, \top"`, then we can show the strict
+decreases as required. Since `\top` is implicitly appended, the two
+decreases clauses declared in the program text can be:
+```
+method Outer(x: nat)
+ decreases x
+
+method Inner(x: nat, y: nat)
+ decreases x, y
+```
+Moreover, remember that if a function or method has no user-declared
+**decreases** clause, Dafny will make a guess. The guess is (usually)
+the list of arguments of the function/method, in the order given. This is
+exactly the decreases clauses needed here. Thus, Dafny successfully
+verifies the program without any explicit decreases clauses:
+```
+method Outer(x: nat)
+{
+ var y :| 0 <= y;
+ Inner(x, y);
+}
+
+method Inner(x: nat, y: nat)
+{
+ if y != 0 {
+ Inner(x, y-1);
+ } else if x != 0 {
+ Outer(x-1);
+ }
+}
+```
+The ingredients are simple, but the end result may seem like magic. For many users, however, there may be no magic at all -- the end result may be so natural that the user never even has to bothered to think about that there was a need to prove termination in the first place.
+
+
+### Framing
+````
+FrameExpression(allowLemma, allowLambda) =
+ ( Expression(allowLemma, allowLambda) [ FrameField ]
+ | FrameField )
+````
+
+````
+FrameField = "`" Ident
+````
+
+````
+PossiblyWildFrameExpression(allowLemma) =
+ ( "*" | FrameExpression(allowLemma, allowLambda: false) )
+````
+
+Frame expressions are used to denote the set of memory locations
+that a Dafny program element may read or write. A frame
+expression is a set expression. The form `{}` (that is, the empty set)
+says that no memory locations may be modified,
+which is also the default if no **modifies** clause is given explicitly.
+
+Note that framing only applies to the heap, or memory accessed through
+references. Local variables are not stored on the heap, so they cannot be
+mentioned (well, they are not in scope in the declaration) in reads
+annotations. Note also that types like sets, sequences, and multisets are
+value types, and are treated like integers or local variables. Arrays and
+objects are reference types, and they are stored on the heap (though as
+always there is a subtle distinction between the reference itself and the
+value it points to.)
+
+The ``FrameField`` construct is used to specify a field of a
+class object. The identifier following the back-quote is the
+name of the field being referenced.
+If the `FrameField` is preceded by an expression the expression
+must be a reference to an object having that field.
+If the `FrameField` is not preceded by an expression then
+the frame expression is referring to that field of the current
+object. This form is only used from a method of a class.
+
+The use of ``FrameField`` is discouraged as in practice it has not
+been shown to either be more concise or to perform better.
+Also, there's (unfortunately) no form of it for array
+elements---one could imagine
+
+```
+ modifies a`[j]
+```
+Also, ``FrameField`` is not taken into consideration for
+lambda expressions.
+
+### Reads Clause
+````
+FunctionReadsClause_ =
+ "reads"
+ PossiblyWildFrameExpression (allowLemma: false)
+ { "," PossiblyWildFrameExpression(allowLemma: false) }
+LambdaReadsClause_ =
+ "reads" PossiblyWildFrameExpression(allowLemma: true)
+IteratorReadsClause_ =
+ "reads" { Attribute }
+ FrameExpression(allowLemma: false, allowLambda: false)
+ { "," FrameExpression(allowLemma: false, allowLambda: false) }
+PossiblyWildExpression(allowLambda) =
+ ( "*" | Expression(allowLemma: false, allowLambda) )
+````
+
+Functions are not allowed to have side effects but may be restricted in
+what they can read. The _reading frame_ of a function (or predicate) is all
+the memory locations that the function is allowed to read. The reason we
+might limit what a function can read is so that when we write to memory,
+we can be sure that functions that did not read that part of memory have
+the same value they did before. For example, we might have two arrays,
+one of which we know is sorted. If we did not put a reads annotation on
+the sorted predicate, then when we modify the unsorted array, we cannot
+determine whether the other array stopped being sorted. While we might be
+able to give invariants to preserve it in this case, it gets even more
+complex when manipulating data structures. In this case, framing is
+essential to making the verification process feasible.
+
+It is not just the body of a function that is subject to **reads**
+checks, but also its precondition and the **reads** clause itself.
+
+A reads clause can list a wildcard ("*"), which allows the enclosing
+function to read anything. In many cases, and in particular in all cases
+where the function is defined recursively, this makes it next to
+impossible to make any use of the function. Nevertheless, as an
+experimental feature, the language allows it (and it is sound).
+Note that a "*" makes the rest of the frame expression irrelevant.
+
+A **reads** clause specifies the set of memory locations that a function,
+lambda, or iterator may read. If more than one **reads** clause is given
+in a specification the effective read set is the union of the sets
+specified. If there are no **reads** clauses the effective read set is
+empty. If `"*"` is given in a **reads** clause it means any memory may be
+read.
+
+TODO: It would be nice if the different forms of read clauses could be
+combined. In a future version the single form of read clause will allow
+a list and attributes.
+
+### Modifies Clause
+
+````
+ModifiesClause_ =
+ "modifies" { Attribute }
+ FrameExpression(allowLemma: false, allowLambda: false)
+ { "," FrameExpression(allowLemma: false, allowLambda: false) }
+````
+
+Frames also affect methods. As you might have guessed, methods are not
+required to list the things they read. Methods are allowed to read
+whatever memory they like, but they are required to list which parts of
+memory they modify, with a modifies annotation. They are almost identical
+to their reads cousins, except they say what can be changed, rather than
+what the value of the function depends on. In combination with reads,
+modification restrictions allow Dafny to prove properties of code that
+would otherwise be very difficult or impossible. Reads and modifies are
+one of the tools that allow Dafny to work on one method at a time,
+because they restrict what would otherwise be arbitrary modifications of
+memory to something that Dafny can reason about.
+
+Note that fields of newly allocated objects can always be modified.
+
+It is also possible to frame what can be modified by a block statement
+by means of the block form of the
+[modify statement](#sec-modify-statement) (Section [#sec-modify-statement]).
+
+A **modifies** clause specifies the set of memory locations that a
+method, iterator or loop body may modify. If more than one **modifies**
+clause is given in a specification, the effective modifies set is the
+union of the sets specified. If no **modifies** clause is given the
+effective modifies set is empty. A loop can also have a
+**modifies** clause. If none is given, the loop gets to modify anything
+the enclosing context is allowed to modify.
+
+### Invariant Clause
+````
+InvariantClause_ =
+ "invariant" { Attribute }
+ Expression(allowLemma: false, allowLambda: true)
+````
+
+An **invariant** clause is used to specify an invariant
+for a loop. If more than one **invariant** clause is given for
+a loop the effective invariant is the conjunction of
+the conditions specified.
+
+The invariant must hold on entry to the loop. And assuming it
+is valid on entry, Dafny must be able to prove that it then
+holds at the end of the loop.
+
+## Method Specification
+````
+MethodSpec =
+ { ModifiesClause_
+ | RequiresClause_
+ | EnsuresClause_
+ | DecreasesClause_(allowWildcard: true, allowLambda: false)
+ }
+````
+
+A method specification is zero or more **modifies**, **requires**,
+**ensures** or **decreases** clauses, in any order.
+A method does not have **reads** clauses because methods are allowed to
+read any memory.
+
+## Function Specification
+````
+FunctionSpec =
+ { RequiresClause_
+ | FunctionReadsClause_
+ | FunctionEnsuresClause_
+ | FunctionDecreasesClause_(allowWildcard: false, allowLambda: false)
+ }
+````
+
+A function specification is zero or more **reads**, **requires**,
+**ensures** or **decreases** clauses, in any order. A function
+specification does not have **modifies** clauses because functions are not
+allowed to modify any memory.
+
+## Lambda Specification
+````
+LambdaSpec_ =
+ { LambdaReadsClause_
+ | RequiresClause_
+ }
+````
+
+A lambda specification is zero or more **reads** or **requires** clauses.
+Lambda specifications do not have **ensures** clauses because the body
+is never opaque.
+Lambda specifications do not have **decreases**
+clauses because they do not have names and thus cannot be recursive. A
+lambda specification does not have **modifies** clauses because lambdas
+are not allowed to modify any memory.
+
+## Iterator Specification
+````
+IteratorSpec =
+ { IteratorReadsClause_
+ | ModifiesClause_
+ | [ "yield" ] RequiresClause_
+ | [ "yield" ] EnsuresClause_
+ | DecreasesClause_(allowWildcard: false, allowLambda: false)
+ }
+````
+
+An iterator specification applies both to the iterator's constructor
+method and to its `MoveNext` method. The **reads** and **modifies**
+clauses apply to both of them. For the **requires** and **ensures**
+clauses, if `yield` is not present they apply to the constructor,
+but if `yield` is present they apply to the `MoveNext` method.
+
+TODO: What is the meaning of a **decreases** clause on an iterator?
+Does it apply to `MoveNext`? Make sure our description of
+iterators explains these.
+
+TODO: What is the relationship between the post condition and
+the `Valid()` predicate?
+
+## Loop Specification
+````
+LoopSpec =
+ { InvariantClause_
+ | DecreasesClause_(allowWildcard: true, allowLambda: true)
+ | ModifiesClause_
+ }
+````
+
+A loop specification provides the information Dafny needs to
+prove properties of a loop. The ``InvariantClause_`` clause
+is effectively a precondition and it along with the
+negation of the loop test condition provides the postcondition.
+The ``DecreasesClause_`` clause is used to prove termination.
+
+# Types
+````
+Type = DomainType [ "->" Type ]
+````
+A Dafny type is a domain type (i.e. a type that can be the domain of a
+function type) optionally followed by an arrow and a range type.
+
+````
+DomainType =
+ ( BoolType_ | CharType_ | NatType_ | IntType_ | RealType_ | ObjectType_
+ | FiniteSetType_ | InfiniteSetType_ | MultisetType_
+ | SequenceType_ | StringType_
+ | FiniteMapType_ | InfiniteMapType_ | ArrayType_
+ | TupleType_ | NamedType_ )
+````
+The domain types comprise the builtin scalar types, the builtin
+collection types, tuple types (including as a special case
+a parenthesized type) and reference types.
+
+
+Dafny types may be categorized as either value types or reference types.
+
+## Value Types
+The value types are those whose values do not lie in the program heap.
+These are:
+
+* The basic scalar types: `bool`, `char`, `nat`, `int`, `real`
+* The built-in collection types: `set`, `multiset`, `seq`, `string`, `map`, `imap`
+* Tuple Types
+* Inductive and co-inductive types
+
+Data items having value types are passed by value. Since they are not
+considered to occupy _memory_, framing expressions do not reference them.
+
+## Reference Types
+Dafny offers a host of _reference types_. These represent
+_references_ to objects allocated dynamically in the program heap. To
+access the members of an object, a reference to (that is, a _pointer_
+to or _object identity_ of) the object is _dereferenced_.
+
+The reference types are class types, traits and array types.
+
+The special value `null` is part of every reference
+type.[^fn-nullable]
+
+[^fn-nullable]: This will change in a future version of Dafny that
+ will support both nullable and (by default) non-null reference
+ types.
+
+## Named Types
+````
+NamedType_ = NameSegmentForTypeName { "." NameSegmentForTypeName }
+````
+
+A ``NamedType_`` is used to specify a user-defined type by name
+(possibly module-qualified). Named types are introduced by
+class, trait, inductive, co-inductive, synonym and opaque
+type declarations. They are also used to refer to type variables.
+
+````
+NameSegmentForTypeName = Ident [ GenericInstantiation ]
+````
+A ``NameSegmentForTypeName`` is a type name optionally followed by a
+``GenericInstantiation`` which supplies type parameters to a generic
+type, if needed. It is a special case of a ``NameSegment``
+(See Section [#sec-name-segment])
+that does not allow a ``HashCall``.
+
+The following sections describe each of these kinds of types in more detail.
+
+# Basic types
+
+Dafny offers these basic types: `bool` for booleans, `char` for
+characters, `int` and `nat` for integers, and `real` for reals.
+
+## Booleans
+````
+BoolType_ = "bool"
+````
+
+There are two boolean values and each has a corresponding literal in
+the language: `false` and `true`.
+
+In addition to equality (`==`) and disequality (`!=`), which are
+defined on all types, type `bool` supports the following operations:
+
++--------------------+------------------------------------+
+| operator | description |
++--------------------+------------------------------------+
+| `<==>` | equivalence (if and only if) |
++--------------------+------------------------------------+
+| `==>` | implication (implies) |
+| `<==` | reverse implication (follows from) |
++--------------------+------------------------------------+
+| `&&` | conjunction (and) |
+| [\|\|]{.monospace} | disjunction (or) |
++--------------------+------------------------------------+
+| `!` | negation (not) |
++--------------------+------------------------------------+
+
+Negation is unary; the others are binary. The table shows the operators
+in groups of increasing binding power, with equality binding stronger
+than conjunction and disjunction, and weaker than negation. Within
+each group, different operators do not associate, so parentheses need
+to be used. For example,
+```
+A && B || C // error
+```
+would be ambiguous and instead has to be written as either
+```
+(A && B) || C
+```
+or
+```
+A && (B || C)
+```
+depending on the intended meaning.
+
+### Equivalence Operator
+The expressions `A <==> B` and `A == B` give the same value, but note
+that `<==>` is _associative_ whereas `==` is _chaining_. So,
+```
+A <==> B <==> C
+```
+is the same as
+```
+A <==> (B <==> C)
+```
+and
+```
+(A <==> B) <==> C
+```
+whereas
+```
+A == B == C
+```
+is simply a shorthand for
+```
+A == B && B == C
+```
+
+### Conjunction and Disjunction
+Conjunction is associative and so is disjunction. These operators are
+_short circuiting (from left to right)_, meaning that their second
+argument is evaluated only if the evaluation of the first operand does
+not determine the value of the expression. Logically speaking, the
+expression `A && B` is defined when `A` is defined and either `A`
+evaluates to `false` or `B` is defined. When `A && B` is defined, its
+meaning is the same as the ordinary, symmetric mathematical
+conjunction &and;. The same holds for `||` and &or;.
+
+### Implication and Reverse Implication
+Implication is _right associative_ and is short-circuiting from left
+to right. Reverse implication `B <== A` is exactly the same as
+`A ==> B`, but gives the ability to write the operands in the opposite
+order. Consequently, reverse implication is _left associative_ and is
+short-circuiting from _right to left_. To illustrate the
+associativity rules, each of the following four lines expresses the
+same property, for any `A`, `B`, and `C` of type `bool`:
+```
+A ==> B ==> C
+A ==> (B ==> C) // parentheses redundant, since ==> is right associative
+C <== B <== A
+(C <== B) <== A // parentheses redundant, since <== is left associative
+```
+To illustrate the short-circuiting rules, note that the expression
+`a.Length` is defined for an array `a` only if `a` is not `null` (see
+Section [#sec-reference-types]), which means the following two
+expressions are well-formed:
+```
+a != null ==> 0 <= a.Length
+0 <= a.Length <== a != null
+```
+The contrapositive of these two expressions would be:
+```
+a.Length < 0 ==> a == null // not well-formed
+a == null <== a.Length < 0 // not well-formed
+```
+but these expressions are not well-formed, since well-formedness
+requires the left (and right, respectively) operand, `a.Length < 0`,
+to be well-formed by itself.
+
+Implication `A ==> B` is equivalent to the disjunction `!A || B`, but
+is sometimes (especially in specifications) clearer to read. Since,
+`||` is short-circuiting from left to right, note that
+```
+a == null || 0 <= a.Length
+```
+is well-formed, whereas
+```
+0 <= a.Length || a == null // not well-formed
+```
+is not.
+
+In addition, booleans support _logical quantifiers_ (forall and
+exists), described in section [#sec-quantifier-expression].
+
+
+## Numeric types
+
+````
+IntType_ = "int"
+RealType_ = "real"
+````
+
+Dafny supports _numeric types_ of two kinds, _integer-based_, which
+includes the basic type `int` of all integers, and _real-based_, which
+includes the basic type `real` of all real numbers. User-defined
+numeric types based on `int` and `real`, called _newtypes_, are
+described in Section [#sec-newtypes]. Also, the _subset type_
+`nat`, representing the non-negative subrange of `int`, is described
+in Section [#sec-subset-types].
+
+The language includes a literal for each non-negative integer, like
+`0`, `13`, and `1985`. Integers can also be written in hexadecimal
+using the prefix "`0x`", as in `0x0`, `0xD`, and `0x7c1` (always with
+a lower case `x`, but the hexadecimal digits themselves are case
+insensitive). Leading zeros are allowed. To form negative integers,
+use the unary minus operator.
+
+There are also literals for some of the non-negative reals. These are
+written as a decimal point with a nonempty sequence of decimal digits
+on both sides. For example, `1.0`, `1609.344`, and `0.5772156649`.
+
+For integers (in both decimal and hexadecimal form) and reals,
+any two digits in a literal may be separated by an underscore in order
+to improve human readability of the literals. For example:
+```
+1_000_000 // easier to read than 1000000
+0_12_345_6789 // strange but legal formatting of 123456789
+0x8000_0000 // same as 0x80000000 -- hex digits are often placed in groups of 4
+0.000_000_000_1 // same as 0.0000000001 -- 1 \([&Aring;ngstr&ouml;m]{.comment-color}\)
+```
+
+In addition to equality and disequality, numeric types
+support the following relational operations:
+
++-----------------+------------------------------------+
+| operator | description |
++-----------------+------------------------------------+
+| [<]{.monospace} | less than |
+| `<=` | at most |
+| `>=` | at least |
+| `>` | greater than |
++-----------------+------------------------------------+
+
+Like equality and disequality, these operators are chaining, as long
+as they are chained in the "same direction". That is,
+```
+A <= B < C == D <= E
+```
+is simply a shorthand for
+```
+A <= B && B < C && C == D && D <= E
+```
+whereas
+```
+A < B > C
+```
+is not allowed.
+
+There are also operators on each numeric type:
+
++---------------+------------------------------------+
+| operator | description |
++---------------+------------------------------------+
+| `+` | addition (plus) |
+| `-` | subtraction (minus) |
++---------------+------------------------------------+
+| `*` | multiplication (times) |
+| `/` | division (divided by) |
+| `%` | modulus (mod) |
++---------------+------------------------------------+
+| `-` | negation (unary minus) |
++---------------+------------------------------------+
+
+The binary operators are left associative, and they associate with
+each other in the two groups. The groups are listed in order of
+increasing binding power, with equality binding more strongly than the
+multiplicative operators and weaker than the unary operator.
+Modulus is supported only for integer-based numeric types. Integer
+division and modulus are the _Euclidean division and modulus_. This
+means that modulus always returns a non-negative, regardless of the
+signs of the two operands. More precisely, for any integer `a` and
+non-zero integer `b`,
+```
+a == a / b * b + a % b
+0 <= a % b < B
+```
+where `B` denotes the absolute value of `b`.
+
+Real-based numeric types have a member `Trunc` that returns the
+_floor_ of the real value, that is, the largest integer not exceeding
+the real value. For example, the following properties hold, for any
+`r` and `r'` of type `real`:
+```
+3.14.Trunc == 3
+(-2.5).Trunc == -3
+-2.5.Trunc == -2
+real(r.Trunc) <= r
+r <= r' ==> r.Trunc <= r'.Trunc
+```
+Note in the third line that member access (like `.Trunc`) binds
+stronger than unary minus. The fourth line uses the conversion
+function `real` from `int` to `real`, as described in Section
+[#sec-numeric-conversion-operations].
+
+## Characters
+
+````
+CharType_ = "char"
+````
+
+Dafny supports a type `char` of _characters_. Character literals are
+enclosed in single quotes, as in `'D'`. Their form is described
+by the ``charToken`` nonterminal in the grammar. To write a single quote as a
+character literal, it is necessary to use an _escape sequence_.
+Escape sequences can also be used to write other characters. The
+supported escape sequences are as follows:
+
++--------------------+------------------------------------------------------------+
+| escape sequence | meaning |
++--------------------+------------------------------------------------------------+
+| `\'` | the character `'` |
+| [\\\"]{.monospace} | the character [\"]{.monospace} |
+| `\\` | the character `\` |
+| `\0` | the null character, same as `\u0000` |
+| `\n` | line feed |
+| `\r` | carriage return |
+| `\t` | horizontal tab |
+| `\u\(_xxxx_\)` | universal character whose hexadecimal code is `\(_xxxx_\)` |
++--------------------+------------------------------------------------------------+
+
+The escape sequence for a double quote is redundant, because
+[\'\"\']{.monospace} and [\'\\\"\']{.monospace} denote the same
+character---both forms are provided in order to support the same
+escape sequences as for string literals (Section [#sec-strings]).
+In the form `\u\(_xxxx_\)`, the `u` is always lower case, but the four
+hexadecimal digits are case insensitive.
+
+Character values are ordered and can be compared using the standard
+relational operators:
+
++-----------------+------------------------------------+
+| operator | description |
++-----------------+------------------------------------+
+| [<]{.monospace} | less than |
+| `<=` | at most |
+| `>=` | at least |
+| `>` | greater than |
++-----------------+------------------------------------+
+
+Sequences of characters represent _strings_, as described in Section
+[#sec-strings].
+
+The only other operations on characters are obtaining a character
+by indexing into a string, and the implicit conversion to string
+when used as a parameter of a `print` statement.
+
+TODO: Are there any conversions between `char` values and numeric values?
+
+# Type parameters
+
+````
+GenericParameters = "<" TypeVariableName [ "(" "==" ")" ]
+ { "," TypeVariableName [ "(" "==" ")" ] } ">"
+````
+Many of the types (as well as functions and methods) in Dafny can be
+parameterized by types. These _type parameters_ are typically
+declared inside angle brackets and can stand for any type.
+
+It is sometimes necessary to restrict these type parameters so that
+they can only be instantiated by certain families of types. As such,
+Dafny distinguishes types that support the equality operation
+not only in ghost contexts but also in compiled contexts. To indicate
+that a type parameter is restricted to such _equality supporting_
+types, the name of the type parameter takes the suffix
+"`(==)`".[^fn-type-mode] For example,
+```
+method Compare<T(==)>(a: T, b: T) returns (eq: bool)
+{
+ if a == b { eq := true; } else { eq := false; }
+}
+```
+is a method whose type parameter is restricted to equality-supporting
+types. Again, note that _all_ types support equality in _ghost_
+contexts; the difference is only for non-ghost (that is, compiled)
+code. Co-inductive datatypes, function types, as well as inductive
+datatypes with ghost parameters are examples of types that are not
+equality supporting.
+
+[^fn-type-mode]: Being equality-supporting is just one of many
+ _modes_ that one can imagine types in a rich type system to have.
+ For example, other modes could include having a total order,
+ being zero-initializable, and possibly being uninhabited. If
+ Dafny were to support more modes in the future, the "`(\(&nbsp;\))`"-suffix
+ syntax may be extended. For now, the suffix can only indicate the
+ equality-supporting mode.
+
+Dafny has some inference support that makes certain signatures less
+cluttered (described in a different part of the Dafny language
+reference). In some cases, this support will
+infer that a type parameter must be restricted to equality-supporting
+types, in which case Dafny adds the "`(==)`" automatically.
+
+TODO: Need to describe type inference somewhere.
+
+# Generic Instantiation
+````
+GenericInstantiation = "<" Type { "," Type } ">"
+````
+When a generic entity is used, actual types must be specified for each
+generic parameter. This is done using a ``GenericInstantiation``.
+If the `GenericInstantiation` is omitted, type inference will try
+to fill these in.
+
+# Collection types
+
+Dafny offers several built-in collection types.
+
+## Sets
+````
+FiniteSetType_ = "set" [ GenericInstantiation ]
+InfiniteSetType_ = "iset" [ GenericInstantiation ]
+````
+
+For any type `T`, each value of type `set<T>` is a finite set of
+`T` values.
+
+TODO:
+Set membership is determined by equality in the type `T`,
+so `set<T>` can be used in a non-ghost context only if `T` is equality
+supporting.
+
+For any type `T`, each value of type `iset<T>` is a potentially infinite
+set of `T` values.
+
+A set can be formed using a _set display_ expression, which is a
+possibly empty, unordered, duplicate-insensitive list of expressions
+enclosed in curly braces. To illustrate,
+```
+{} {2, 7, 5, 3} {4+2, 1+5, a*b}
+```
+are three examples of set displays. There is also a _set comprehension_
+expression (with a binder, like in logical quantifications), described in
+section [#sec-set-comprehension-expressions].
+
+In addition to equality and disequality, set types
+support the following relational operations:
+
++-----------------+------------------------------------+
+| operator | description |
++-----------------+------------------------------------+
+| [<]{.monospace} | proper subset |
+| `<=` | subset |
+| `>=` | superset |
+| `>` | proper superset |
++-----------------+------------------------------------+
+
+Like the arithmetic relational operators, these operators are
+chaining.
+
+Sets support the following binary operators, listed in order of
+increasing binding power:
+
++---------------+------------------------------------+
+| operator | description |
++---------------+------------------------------------+
+| `!!` | disjointness |
++---------------+------------------------------------+
+| `+` | set union |
+| `-` | set difference |
++---------------+------------------------------------+
+| `*` | set intersection |
++---------------+------------------------------------+
+
+The associativity rules of `+`, `-`, and `*` are like those of the
+arithmetic operators with the same names. The expression `A !! B`,
+whose binding power is the same as equality (but which neither
+associates nor chains with equality), says that sets `A` and `B` have
+no elements in common, that is, it is equivalent to
+```
+A * B == {}
+```
+However, the disjointness operator is chaining, so `A !! B !! C !! D`
+means:
+```
+A * B == {} && (A + B) * C == {} && (A + B + C) * D == {}
+```
+
+In addition, for any set `s` of type `set<T>` or `iset<T>` and any
+expression `e` of type `T`, sets support the following operations:
+
++---------------------+------------------------------------+
+| expression | description |
++---------------------+------------------------------------+
+| [\|s\|]{.monospace} | set cardinality |
+| `e in s` | set membership |
+| `e !in s` | set non-membership |
++---------------------+------------------------------------+
+
+The expression `e !in s` is a syntactic shorthand for `!(e in s)`.
+
+## Multisets
+````
+MultisetType_ = "multiset" [ GenericInstantiation ]
+````
+
+A _multiset_ is similar to a set, but keeps track of the multiplicity
+of each element, not just its presence or absence. For any type `T`,
+each value of type `multiset<T>` is a map from `T` values to natural
+numbers denoting each element's multiplicity. Multisets in Dafny
+are finite, that is, they contain a finite number of each of a finite
+set of elements. Stated differently, a multiset maps only a finite
+number of elements to non-zero (finite) multiplicities.
+
+Like sets, multiset membership is determined by equality in the type
+`T`, so `multiset<T>` can be used in a non-ghost context only if `T`
+is equality supporting.
+
+A multiset can be formed using a _multiset display_ expression, which
+is a possibly empty, unordered list of expressions enclosed in curly
+braces after the keyword `multiset`. To illustrate,
+```
+multiset{} multiset{0, 1, 1, 2, 3, 5} multiset{4+2, 1+5, a*b}
+```
+are three examples of multiset displays. There is no multiset
+comprehension expression.
+
+In addition to equality and disequality, multiset types
+support the following relational operations:
+
++-----------------+------------------------------------+
+| operator | description |
++-----------------+------------------------------------+
+| [<]{.monospace} | proper multiset subset |
+| `<=` | multiset subset |
+| `>=` | multiset superset |
+| `>` | proper multiset superset |
++-----------------+------------------------------------+
+
+Like the arithmetic relational operators, these operators are
+chaining.
+
+Multisets support the following binary operators, listed in order of
+increasing binding power:
+
++---------------+------------------------------------+
+| operator | description |
++---------------+------------------------------------+
+| `!!` | multiset disjointness |
++---------------+------------------------------------+
+| `+` | multiset union |
+| `-` | multiset difference |
++---------------+------------------------------------+
+| `*` | multiset intersection |
++---------------+------------------------------------+
+
+The associativity rules of `+`, `-`, and `*` are like those of the
+arithmetic operators with the same names. The `+` operator
+adds the multiplicity of corresponding elements, the `-` operator
+subtracts them (but 0 is the minimum multiplicity),
+and the `*` has multiplicity that is the minimum of the
+multiplicity of the operands.
+
+The expression `A !! B`
+says that multisets `A` and `B` have no elements in common, that is,
+it is equivalent to
+```
+A * B == multiset{}
+```
+Like the analogous set operator, `!!` is chaining.
+
+In addition, for any multiset `s` of type `multiset<T>`,
+expression `e` of type `T`, and non-negative integer-based numeric
+`n`, multisets support the following operations:
+
++---------------------+------------------------------------------+
+| expression | description |
++---------------------+------------------------------------------+
+| [\|s\|]{.monospace} | multiset cardinality |
+| `e in s` | multiset membership |
+| `e !in s` | multiset non-membership |
+| `s[e]` | multiplicity of `e` in `s` |
+| `s[e := n]` | multiset update (change of multiplicity) |
++---------------------+------------------------------------------+
+
+The expression `e in s` returns `true` if and only if `s[e] != 0`.
+The expression `e !in s` is a syntactic shorthand for `!(e in s)`.
+The expression `s[e := n]` denotes a multiset like
+`s`, but where the multiplicity of element `e` is `n`. Note that
+the multiset update `s[e := 0]` results in a multiset like `s` but
+without any occurrences of `e` (whether or not `s` has occurrences of
+`e` in the first place). As another example, note that
+`s - multiset{e}` is equivalent to:
+```
+if e in s then s[e := s[e] - 1] else s
+```
+
+## Sequences
+````
+SequenceType_ = "seq" [ GenericInstantiation ]
+````
+
+For any type `T`, a value of type `seq<T>` denotes a _sequence_ of `T`
+elements, that is, a mapping from a finite downward-closed set of natural
+numbers (called _indices_) to `T` values. (Thinking of it as a map,
+a sequence is therefore something of a dual of a multiset.)
+
+### Sequence Displays
+A sequence can be formed using a _sequence display_ expression, which
+is a possibly empty, ordered list of expressions enclosed in square
+brackets. To illustrate,
+```
+[] [3, 1, 4, 1, 5, 9, 3] [4+2, 1+5, a*b]
+```
+are three examples of sequence displays. There is no sequence
+comprehension expression.
+
+### Sequence Relational Operators
+In addition to equality and disequality, sequence types
+support the following relational operations:
+
++-----------------+------------------------------------+
+| operator | description |
++-----------------+------------------------------------+
+| [<]{.monospace} | proper prefix |
+| `<=` | prefix |
++-----------------+------------------------------------+
+
+Like the arithmetic relational operators, these operators are
+chaining. Note the absence of `>` and `>=`.
+
+### Sequence Concatenation
+Sequences support the following binary operator:
+
++---------------+------------------------------------+
+| operator | description |
++---------------+------------------------------------+
+| `+` | concatenation |
++---------------+------------------------------------+
+
+Operator `+` is associative, like the arithmetic operator with the
+same name.
+
+### Other Sequence Expressions
+In addition, for any sequence `s` of type `seq<T>`, expression `e`
+of type `T`, integer-based numeric `i` satisfying `0 <= i < |s|`, and
+integer-based numerics `lo` and `hi` satisfying
+`0 <= lo <= hi <= |s|`, sequences support the following operations:
+
++---------------------+----------------------------------------+
+| expression | description |
++---------------------+----------------------------------------+
+| [\|s\|]{.monospace} | sequence length |
+| `s[i]` | sequence selection |
+| `s[i := e]` | sequence update |
+| `e in s` | sequence membership |
+| `e !in s` | sequence non-membership |
+| `s[lo..hi]` | subsequence |
+| `s[lo..]` | drop |
+| `s[..hi]` | take |
+| `s[\(_slices_\)]` | slice |
+| `multiset(s)` | sequence conversion to a `multiset<T>` |
++---------------------+----------------------------------------+
+
+Expression `s[i := e]` returns a sequence like `s`, except that the
+element at index `i` is `e`. The expression `e in s` says there
+exists an index `i` such that `s[i] == e`. It is allowed in non-ghost
+contexts only if the element type `T` is equality supporting.
+The expression `e !in s` is a syntactic shorthand for `!(e in s)`.
+
+Expression `s[lo..hi]` yields a sequence formed by taking the first
+`hi` elements and then dropping the first `lo` elements. The
+resulting sequence thus has length `hi - lo`. Note that `s[0..|s|]`
+equals `s`. If the upper bound is omitted, it
+defaults to `|s|`, so `s[lo..]` yields the sequence formed by dropping
+the first `lo` elements of `s`. If the lower bound is omitted, it
+defaults to `0`, so `s[..hi]` yields the sequence formed by taking the
+first `hi` elements of `s`.
+
+In the sequence slice operation, `\(_slices_\)` is a nonempty list of
+length designators separated and optionally terminated by a colon, and
+there is at least one colon. Each length designator is a non-negative
+integer-based numeric, whose sum is no greater than `|s|`. If there
+are _k_ colons, the operation produces _k + 1_ consecutive subsequences
+from `s`, each of the length indicated by the corresponding length
+designator, and returns these as a sequence of
+sequences.[^fn-slice-into-tuple] If `\(_slices_\)` is terminated by a
+colon, then the length of the last slice extends until the end of `s`,
+that is, its length is `|s|` minus the sum of the given length
+designators. For example, the following equalities hold, for any
+sequence `s` of length at least `10`:
+```
+var t := [3.14, 2.7, 1.41, 1985.44, 100.0, 37.2][1:0:3];
+assert |t| == 3 && t[0] == [3.14] && t[1] == [];
+assert t[2] == [2.7, 1.41, 1985.44];
+var u := [true, false, false, true][1:1:];
+assert |u| == 3 && u[0][0] && !u[1][0] && u[2] == [false, true];
+assert s[10:][0] == s[..10];
+assert s[10:][1] == s[10..];
+```
+
+[^fn-slice-into-tuple]: Now that Dafny supports built-in tuples, the
+ plan is to change the sequence slice operation to return not a
+ sequence of subsequences, but a tuple of subsequences.
+
+The operation `multiset(s)` yields the multiset of elements of
+sequence `s`. It is allowed in non-ghost contexts only if the element
+type `T` is equality supporting.
+
+### Strings
+````
+StringType_ = "string"
+````
+
+A special case of a sequence type is `seq<char>`, for which Dafny
+provides a synonym: `string`. Strings are like other sequences, but
+provide additional syntax for sequence display expressions, namely
+_string literals_. There are two forms of the syntax for string
+literals: the _standard form_ and the _verbatim form_.
+
+String literals of the standard form are enclosed in double quotes, as
+in `"Dafny"`. To include a double quote in such a string literal,
+it is necessary to use an escape sequence. Escape sequences can also
+be used to include other characters. The supported escape sequences
+are the same as those for character literals, see Section [#sec-characters].
+For example, the Dafny expression `"say \"yes\""` represents the
+string `'say "yes"'`.
+The escape sequence for a single quote is redundant, because
+[\"\'\"]{.monospace} and [\"\\\'\"]{.monospace} denote the same
+string---both forms are provided in order to support the same
+escape sequences as for character literals.
+
+String literals of the verbatim form are bracketed by
+[@\"]{.monospace} and [\"]{.monospace}, as in `@"Dafny"`. To include
+a double quote in such a string literal, it is necessary to use the
+escape sequence [\"\"]{.monospace}, that is, to write the character
+twice. In the verbatim form, there are no other escape sequences.
+Even characters like newline can be written inside the string literal
+(hence spanning more than one line in the program text).
+
+For example, the following three expressions denote the same string:
+```
+"C:\\tmp.txt"
+@"C:\tmp.txt"
+['C', ':', '\\', 't', 'm', 'p', '.', 't', 'x', 't']
+```
+
+Since strings are sequences, the relational operators [<]{.monospace}
+and `<=` are defined on them. Note, however, that these operators
+still denote proper prefix and prefix, respectively, not some kind of
+alphabetic comparison as might be desirable, for example, when
+sorting strings.
+
+## Finite and Infinite Maps
+````
+FiniteMapType_ = "map" [ GenericInstantiation ]
+InfiniteMapType_ = "imap" [ GenericInstantiation ]
+````
+
+For any types `T` and `U`, a value of type `map<T,U>` denotes a
+_(finite) map_
+from `T` to `U`. In other words, it is a look-up table indexed by
+`T`. The _domain_ of the map is a finite set of `T` values that have
+associated `U` values. Since the keys in the domain are compared
+using equality in the type `T`, type `map<T,U>` can be used in a
+non-ghost context only if `T` is equality supporting.
+
+Similarly, for any types `T` and `U`, a value of type `imap<T,U>`
+denotes a _(possibly) infinite map_. In most regards, `imap<T,U>` is
+like `map<T,U>`, but a map of type `imap<T,U>` is allowed to have an
+infinite domain.
+
+A map can be formed using a _map display_ expression (see ``MapDisplayExpr``),
+which is a possibly empty, ordered list of _maplets_, each maplet having the
+form `t := u` where `t` is an expression of type `T` and `u` is an
+expression of type `U`, enclosed in square brackets after the keyword
+`map`. To illustrate,
+```
+map[] map[20 := true, 3 := false, 20 := false] map[a+b := c+d]
+```
+are three examples of map displays. By using the keyword `imap`
+instead of `map`, the map produced will be of type `imap<T,U>`
+instead of `map<T,U>`. Note that an infinite map (`imap`) is allowed
+to have a finite domain, whereas a finite map (`map`) is not allowed
+to have an infinite domain.
+If the same key occurs more than
+once, only the last occurrence appears in the resulting
+map.[^fn-map-display] There is also a _map comprehension expression_,
+explained in section [#sec-map-comprehension-expression].
+
+[^fn-map-display]: This is likely to change in the future to disallow
+ multiple occurrences of the same key.
+
+For any map `fm` of type `map<T,U>`,
+any map `m` of type `map<T,U>` or `imap<T,U>`,
+any expression `t` of type `T`,
+any expression `u` of type `U`, and any `d` in the domain of `m` (that
+is, satisfying `d in m`), maps support the following operations:
+
++----------------------+------------------------------------+
+| expression | description |
++----------------------+------------------------------------+
+| [\|fm\|]{.monospace} | map cardinality |
+| `m[d]` | map selection |
+| `m[t := u]` | map update |
+| `t in m` | map domain membership |
+| `t !in m` | map domain non-membership |
++----------------------+------------------------------------+
+
+`|fm|` denotes the number of mappings in `fm`, that is, the
+cardinality of the domain of `fm`. Note that the cardinality operator
+is not supported for infinite maps.
+Expression `m[d]` returns the `U` value that `m` associates with `d`.
+Expression `m[t := u]` is a map like `m`, except that the
+element at key `t` is `u`. The expression `t in m` says `t` is in the
+domain of `m` and `t !in m` is a syntactic shorthand for
+`!(t in m)`.[^fn-map-membership]
+
+[^fn-map-membership]: This is likely to change in the future as
+ follows: The `in` and `!in` operations will no longer be
+ supported on maps. Instead, for any map `m`, `m.Domain` will
+ return its domain as a set and `m.Range` will return, also as a
+ set, the image of `m` under its domain.
+
+Here is a small example, where a map `cache` of type `map<int,real>`
+is used to cache computed values of Joule-Thomson coefficients for
+some fixed gas at a given temperature:
+```
+if K in cache { // check if temperature is in domain of cache
+ coeff := cache[K]; // read result in cache
+} else {
+ coeff := ComputeJouleThomsonCoefficient(K); // do expensive computation
+ cache := cache[K := coeff]; // update the cache
+}
+```
+
+# Types that stand for other types
+````
+SynonymTypeDecl =
+ ( SynonymTypeDefinition_ | OpaqueTypeDefinition_ ) [ ";" ]
+````
+It is sometimes useful to know a type by several names or to treat a
+type abstractly. Synonym and opaque types serve this purpose.
+
+## Type synonyms
+````
+SynonymTypeDefinition_ =
+ "type" { Attribute } SynonymTypeName [ GenericParameters ] "=" Type
+````
+
+A _type synonym_ declaration:
+```
+type Y<T> = G
+```
+declares `Y<T>` to be a synonym for the type `G`. Here, `T` is a
+nonempty list of type parameters (each of which is optionally
+designated with the suffix "`(==)`"), which can be used as free type
+variables in `G`. If the synonym has no type parameters, the "`<T>`"
+is dropped. In all cases, a type synonym is just a synonym. That is,
+there is never a difference, other than possibly in error messages
+produced, between `Y<T>` and `G`.
+
+For example, the names of the following type synonyms may improve the
+readability of a program:
+```
+type Replacements<T> = map<T,T>
+type Vertex = int
+```
+
+As already described in Section [#sec-strings], `string` is a built-in
+type synonym for `seq<char>`, as if it would have been declared as
+follows:
+```
+type string = seq<char>
+```
+
+## Opaque types
+````
+OpaqueTypeDefinition_ = "type" { Attribute } SynonymTypeName
+ [ "(" "==" ")" ] [ GenericParameters ]
+````
+
+A special case of a type synonym is one that is underspecified. Such
+a type is declared simply by:
+```
+type Y<T>
+```
+It is known as an _opaque type_. Its definition can be revealed in a
+refining module. To indicate that `Y` designates an
+equality-supporting type, "`(==)`" can be written immediately
+following the name "`Y`".
+
+For example, the declarations
+```
+type T
+function F(t: T): T
+```
+can be used to model an uninterpreted function `F` on some
+arbitrary type `T`. As another example,
+```
+type Monad<T>
+```
+can be used abstractly to represent an arbitrary parameterized monad.
+
+# Well-founded Functions and Extreme Predicates
+This section is a tutorial on well-founded functions and extreme predicates.
+We place it here in preparation for Section [#sec-class-types]
+where function and predicate definitions are described.
+
+Recursive functions are a core part of computer science and mathematics.
+Roughly speaking, when the definition of such a function spells out a
+terminating computation from given arguments, we may refer to
+it as a _well-founded function_. For example, the common factorial and
+Fibonacci functions are well-founded functions.
+
+There are also other ways to define functions. An important case
+regards the definition of a boolean function as an extreme solution
+(that is, a least or greatest solution) to some equation. For
+computer scientists with interests in logic or programming languages,
+these _extreme predicates_ are important because they describe the
+judgments that can be justified by a given set of inference rules
+(see, e.g., [@CamilleriMelham:InductiveRelations;
+@Winskel:FormalSemantics;
+ @LeroyGrall:CoinductiveBigStep; @Pierce:SoftwareFoundations;
+ @NipkowKlein:ConcreteSemantics]).
+
+To benefit from machine-assisted reasoning, it is necessary not just
+to understand extreme predicates but also to have techniques for
+proving theorems about them. A foundation for this reasoning was
+developed by Paulin-Mohring [@PaulinMohring:InductiveCoq] and is the
+basis of the constructive logic supported by Coq [@Coq:book] as well
+as other proof assistants [@BoveDybjerNorell:BriefAgda;
+@SwamyEtAl:Fstar2011]. Essentially, the idea is to represent the
+knowledge that an extreme predicate holds by the proof term by which
+this knowledge was derived. For a predicate defined as the least
+solution, such proof terms are values of an inductive datatype (that
+is, finite proof trees), and for the greatest solution, a coinductive
+datatype (that is, possibly infinite proof trees). This means that
+one can use induction and coinduction when reasoning about these proof
+trees. Therefore, these extreme predicates are known as,
+respectively, _inductive predicates_ and _coinductive predicates_ (or,
+_co-predicates_ for short). Support for extreme predicates is also
+available in the proof assistants Isabelle [@Paulson:CADE1994] and HOL
+[@Harrison:InductiveDefs].
+
+Dafny supports both well-founded functions and extreme predicates.
+This section is a tutorial that describes the difference in general
+terms, and then describes novel syntactic support in Dafny for
+defining and proving lemmas with extreme predicates. Although Dafny's
+verifier has at its core a first-order SMT solver, Dafny's logical
+encoding makes it possible to reason about fixpoints in an automated
+way.
+
+The encoding for coinductive predicates in Dafny was described previously
+[@LeinoMoskal:Coinduction] and is here described in Section
+[#sec-co-inductive-datatypes].
+
+## Function Definitions
+
+To define a function $f \colon X \to Y$ in terms of itself, one can
+write an equation like
+
+~ Equation {#eq-general}
+ f \Equal \F(f)
+~
+
+where $\mathcal{F}$ is a non-recursive function of type
+$(X \to Y) \to X \to Y$. Because it takes a function as an argument,
+$\mathcal{F}$ is referred to as a _functor_ (or _functional_, but not to be
+confused by the category-theory notion of a functor).
+Throughout, I will assume that $\F(f)$ by itself is well defined,
+for example that it does not divide by zero. I will also assume that $f$ occurs
+only in fully applied calls in $\F(f)$; eta expansion can be applied to
+ensure this. If $f$ is a boolean function, that is, if $Y$ is
+the type of booleans, then I call $f$ a _predicate_.
+
+For example, the common Fibonacci function over the
+natural numbers can be defined by the equation
+
+~ Equation
+ \fib \Equal
+ \lambda n \bullet\; \ite{n < 2}{n}{\fib(n-2) + \fib(n-1)}
+~
+
+With the understanding that the argument $n$ is universally
+quantified, we can write this equation equivalently as
+
+~ Equation {#eq-fib}
+ \fib(n) \Equal
+ \ite{n < 2}{n}{\fib(n-2) + \fib(n-1)}
+~
+
+The fact that the function being defined occurs on both sides of the equation
+causes concern that we might not be defining the function properly, leading to a
+logical inconsistency. In general, there
+could be many solutions to an equation like [#eq-general] or there could be none.
+Let's consider two ways to make sure we're defining the function uniquely.
+
+### Well-founded Functions
+
+A standard way to ensure that equation [#eq-general] has a unique solution in $f$ is
+to make sure the recursion is well-founded, which roughly means that the
+recursion terminates. This is done by introducing any well-founded
+relation $\Less$ on the domain of $f$ and making sure that the argument to each recursive
+call goes down in this ordering. More precisely, if we formulate [#eq-general] as
+
+~ Equation
+ f(x) \Equal \F'(f)
+~
+
+then we want to check $E \Less x$ for each call $f(E)$ in $\F'(f)$. When a function
+definition satisfies this _decrement condition_, then the function is said to be
+_well-founded_.
+
+For example, to check the decrement condition for $\fib$ in [#eq-fib], we can pick
+$\Less$ to be the arithmetic less-than relation on natural numbers and check the
+following, for any $n$:
+
+~ Equation
+ 2 \leq n \;\;\Imp\;\; n-2 \Less n \;\And\; n-1 \Less n
+~
+
+Note that we are entitled to using the antecedent $2 \leq n$, because that is the
+condition under which the else branch in [#eq-fib] is evaluated.
+
+A well-founded function is often thought of as "terminating" in the sense
+that the recursive _depth_ in evaluating $f$
+on any given argument is finite. That is, there are no infinite descending chains
+of recursive calls. However, the evaluation of $f$ on a given argument
+may fail to terminate, because its _width_ may be infinite. For example, let $P$
+be some predicate defined on the ordinals and let $\PDownward$ be a predicate on the
+ordinals defined by the following equation:
+
+~ Equation
+ \PDownward(o) \Equal
+ P(o) \And \forall p \bullet\; p \Less o \Imp \PDownward(p)
+~
+
+With $\Less$ as the usual ordering on ordinals, this equation satisfies the decrement
+condition, but evaluating $\PDownward(\omega)$ would require evaluating
+$\PDownward(n)$ for every natural number $n$. However, what we are concerned
+about here is to avoid mathematical inconsistencies, and that is
+indeed a consequence of the decrement condition.
+
+#### Example with Well-founded Functions {#sec-fib-example}
+
+So that we can later see how inductive proofs are done in Dafny, let's prove that
+for any $n$, $\fib(n)$ is even iff $n$ is a multiple of $3$.
+We split our task into
+two cases. If $n < 2$, then the property follows directly from the definition
+of $\fib$. Otherwise, note that exactly one of the three numbers $n-2$, $n-1$, and $n$
+is a multiple of 3. If $n$ is the multiple of 3, then by invoking the
+induction hypothesis on $n-2$
+and $n-1$, we obtain that $\fib(n-2) + \fib(n-1)$ is the sum of two odd numbers,
+which is even. If $n-2$ or $n-1$ is a multiple of 3, then by invoking the induction
+hypothesis on $n-2$ and $n-1$, we obtain that $\fib(n-2) + \fib(n-1)$ is the sum of an
+even number and an odd number, which is odd. In this proof, we invoked the induction
+hypothesis on $n-2$ and on $n-1$. This is allowed, because both are smaller than
+$n$, and hence the invocations go down in the well-founded ordering on natural numbers.
+
+### Extreme Solutions
+
+We don't need to exclude the possibility of equation [#eq-general] having multiple
+solutions---instead, we can just be clear about which one of them we want.
+Let's explore this, after a smidgen of lattice theory.
+
+For any complete lattice $(Y,\leq)$ and any set $X$, we can by _pointwise extension_ define
+a complete lattice $(X \to Y, \FBelow)$, where for any $f,g \colon X \to Y$,
+
+~ Equation
+ f \FBelow q \Equiv
+ \forall x \bullet\; f(x) \leq g(x)
+~
+
+In particular, if $Y$ is the set of booleans ordered by implication ($\false \leq \true$),
+then the set of predicates over any domain $X$ forms a complete lattice.
+Tarski's Theorem [@Tarski:theorem] tells us that any monotonic function over a
+complete lattice has a least and a greatest fixpoint. In particular, this means that
+$\F$ has a least fixpoint and a greatest fixpoint, provided $\F$ is monotonic.
+
+Speaking about the _set of solutions_ in $f$ to [#eq-general] is the same as speaking
+about the _set of fixpoints_ of functor $\F$. In particular, the least and greatest
+solutions to [#eq-general] are the same as the least and greatest fixpoints of $\F$.
+In casual speak, it happens that we say "fixpoint of [#eq-general]", or more
+grotesquely, "fixpoint of $f$" when we really mean "fixpoint of $\F$".
+
+In conclusion of our little excursion into lattice theory, we have that, under the
+proviso of $\F$ being monotonic, the set of solutions in $f$ to [#eq-general] is nonempty,
+and among these solutions, there is in the $\FBelow$ ordering a least solution (that is,
+a function that returns $\false$ more often than any other) and a greatest solution (that
+is, a function that returns $\true$ more often than any other).
+
+When discussing extreme solutions, I will now restrict my attention to boolean functions
+(that is, with $Y$ being the type of booleans). Functor $\F$ is monotonic
+if the calls to $f$ in $\F'(f)$ are in _positive positions_ (that is, under an even number
+of negations). Indeed, from now on, I will restrict my attention to such monotonic
+functors $\F$.
+
+Let me introduce a running example. Consider the following equation,
+where $x$ ranges over the integers:
+
+~ Equation {#eq-EvenNat}
+ g(x) \Equal (x = 0 \Or g(x-2))
+~
+
+This equation has four solutions in $g$. With $w$ ranging over the integers, they are:
+
+~ Equation
+ \begin{array}{r@{}l}
+ g(x) \Equiv{}& x \in \{w \;|\; 0 \leq w \And w\textrm{ even}\} \\
+ g(x) \Equiv{}& x \in \{w \;|\; w\textrm{ even}\} \\
+ g(x) \Equiv{}& x \in \{w \;|\; (0 \leq w \And w\textrm{ even}) \Or w\textrm{ odd}\} \\
+ g(x) \Equiv{}& x \in \{w \;|\; \true\}
+ \end{array}
+~
+
+The first of these is the least solution and the last is the greatest solution.
+
+In the literature, the definition of an extreme predicate is often given as a set of
+_inference rules_. To designate the least solution, a single line separating the
+antecedent (on top) from conclusion (on bottom) is used:
+
+~ Equation {#g-ind-rule}
+ \frac{}{g(0)}
+ \qquad\qquad
+ \frac{g(x-2)}{g(x)}
+~
+
+Through repeated applications of such rules, one can show that the predicate holds for
+a particular value. For example, the _derivation_, or _proof tree_,
+to the left in Figure [#fig-proof-trees] shows that $g(6)$ holds.
+(In this simple example, the derivation is a rather degenerate proof "tree".)
+The use of these inference rules gives rise to a least solution, because proof trees are
+accepted only if they are _finite_.
+
+~ Begin Figure { #fig-proof-trees caption="Left: a finite proof tree that uses the rules of [#g-ind-rule] to establish $g(6)$. Right: an infinite proof tree that uses the rules of [#g-coind-rule] to establish $g(1)$." }
+~ Begin Columns
+~~ Column { vertical-align=bottom }
+~ Math
+\dfrac{
+ \dfrac{
+ \dfrac{
+ \dfrac{}{g(0)\xstrut}
+ }{g(2)\xstrut}
+ }{g(4)\xstrut}
+ }{g(6)\xupstrut}
+~
+~~
+~~ Column { width=5em }
+
+~~
+~~ Column { vertical-align=bottom }
+~ Math
+\Dfrac{
+ \Dfrac{
+ \Dfrac{
+ \Dfrac{
+ {}_{\vdots }
+ }{{g(-5)}}
+ }{{g(-3)}}
+ }{{g(-1)}}
+ }{g(1)}
+~
+~~
+~ End Columns
+~ End Figure
+
+When inference rules are to designate the greatest solution, a double
+line is used:
+
+~ Equation {#g-coind-rule}
+ \Dfrac{}{g(0)}
+ \qquad\qquad
+ \Dfrac{g(x-2)}{g(x)}
+~
+
+In this case, proof trees are allowed to be infinite. For example, the (partial depiction
+of the) infinite proof tree on the right in Figure [#fig-proof-trees] shows that $g(1)$ holds.
+
+Note that derivations may not be unique. For example, in the case of the greatest
+solution for $g$, there are two proof trees that establish $g(0)$: one is the finite
+proof tree that uses the left-hand rule of [#g-coind-rule] once, the other is the infinite
+proof tree that keeps on using the right-hand rule of [#g-coind-rule].
+
+### Working with Extreme Predicates
+
+In general, one cannot evaluate whether or not an extreme predicate holds for some
+input, because doing so may take an infinite number of steps. For example, following
+the recursive calls in the definition [#eq-EvenNat] to try to evaluate $g(7)$ would never
+terminate. However, there are useful ways to establish that an extreme predicate holds
+and there are ways to make use of one once it has been established.
+
+For any $\F$ as in [#eq-general], I define two infinite series of well-founded
+functions, $\iter{f}_k$ and $\Iter{f}_k$
+where $k$ ranges over the natural numbers:
+
+~ Equation {#eq-least-approx}
+ \iter{f}_k(x) \Equal \left\{
+ \begin{array}{ll}
+ \false & \textrm{if } k = 0 \\
+ \F(\iter{f}_{k-1})(x) & \textrm{if } k > 0
+ \end{array}
+ \right.
+~
+~ Equation {#eq-greatest-approx}
+ \Iter{f}_k(x) \Equal \left\{
+ \begin{array}{ll}
+ \true & \textrm{if } k = 0 \\
+ \F(\Iter{f}_{k-1})(x) & \textrm{if } k > 0
+ \end{array}
+ \right.
+~
+
+These functions are called the _iterates_ of $f$, and I will also refer to them
+as the _prefix predicates_ of $f$ (or the _prefix predicate_ of $f$, if we think
+of $k$ as being a parameter).
+Alternatively, we can define $\iter{f}_k$ and $\Iter{f}_k$ without mentioning $x$:
+Let $\bot$ denote the function that always returns $\false$, let $\top$
+denote the function that always returns $\true$, and let a superscript on $\F$ denote
+exponentiation (for example, $\F^0(f) = f$ and $\F^2(f) = \F(\F(f))$).
+Then, [#eq-least-approx] and [#eq-greatest-approx] can be stated equivalently as
+$\iter{f}_k = \F^k(\bot)$ and $\Iter{f}_k = \F^k(\top)$.
+
+For any solution $f$ to equation [#eq-general], we have, for any $k$ and $\ell$
+such that $k \leq \ell$:
+
+~ Equation {#eq-prefix-postfix}
+ \iter{f}_k \quad\FBelow\quad
+ \iter{f}_\ell \quad\FBelow\quad
+ f \quad\FBelow\quad
+ \Iter{f}_\ell \quad\FBelow\quad
+ \Iter{f}_k
+~
+
+In other words, every $\iter{f}_k$ is a _pre-fixpoint_ of $f$ and every $\Iter{f}_k$ is a _post-fixpoint_
+of $f$. Next, I define two functions, $f\least$ and $f\greatest$, in
+terms of the prefix predicates:
+
+~ Equation {#eq-least-is-exists}
+ f\least(x) \Equal \exists k \bullet\; \iter{f}_k(x)
+~
+~ Equation {#eq-greatest-is-forall}
+ f\greatest(x) \Equal \forall k \bullet\; \Iter{f}_k(x)
+~
+
+By [#eq-prefix-postfix], we also have that $f\least$ is a pre-fixpoint of $\F$ and $f\greatest$
+is a post-fixpoint of $\F$. The marvelous thing is that, if $\F$ is _continuous_, then
+$f\least$ and $f\greatest$ are the least and greatest fixpoints of $\F$.
+These equations let us do proofs by induction when dealing with extreme predicates.
+I will explain in Section [#sec-friendliness] how to check for continuity.
+
+Let's consider two examples, both involving function $g$ in
+[#eq-EvenNat]. As it turns out, $g$'s defining functor is continuous,
+and therefore I will write $g\least$ and $g\greatest$ to denote the
+least and greatest solutions for $g$ in [#eq-EvenNat].
+
+#### Example with Least Solution {#sec-example-least-solution}
+
+The main technique for establishing that $g\least(x)$ holds for some
+$x$, that is, proving something of the form $Q \Imp g\least(x)$, is to
+construct a proof tree like the one for $g(6)$ in Figure
+[#fig-proof-trees]. For a proof in this direction, since we're just
+applying the defining equation, the fact that
+we're using a least solution for $g$ never plays a role (as long as we
+limit ourselves to finite derivations).
+
+The technique for going in the other direction, proving something _from_ an established
+$g\least$ property, that is, showing something of the form $g\least(x) \Imp R$, typically
+uses induction on the structure of the proof tree. When the antecedent of our proof
+obligation includes a predicate term $g\least(x)$, it is sound to
+imagine that we have been given a proof tree for $g\least(x)$. Such a proof tree
+would be a data structure---to be more precise, a term in an
+_inductive datatype_.
+For this reason, least solutions like $g\least$ have been given the
+name _inductive predicate_.
+
+Let's prove $g\least(x) \Imp 0 \leq x \And x \textrm{ even}$.
+We split our task into two cases, corresponding to which of the two
+proof rules in [#g-ind-rule] was the
+last one applied to establish $g\least(x)$. If it was the left-hand rule, then $x=0$,
+which makes it easy to establish the conclusion of our proof goal. If it was the
+right-hand rule, then we unfold the proof tree one level and obtain $g\least(x-2)$.
+Since the proof tree for $g\least(x-2)$ is smaller than where we started, we invoke
+the _induction hypothesis_ and obtain $0 \leq (x-2) \And (x-2) \textrm{ even}$, from which
+it is easy to establish the conclusion of our proof goal.
+
+Here's how we do the proof formally using [#eq-least-is-exists]. We massage the
+general form of our proof goal:
+
+|~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
+| | $f\greatest(x) \Imp R$ |
+| = | &nbsp;&nbsp;&nbsp;&nbsp; { [#eq-least-is-exists] } |
+| | $(\exists k \bullet\; \iter{f}_k(x)) \Imp R$ |
+| = | &nbsp;&nbsp;&nbsp;&nbsp; { distribute $\Imp$ over $\exists$ to the left } |
+| | $\forall k \bullet\; (\iter{f}_k(x) \Imp R)$ |
+
+The last line can be proved by induction over $k$. So, in our case, we prove
+$\iter{g}_k(x) \Imp 0 \leq x \And x \textrm{ even}$ for every $k$.
+If $k=0$, then $\iter{g}_k(x)$ is $\false$, so our goal holds trivially.
+If $k > 0$, then $\iter{g}_k(x) = (x = 0 \Or \iter{g}_{k-1}(x-2))$. Our goal holds easily
+for the first disjunct ($x=0$). For the other disjunct,
+we apply the induction hypothesis (on the smaller $k-1$ and with $x-2$) and
+obtain $0 \leq (x-2) \And (x-2) \textrm{ even}$, from which our proof goal
+follows.
+
+#### Example with Greatest Solution {#sec-example-greatest-solution}
+
+We can think of a given predicate $g\greatest(x)$ as being represented
+by a proof tree---in this case a term in a _coinductive datatype_,
+since the proof may be infinite.
+For this reason, greatest solutions like $g\greatest$ have
+been given the name _coinductive predicate_, or _co-predicate_ for short.
+The main technique for proving something from a given proof tree, that
+is, to prove something of the form $g\greatest(x) \Imp R$, is to
+destruct the proof. Since this is just unfolding the defining
+equation, the fact that we're using a greatest solution for $g$ never
+plays a role (as long as we limit ourselves to a finite number of
+unfoldings).
+
+To go in the other direction, to establish a predicate defined as a greatest solution,
+like $Q \Imp g\greatest(x)$, we may need an infinite number of steps. For this purpose,
+we can use induction's dual, _coinduction_. Were it not for one little detail, coinduction
+is as simple as continuations in programming: the next part of the proof obligation
+is delegated to the _coinduction hypothesis_. The little detail is making sure that
+it is the "next" part we're passing on for the continuation, not the same part. This
+detail is called _productivity_ and corresponds to the requirement in
+induction of making sure we're going down a well-founded relation when
+applying the induction hypothesis. There are
+many sources with more information, see for example the classic account by
+Jacobs and Rutten [@JacobsRutten:IntroductionCoalgebra]
+or a new attempt by Kozen and Silva
+that aims to emphasize the simplicity, not the mystery, of
+coinduction [@KozenSilva:Coinduction].
+
+Let's prove $\true \Imp g\greatest(x)$. The intuitive coinductive proof goes like this:
+According to the right-hand rule of [#g-coind-rule], $g\greatest(x)$ follows if we
+establish $g\greatest(x-2)$, and that's easy to do by invoking the coinduction hypothesis.
+The "little detail", productivity, is satisfied in this proof because we applied
+a rule in [#g-coind-rule] before invoking the coinduction hypothesis.
+
+For anyone who may have felt that the intuitive proof felt too easy, here is a formal
+proof using [#eq-greatest-is-forall], which relies only on induction. We massage the
+general form of our proof goal:
+
+|~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
+| | $Q \Imp f\greatest(x)$ |
+| = | &nbsp;&nbsp;&nbsp;&nbsp; { [#eq-greatest-is-forall] } |
+| | $Q \Imp \forall k \bullet\; \Iter{f}_k(x)$ |
+| = | &nbsp;&nbsp;&nbsp;&nbsp; { distribute $\Imp$ over $\forall$ to the right } |
+| | $\forall k \bullet\; Q \Imp \Iter{f}_k(x)$ |
+
+The last line can be proved by induction over $k$. So, in our case, we prove
+$\true \Imp \Iter{g}_k(x)$ for every $k$.
+If $k=0$, then $\Iter{g}_k(x)$ is $\true$, so our goal holds trivially.
+If $k > 0$, then $\Iter{g}_k(x) = (x = 0 \Or \Iter{g}_{k-1}(x-2))$. We establish the second
+disjunct by applying the induction hypothesis (on the smaller $k-1$ and with $x-2$).
+
+### Other Techniques
+
+Although in this paper I consider only well-founded functions and extreme
+predicates, it is worth mentioning that there are additional ways of making sure that
+the set of solutions to [#eq-general] is nonempty. For example, if all calls to $f$ in
+$\F'(f)$ are _tail-recursive calls_, then (under the assumption that $Y$ is nonempty) the set of
+solutions is nonempty. To see this, consider an attempted evaluation of $f(x)$ that fails
+to determine a definite result value because of an infinite chain of calls that applies $f$
+to each value of some subset $X'$ of $X$. Then, apparently, the value of $f$ for any one
+of the values in $X'$ is not determined by the equation, but picking any particular result
+values for these makes for a consistent definition.
+This was pointed out by Manolios and Moore [@ManoliosMoore:PartialFunctions].
+Functions can be underspecified in this way in the proof assistants ACL2 [@ACL2:book]
+and HOL [@Krauss:PhD].
+
+## Functions in Dafny
+
+In this section, I explain with examples the support in
+Dafny[^fn-on-da-web] for well-founded functions, extreme predicates,
+and proofs regarding these.
+
+[^fn-on-da-web]: Dafny is open source at [dafny.codeplex.com](http://dafny.codeplex.com) and can also be used online at [rise4fun.com/dafny](http://rise4fun.com/dafny).
+
+### Well-founded Functions in Dafny
+
+Declarations of well-founded functions are unsurprising. For example, the Fibonacci
+function is declared as follows:
+
+```
+function fib(n: nat): nat
+{
+ if n < 2 then n else fib(n-2) + fib(n-1)
+}
+```
+
+Dafny verifies that the body (given as an expression in curly braces) is well defined.
+This includes decrement checks for recursive (and mutually recursive) calls. Dafny
+predefines a well-founded relation on each type and extends it to lexicographic tuples
+of any (fixed) length. For example, the well-founded relation $x \Less y$ for integers
+is $x < y \And 0 \leq y$, the one for reals is $x \leq y - 1.0 \And 0.0 \leq y$
+(this is the same ordering as for integers, if you read the integer
+relation as $x \leq y - 1 \And 0 \leq y$), the one for inductive
+datatypes is structural inclusion,
+and the one for coinductive datatypes is $\false$.
+
+Using a `decreases` clause, the programmer can specify the term in this predefined
+order. When a function definition omits a `decreases` clause, Dafny makes a simple
+guess. This guess (which can be inspected by hovering over the function name in the
+Dafny IDE) is very often correct, so users are rarely bothered to provide explicit
+`decreases` clauses.
+
+If a function returns `bool`, one can drop the result type `: bool` and change the
+keyword `function` to `predicate`.
+
+### Proofs in Dafny
+
+Dafny has `lemma` declarations. These are really just special cases of methods:
+they can have pre- and postcondition specifications and their body is a code block.
+Here is the lemma we stated and proved in Section [#sec-fib-example]:
+
+```
+lemma FibProperty(n: nat)
+ ensures fib(n) % 2 == 0 <==> n % 3 == 0
+{
+ if n < 2 {
+ } else {
+ FibProperty(n-2); FibProperty(n-1);
+ }
+}
+```
+
+The postcondition of this lemma (keyword `ensures`) gives the proof
+goal. As in any program-correctness logic (e.g.,
+[@Hoare:AxiomaticBasis]), the postcondition must
+be established on every control path through the lemma's body. For
+`FibProperty`, I give the proof by
+an `if` statement, hence introducing a case split. The then branch is empty, because
+Dafny can prove the postcondition automatically in this case. The else branch
+performs two recursive calls to the lemma. These are the invocations of the induction
+hypothesis and they follow the usual program-correctness rules,
+namely: the precondition must hold at the call site, the call must terminate, and then
+the caller gets to assume the postcondition upon return. The "proof glue" needed
+to complete the proof is done automatically by Dafny.
+
+Dafny features an aggregate statement using which it is possible to make (possibly
+infinitely) many calls at once. For example, the induction hypothesis can be called
+at once on all values `n'` smaller than `n`:
+
+```
+forall n' | 0 <= n' < n {
+ FibProperty(n');
+}
+```
+
+For our purposes, this corresponds to _strong induction_. More
+generally, the `forall` statement has the form
+
+```
+forall k | P(k)
+ ensures Q(k)
+{ Statements; }
+```
+
+Logically, this statement corresponds to _universal introduction_: the body proves that
+`Q(k)` holds for an arbitrary `k` such that `P(k)`, and the conclusion of the `forall` statement
+is then $\forall k \bullet\; P(k) \Imp Q(k)$. When the body of the `forall` statement is
+a single call (or `calc` statement), the `ensures` clause is inferred and can be omitted,
+like in our `FibProperty` example.
+
+Lemma `FibProperty` is simple enough that its whole body can be replaced by the one
+`forall` statement above. In fact, Dafny goes one step further: it automatically
+inserts such a `forall` statement at the beginning of every lemma [@Leino:induction].
+Thus, `FibProperty` can be declared and proved simply by:
+
+``` {.para-end}
+lemma FibProperty(n: nat)
+ ensures fib(n) % 2 == 0 <==> n % 3 == 0
+{ }
+```
+
+Going in the other direction from universal introduction is existential elimination,
+also known as Skolemization. Dafny has a statement for this, too:
+for any variable `x` and boolean expression `Q`, the
+_assign such that_ statement `x :| Q;` says to assign to `x` a value such that `Q`
+will hold. A proof obligation when using this statement is to show that there
+exists an `x` such that `Q` holds. For example, if the fact
+$\exists k \bullet\; 100 \leq \fib(k) < 200$ is known, then the statement
+`k :| 100 <= fib(k) < 200;` will assign to `k` some value (chosen arbitrarily)
+for which `fib(k)` falls in the given range.
+
+### Extreme Predicates in Dafny {#sec-friendliness}
+
+In this previous subsection, I explained that a `predicate` declaration introduces a
+well-founded predicate. The declarations for introducing extreme predicates are
+`inductive predicate` and `copredicate`. Here is the definition of the least and
+greatest solutions of $g$ from above, let's call them `g` and `G`:
+
+```
+inductive predicate g(x: int) { x == 0 || g(x-2) }
+copredicate G(x: int) { x == 0 || G(x-2) }
+```
+
+When Dafny receives either of these definitions, it automatically declares the corresponding
+prefix predicates. Instead of the names $\iter{g}_k$ and $\Iter{g}_k$ that I used above, Dafny
+names the prefix predicates `g#[k]` and `G#[k]`, respectively, that is, the name of
+the extreme predicate appended with `#`, and the subscript is given as an argument in
+square brackets. The definition of the prefix predicate derives from the body of
+the extreme predicate and follows the form in [#eq-least-approx] and [#eq-greatest-approx].
+Using a faux-syntax for illustrative purposes, here are the prefix
+predicates that Dafny defines automatically from the extreme
+predicates `g` and `G`:
+
+```
+predicate g#[_k: nat](x: int) { _k != 0 && (x == 0 || g#[_k-1](x-2)) }
+predicate G#[_k: nat](x: int) { _k != 0 ==> (x == 0 || G#[_k-1](x-2)) }
+```
+
+The Dafny verifier is aware of the connection between extreme predicates and their
+prefix predicates, [#eq-least-is-exists] and [#eq-greatest-is-forall].
+
+Remember that to be well defined, the defining functor of an extreme predicate
+must be monotonic, and for [#eq-least-is-exists] and [#eq-greatest-is-forall] to hold,
+the functor must be continuous. Dafny enforces the former of these by checking that
+recursive calls of extreme predicates are in positive positions. The continuity
+requirement comes down to checking that they are also in _continuous positions_:
+that recursive calls to inductive predicates are
+not inside unbounded universal quantifiers and that recursive calls to co-predicates
+are not inside unbounded existential quantifiers [@Milner:CCS; @LeinoMoskal:Coinduction].
+
+### Proofs about Extreme Predicates
+
+From what I have presented so far, we can do the formal proofs from Sections
+[#sec-example-least-solution] and [#sec-example-greatest-solution]. Here is the
+former:
+
+```
+lemma EvenNat(x: int)
+ requires g(x)
+ ensures 0 <= x && x % 2 == 0
+{
+ var k: nat :| g#[k](x);
+ EvenNatAux(k, x);
+}
+lemma EvenNatAux(k: nat, x: int)
+ requires g#[k](x)
+ ensures 0 <= x && x % 2 == 0
+{
+ if x == 0 { } else { EvenNatAux(k-1, x-2); }
+}
+```
+
+Lemma `EvenNat` states the property we wish to prove. From its
+precondition (keyword `requires`) and
+[#eq-least-is-exists], we know there is some `k` that will make the condition in the
+assign-such-that statement true. Such a value is then assigned to `k` and passed to
+the auxiliary lemma, which promises to establish the proof goal. Given the condition
+`g#[k](x)`, the definition of `g#` lets us conclude `k != 0` as well as the disjunction
+`x == 0 || g#[k-1](x-2)`. The then branch considers the case of the first disjunct,
+from which the proof goal follows automatically. The else branch can then assume
+`g#[k-1](x-2)` and calls the induction hypothesis with those parameters. The proof
+glue that shows the proof goal for `x` to follow from the proof goal with `x-2` is
+done automatically.
+
+Because Dafny automatically inserts the statement
+
+```
+forall k', x' | 0 <= k' < k && g#[k'](x') {
+ EvenNatAux(k', x');
+}
+```
+
+at the beginning of the body of `EvenNatAux`, the body can be left empty and Dafny
+completes the proof automatically.
+
+Here is the Dafny program that gives the proof from Section [#sec-example-greatest-solution]:
+
+``` {.para-end}
+lemma Always(x: int)
+ ensures G(x)
+{ forall k: nat { AlwaysAux(k, x); } }
+lemma AlwaysAux(k: nat, x: int)
+ ensures G#[k](x)
+{ }
+```
+
+While each of these proofs involves only basic proof rules, the setup feels a bit clumsy,
+even with the empty body of the auxiliary lemmas. Moreover,
+the proofs do not reflect the intuitive proofs I described in
+Section [#sec-example-least-solution] and [#sec-example-greatest-solution].
+These shortcoming are addressed in the next subsection.
+
+### Nicer Proofs of Extreme Predicates
+
+The proofs we just saw follow standard forms:
+use Skolemization to convert the inductive predicate into a prefix predicate for some `k`
+and then do the proof inductively over `k`; respectively,
+by induction over `k`, prove the prefix predicate for every `k`, then use
+universal introduction to convert to the coinductive predicate.
+With the declarations `inductive lemma` and `colemma`, Dafny offers to
+set up the proofs
+in these standard forms. What is gained is not just fewer characters in the program
+text, but also a possible intuitive reading of the proofs. (Okay, to be fair, the
+reading is intuitive for simpler proofs; complicated proofs may or may not be intuitive.)
+
+Somewhat analogous to the creation of prefix predicates from extreme predicates, Dafny
+automatically creates a _prefix lemma_ `L#` from each "extreme lemma" `L`. The pre-
+and postconditions of a prefix lemma are copied from those of the extreme lemma,
+except for the following replacements:
+For an inductive lemma, Dafny looks in the precondition to find calls (in positive, continuous
+positions) to inductive predicates `P(x)` and replaces these with `P#[_k](x)`.
+For a
+co-lemma, Dafny looks in the postcondition to find calls (in positive, continuous positions)
+to co-predicates `P` (including equality among coinductive datatypes, which is a built-in
+co-predicate) and replaces these with `P#[_k](x)`.
+In each case, these predicates `P` are the lemma's _focal predicates_.
+
+The body of the extreme lemma is moved to the prefix lemma, but with
+replacing each recursive
+call `L(x)` with `L#[_k-1](x)` and replacing each occurrence of a call
+to a focal predicate
+`P(x)` with `P#[_k-1](x)`. The bodies of the extreme lemmas are then replaced as shown
+in the previous subsection. By construction, this new body correctly leads to the
+extreme lemma's postcondition.
+
+Let us see what effect these rewrites have on how one can write proofs. Here are the proofs
+of our running example:
+
+```
+inductive lemma EvenNat(x: int)
+ requires g(x)
+ ensures 0 <= x && x % 2 == 0
+{ if x == 0 { } else { EvenNat(x-2); } }
+colemma Always(x: int)
+ ensures G(x)
+{ Always(x-2); }
+```
+
+Both of these proofs follow the intuitive proofs given in Sections
+[#sec-example-least-solution] and [#sec-example-greatest-solution]. Note that in these
+simple examples, the user is never bothered with either prefix predicates nor
+prefix lemmas---the proofs just look like "what you'd expect".
+
+Since Dafny automatically inserts calls to the induction hypothesis at the beginning of
+each lemma, the bodies of the given extreme lemmas `EvenNat` and
+`Always` can be empty and Dafny still completes the proofs.
+Folks, it doesn't get any simpler than that!
+
+# Class Types
+
+````
+ClassDecl = "class" { Attribute } ClassName [ GenericParameters ]
+ ["extends" Type {"," Type} ]
+ "{" { { DeclModifier } ClassMemberDecl(moduleLevelDecl: false) } "}"
+````
+
+````
+ClassMemberDecl(moduleLevelDecl) =
+ ( FieldDecl | FunctionDecl |
+ MethodDecl(isGhost: ("ghost" was present),
+ allowConstructor: !moduleLevelDecl)
+ )
+````
+The ``ClassMemberDecl`` parameter `moduleLevelDecl` will be true if
+the member declaration is at the top level or directly within a
+module declaration. It will be false for ``ClassMemberDecl``s
+that are part of a class or trait declaration. If `moduleLevelDecl` is
+false ``FieldDecl``s are not allowed.
+
+A _class_ `C` is a reference type declared as follows:
+```
+class C<T> extends J1, ..., Jn
+{
+ \(_members_\)
+}
+```
+where the list of type parameters `T` is optional and so is
+"`extends J1, ..., Jn`", which says that the class extends traits `J1` ... `Jn`.
+The members of a class are _fields_, _functions_, and
+_methods_. These are accessed or invoked by dereferencing a reference
+to a `C` instance.
+
+A function or method is invoked on an _instance_
+of `C`, unless the function or method is declared `static`.
+A function or method that is not `static` is called an
+_instance_ function or method.
+
+An instance function or method takes an implicit _receiver_
+parameter, namely, the instance used to access the member. In the
+specification and body of an instance function or method, the receiver
+parameter can be referred to explicitly by the keyword `this`.
+However, in such places, members of `this` can also be mentioned
+without any qualification. To illustrate, the qualified `this.f` and
+the unqualified `f` refer to the same field of the same object in the
+following example:
+```
+class C {
+ var f: int
+ method Example() returns (b: bool)
+ {
+ b := f == this.f;
+ }
+}
+```
+so the method body always assigns `true` to the out-parameter `b`.
+There is no semantic difference between qualified and
+unqualified accesses to the same receiver and member.
+
+A `C` instance is created using `new`, for example:
+```
+c := new C;
+```
+
+Note that `new` simply allocates a `C` object and returns a reference
+to it; the initial values of its fields are arbitrary values of their
+respective types. Therefore, it is common to invoke a method, known
+as an _initialization method_, immediately after creation, for
+example:
+```
+c := new C;
+c.InitFromList(xs, 3);
+```
+When an initialization method has no out-parameters and modifies no
+more than `this`, then the two statements above can be combined into
+one:
+```
+c := new C.InitFromList(xs, 3);
+```
+Note that a class can contain several initialization methods, that
+these methods can be invoked at any time, not just as part of a `new`,
+and that `new` does not require that an initialization method be
+invoked at creation.
+
+A clas can declare special initializing methods called _constructor methods_.
+See Section [#sec-method-declarations].
+
+## Field Declarations
+````
+FieldDecl = "var" { Attribute } FIdentType { "," FIdentType }
+````
+An ``FIdentType`` is used to declare a field. The field name is either an
+identifier (that is not allowed to start with a leading underscore) or
+some digits. Digits are used if you want to number your fields, e.g. "0",
+"1", etc.
+````
+FIdentType = ( FieldIdent | digits ) ":" Type
+````
+
+A field x of some type T is declared as:
+```
+var x: T
+```
+
+A field declaration declares one or more fields of the enclosing class.
+Each field is a named part of the state of an object of that class. A
+field declaration is similar to but distinct from a variable declaration
+statement. Unlike for local variables and bound variables, the type is
+required and will not be inferred.
+
+Unlike method and function declarations, a field declaration
+cannot be given at the top level. Fields can be declared in either a
+class or a trait. A class that inherits from multiple traits will
+have all the fields declared in any of its parent traits.
+
+Fields that are declared as `ghost` can only be used in specifications,
+not in code that will be compiled into executable code.
+
+Fields may not be declared static.
+
+`protected` is not allowed for fields.
+
+## Method Declarations
+````
+MethodDecl(isGhost, allowConstructor) =
+ MethodKeyword { Attribute } [ MethodName ]
+ ( MethodSignature(isGhost) | SignatureEllipsis_ )
+ MethodSpec [ BlockStmt ]
+````
+The `isGhost` parameter is true iff the `ghost` keyword
+preceded the method declaration.
+
+If the `allowConstructor` parameter is false then
+the ``MethodDecl`` must not be a `constructor`
+declaration.
+
+````
+MethodKeyword = ("method" | "lemma" | "colemma"
+ | "inductive" "lemma" | "constructor" )
+````
+The method keyword is used to specify special kinds of methods
+as explained below.
+
+````
+MethodSignature(isGhost) =
+ [ GenericParameters ]
+ Formals(allowGhost: !isGhost)
+ [ "returns" Formals(allowGhost: !isGhost) ]
+````
+A method signature specifies the method generic parameters,
+input parameters and return parameters.
+The formal parameters are not allowed to have `ghost` specified
+if `ghost` was already specified for the method.
+
+````
+SignatureEllipsis_ = "..."
+````
+A ``SignatureEllipsis_`` is used when a method or function is being redeclared
+in module that refines another module. In that case the signature is
+copied from the module that is being refined. This works because
+Dafny does not support method or function overloading, so the
+name of the class method uniquely identifies it without the
+signature.
+
+````
+Formals(allowGhostKeyword) =
+ "(" [ GIdentType(allowGhostKeyword)
+ { "," GIdentType(allowGhostKeyword) } ] ")"
+````
+The ``Formals`` specifies the names and types of the method input or
+output parameters.
+
+See section [#sec-method-specification] for a description of ``MethodSpec``.
+
+A method declaration adheres to the ``MethodDecl`` grammar above.
+Here is an example of a method declaration.
+
+```
+method {:att1}{:att2} M<T1, T2>(a: A, b: B, c: C) returns (x: X, y: Y, z: Z)
+ requires Pre
+ modifies Frame
+ ensures Post
+ decreases Rank
+{
+ Body
+}
+```
+
+where `:att1` and `:att2` are attributes of the method,
+`T1` and `T2` are type parameters of the method (if generic),
+`a, b, c` are the method’s in-parameters, `x, y, z` are the
+method’s out-parameters, `Pre` is a boolean expression denoting the
+method’s precondition, `Frame` denotes a set of objects whose fields may
+be updated by the method, `Post` is a boolean expression denoting the
+method’s postcondition, `Rank` is the method’s variant function, and
+`Body` is a statement that implements the method. `Frame` can be a list
+of expressions, each of which is a set of objects or a single object, the
+latter standing for the singleton set consisting of that one object. The
+method’s frame is the union of these sets, plus the set of objects
+allocated by the method body. For example, if `c` and `d` are parameters
+of a class type `C`, then
+
+```
+modifies {c, d}
+
+modifies {c} + {d}
+
+modifies c, {d}
+
+modifies c, d
+```
+
+all mean the same thing.
+
+A method can be declared as ghost by preceding the declaration with the
+keyword ghost. By default, a method has an implicit receiver parameter,
+this. This parameter can be removed by preceding the method declaration
+with the keyword static. A static method M in a class C can be invoked by
+C.M(…).
+
+In a class, a method can be declared to be a constructor method by
+replacing the keyword `method` with the keyword `constructor`. A constructor
+can only be called at the time an object is allocated (see
+object-creation examples below), and for a class that contains one or
+more constructors, object creation must be done in conjunction with a
+call to a constructor.
+
+An ordinary method is declared with the `method` keyword.
+Section [#sec-constructors] explains methods that instead use the
+`constructor` keyword. Section [#sec-lemmas] discusses methods that are
+declared with the `lemma` keyword. Methods declared with the `inductive`
+`lemma` keywords are discussed later in the context of inductive
+predicates (see [#sec-inductive-datatypes]). Methods declared with the
+`colemma` keyword are discussed later in the context of co-inductive
+types, in section [#sec-colemmas].
+
+A method without is body is _abstract_. A method is allowed to be
+abstract under the following circumstances:
+
+* It contains an `{:axiom}` attribute
+* It contains an `{:imported}` attribute
+* It contains a `{:decl}` attribute
+* It is a declaration in an abstract module.
+Note that when there is no body, Dafny assumes that the *ensures*
+clauses are true without proof.
+
+### Constructors
+To write structured object-oriented programs, one often relies on that
+objects are constructed only in certain ways. For this purpose, Dafny
+provides _constructor (method)s_, which are a restricted form of
+initialization methods. A constructor is declared with the keyword
+`constructor` instead of `method`. When a class contains a
+constructor, every call to `new` for that class must be accompanied
+with a call to one of the constructors. Moreover, a constructor
+cannot be called at other times, only during object creation. Other
+than these restrictions, there is no semantic difference between using
+ordinary initialization methods and using constructors.
+
+The Dafny design allows the constructors to be named, which promotes
+using names like `InitFromList` above. Still, many classes have just
+one constructor or have a typical constructor. Therefore, Dafny
+allows one _anonymous constructor_, that is, a constructor whose name
+is essentially "". For example:
+```
+class Item {
+ constructor (x: int, y: int)
+ // ...
+}
+```
+When invoking this constructor, the "`.`" is dropped, as in:
+```
+m := new Item(45, 29);
+```
+Note that an anonymous constructor is just one way to name a
+constructor; there can be other constructors as well.
+
+### Lemmas
+Sometimes there are steps of logic required to prove a program correct,
+but they are too complex for Dafny to discover and use on its own. When
+this happens, we can often give Dafny assistance by providing a lemma.
+This is done by declaring a method with the `lemma` keyword.
+Lemmas are implicitly ghost methods and the `ghost` keyword cannot
+be applied to them.
+
+For an example, see the `FibProperty` lemma in
+Section [#sec-proofs-in-dafny].
+
+See [the Dafny Lemmas tutorial](http://rise4fun.com/Dafny/tutorial/Lemmas)
+for more examples and hints for using lemmas.
+
+## Function Declarations
+
+````
+FunctionDecl =
+ ( "function" [ "method" ] { Attribute }
+ FunctionName
+ FunctionSignatureOrEllipsis_(allowGhostKeyword: ("method" present))
+ | "predicate" [ "method" ] { Attribute }
+ PredicateName
+ PredicateSignatureOrEllipsis_(allowGhostKeyword: ("method" present))
+ | "inductive" "predicate" { Attribute }
+ PredicateName
+ PredicateSignatureOrEllipsis_(allowGhostKeyword: false)
+ | "copredicate" { Attribute }
+ CopredicateName
+ PredicateSignatureOrEllipsis_(allowGhostKeyword: false)
+ )
+ FunctionSpec [ FunctionBody ]
+
+FunctionSignatureOrEllipsis_(allowGhostKeyword) =
+ FunctionSignature_ | SignatureEllipsis_
+FunctionSignature_(allowGhostKeyword) =
+ [ GenericParameters ] Formals(allowGhostKeyword) ":" Type
+
+PredicateSignatureOrEllipsis_(allowGhostKeyword) =
+ PredicateSignature_(allowGhostKeyword) | SignatureEllipsis_
+PredicateSignature_(allowGhostKeyword) =
+ [ GenericParameters ] Formals(allowGhostKeyword)
+
+FunctionBody = "{" Expression(allowLemma: true, allowLambda: true) "}"
+````
+In the above productions, allowGhostKeyword is true if the optional
+"method" keyword was specified. This allows some of the
+formal parameters of a function method to be specified as ghost.
+
+See section [#sec-function-specification] for a description of ``FunctionSpec``.
+
+A Dafny function is a pure mathematical function. It is allowed to
+read memory that was specified in its `reads` expression but is not
+allowed to have any side effects.
+
+Here is an example function declaration:
+```
+function {:att1}{:att2} F<T1, T2>(a: A, b: B, c: C): T
+ requires Pre
+ reads Frame
+ ensures Post
+ decreases Rank
+{
+ Body
+}
+```
+
+where `:att1` and `:att2` are attributes of the function, if any, `T1`
+and `T2` are type parameters of the function (if generic), `a, b, c` are
+the functions’s parameters, `T` is the type of the function’s result,
+`Pre` is a boolean expression denoting the function’s precondition,
+`Frame` denotes a set of objects whose fields the function body may
+depend on, `Post` is a boolean expression denoting the function’s
+postcondition, `Rank` is the function’s variant function, and `Body` is
+an expression that defines the function return value. The precondition
+allows a function to be partial, that is, the precondition says when the
+function is defined (and Dafny will verify that every use of the function
+meets the precondition). The postcondition is usually not needed, since
+the body of the function gives the full definition. However, the
+postcondition can be a convenient place to declare properties of the
+function that may require an inductive proof to establish. For example:
+
+````
+function Factorial(n: int): int
+ requires 0 <= n
+ ensures 1 <= Factorial(n)
+{
+ if n == 0 then 1 else Factorial(n-1) * n
+}
+````
+
+says that the result of Factorial is always positive, which Dafny
+verifies inductively from the function body. To refer to the function’s
+result in the postcondition, use the function itself, as shown in the
+example.
+
+By default, a function is *ghost*, and cannot be called from non-ghost
+code. To make it non-ghost, replace the keyword function with the two
+keywords "function method".
+
+By default, a function has an implicit receiver parameter, `this`. This
+parameter can be removed by preceding the function declaration with the
+keyword `static`. A static function `F` in a class `C` can be invoked
+by `C.F(…)`. This can give a convenient way to declare a number of helper
+functions in a separate class.
+
+As for methods, a ``SignatureEllipsis_`` is used when declaring
+a function in a module refinement. For example, if module `M0` declares
+function `F`, a module `M1` can be declared to refine `M0` and
+`M1` can then refine `F`. The refinement function, `M1.F` can have
+a ``SignatureEllipsis_`` which means to copy the signature form
+`M0.F`. A refinement function can furnish a body for a function
+(if `M0.F` does not provide one). It can also add **ensures**
+clauses. And if `F` is a predicate, it can add conjuncts to
+a previously given body.
+
+### Function Transparency
+A function is said to be _transparent_ in a location if the
+contents of the body of the function is visible at that point.
+A function is said to be _opaque_ at a location if it is not
+transparent. However the ``FunctionSpec`` of a function
+is always available.
+
+A function is usually transparent up to some unrolling level (up to
+1, or maybe 2 or 3). If its arguments are all literals it is
+transparent all the way.
+
+But the transparency of a function is affected by the following:
+
+* whether the function was declared to be protected, and
+* whether the function was given the `{:opaque}` attribute (as explained
+in Section [#sec-opaque]).
+
+The following table summarizes where the function is transparent.
+The module referenced in the table is the module in which the
+function is defined.
+
++------------+--------------+-------------+-------------+
+| Protected? | `{:opaque}`? | Transparent | Transparent |
+| | | Inside | Outside |
+| | | Module | Module |
++:----------:+:------------:+:-----------:+:-----------:+
+| N | N | Y | Y |
+| Y | N | Y | N |
+| N | Y | N | N |
++------------+--------------+-------------+-------------+
+
+When `{:opaque}` is specified for function `g`, `g` is opaque,
+however the lemma `reveal_g` is available to give the semantics
+of `g` whether in the defining module or outside.
+
+It currently is not allowed to have both `protected` and
+`{:opaque}` specified for a function.
+
+### Predicates
+A function that returns a `bool` results is called a _predicate_. As an
+alternative syntax, a predicate can be declared by replacing the `function`
+keyword with the `predicate` keyword and omitting a declaration of the
+return type.
+
+### Inductive Predicates and Lemmas
+See section [#sec-friendliness] for descriptions
+of inductive predicates and lemmas.
+
+# Trait Types
+````
+TraitDecl = "trait" { Attribute } TraitName [ GenericParameters ]
+ "{" { { DeclModifier } ClassMemberDecl(moduleLevelDecl: false) } "}"
+````
+
+A _trait_ is an "abstract superclass", or call it an "interface" or
+"mixin". Traits are new to Dafny and are likely to evolve for a
+while.
+
+The declaration of a trait is much like that of a class:
+```
+trait J
+{
+ \(_members_\)
+}
+```
+where `\(_members_\)` can include fields, functions, and methods, but
+no constructor methods. The functions and methods are allowed to be
+declared `static`.
+
+A reference type `C` that extends a trait `J` is assignable to `J`, but
+not the other way around. The members of `J` are available as members
+of `C`. A member in `J` is not allowed to be redeclared in `C`,
+except if the member is a non-`static` function or method without a
+body in `J`. By doing so, type `C` can supply a stronger
+specification and a body for the member.
+
+`new` is not allowed to be used with traits. Therefore, there is no
+object whose allocated type is a trait. But there can of course be
+objects of a class `C` that implements a trait `J`, and a reference to
+such a `C` object can be used as a value of type `J`.
+
+As an example, the following trait represents movable geometric shapes:
+```
+trait Shape
+{
+ function method Width(): real
+ reads this
+ method Move(dx: real, dy: real)
+ modifies this
+ method MoveH(dx: real)
+ modifies this
+ {
+ Move(dx, 0.0);
+ }
+}
+```
+Members `Width` and `Move` are _abstract_ (that is, body less) and can
+be implemented differently by different classes that extend the trait.
+The implementation of method `MoveH` is given in the trait and thus
+gets used by all classes that extend `Shape`. Here are two classes
+that each extends `Shape`:
+```
+class UnitSquare extends Shape
+{
+ var x: real, y: real
+ function method Width(): real { // note the empty reads clause
+ 1.0
+ }
+ method Move(dx: real, dy: real)
+ modifies this
+ {
+ x, y := x + dx, y + dy;
+ }
+}
+class LowerRightTriangle extends Shape
+{
+ var xNW: real, yNW: real, xSE: real, ySE: real
+ function method Width(): real
+ reads this
+ {
+ xSE - xNW
+ }
+ method Move(dx: real, dy: real)
+ modifies this
+ {
+ xNW, yNW, xSE, ySE := xNW + dx, yNW + dy, xSE + dx, ySE + dy;
+ }
+}
+```
+Note that the classes can declare additional members, that they supply
+implementations for the abstract members of the trait,
+that they repeat the member signatures, and that they are responsible
+for providing their own member specifications that both strengthen the
+corresponding specification in the trait and are satisfied by the
+provided body.
+Finally, here is some code that creates two class instances and uses
+them together as shapes:
+```
+var myShapes: seq<Shape>;
+var A := new UnitSquare;
+myShapes := [A];
+var tri := new LowerRightTriangle;
+// myShapes contains two Shape values, of different classes
+myShapes := myShapes + [tri];
+// move shape 1 to the right by the width of shape 0
+myShapes[1].MoveH(myShapes[0].Width());
+```
+
+# Array Types
+````
+ArrayType_ = arrayToken [ GenericInstantiation ]
+````
+
+Dafny supports mutable fixed-length _array types_ of any positive
+dimension. Array types are reference types.
+
+## One-dimensional arrays
+
+A one-dimensional array of `n` `T` elements is created as follows:
+```
+a := new T[n];
+```
+The initial values of the array elements are arbitrary values of type
+`T`.
+The length of an array is retrieved using the immutable `Length`
+member. For example, the array allocated above satisfies:
+```
+a.Length == n
+```
+
+For any integer-based numeric `i` in the range `0 <= i < a.Length`,
+the _array selection_ expression `a[i]` retrieves element `i` (that
+is, the element preceded by `i` elements in the array). The
+element stored at `i` can be changed to a value `t` using the array
+update statement:
+```
+a[i] := t;
+```
+
+Caveat: The type of the array created by `new T[n]` is
+`array<T>`. A mistake that is simple to make and that can lead to
+befuddlement is to write `array<T>` instead of `T` after `new`.
+For example, consider the following:
+```
+var a := new array<T>;
+var b := new array<T>[n];
+var c := new array<T>(n); // resolution error
+var d := new array(n); // resolution error
+```
+The first statement allocates an array of type `array<T>`, but of
+unknown length. The second allocates an array of type
+`array<array<T>>` of length `n`, that is, an array that holds `n`
+values of type `array<T>`. The third statement allocates an
+array of type `array<T>` and then attempts to invoke an anonymous
+constructor on this array, passing argument `n`. Since `array` has no
+constructors, let alone an anonymous constructor, this statement
+gives rise to an error. If the type-parameter list is omitted for a
+type that expects type parameters, Dafny will attempt to fill these
+in, so as long as the `array` type parameter can be inferred, it is
+okay to leave off the "`<T>`" in the fourth statement above. However,
+as with the third statement, `array` has no anonymous constructor, so
+an error message is generated.
+
+One-dimensional arrays support operations that convert a stretch of
+consecutive elements into a sequence. For any array `a` of type
+`array<T>`, integer-based numerics `lo` and `hi` satisfying
+`0 <= lo <= hi <= a.Length`, the following operations each yields a
+`seq<T>`:
+
++---------------------+------------------------------------+
+| expression | description |
++---------------------+------------------------------------+
+| `a[lo..hi]` | subarray conversion to sequence |
+| `a[lo..]` | drop |
+| `a[..hi]` | take |
+| `a[..]` | array conversion to sequence |
++---------------------+------------------------------------+
+
+The expression `a[lo..hi]` takes the first `hi` elements of the array,
+then drops the first `lo` elements thereof and returns what remains as
+a sequence. The resulting sequence thus has length `hi - lo`.
+The other operations are special instances of the first. If `lo` is
+omitted, it defaults to `0` and if `hi` is omitted, it defaults to
+`a.Length`.
+In the last operation, both `lo` and `hi` have been omitted, thus
+`a[..]` returns the sequence consisting of all the array elements of
+`a`.
+
+The subarray operations are especially useful in specifications. For
+example, the loop invariant of a binary search algorithm that uses
+variables `lo` and `hi` to delimit the subarray where the search `key`
+may be still found can be expressed as follows:
+```
+key !in a[..lo] && key !in a[hi..]
+```
+Another use is to say that a certain range of array elements have not
+been changed since the beginning of a method:
+```
+a[lo..hi] == old(a[lo..hi])
+```
+or since the beginning of a loop:
+```
+ghost var prevElements := a[..];
+while // ...
+ invariant a[lo..hi] == prevElements[lo..hi]
+{
+ // ...
+}
+```
+Note that the type of `prevElements` in this example is `seq<T>`, if
+`a` has type `array<T>`.
+
+A final example of the subarray operation lies in expressing that an
+array's elements are a permutation of the array's elements at the
+beginning of a method, as would be done in most sorting algorithms.
+Here, the subarray operation is combined with the sequence-to-multiset
+conversion:
+```
+multiset(a[..]) == multiset(old(a[..]))
+```
+
+## Multi-dimensional arrays
+
+An array of 2 or more dimensions is mostly like a one-dimensional
+array, except that `new` takes more length arguments (one for each
+dimension), and the array selection expression and the array update
+statement take more indices. For example:
+```
+matrix := new T[m, n];
+matrix[i, j], matrix[x, y] := matrix[x, y], matrix[i, j];
+```
+create a 2-dimensional array whose dimensions have lengths `m` and
+`n`, respectively, and then swaps the elements at `i,j` and `x,y`.
+The type of `matrix` is `array2<T>`, and similarly for
+higher-dimensional arrays (`array3<T>`, `array4<T>`, etc.). Note,
+however, that there is no type `array0<T>`, and what could have been
+`array1<T>` is actually named just `array<T>`.
+
+The `new` operation above requires `m` and `n` to be non-negative
+integer-based numerics. These lengths can be retrieved using the
+immutable fields `Length0` and `Length1`. For example, the following
+holds of the array created above:
+```
+matrix.Length0 == m && matrix.Length1 == n
+```
+Higher-dimensional arrays are similar (`Length0`, `Length1`,
+`Length2`, ...). The array selection expression and array update
+statement require that the indices are in bounds. For example, the
+swap statement above is well-formed only if:
+```
+0 <= i < matrix.Length0 && 0 <= j < matrix.Length1 &&
+0 <= x < matrix.Length0 && 0 <= y < matrix.Length1
+```
+
+In contrast to one-dimensional arrays, there is no operation to
+convert stretches of elements from a multi-dimensional array to a
+sequence.
+
+# Type object
+````
+ObjectType_ = "object"
+````
+
+There is a built-in trait `object` that is like a supertype of all
+reference types.[^fn-object-trait] Every class automatically extends
+object and so does every user-defined trait. The purpose of type `object`
+is to enable a uniform treatment of _dynamic frames_. In particular, it
+is useful to keep a ghost field (typically named `Repr` for
+"representation") of type `set<object>`.
+
+[^fn-object-trait]: The current compiler restriction that `object` cannot
+ be used as a type parameter needs to be removed.
+
+# Iterator types
+````
+IteratorDecl = "iterator" { Attribute } IteratorName
+ ( [ GenericParameters ]
+ Formals(allowGhostKeyword: true)
+ [ "yields" Formals(allowGhostKeyword: true) ]
+ | "..."
+ )
+ IteratorSpec [ BlockStmt ]
+````
+
+See section [#sec-iterator-specification] for a description of ``IteratorSpec``.
+
+An _iterator_ provides a programming abstraction for writing code that
+iteratively returns elements. These CLU-style iterators are
+_co-routines_ in the sense that they keep track of their own program
+counter and control can be transferred into and out of the iterator
+body.
+
+An iterator is declared as follows:
+```
+iterator Iter<T>(\(_in-params_\)) yields (\(_yield-params_\))
+ \(_specification_\)
+{
+ \(_body_\)
+}
+```
+where `T` is a list of type parameters (as usual, if there are no type
+parameters, "`<T>`" is omitted). This declaration gives rise to a
+reference type with the same name, `Iter<T>`. In the signature,
+in-parameters and yield-parameters are the iterator's analog of a
+method's in-parameters and out-parameters. The difference is that the
+out-parameters of a method are returned to a caller just once, whereas
+the yield-parameters of an iterator are returned each time the iterator
+body performs a `yield`. The body consists of statements, like in a
+method body, but with the availability also of `yield` statements.
+
+From the perspective of an iterator client, the `iterator` declaration
+can be understood as generating a class `Iter<T>` with various
+members, a simplified version of which is described next.
+
+The `Iter<T>` class contains an anonymous constructor whose parameters
+are the iterator's in-parameters:
+```
+predicate Valid()
+constructor (\(_in-params_\))
+ modifies this
+ ensures Valid()
+```
+An iterator is created using `new` and this anonymous constructor.
+For example, an iterator willing to return ten consecutive integers
+from `start` can be declared as follows:
+```
+iterator Gen(start: int) yields (x: int)
+{
+ var i := 0;
+ while i < 10 {
+ x := start + i;
+ yield;
+ i := i + 1;
+ }
+}
+```
+An instance of this iterator is created using:
+```
+iter := new Gen(30);
+```
+
+The predicate `Valid()` says when the iterator is in a state where one
+can attempt to compute more elements. It is a postcondition of the
+constructor and occurs in the specification of the `MoveNext` member:
+```
+method MoveNext() returns (more: bool)
+ requires Valid()
+ modifies this
+ ensures more ==> Valid()
+```
+Note that the iterator remains valid as long as `MoveNext` returns
+`true`. Once `MoveNext` returns `false`, the `MoveNext` method can no
+longer be called. Note, the client is under no obligation to keep
+calling `MoveNext` until it returns `false`, and the body of the
+iterator is allowed to keep returning elements forever.
+
+The in-parameters of the iterator are stored in immutable fields of
+the iterator class. To illustrate in terms of the example above, the
+iterator class `Gen` contains the following field:
+```
+var start: int
+```
+The yield-parameters also result in members of the iterator class:
+```
+var x: int
+```
+These fields are set by the `MoveNext` method. If `MoveNext` returns
+`true`, the latest yield values are available in these fields and the
+client can read them from there.
+
+To aid in writing specifications, the iterator class also contains
+ghost members that keep the history of values returned by
+`MoveNext`. The names of these ghost fields follow the names of the
+yield-parameters with an "`s`" appended to the name (to suggest
+plural). Name checking rules make sure these names do not give rise
+to ambiguities. The iterator class for `Gen` above thus contains:
+```
+ghost var xs: seq<int>
+```
+These history fields are changed automatically by `MoveNext`, but are
+not assignable by user code.
+
+Finally, the iterator class contains some special fields for use in
+specifications. In particular, the iterator specification gets
+recorded in the following immutable fields:
+```
+ghost var _reads: set<object>
+ghost var _modifies: set<object>
+ghost var _decreases0: T0
+ghost var _decreases1: T1
+// ...
+```
+where there is a `_decreases\(_i_\): T\(_i_\)` field for each
+component of the iterator's `decreases`
+clause.[^fn-iterator-field-names]
+In addition, there is a field:
+```
+ghost var _new: set<object>;
+```
+to which any objects allocated on behalf of the iterator body get
+added. The iterator body is allowed to remove elements from the
+`_new` set, but cannot by assignment to `_new` add any elements.
+
+[^fn-iterator-field-names]: It would make sense to rename the special
+ fields `_reads` and `_modifies` to have the same names as the
+ corresponding keywords, `reads` and `modifies`, as is done for
+ function values. Also, the various `_decreases\(_i_\)` fields can
+ combined into one field named `decreases` whose type is a
+ _n_-tuple. Thse changes may be incorporated into a future version
+ of Dafny.
+
+Note, in the precondition of the iterator, which is to hold upon
+construction of the iterator, the in-parameters are indeed
+in-parameters, not fields of `this`.
+
+It's regrettably tricky to use iterators. The language really
+ought to have a `foreach` statement to make this easier.
+Here is an example showing definition and use of an iterator.
+
+```
+iterator Iter<T>(s: set<T>) yields (x: T)
+ yield ensures x in s && x !in xs[..|xs|-1];
+ ensures s == set z | z in xs;
+{
+ var r := s;
+ while (r != {})
+ invariant forall z :: z in xs ==> x !in r; // r and xs are disjoint
+ invariant s == r + set z | z in xs;
+ {
+ var y :| y in r;
+ r, x := r - {y}, y;
+ yield;
+ assert y == xs[|xs|-1]; // needed as a lemma to prove loop invariant
+ }
+}
+
+method UseIterToCopy<T>(s: set<T>) returns (t: set<T>)
+ ensures s == t;
+{
+ t := {};
+ var m := new Iter(s);
+ while (true)
+ invariant m.Valid() && fresh(m._new);
+ invariant t == set z | z in m.xs;
+ decreases s - t;
+ {
+ var more := m.MoveNext();
+ if (!more) { break; }
+ t := t + {m.x};
+ }
+}
+```
+
+<!--
+# Async-task types
+
+Another experimental feature in Dafny that is likely to undergo some
+evolution is _asynchronous methods_. When an asynchronous method is
+called, it does not return values for the out-parameters, but instead
+returns an instance of an _async-task type_. An asynchronous method
+declared in a class `C` with the following signature:
+```
+async method AM<T>(\(_in-params_\)) returns (\(_out-params_\))
+```
+also gives rise to an async-task type `AM<T>` (outside the enclosing
+class, the name of the type needs the qualification `C.AM<T>`). The
+async-task type is a reference type and can be understood as a class
+with various members, a simplified version of which is described next.
+
+Each in-parameter `x` of type `X` of the asynchronous method gives
+rise to a immutable ghost field of the async-task type:
+```
+ghost var x: X;
+```
+Each out-parameter `y` of type `Y` gives rise to a field
+```
+var y: Y;
+```
+These fields are changed automatically by the time the asynchronous
+method is successfully awaited, but are not assignable by user code.
+
+The async-task type also gets a number of special fields that are used
+to keep track of dependencies, outstanding tasks, newly allocated
+objects, etc. These fields will be described in more detail as the
+design of asynchronous methods evolves.
+
+-->
+
+# Function types
+
+````
+Type = DomainType "->" Type
+````
+
+Functions are first-class values in Dafny. Function types have the form
+`(T) -> U` where `T` is a comma-delimited list of types and `U` is a
+type. `T` is called the function's _domain type(s)_ and `U` is its
+_range type_. For example, the type of a function
+```
+function F(x: int, b: bool): real
+```
+is `(int, bool) -> real`. Parameters are not allowed to be ghost.
+
+To simplify the appearance of the basic case where a function's
+domain consist of a list of exactly one type, the parentheses around
+the domain type can be dropped in this case, as in `T -> U`.
+This innocent simplification requires additional explanation in the
+case where that one type is a tuple type, since tuple types are also
+written with enclosing parentheses.
+If the function takes a single argument that is a tuple, an additional
+set of parentheses is needed. For example, the function
+```
+function G(pair: (int, bool)): real
+```
+has type `((int, bool)) -> real`. Note the necessary double
+parentheses. Similarly, a function that takes no arguments is
+different from one that takes a 0-tuple as an argument. For instance,
+the functions
+```
+function NoArgs(): real
+function Z(unit: ()): real
+```
+have types `() -> real` and `(()) -> real`, respectively.
+
+The function arrow, `->`, is right associative, so `A -> B -> C` means
+`A -> (B -> C)`. The other association requires explicit parentheses:
+`(A -> B) -> C`.
+
+Note that the receiver parameter of a named function is not part of
+the type. Rather, it is used when looking up the function and can
+then be thought of as being captured into the function definition.
+For example, suppose function `F` above is declared in a class `C` and
+that `c` references an object of type `C`; then, the following is type
+correct:
+```
+var f: (int, bool) -> real := c.F;
+```
+whereas it would have been incorrect to have written something like:
+```
+var f': (C, int, bool) -> real := F; // not correct
+```
+
+Outside its type signature, each function value has three properties,
+described next.
+
+Every function implicitly takes the heap as an argument. No function
+ever depends on the _entire_ heap, however. A property of the
+function is its declared upper bound on the set of heap locations it
+depends on for a given input. This lets the verifier figure out that
+certain heap modifications have no effect on the value returned by a
+certain function. For a function `f: T -> U` and a value `t` of type
+`T`, the dependency set is denoted `f.reads(t)` and has type
+`set<object>`.
+
+The second property of functions stems from the fact that every function
+is potentially _partial_. In other words, a property of a function is its
+_precondition_. For a function `f: T -> U`, the precondition of `f` for a
+parameter value `t` of type `T` is denoted `f.requires(t)` and has type
+`bool`.
+
+The third property of a function is more obvious---the function's
+body. For a function `f: T -> U`, the value that the function yields
+for an input `t` of type `T` is denoted `f(t)` and has type `U`.
+
+Note that `f.reads` and `f.requires` are themselves functions.
+Suppose `f` has type `T -> U` and `t` has type `T`. Then, `f.reads`
+is a function of type `T -> set<object>` whose `reads` and `requires`
+properties are:
+```
+f.reads.reads(t) == f.reads(t)
+f.reads.requires(t) == true
+```
+`f.requires` is a function of type `T -> bool` whose `reads` and
+`requires` properties are:
+```
+f.requires.reads(t) == f.reads(t)
+f.requires.requires(t) == true
+```
+
+Dafny also support anonymous functions by means of
+_lambda expressions_. See section [#sec-lambda-expressions].
+
+# Algebraic Datatypes
+
+Dafny offers two kinds of algebraic datatypes, those defined
+inductively and those defined co-inductively. The salient property of
+every datatype is that each value of the type uniquely identifies one
+of the datatype's constructors and each constructor is injective in
+its parameters.
+
+````
+DatatypeDecl = ( InductiveDatatypeDecl | CoinductiveDatatypeDecl )
+````
+
+## Inductive datatypes
+
+````
+InductiveDatatypeDecl_ = "datatype" { Attribute } DatatypeName [ GenericParameters ]
+ "=" DatatypeMemberDecl { "|" DatatypeMemberDecl } [ ";" ]
+DatatypeMemberDecl = { Attribute } DatatypeMemberName [ FormalsOptionalIds ]
+````
+
+The values of inductive datatypes can be seen as finite trees where
+the leaves are values of basic types, numeric types, reference types,
+co-inductive datatypes, or function types. Indeed, values of
+inductive datatypes can be compared using Dafny's well-founded
+[<]{.monospace} ordering.
+
+An inductive datatype is declared as follows:
+```
+datatype D<T> = \(_Ctors_\)
+```
+where `\(_Ctors_\)` is a nonempty `|`-separated list of
+_(datatype) constructors_ for the datatype. Each constructor has the
+form:
+```
+C(\(_params_\))
+```
+where `\(_params_\)` is a comma-delimited list of types, optionally
+preceded by a name for the parameter and a colon, and optionally
+preceded by the keyword `ghost`. If a constructor has no parameters,
+the parentheses after the constructor name can be omitted. If no
+constructor takes a parameter, the type is usually called an
+_enumeration_; for example:
+```
+datatype Friends = Agnes | Agatha | Jermaine | Jack
+```
+
+For every constructor `C`, Dafny defines a _discriminator_ `C?`, which
+is a member that returns `true` if and only if the datatype value has
+been constructed using `C`. For every named parameter `p` of a
+constructor `C`, Dafny defines a _destructor_ `p`, which is a member
+that returns the `p` parameter from the `C` call used to construct the
+datatype value; its use requires that `C?` holds. For example, for
+the standard `List` type
+```
+datatype List<T> = Nil | Cons(head: T, tail: List<T>)
+```
+the following holds:
+```
+Cons(5, Nil).Cons? && Cons(5, Nil).head == 5
+```
+Note that the expression
+```
+Cons(5, Nil).tail.head
+```
+is not well-formed, since `Cons(5, Nil).tail` does not satisfy
+`Cons?`.
+
+The names of the destructors must be unique across all the
+constructors of the datatype. A constructor can have the same name as
+the enclosing datatype; this is especially useful for
+single-constructor datatypes, which are often called
+_record types_. For example, a record type for black-and-white pixels
+might be represented as follows:
+```
+datatype Pixel = Pixel(x: int, y: int, on: bool)
+```
+
+To call a constructor, it is usually necessary only to mention the
+name of the constructor, but if this is ambiguous, it is always
+possible to qualify the name of constructor by the name of the
+datatype. For example, `Cons(5, Nil)` above can be written
+```
+List.Cons(5, List.Nil)
+```
+
+As an alternative to calling a datatype constructor explicitly, a
+datatype value can be constructed as a change in one parameter from a
+given datatype value using the _datatype update_ expression. For any
+`d` whose type is a datatype that includes a constructor `C` that has
+a parameter (destructor) named `f` of type `T`, and any expression `t`
+of type `T`,
+```
+d[f := t]
+```
+constructs a value like `d` but whose `f` parameter is `t`. The
+operation requires that `d` satisfies `C?`. For example, the
+following equality holds:
+```
+Cons(4, Nil)[tail := Cons(3, Nil)] == Cons(4, Cons(3, Nil))
+```
+
+The datatype update expression also accepts multiple field
+names, provided these are distinct. For example, a node of some
+inductive datatype for trees may be updated as follows:
+
+```
+node[left := L, right := R]
+```
+
+## Tuple types
+````
+TupleType_ = "(" [ Type { "," Type } ] ")"
+````
+
+Dafny builds in record types that correspond to tuples and gives these
+a convenient special syntax, namely parentheses. For example, what
+might have been declared as:
+```
+datatype Pair<T,U> = Pair(0: T, 1: U)
+```
+Dafny provides as the type `(T, U)` and the constructor `(t, u)`, as
+if the datatype's name were "" and its type arguments are given in
+round parentheses, and as if the constructor name were "". Note that
+the destructor names are `0` and `1`, which are legal identifier names
+for members. For example, showing the use of a tuple destructor, here
+is a property that holds of 2-tuples (that is, _pairs_):
+```
+(5, true).1 == true
+```
+
+Dafny declares _n_-tuples where _n_ is 0 or 2 or up. There are no
+1-tuples, since parentheses around a single type or a single value have
+no semantic meaning. The 0-tuple type, `()`, is often known as the
+_unit type_ and its single value, also written `()`, is known as _unit_.
+
+## Co-inductive datatypes
+
+````
+CoinductiveDatatypeDecl_ = "codatatype" { Attribute } DatatypeName [ GenericParameters ]
+ "=" DatatypeMemberDecl { "|" DatatypeMemberDecl } [ ";" ]
+````
+
+Whereas Dafny insists that there is a way to construct every inductive
+datatype value from the ground up, Dafny also supports
+_co-inductive datatypes_, whose constructors are evaluated lazily and
+hence allows infinite structures. A co-inductive datatype is declared
+using the keyword `codatatype`; other than that, it is declared and
+used like an inductive datatype.
+
+For example,
+```
+codatatype IList<T> = Nil | Cons(head: T, tail: IList<T>)
+codatatype Stream<T> = More(head: T, tail: Stream<T>)
+codatatype Tree<T> = Node(left: Tree<T>, value: T, right: Tree<T>)
+```
+declare possibly infinite lists (that is, lists that can be either
+finite or infinite), infinite streams (that is, lists that are always
+infinite), and infinite binary trees (that is, trees where every
+branch goes on forever), respectively.
+
+The paper [Co-induction Simply], by Leino and
+Moskal[@LEINO:Dafny:Coinduction], explains Dafny's implementation and
+verification of co-inductive types. We capture the key features from that
+paper in this section but the reader is referred to that paper for more
+complete details and to supply bibliographic references that we have
+omitted.
+
+Mathematical induction is a cornerstone of programming and program
+verification. It arises in data definitions (e.g., some algebraic data
+structures can be described using induction), it underlies program
+semantics (e.g., it explains how to reason about finite iteration and
+recursion), and it gets used in proofs (e.g., supporting lemmas about
+data structures use inductive proofs). Whereas induction deals with
+finite things (data, behavior, etc.), its dual, co-induction, deals with
+possibly infinite things. Co-induction, too, is important in programming
+and program verification, where it arises in data definitions (e.g., lazy
+data structures), semantics (e.g., concurrency), and proofs (e.g.,
+showing refinement in a co-inductive big-step semantics). It is thus
+desirable to have good support for both induction and co-induction in a
+system for constructing and reasoning about programs.
+
+Co-datatypes and co-recursive functions make it possible to use lazily
+evaluated data structures (like in Haskell or Agda). Co-predicates,
+defined by greatest fix-points, let programs state properties of such
+data structures (as can also be done in, for example, Coq). For the
+purpose of writing co-inductive proofs in the language, we introduce
+co-lemmas. Ostensibly, a co-lemma invokes the co-induction hypothesis
+much like an inductive proof invokes the induction hypothesis. Underneath
+the hood, our co-inductive proofs are actually approached via induction:
+co-lemmas provide a syntactic veneer around this approach.
+
+The following example gives a taste of how the co-inductive features in
+Dafny come together to give straightforward definitions of infinite
+matters.
+```
+// infinite streams
+codatatype IStream<T> = ICons(head: T, tail: IStream)
+
+// pointwise product of streams
+function Mult(a: IStream<int>, b: IStream<int>): IStream<int>
+{ ICons(a.head * b.head, Mult(a.tail, b.tail)) }
+
+// lexicographic order on streams
+copredicate Below(a: IStream<int>, b: IStream<int>)
+{ a.head <= b.head && ((a.head == b.head) ==> Below(a.tail, b.tail)) }
+
+// a stream is Below its Square
+colemma Theorem_BelowSquare(a: IStream<int>)
+ensures Below(a, Mult(a, a))
+{ assert a.head <= Mult(a, a).head;
+ if a.head == Mult(a, a).head {
+ Theorem_BelowSquare(a.tail);
+ }
+}
+
+// an incorrect property and a bogus proof attempt
+colemma NotATheorem_SquareBelow(a: IStream<int>)
+ ensures Below(Mult(a, a), a); // ERROR
+{
+ NotATheorem_SquareBelow(a);
+}
+```
+
+It defines a type `IStream` of infinite streams, with constructor `ICons` and
+destructors `head` and `tail`. Function `Mult` performs pointwise
+multiplication on infinite streams of integers, defined using a
+co-recursive call (which is evaluated lazily). Co-predicate `Below` is
+defined as a greatest fix-point, which intuitively means that the
+co-predicate will take on the value true if the recursion goes on forever
+without determining a different value. The co-lemma states the theorem
+`Below(a, Mult(a, a))`. Its body gives the proof, where the recursive
+invocation of the co-lemma corresponds to an invocation of the
+co-induction hypothesis.
+
+The proof of the theorem stated by the first co-lemma lends
+itself to the following intuitive reading: To prove that `a` is below
+`Mult(a, a)`, check that their heads are ordered and, if the heads are
+equal, also prove that the tails are ordered. The second co-lemma states
+a property that does not always hold; the verifier is not fooled by the
+bogus proof attempt and instead reports the property as unproved.
+
+We argue that these definitions in Dafny are simple enough to level the
+playing field between induction (which is familiar) and co-induction
+(which, despite being the dual of induction, is often perceived as eerily
+mysterious). Moreover, the automation provided by our SMT-based verifier
+reduces the tedium in writing co-inductive proofs. For example, it
+verifies `Theorem_BelowSquare` from the program text given above no
+additional lemmas or tactics are needed. In fact, as a consequence of the
+automatic-induction heuristic in Dafny, the verifier will
+automatically verify Theorem_BelowSquare even given an empty body.
+
+Just like there are restrictions on when an _inductive hypothesis_ can be
+invoked, there are restriction on how a _co-inductive_ hypothesis can be
+_used_. These are, of course, taken into consideration by our verifier.
+For example, as illustrated by the second co-lemma above, invoking the
+co-inductive hypothesis in an attempt to obtain the entire proof goal is
+futile. (We explain how this works in section [#sec-colemmas]) Our initial experience
+with co-induction in Dafny shows it to provide an intuitive, low-overhead
+user experience that compares favorably to even the best of today’s
+interactive proof assistants for co-induction. In addition, the
+co-inductive features and verification support in Dafny have other
+potential benefits. The features are a stepping stone for verifying
+functional lazy programs with Dafny. Co-inductive features have also
+shown to be useful in defining language semantics, as needed to verify
+the correctness of a compiler, so this opens the possibility that
+such verifications can benefit from SMT automation.
+
+### Well-Founded Function/Method Definitions
+The Dafny programming language supports functions and methods. A _function_
+in Dafny is a mathematical function (i.e., it is well-defined,
+deterministic, and pure), whereas a _method_ is a body of statements that
+can mutate the state of the program. A function is defined by its given
+body, which is an expression. To ensure that function definitions
+are mathematically consistent, Dafny insists that recursive calls be well-founded,
+enforced as follows: Dafny computes the call graph of functions. The strongly connected
+components within it are _clusters_ of mutually recursive definitions arranged in
+a DAG. This stratifies the functions so that a call from one cluster in the DAG to a
+lower cluster is allowed arbitrarily. For an intra-cluster call, Dafny prescribes a proof
+obligation that gets taken through the program verifier’s reasoning engine. Semantically,
+each function activation is labeled by a _rank_â€â€a lexicographic tuple determined
+by evaluating the function’s **decreases** clause upon invocation of the function. The
+proof obligation for an intra-cluster call is thus that the rank of the callee is strictly less
+(in a language-defined well-founded relation) than the rank of the caller. Because
+these well-founded checks correspond to proving termination of executable code, we
+will often refer to them as “termination checksâ€Â. The same process applies to methods.
+
+Lemmas in Dafny are commonly introduced by declaring a method, stating
+the property of the lemma in the _postcondition_ (keyword **ensures**) of
+the method, perhaps restricting the domain of the lemma by also giving a
+_precondition_ (keyword **requires**), and using the lemma by invoking
+the method. Lemmas are stated, used, and proved as methods, but
+since they have no use at run time, such lemma methods are typically
+declared as _ghost_, meaning that they are not compiled into code. The
+keyword **lemma** introduces such a method. Control flow statements
+correspond to proof techniquesâ€â€case splits are introduced with if
+statements, recursion and loops are used for induction, and method calls
+for structuring the proof. Additionally, the statement:
+```
+forall x | P(x) { Lemma(x); }
+```
+is used to invoke `Lemma(x)` on all `x` for which `P(x)` holds. If
+`Lemma(x)` ensures `Q(x)`, then the forall statement establishes
+```
+forall x :: P(x) ==> Q(x).
+```
+
+### Defining Co-inductive Datatypes
+Each value of an inductive datatype is finite, in the sense that it can
+be constructed by a finite number of calls to datatype constructors. In
+contrast, values of a co-inductive datatype, or co-datatype for short,
+can be infinite. For example, a co-datatype can be used to represent
+infinite trees.
+
+Syntactically, the declaration of a co-datatype in Dafny looks like that
+of a datatype, giving prominence to the constructors (following Coq). The
+following example defines a co-datatype Stream of possibly
+infinite lists.
+
+```
+codatatype Stream<T> = SNil | SCons(head: T, tail: Stream)
+function Up(n: int): Stream<int> { SCons(n, Up(n+1)) }
+function FivesUp(n: int): Stream<int>
+ decreases 4 - (n - 1) % 5
+{
+ if (n % 5 == 0) then
+ SCons(n, FivesUp(n+1))
+ else
+ FivesUp(n+1)
+}
+```
+
+`Stream` is a co-inductive datatype whose values are possibly infinite
+lists. Function `Up` returns a stream consisting of all integers upwards
+of `n` and `FivesUp` returns a stream consisting of all multiples of 5
+upwards of `n` . The self-call in `Up` and the first self-call in `FivesUp`
+sit in productive positions and are therefore classified as co-recursive
+calls, exempt from termination checks. The second self-call in `FivesUp` is
+not in a productive position and is therefore subject to termination
+checking; in particular, each recursive call must decrease the rank
+defined by the **decreases** clause.
+
+Analogous to the common finite list datatype, Stream declares two
+constructors, `SNil` and `SCons`. Values can be destructed using match
+expressions and statements. In addition, like for inductive datatypes,
+each constructor `C` automatically gives rise to a discriminator `C?` and
+each parameter of a constructor can be named in order to introduce a
+corresponding destructor. For example, if `xs` is the stream
+`SCons(x, ys)`, then `xs.SCons?` and `xs.head == x` hold. In contrast
+to datatype declarations, there is no grounding check for
+co-datatypesâ€â€since a codatatype admits infinite values, the type is
+nevertheless inhabited.
+
+### Creating Values of Co-datatypes
+To define values of co-datatypes, one could imagine a “co-functionâ€Â
+language feature: the body of a “co-function†could include possibly
+never-ending self-calls that are interpreted by a greatest fix-point
+semantics (akin to a **CoFixpoint** in Coq). Dafny uses a different design:
+it offers only functions (not “co-functionsâ€Â), but it classifies each
+intra-cluster call as either _recursive_ or _co-recursive_. Recursive calls
+are subject to termination checks. Co-recursive calls may be
+never-ending, which is what is needed to define infinite values of a
+co-datatype. For example, function `Up(n )` in the preceding example is defined as the
+stream of numbers from `n` upward: it returns a stream that starts with `n`
+and continues as the co-recursive call `Up(n + 1)`.
+
+To ensure that co-recursive calls give rise to mathematically consistent definitions,
+they must occur only in productive positions. This says that it must be possible to determine
+each successive piece of a co-datatype value after a finite amount of work. This
+condition is satisfied if every co-recursive call is syntactically guarded by a constructor
+of a co-datatype, which is the criterion Dafny uses to classify intra-cluster calls as being
+either co-recursive or recursive. Calls that are classified as co-recursive are exempt from
+termination checks.
+
+A consequence of the productivity checks and termination checks is that, even in the
+absence of talking about least or greatest fix-points of self-calling functions, all functions
+in Dafny are deterministic. Since there is no issue of several possible fix-points,
+the language allows one function to be involved in both recursive and co-recursive calls,
+as we illustrate by the function `FivesUp`.
+
+### Copredicates
+Determining properties of co-datatype values may require an infinite
+number of observations. To that avail, Dafny provides _co-predicates_
+which are function declarations that use the `copredicate` keyword.
+Self-calls to a co-predicate need not terminate. Instead, the value
+defined is the greatest fix-point of the given recurrence equations.
+Continuing the preceding example, the following code defines a
+co-predicate that holds for exactly those streams whose payload consists
+solely of positive integers. The co-predicate definition implicitly also
+gives rise to a corresponding prefix predicate, `Pos#`. The syntax for
+calling a prefix predicate sets apart the argument that specifies the
+prefix length, as shown in the last line; for this figure, we took the
+liberty of making up a coordinating syntax for the signature of the
+automatically generated prefix predicate (which is not part of
+Dafny syntax).
+
+```
+copredicate Pos(s: Stream<int>)
+{
+ match s
+ case SNil => true
+ case SCons(x, rest) => x > 0 && Pos(rest)
+}
+// Automatically generated by the Dafny compiler:
+predicate Pos#[_k: nat](s: Stream<int>)
+ decreases _k
+{ if _k = 0 then true else
+ match s
+ case SNil => true
+ case SCons(x, rest) => x > 0 && Pos#[_k-1](rest)
+}
+```
+
+Some restrictions apply. To guarantee that the greatest fix-point always
+exists, the (implicit functor defining the) co-predicate must be
+monotonic. This is enforced by a syntactic restriction on the form of the
+body of co-predicates: after conversion to negation normal form (i.e.,
+pushing negations down to the atoms), intra-cluster calls of
+co-predicates must appear only in _positive_ positionsâ€â€that is, they must
+appear as atoms and must not be negated. Additionally, to guarantee
+soundness later on, we require that they appear in _co-friendly_
+positionsâ€â€that is, in negation normal form, when they appear under
+existential quantification, the quantification needs to be limited to a
+finite range[^fn-copredicate-restriction]. Since the evaluation of a co-predicate might not
+terminate, co-predicates are always ghost. There is also a restriction on
+the call graph that a cluster containing a co-predicate must contain only
+co-predicates, no other kinds of functions.
+
+[^fn-copredicate-restriction]: Higher-order function support in Dafny is
+ rather modest and typical reasoning patterns do not involve them, so this
+ restriction is not as limiting as it would have been in, e.g., Coq.
+
+A **copredicate** declaration of `P` defines not just a co-predicate, but
+also a corresponding _prefix predicate_ `P#`. A prefix predicate is a
+finite unrolling of a co-predicate. The prefix predicate is constructed
+from the co-predicate by
+
+* adding a parameter _k of type nat to denote the prefix length,
+
+* adding the clause "**decreases** `_k;`" to the prefix predicate (the
+ co-predicate itself is not allowed to have a decreases clause),
+
+* replacing in the body of the co-predicate every intra-cluster
+ call `Q(args)` to a copredicate by a call `Q#[_k - 1](args)`
+ to the corresponding prefix predicate, and then
+
+* prepending the body with `if _k = 0 then true else`.
+
+For example, for co-predicate `Pos`, the definition of the prefix
+predicate `Pos#` is as suggested above. Syntactically, the prefix-length
+argument passed to a prefix predicate to indicate how many times to
+unroll the definition is written in square brackets, as in `Pos#[k](s)`.
+In the Dafny grammar this is called a ``HashCall``. The definition of
+`Pos#` is available only at clusters strictly higher than that of `Pos`;
+that is, `Pos` and `Pos#` must not be in the same cluster. In other
+words, the definition of `Pos` cannot depend on `Pos#`.
+
+#### Co-Equality
+Equality between two values of a co-datatype is a built-in co-predicate.
+It has the usual equality syntax `s == t`, and the corresponding prefix
+equality is written `s ==#[k] t`. And similarly for `s != t`
+and `s !=#[k] t`.
+
+### Co-inductive Proofs
+From what we have said so far, a program can make use of properties of
+co-datatypes. For example, a method that declares `Pos(s)` as a
+precondition can rely on the stream `s` containing only positive integers.
+In this section, we consider how such properties are established in the
+first place.
+
+#### Properties About Prefix Predicates
+Among other possible strategies for establishing co-inductive properties
+we take the time-honored approach of reducing co-induction to
+induction. More precisely, Dafny passes to the SMT solver an
+assumption `D(P)` for every co-predicate `P`, where:
+
+```
+D(P) = ? x • P(x) <==> ? k • P#[k](x)
+```
+
+In other words, a co-predicate is true iff its corresponding prefix
+predicate is true for all finite unrollings.
+
+In Sec. 4 of the paper [Co-induction Simply] a soundness theorem of such
+assumptions is given, provided the co-predicates meet the co-friendly
+restrictions. An example proof of `Pos(Up(n))` for every `n > 0` is
+here shown:
+
+```
+lemma UpPosLemma(n: int)
+ requires n > 0
+ ensures Pos(Up(n))
+{
+ forall k | 0 <= k { UpPosLemmaK(k, n); }
+}
+
+lemma UpPosLemmaK(k: nat, n: int)
+ requires n > 0
+ ensures Pos#[k](Up(n))
+ decreases k
+{
+ if k != 0 {
+ // this establishes Pos#[k-1](Up(n).tail)
+ UpPosLemmaK(k-1, n+1);
+ }
+}
+```
+
+The lemma `UpPosLemma` proves `Pos(Up(n))` for every `n > 0`. We first
+show `Pos#[k](Up(n ))`, for `n > 0` and an arbitrary `k`, and then use
+the forall statement to show `? k • Pos#[k](Up(n))`. Finally, the axiom
+`D(Pos)` is used (automatically) to establish the co-predicate.
+
+
+#### Colemmas
+As we just showed, with help of the `D` axiom we can now prove a
+co-predicate by inductively proving that the corresponding prefix
+predicate holds for all prefix lengths `k` . In this section, we introduce
+_co-lemma_ declarations, which bring about two benefits. The first benefit
+is that co-lemmas are syntactic sugar and reduce the tedium of having to
+write explicit quantifications over `k` . The second benefit is that, in
+simple cases, the bodies of co-lemmas can be understood as co-inductive
+proofs directly. As an example consider the following co-lemma.
+
+```
+colemma UpPosLemma(n: int)
+ requires n > 0
+ ensures Pos(Up(n))
+{
+ UpPosLemma(n+1);
+}
+```
+This co-lemma can be understood as follows: `UpPosLemma` invokes itself
+co-recursively to obtain the proof for `Pos(Up(n).tail)` (since `Up(n).tail`
+equals `Up(n+1)`). The proof glue needed to then conclude `Pos(Up(n))` is
+provided automatically, thanks to the power of the SMT-based verifier.
+
+#### Prefix Lemmas
+To understand why the above `UpPosLemma` co-lemma code is a sound proof,
+let us now describe the details of the desugaring of co-lemmas. In
+analogy to how a **copredicate** declaration defines both a co-predicate and
+a prefix predicate, a **colemma** declaration defines both a co-lemma and
+_prefix lemma_. In the call graph, the cluster containing a co-lemma must
+contain only co-lemmas and prefix lemmas, no other methods or function.
+By decree, a co-lemma and its corresponding prefix lemma are always
+placed in the same cluster. Both co-lemmas and prefix lemmas are always
+ghosts.
+
+The prefix lemma is constructed from the co-lemma by
+
+* adding a parameter `_k` of type `nat` to denote the prefix length,
+
+* replacing in the co-lemma’s postcondition the positive co-friendly
+ occurrences of co-predicates by corresponding prefix predicates,
+ passing in `_k` as the prefix-length argument,
+
+* prepending `_k` to the (typically implicit) **decreases** clause of the co-lemma,
+
+* replacing in the body of the co-lemma every intra-cluster call
+ `M(args)` to a colemma by a call `M#[_k - 1](args)` to the
+ corresponding prefix lemma, and then
+
+* making the body’s execution conditional on `_k != 0`.
+
+Note that this rewriting removes all co-recursive calls of co-lemmas,
+replacing them with recursive calls to prefix lemmas. These recursive
+call are, as usual, checked to be terminating. We allow the pre-declared
+identifier `_k` to appear in the original body of the
+co-lemma.[^fn-co-predicate-co-lemma-diffs]
+
+[^fn-co-predicate-co-lemma-diffs]: Note, two places where co-predicates
+ and co-lemmas are not analogous are: co-predicates must not make
+ recursive calls to their prefix predicates, and co-predicates cannot
+ mention _k.
+
+We can now think of the body of the co-lemma as being replaced by a
+**forall** call, for every _k_ , to the prefix lemma. By construction,
+this new body will establish the colemma’s declared postcondition (on
+account of the `D` axiom, and remembering that only the positive
+co-friendly occurrences of co-predicates in the co-lemma’s postcondition
+are rewritten), so there is no reason for the program verifier to check
+it.
+
+The actual desugaring of our co-lemma `UpPosLemma` is in fact the
+previous code for the `UpPosLemma` lemma except that `UpPosLemmaK` is
+named `UpPosLemma#` and modulo a minor syntactic difference in how the
+`k` argument is passed.
+
+In the recursive call of the prefix lemma, there is a proof obligation
+that the prefixlength argument `_k - 1` is a natural number.
+Conveniently, this follows from the fact that the body has been wrapped
+in an `if _k != 0` statement. This also means that the postcondition must
+hold trivially when `_k = 0`, or else a postcondition violation will be
+reported. This is an appropriate design for our desugaring, because
+co-lemmas are expected to be used to establish co-predicates, whose
+corresponding prefix predicates hold trivially when `_k = 0`. (To prove
+other predicates, use an ordinary lemma, not a co-lemma.)
+
+It is interesting to compare the intuitive understanding of the
+co-inductive proof in using a co-lemma with the inductive proof in using
+the lemma. Whereas the inductive proof is performing proofs for deeper
+and deeper equalities, the co-lemma can be understood as producing the
+infinite proof on demand.
+
+# Newtypes
+````
+NewtypeDecl = "newtype" { Attribute } NewtypeName "="
+ ( NumericTypeName [ ":" Type ] "|" Expression(allowLemma: false, allowLambda: true)
+ | Type
+ )
+````
+
+A new numeric type can be declared with the _newtype_
+declaration[^fn-newtype-name], for example:
+```
+newtype N = x: M | Q
+```
+where `M` is a numeric type and `Q` is a boolean expression that can
+use `x` as a free variable. If `M` is an integer-based numeric type,
+then so is `N`; if `M` is real-based, then so is `N`. If the type `M`
+can be inferred from `Q`, the "`: M`" can be omitted. If `Q` is just
+`true`, then the declaration can be given simply as:
+```
+newtype N = M
+```
+Type `M` is known as the _base type_ of `N`.
+
+[^fn-newtype-name]: Should `newtype` perhaps be renamed to `numtype`?
+
+A newtype is a numeric type that supports the same operations as its
+base type. The newtype is distinct from and incompatible with other
+numeric types; in particular, it is not assignable to its base type
+without an explicit conversion. An important difference between the
+operations on a newtype and the operations on its base type is that
+the newtype operations are defined only if the result satisfies the
+predicate `Q`, and likewise for the literals of the
+newtype.[^fn-newtype-design-question]
+
+[^fn-newtype-design-question]: Would it be useful to also
+ automatically define `predicate N?(m: M) { Q }`?
+
+For example, suppose `lo` and `hi` are integer-based numerics that
+satisfy `0 <= lo <= hi` and consider the following code fragment:
+```
+var mid := (lo + hi) / 2;
+```
+If `lo` and `hi` have type `int`, then the code fragment is legal; in
+particular, it never overflows, since `int` has no upper bound. In
+contrast, if `lo` and `hi` are variables of a newtype `int32` declared
+as follows:
+```
+newtype int32 = x | -0x80000000 <= x < 0x80000000
+```
+then the code fragment is erroneous, since the result of the addition
+may fail to satisfy the predicate in the definition of `int32`. The
+code fragment can be rewritten as
+```
+var mid := lo + (hi - lo) / 2;
+```
+in which case it is legal for both `int` and `int32`.
+
+Since a newtype is incompatible with its base type and since all
+results of the newtype's operations are members of the newtype, a
+compiler for Dafny is free to specialize the run-time representation
+of the newtype. For example, by scrutinizing the definition of
+`int32` above, a compiler may decide to store `int32` values using
+signed 32-bit integers in the target hardware.
+
+Note that the bound variable `x` in `Q` has type `M`, not `N`.
+Consequently, it may not be possible to state `Q` about the `N`
+value. For example, consider the following type of 8-bit 2's
+complement integers:
+```
+newtype int8 = x: int | -128 <= x < 128
+```
+and consider a variable `c` of type `int8`. The expression
+```
+-128 <= c < 128
+```
+is not well-defined, because the comparisons require each operand to
+have type `int8`, which means the literal `128` is checked to be of
+type `int8`, which it is not. A proper way to write this expression
+would be to use a conversion operation, described next, on `c` to
+convert it to the base type:
+```
+-128 <= int(c) < 128
+```
+
+If possible Dafny will represent values of the newtype using
+a native data type for the sake of efficiency. This action can
+be inhibited or a specific native data type selected by
+using the `(:nativeType)` attribute, as explained in
+section [#sec-nativetype].
+
+There is a restriction that the value `0` must be part of every
+newtype.[^fn-newtype-zero]
+
+[^fn-newtype-zero]: The restriction is due to a current limitation in
+ the compiler. This will change in the future and will also open
+ up the possibility for subset types and non-null reference
+ types.
+
+## Numeric conversion operations
+
+For every numeric type `N`, there is a conversion function with the
+same name. It is a partial identity function. It is defined when the
+given value, which can be of any numeric type, is a member of the type
+converted to. When the conversion is from a real-based numeric type
+to an integer-based numeric type, the operation requires that the
+real-based argument has no fractional part. (To round a real-based
+numeric value down to the nearest integer, use the `.Trunc` member,
+see Section [#sec-numeric-types].)
+
+To illustrate using the example from above, if `lo` and `hi` have type
+`int32`, then the code fragment can legally be written as follows:
+```
+var mid := (int(lo) + int(hi)) / 2;
+```
+where the type of `mid` is inferred to be `int`. Since the result
+value of the division is a member of type `int32`, one can introduce
+yet another conversion operation to make the type of `mid` be `int32`:
+```
+var mid := int32((int(lo) + int(hi)) / 2);
+```
+If the compiler does specialize the run-time representation for
+`int32`, then these statements come at the expense of two,
+respectively three, run-time conversions.
+
+# Subset types
+````
+NatType_ = "nat"
+````
+
+A _subset type_ is a restricted use of an existing type, called
+the _base type_ of the subset type. A subset type is like a
+combined use of the base type and a predicate on the base
+type.
+
+An assignment from a subset type to its base type is always
+allowed. An assignment in the other direction, from the base type to
+a subset type, is allowed provided the value assigned does indeed
+satisfy the predicate of the subset type.
+(Note, in contrast, assignments between a newtype and its base type
+are never allowed, even if the value assigned is a value of the target
+type. For such assignments, an explicit conversion must be used, see
+Section [#sec-numeric-conversion-operations].)
+
+Dafny supports one subset type, namely the built-in type `nat`,
+whose base type is `int`.[^fn-more-subset-types] Type `nat`
+designates the non-negative subrange of `int`. A simple example that
+puts subset type `nat` to good use is the standard Fibonacci
+function:
+```
+function Fib(n: nat): nat
+{
+ if n < 2 then n else Fib(n-2) + Fib(n-1)
+}
+```
+An equivalent, but clumsy, formulation of this function (modulo the
+wording of any error messages produced at call sites) would be to use
+type `int` and to write the restricting predicate in pre- and
+postconditions:
+```
+function Fib(n: int): int
+ requires 0 <= n; // the function argument must be non-negative
+ ensures 0 <= Fib(n); // the function result is non-negative
+{
+ if n < 2 then n else Fib(n-2) + Fib(n-1)
+}
+```
+
+[^fn-more-subset-types]: A future version of Dafny will support
+ user-defined subset types.
+
+Type inference will never infer the type of a variable to be a
+subset type. It will instead infer the type to be the base type
+of the subset type. For example, the type of `x` in
+```
+forall x :: P(x)
+```
+will be `int`, even if predicate `P` declares its argument to have
+type `nat`.
+
+# Statements
+````
+Stmt = ( BlockStmt | AssertStmt | AssumeStmt | PrintStmt | UpdateStmt
+ | VarDeclStatement | IfStmt | WhileStmt | MatchStmt | ForallStmt
+ | CalcStmt | ModifyStmt | LabeledStmt_ | BreakStmt_ | ReturnStmt
+ | YieldStmt | SkeletonStmt
+ )
+````
+Many of Dafny's statements are similar to those in traditional
+programming languages, but a number of them are significantly different.
+This grammar production shows the different kinds of Dafny statements.
+They are described in subsequent sections.
+
+## Labeled Statement
+````
+LabeledStmt_ = "label" LabelName ":" Stmt
+````
+A labeled statement is just the keyword `label` followed by and
+identifier which is the label followed by a colon and a
+statement. The label may be referenced in a break statement
+to transfer control to the location after that statement.
+
+## Break Statement
+````
+BreakStmt_ = "break" ( LabelName | { "break" } ) ";"
+````
+A break statement breaks out of one or more loops (if the
+statement consists solely of one or more `break` keywords),
+or else transfers control to just past the statement
+bearing the referenced label, if a label was used.
+
+## Block Statement
+````
+BlockStmt = "{" { Stmt } "}"
+````
+A block statement is just a sequence of statements enclosed by curly braces.
+
+## Return Statement
+````
+ReturnStmt = "return" [ Rhs { "," Rhs } ] ";"
+````
+A return statement can only be used in a method. It is used
+to terminate the execution of the method.
+To return a value from a method, the value is assigned to one
+of the named return values sometime before a return statement.
+In fact, the return values act very much like local variables,
+and can be assigned to more than once. Return statements are
+used when one wants to return before reaching the end of the
+body block of the method. Return statements can be just the
+return keyword (where the current value of the out parameters
+are used), or they can take a list of values to return.
+If a list is given the number of values given must be the
+same as the number of named return values.
+
+## Yield Statement
+````
+YieldStmt = "yield" [ Rhs { "," Rhs } ] ";"
+````
+
+A yield statement can only be used in an iterator.
+See section [Iterator types](#sec-iterator-types) for more details
+about iterators.
+
+The body of an iterator is a _co-routine_. It is used
+to yield control to its caller, signaling that a new
+set of values for the iterator's yield parameters (if any)
+are available. Values are assigned to the yield parameters
+at or before a yield statement.
+In fact, the yield parameters act very much like local variables,
+and can be assigned to more than once. Yield statements are
+used when one wants to return new yield parameter values
+to the caller. Yield statements can be just the
+**yield** keyword (where the current value of the yield parameters
+are used), or they can take a list of values to yield.
+If a list is given the number of values given must be the
+same as the number of named return yield parameters.
+
+## Update Statement
+````
+UpdateStmt = Lhs { "," Lhs }
+ ( ":=" Rhs { "," Rhs }
+ | ":|" [ "assume" ] Expression(allowLemma: false, allowLambda: true)
+ )
+ ";""
+````
+
+The update statement has two forms. The first more normal form
+allows for parallel assignment of right-hand-side values to the
+left-hand side. For example `x,y := y,x` to swap the values
+of `x` and `y`. Of course the common case will have only one
+rhs and one lhs.
+
+The form that uses "`:|`" assigns some values to the left-hand-side
+variables such that the boolean expression on the right hand side
+is satisfied. This can be used to make a choice as in the
+following example where we choose an element in a set.
+
+```
+function PickOne<T>(s: set<T>): T
+ requires s != {}
+{
+ var x :| x in s; x
+}
+```
+
+Dafny will report an error if it cannot prove that values
+exist which satisfy the condition.
+
+In addition, though the choice is arbitrary, given identical
+circumstances the choice will be made consistently.
+
+
+In the actual grammar two additional forms are recognized for
+purposes of error detection. The form:
+
+````
+Lhs { Attribute} ;
+````
+
+is assumed to be a mal-formed call.
+
+The form
+
+````
+Lhs ":"
+````
+
+is diagnosed as a label in which the user forgot the **label** keyword.
+
+## Variable Declaration Statement
+````
+VarDeclStatement = [ "ghost" ] "var" { Attribute }
+ (
+ LocalIdentTypeOptional { "," { Attribute } LocalIdentTypeOptional }
+ [ ":=" Rhs { "," Rhs }
+ | { Attribute } ":|" [ "assume" ] Expression(allowLemma: false, allowLambda: true)
+ ]
+ |
+ "(" CasePattern { "," CasePattern } ")"
+ ":=" Expression(allowLemma: false, allowLambda: true)
+ )
+ ";"
+````
+
+A ``VarDeclStatement`` is used to declare one or more local variables in a method or function.
+The type of each local variable must be given unless the variable is given an initial
+value in which case the type will be inferred. If initial values are given, the number of
+values must match the number of variables declared.
+
+Note that the type of each variable must be given individually. The following code
+
+```
+var x, y : int;
+```
+does not declare both `x` and `y` to be of type `int`. Rather it will give an
+error explaining that the type of `x` is underspecified.
+
+The lefthand side can also contain a tuple of patterns which will be
+matched against the right-hand-side. For example:
+
+```
+function returnsTuple() : (int, int)
+{
+ (5, 10)
+}
+
+function usesTuple() : int
+{
+ var (x, y) := returnsTuple();
+ x + y
+}
+```
+
+## Guards
+````
+Guard = ( "*" | "(" "*" ")" | Expression(allowLemma: true, allowLambda: true) )
+````
+Guards are used in `if` and `while` statements as boolean expressions. Guards
+take two forms.
+
+The first and most common form is just a boolean expression.
+
+The second form is either `*` or `(*)`. These have the same meaning. An
+unspecified boolean value is returned. The value returned
+may be different each time it is executed.
+
+## Binding Guards
+````
+BindingGuard(allowLambda) =
+ IdentTypeOptional { "," IdentTypeOptional } { Attribute }
+ ":|" Expression(allowLemma: true, allowLambda)
+````
+
+A ``BindingGuard`` is used as a condition in an ``IfStmt``.
+It binds the identifiers declared in the ``IdentTypeOptional``s.
+If there exists one or more assignments of values to the bound identifiers
+for which ``Expression`` is true, then the ``BindingGuard``
+returns true and the identifiers are bound to values that make the
+``Expression`` true.
+
+The identifiers bound by ``BindingGuard`` are ghost variables
+and cannot be assigned to non-ghost variables. They are only
+used in specification contexts.
+
+Here is an example:
+
+```
+predicate P(n: int)
+{
+ n % 2 == 0
+}
+
+method M1() returns (ghost y: int)
+ requires exists x :: P(x)
+ ensures P(y)
+{
+ if x : int :| P(x) {
+ y := x;
+ }
+}
+```
+
+## If Statement
+````
+IfStmt = "if"
+ ( IfAlternativeBlock
+ |
+ ( BindingGuard(allowLambda: true)
+ | Guard
+ | "..."
+ )
+ BlockStmt [ "else" ( IfStmt | BlockStmt ) ]
+ )
+````
+
+In the simplest form an `if` statement uses a guard that is a boolean
+expression. It then has the same form as in C# and other common
+programming languages. For example
+
+```
+ if x < 0 {
+ x := -x;
+ }
+```
+
+If the guard is an asterisk then a non-deterministic choice is made:
+
+```
+ if * {
+ print "True";
+ } else {
+ print "False";
+ }
+```
+
+````
+IfAlternativeBlock =
+ "{" { "case"
+ (
+ BindingGuard(allowLambda:false)
+ | Expression(allowLemma: true, allowLambda: false)
+ ) "=>" { Stmt } } "}" .
+````
+
+The `if` statement using the `IfAlternativeBlock` form is similar to the
+`if ... fi` construct used in the book "A Discipline of Programming" by
+Edsger W. Dijkstra. It is used for a multi-branch `if`.
+
+For example:
+```
+ if {
+ case x <= y => max := y;
+ case y <= x => max := y;
+ }
+```
+
+In this form the expressions following the `case` keyword are called
+_guards_. The statement is evaluated by evaluating the guards in an
+undetermined order until one is found that is `true` or else all have
+evaluated to `false`. If none of them evaluate to `true` then the `if`
+statement does nothing. Otherwise the statements to the right of `=>`
+for the guard that evaluated to `true` are executed.
+
+## While Statement
+````
+WhileStmt = "while"
+ ( LoopSpecWhile WhileAlternativeBlock
+ | ( Guard | "..." ) LoopSpec
+ ( BlockStmt
+ | "..."
+ | /* go body-less */
+ )
+ )
+````
+
+````
+WhileAlternativeBlock =
+ "{" { "case" Expression(allowLemma: true, allowLambda: false) "=>" { Stmt } } "}" .
+````
+
+See section [#sec-loop-specification] for a description of ``LoopSpec``.
+
+The `while` statement is Dafny's only loop statement. It has two general
+forms.
+
+The first form is similar to a while loop in a C-like language. For
+example:
+
+```
+ var i := 0;
+ while i < 5 {
+ i := i + 1;
+ }
+```
+
+In this form the condition following the `while` is one of these:
+
+* A boolean expression. If true it means execute one more
+iteration of the loop. If false then terminate the loop.
+* An asterisk (`*`), meaning non-deterministically yield either
+`true` or `false` as the value of the condition
+* An ellipsis (`...`), which makes the while statement a _skeleton_
+`while` statement. TODO: What does that mean?
+
+The _body_ of the loop is usually a block statement, but it can also
+be a _skeleton_, denoted by ellipsis, or missing altogether.
+TODO: Wouldn't a missing body cause problems? Isn't it clearer to have
+a block statement with no statements inside?
+
+The second form uses the `WhileAlternativeBlock`. It is similar to the
+`do ... od` construct used in the book "A Discipline of Programming" by
+Edsger W. Dijkstra. For example:
+
+```
+ while
+ decreases if 0 <= r then r else -r;
+ {
+ case r < 0 =>
+ r := r + 1;
+ case 0 < r =>
+ r := r - 1;
+ }
+```
+For this form the guards are evaluated in some undetermined order
+until one is found that is true, in which case the corresponding statements
+are executed. If none of the guards evaluates to true then the
+loop execution is terminated.
+
+### Loop Specifications
+For some simple loops such as those mentioned previously Dafny can figure
+out what the loop is doing without more help. However in general the user
+must provide more information in order to help Dafny prove the effect of
+the loop. This information is provided by a ``LoopSpec``. A
+``LoopSpec`` provides information about invariants, termination, and
+what the loop modifies. ``LoopSpecs`` are explained in
+section [#sec-loop-specification]. However the following sections
+present additional rationale and tutorial on loop specifications.
+
+#### Loop Invariants
+
+`While` loops present a problem for Dafny. There is no way for Dafny to
+know in advance how many times the code will go around the loop. But
+Dafny needs to consider all paths through a program, which could include
+going around the loop any number of times. To make it possible for Dafny
+to work with loops, you need to provide loop invariants, another kind of
+annotation.
+
+A loop invariant is an expression that holds upon entering a loop, and
+after every execution of the loop body. It captures something that is
+invariant, i.e. does not change, about every step of the loop. Now,
+obviously we are going to want to change variables, etc. each time around
+the loop, or we wouldn't need the loop. Like pre- and postconditions, an
+invariant is a property that is preserved for each execution of the loop,
+expressed using the same boolean expressions we have seen. For example,
+
+```
+var i := 0;
+while i < n
+ invariant 0 <= i
+{
+ i := i + 1;
+}
+```
+
+When you specify an invariant, Dafny proves two things: the invariant
+holds upon entering the loop, and it is preserved by the loop. By
+preserved, we mean that assuming that the invariant holds at the
+beginning of the loop, we must show that executing the loop body once
+makes the invariant hold again. Dafny can only know upon analyzing the
+loop body what the invariants say, in addition to the loop guard (the
+loop condition). Just as Dafny will not discover properties of a method
+on its own, it will not know any but the most basic properties of a loop
+are preserved unless it is told via an invariant.
+
+#### Loop Termination
+
+Dafny proves that code terminates, i.e. does not loop forever, by using
+`decreases` annotations. For many things, Dafny is able to guess the right
+annotations, but sometimes it needs to be made explicit. In fact, for all
+of the code we have seen so far, Dafny has been able to do this proof on
+its own, which is why we haven't seen the decreases annotation explicitly
+yet. There are two places Dafny proves termination: loops and recursion.
+Both of these situations require either an explicit annotation or a
+correct guess by Dafny.
+
+A `decreases` annotation, as its name suggests, gives Dafny an expression
+that decreases with every loop iteration or recursive call. There are two
+conditions that Dafny needs to verify when using a decreases expression:
+
+* that the expression actually gets smaller, and
+* that it is bounded.
+
+Many times, an integral value (natural or plain integer) is the quantity
+that decreases, but other things that can be used as well. In the case of
+integers, the bound is assumed to be zero. For example, the following is
+a proper use of decreases on a loop (with its own keyword, of course):
+
+```
+ while 0 < i
+ invariant 0 <= i
+ decreases i
+ {
+ i := i - 1;
+ }
+```
+
+Here Dafny has all the ingredients it needs to prove termination. The
+variable i gets smaller each loop iteration, and is bounded below by
+zero. This is fine, except the loop is backwards from most loops, which
+tend to count up instead of down. In this case, what decreases is not the
+counter itself, but rather the distance between the counter and the upper
+bound. A simple trick for dealing with this situation is given below:
+
+```
+ while i < n
+ invariant 0 <= i <= n
+ decreases n - i
+ {
+ i := i + 1;
+ }
+```
+
+This is actually Dafny's guess for this situation, as it sees `i < n` and
+assumes that `n - i` is the quantity that decreases. The upper bound of the
+loop invariant implies that `0 <= n – i`, and gives Dafny a lower bound on
+the quantity. This also works when the bound `n` is not constant, such as
+in the binary search algorithm, where two quantities approach each other,
+and neither is fixed.
+
+If the **decreases** clause of a loop specified "*", then no
+termination check will be performed. Use of this feature is sound only with
+respect to partial correctness.
+
+#### Loop Framing
+In some cases we also must specify what memory locations the loop body
+is allowed to modify. This is done using a `modifies` clause.
+See the discussion of framing in methods for a fuller discussion.
+
+## Match Statement
+````
+MatchStmt = "match" Expression(allowLemma: true, allowLambda: true)
+ ( "{" { CaseStatement } "}"
+ | { CaseStatement }
+ )
+
+CaseStatement = CaseBinding_ "=>" { Stmt }
+````
+
+The `match` statement is used to do case analysis on a value of inductive
+or co-inductive type. The form with no leading ``Ident`` is for matching
+tuples. The expression after the `match` keyword is the (co)inductive
+value being matched. The expression is evaluated and then matched against
+each of the case clauses.
+
+There must be a case clause for each constructor of the data type.
+The identifier after the `case` keyword in a case clause, if present,
+must be the name of one of the data type's constructors.
+If the constructor takes parameters then a parenthesis-enclosed
+list of identifiers (with optional type) must follow the
+constructor. There must be as many identifiers as the constructor
+has parameters. If the optional type is given it must be the same
+as the type of the corresponding parameter of the constructor.
+If no type is given then the type of the corresponding parameter
+is the type assigned to the identifier.
+
+When an inductive value that was created using constructor
+expression `C1(v1, v2)` is matched against a case clause
+`C2(x1, x2`), there is a match provided that `C1` and `C2` are the
+same constructor. In that case `x1` is bound to value `v1` and
+`x2` is bound to `v2`. The identifiers in the case pattern
+are not mutable. Here is an example of the use of a `match` statement.
+
+```
+datatype Tree = Empty | Node(left: Tree, data: int, right: Tree)
+
+// Return the sum of the data in a tree.
+method Sum(x: Tree) returns (r: int)
+{
+ match x {
+ case Empty => r := -1;
+ case Node(t1 : Tree, d, t2) => {
+ var v1 := Sum(t1);
+ var v2 := Sum(t2);
+ r := v1 + d + v2;
+ }
+ }
+}
+```
+
+Note that the `Sum` method is recursive yet has no `decreases` annotation.
+In this case it is not needed because Dafny is able to deduce that
+`t1` and `t2` are _smaller_ (structurally) than `x`. If `Tree` had been
+coinductive this would not have been possible since `x` might have been
+infinite.
+
+## Assert Statement
+````
+AssertStmt =
+ "assert" { Attribute }
+ ( Expression(allowLemma: false, allowLambda: true)
+ | "..."
+ ) ";"
+````
+
+`Assert` statements are used to express logical proposition that are
+expected to be true. Dafny will attempt to prove that the assertion
+is true and give an error if not. Once it has proved the assertion
+it can then use its truth to aid in following deductions.
+Thus if Dafny is having a difficult time verifying a method
+the user may help by inserting assertions that Dafny can prove,
+and whose true may aid in the larger verification effort.
+
+If the proposition is `...` then (TODO: what does this mean?).
+
+## Assume Statement
+````
+AssumeStmt =
+ "assume" { Attribute }
+ ( Expression(allowLemma: false, allowLambda: true)
+ | "..."
+ ) ";"
+````
+
+The `Assume` statement lets the user specify a logical proposition
+that Dafny may assume to be true without proof. If in fact the
+proposition is not true this may lead to invalid conclusions.
+
+An `Assume` statement would ordinarily be used as part of a larger
+verification effort where verification of some other part of
+the program required the proposition. By using the `Assume` statement
+the other verification can proceed. Then when that is completed the
+user would come back and replace the `assume` with `assert`.
+
+If the proposition is `...` then (TODO: what does this mean?).
+
+## Print Statement
+````
+PrintStmt =
+ "print" Expression(allowLemma: false, allowLambda: true)
+ { "," Expression(allowLemma: false, allowLambda: true) } ";"
+````
+
+The `print` statement is used to print the values of a comma-separated
+list of expressions to the console. The generated C# code uses
+the `System.Object.ToString()` method to convert the values to printable
+strings. The expressions may of course include strings that are used
+for captions. There is no implicit new line added, so to get a new
+line you should include "\n" as part of one of the expressions.
+Dafny automatically creates overrides for the ToString() method
+for Dafny data types. For example,
+
+```
+datatype Tree = Empty | Node(left: Tree, data: int, right: Tree)
+method Main()
+{
+ var x : Tree := Node(Node(Empty, 1, Empty), 2, Empty);
+ print "x=", x, "\n";
+}
+```
+
+produces this output:
+
+```
+x=Tree.Node(Tree.Node(Tree.Empty, 1, Tree.Empty), 2, Tree.Empty)
+```
+
+## Forall Statement
+````
+ForallStmt = "forall"
+ ( "(" [ QuantifierDomain ] ")"
+ | [ QuantifierDomain ]
+ )
+ { [ "free" ] ForAllEnsuresClause_ }
+ [ BlockStmt ]
+````
+
+The `forall` statement executes ensures expressions or a body in
+parallel for all quantified values in the specified range.
+The use of the `parallel` keyword is deprecated. Use
+`forall` instead. There are several variant uses of the `forall`
+statement. And there are a number of restrictions.
+
+In particular a `forall` statement can be classified as one of the following:
+
+* _Assign_ - the `forall` statement is used for simultaneous assignment.
+The target must be an array element or an object field.
+* _Call_ - The body consists of a single call to a method without side effects
+* _Proof_ - The `forall` has `ensure` expressions which are effectively
+quantified or proved by the body (if present).
+
+An _assign_ `forall` statement is to perform simultaneous assignment.
+The following is an excerpt of an example given by Leino in
+[Developing Verified Programs with Dafny][leino233].
+When the buffer holding the queue needs to be resized,
+the `forall` statement is used to simultaneously copy the old contents
+into the new buffer.
+
+[leino233]: http://research.microsoft.com/en-us/um/people/leino/papers/krml233.pdf
+
+```
+class {:autocontracts} SimpleQueue<Data>
+{
+ ghost var Contents: seq<Data>;
+ var a: array<Data>; // Buffer holding contents of queue.
+ var m: int // Index head of queue.
+ var n: int; // Index just past end of queue
+ ...
+ method Enqueue(d: Data)
+ ensures Contents == old(Contents) + [d]
+ {
+ if n == a.Length {
+ var b := a;
+ if m == 0 { b := new Data[2 * a.Length]; }
+ forall (i | 0 <= i < n - m) {
+ b[i] := a[m + i];
+ }
+ a, m, n := b, 0, n - m;
+ }
+ a[n], n, Contents := d, n + 1, Contents + [d];
+ }
+}
+```
+
+Here is an example of a _call_ `forall` statement and the
+callee. This is contained in the CloudMake-ConsistentBuilds.dfy
+test in the Dafny repository.
+
+```
+forall (cmd', deps', e' | Hash(Loc(cmd', deps', e')) == Hash(Loc(cmd, deps, e))) {
+ HashProperty(cmd', deps', e', cmd, deps, e);
+}
+
+ghost method HashProperty(cmd: Expression, deps: Expression, ext: string,
+ cmd': Expression, deps': Expression, ext': string)
+ requires Hash(Loc(cmd, deps, ext)) == Hash(Loc(cmd', deps', ext'))
+ ensures cmd == cmd' && deps == deps' && ext == ext'
+```
+
+From the same file here is an example of a _proof_ `forall` statement.
+
+```
+forall (p | p in DomSt(stCombinedC.st) && p in DomSt(stExecC.st))
+ ensures GetSt(p, stCombinedC.st) == GetSt(p, stExecC.st)
+{
+ assert DomSt(stCombinedC.st) <= DomSt(stExecC.st);
+ assert stCombinedC.st == Restrict(DomSt(stCombinedC.st), stExecC.st);
+}
+```
+
+More generally the statement
+```
+forall x | P(x) { Lemma(x); }
+```
+is used to invoke `Lemma(x)` on all `x` for which `P(x)` holds. If
+`Lemma(x)` ensures `Q(x)`, then the forall statement establishes
+```
+forall x :: P(x) ==> Q(x).
+```
+
+The `forall` statement is also used extensively in the desugared forms of
+co-predicates and co-lemmas. See section [#sec-co-inductive-datatypes].
+
+TODO: List all of the restrictions on the `forall` statement.
+
+## Modify Statement
+````
+ModifyStmt =
+ "modify" { Attribute }
+ ( FrameExpression(allowLemma: false, allowLambda: true)
+ { "," FrameExpression(allowLemma: false, allowLambda: true) }
+ | "..."
+ )
+ ( BlockStmt | ";" )
+````
+
+The `modify` statement has two forms which have two different
+purposes.
+
+When the `modify` statement ends with a semi-colon rather than
+a block statement its effect is to say that some undetermined
+modifications have been made to any or all of the memory
+locations specified by the [frame expressions](#sec-frame-expressions).
+In the following example, a value is assigned to field `x`
+followed by a `modify` statement that may modify any field
+in the object. After that we can no longer prove that the field
+`x` still has the value we assigned to it.
+
+```
+class MyClass {
+ var x: int;
+ method N()
+ modifies this
+ {
+ x := 18;
+ modify this;
+ assert x == 18; // error: cannot conclude this here
+ }
+}
+```
+
+When the `modify` statement is followed by a block statement
+we are instead specifying what can be modified in that
+block statement. Namely, only memory locations specified
+by the frame expressions of the block `modify` statement
+may be modified. Consider the following example.
+
+```
+class ModifyBody {
+ var x: int;
+ var y: int;
+ method M0()
+ modifies this
+ {
+ modify {} {
+ x := 3; // error: violates modifies clause of the modify statement
+ }
+ }
+ method M1()
+ modifies this
+ {
+ modify {} {
+ var o := new ModifyBody;
+ o.x := 3; // fine
+ }
+ }
+ method M2()
+ modifies this
+ {
+ modify this {
+ x := 3;
+ }
+ }
+}
+```
+
+The first `modify` statement in the example has an empty
+frame expression so it cannot modify any memory locations.
+So an error is reported when it tries to modify field `x`.
+
+The second `modify` statement also has an empty frame
+expression. But it allocates a new object and modifies it.
+Thus we see that the frame expressions on a block `modify`
+statement only limits what may be modified of existing
+memory. It does not limit what may be modified in
+new memory that is allocated.
+
+The third `modify` statement has a frame expression that
+allows it to modify any of the fields of the current object,
+so the modification of field `x` is allowed.
+
+## Calc Statement
+````
+CalcStmt = "calc" { Attribute } [ CalcOp ] "{" CalcBody "}"
+CalcBody = { CalcLine [ CalcOp ] Hints }
+CalcLine = Expression(allowLemma: false, allowLambda: true) ";"
+Hints = { ( BlockStmt | CalcStmt ) }
+CalcOp =
+ ( "==" [ "#" "[" Expression(allowLemma: true, allowLambda: true) "]" ]
+ | "<" | ">"
+ | "!=" | "<=" | ">="
+ | "<==>" | "==>" | "<=="
+ )
+````
+
+The `calc` statement supports _calculational proofs_ using a language feature called _program-oriented calculations_ (poC). This feature was introduced and explained in the [Verified Calculations] paper by
+Leino and Polikarpova[@LEINO:Dafny:Calc]. Please see that paper for a more complete explanation
+of the `calc` statement. We here mention only the highlights.
+
+[Verified Calculations]: http://research.microsoft.com/en-us/um/people/leino/papers/krml231.pdf
+
+Calculational proofs are proofs by stepwise formula manipulation
+as is taught in elementary algebra. The typical example is to prove
+an equality by starting with a left-hand-side, and through a series of
+transformations morph it into the desired right-hand-side.
+
+Non-syntactic rules further restrict hints to only ghost and side-effect
+free statements, as well as impose a constraint that only
+chain-compatible operators can be used together in a calculation. The
+notion of chain-compatibility is quite intuitive for the operators
+supported by poC; for example, it is clear that "<" and ">" cannot be used within
+the same calculation, as there would be no relation to conclude between
+the first and the last line. See the [paper][Verified Calculations] for
+a more formal treatment of chain-compatibility.
+
+Note that we allow a single occurrence of the intransitive operator "!=" to
+appear in a chain of equalities (that is, "!=" is chain-compatible with
+equality but not with any other operator, including itself). Calculations
+with fewer than two lines are allowed, but have no effect. If a step
+operator is omitted, it defaults to the calculation-wide operator,
+defined after the `calc` keyword. If that operator if omitted, it defaults
+to equality.
+
+Here is an example using `calc` statements to prove an elementary
+algebraic identity. As it turns out Dafny is able to prove this without
+the `calc` statements, but it helps to illustrate the syntax.
+
+```
+lemma docalc(x : int, y: int)
+ ensures (x + y) * (x + y) == x * x + 2 * x * y + y * y
+{
+ calc {
+ (x + y) * (x + y); ==
+ // distributive law: (a + b) * c == a * c + b * c
+ x * (x + y) + y * (x + y); ==
+ // distributive law: a * (b + c) == a * b + a * c
+ x * x + x * y + y * x + y * y; ==
+ calc {
+ y * x; ==
+ x * y;
+ }
+ x * x + x * y + x * y + y * y; ==
+ calc {
+ x * y + x * y; ==
+ // a = 1 * a
+ 1 * x * y + 1 * x * y; ==
+ // Distributive law
+ (1 + 1) * x * y; ==
+ 2 * x * y;
+ }
+ x * x + 2 * x * y + y * y;
+ }
+}
+```
+
+Here we started with `(x + y) * (x + y)` as the left-hand-side
+expressions and gradually transformed it using distributive,
+commutative and other laws into the desired right-hand-side.
+
+The justification for the steps are given as comments, or as
+nested `calc` statements that prove equality of some sub-parts
+of the expression.
+
+The `==` to the right of the semicolons show the relation between
+that expression and the next. Because of the transitivity of
+equality we can then conclude that the original left-hand-side is
+equal to the final expression.
+
+We can avoid having to supply the relational operator between
+every pair of expressions by giving a default operator between
+the `calc` keyword and the opening brace as shown in this abbreviated
+version of the above calc statement:
+
+```
+calc == {
+ (x + y) * (x + y);
+ x * (x + y) + y * (x + y);
+ x * x + x * y + y * x + y * y;
+ x * x + x * y + x * y + y * y;
+ x * x + 2 * x * y + y * y;
+}
+```
+
+And since equality is the default operator we could have omitted
+it after the `calc` keyword.
+The purpose of the block statements or the `calc` statements between
+the expressions is to provide hints to aid Dafny in proving that
+step. As shown in the example, comments can also be used to aid
+the human reader in cases where Dafny can prove the step automatically.
+
+## Skeleton Statement
+````
+SkeletonStmt =
+ "..."
+ ["where" Ident {"," Ident } ":="
+ Expression(allowLemma: false, allowLambda: true)
+ {"," Expression(allowLemma: false, allowLambda: true) }
+ ] ";"
+````
+
+# Expressions
+The grammar of Dafny expressions follows a hierarchy that
+reflects the precedence of Dafny operators. The following
+table shows the Dafny operators and their precedence
+in order of increasing binding power.
+
++--------------------------+------------------------------------+
+| operator | description |
++--------------------------+------------------------------------+
+| `;` | In LemmaCall;Expression |
++--------------------------+------------------------------------+
+| `<==>`, &hArr; | equivalence (if and only if) |
++--------------------------+------------------------------------+
+| `==>`, &rArr; | implication (implies) |
+| `<==`, &lArr; | reverse implication (follows from) |
++--------------------------+------------------------------------+
+| `&&`, &and; | conjunction (and) |
+| [\|\|]{.monospace}, &or; | disjunction (or) |
++--------------------------+------------------------------------+
+| `!`, &not; | negation (not) |
++--------------------------+------------------------------------+
+| `==` | equality |
+| `==#[k]` | prefix equality (co-inductive) |
+| `!=` | disequality |
+| `!=#[k]` | prefix disequality (co-inductive) |
+| [<]{.monospace} | less than |
+| `<=` | at most |
+| `>=` | at least |
+| `>` | greater than |
+| `in` | collection membership |
+| `!in` | collection non-membership |
+| `!!` | disjointness |
++--------------------------+------------------------------------+
+| `+` | addition (plus) |
+| `-` | subtraction (minus) |
++--------------------------+------------------------------------+
+| `*` | multiplication (times) |
+| `/` | division (divided by) |
+| `%` | modulus (mod) |
++--------------------------+------------------------------------+
+| `-` | arithmetic negation (unary minus) |
+| `!`, &not; | logical negation |
+| Primary Expressions | |
++--------------------------+------------------------------------+
+
+We are calling the ``UnaryExpression``s that are neither
+arithmetic nor logical negation the _primary expressions_.
+They are the most tightly bound.
+
+In the grammar entries below we explain the meaning when the
+operator for that precedence level is present. If the
+operator is not present then we just descend to the
+next precedence level.
+
+## Top-level expressions
+````
+Expression(allowLemma, allowLambda) =
+ EquivExpression(allowLemma, allowLambda)
+ [ ";" Expression(allowLemma, allowLambda) ]
+````
+
+The "allowLemma" argument says whether or not the expression
+to be parsed is allowed to have the form S;E where S is a call to a lemma.
+"allowLemma" should be passed in as "false" whenever the expression to
+be parsed sits in a context that itself is terminated by a semi-colon.
+
+The "allowLambda" says whether or not the expression to be parsed is
+allowed to be a lambda expression. More precisely, an identifier or
+parenthesized-enclosed comma-delimited list of identifiers is allowed to
+continue as a lambda expression (that is, continue with a "reads", "requires",
+or "=>") only if "allowLambda" is true. This affects function/method/iterator
+specifications, if/while statements with guarded alternatives, and expressions
+in the specification of a lambda expression itself.
+
+Sometimes an expression will fail unless some relevant fact is known.
+In the following example the `F_Fails` function fails to verify
+because the `Fact(n)` divisor may be zero. But preceding
+the expression by a lemma that ensures that the denominator
+is not zero allows function `F_Succeeds` to succeed.
+```
+function Fact(n: nat): nat
+{
+ if n == 0 then 1 else n * Fact(n-1)
+}
+
+lemma L(n: nat)
+ ensures 1 <= Fact(n)
+{
+}
+
+function F_Fails(n: nat): int
+{
+ 50 / Fact(n) // error: possible division by zero
+}
+
+function F_Succeeds(n: nat): int
+{
+ L(n);
+ 50 / Fact(n)
+}
+```
+
+## Equivalence Expressions
+````
+EquivExpression(allowLemma, allowLambda) =
+ ImpliesExpliesExpression(allowLemma, allowLambda)
+ { "<==>" ImpliesExpliesExpression(allowLemma, allowLambda) }
+````
+An ``EquivExpression`` that contains one or more "<==>"s is
+a boolean expression and all the contained ``ImpliesExpliesExpression``
+must also be boolean expressions. In that case each "<==>"
+operator tests for logical equality which is the same as
+ordinary equality.
+
+See section [#sec-equivalence-operator] for an explanation of the
+`<==>` operator as compared with the `==` operator.
+
+## Implies or Explies Expressions
+````
+ImpliesExpliesExpression(allowLemma, allowLambda) =
+ LogicalExpression(allowLemma, allowLambda)
+ [ ( "==>" ImpliesExpression(allowLemma, allowLambda)
+ | "<==" LogicalExpression(allowLemma, allowLambda)
+ { "<==" LogicalExpression(allowLemma, allowLambda) }
+ )
+ ]
+
+ImpliesExpression(allowLemma, allowLambda) =
+ LogicalExpression(allowLemma, allowLambda)
+ [ "==>" ImpliesExpression(allowLemma, allowLambda) ]
+````
+
+See section [#sec-implication-and-reverse-implication] for an explanation
+of the `==>` and `<==` operators.
+
+## Logical Expressions
+
+````
+LogicalExpression(allowLemma, allowLambda) =
+ RelationalExpression(allowLemma, allowLambda)
+ [ ( "&&" RelationalExpression(allowLemma, allowLambda)
+ { "&&" RelationalExpression(allowLemma, allowLambda) }
+ | "||" RelationalExpression(allowLemma, allowLambda)
+ { "||" RelationalExpression(allowLemma, allowLambda) }
+ )
+ ]
+````
+
+See section [#sec-conjunction-and-disjunction] for an explanation
+of the `&&` (or &and;) and `||` (or &or;) operators.
+
+## Relational Expressions
+````
+RelationalExpression(allowLemma, allowLambda) =
+ Term(allowLemma, allowLambda)
+ [ RelOp Term(allowLemma, allowLambda)
+ { RelOp Term(allowLemma, allowLambda) } ]
+
+RelOp =
+ ( "==" [ "#" "[" Expression(allowLemma: true, allowLambda: true) "]" ]
+ | "<" | ">" | "<=" | ">="
+ | "!=" [ "#" "[" Expression(allowLemma: true, allowLambda: true) "]" ]
+ | "in"
+ | "!in"
+ | "!!"
+ )
+
+````
+
+The relation expressions that have a ``RelOp`` compare two or more terms.
+As explained in section [#sec-basic-types], `==`, `!=`, ``<``, `>`, `<=`, and `>=`
+and their corresponding Unicode equivalents are _chaining_.
+
+The `in` and `!in` operators apply to collection types as explained in
+section [#sec-collection-types] and represent membership or non-membership
+respectively.
+
+The `!!` represents disjointness for sets and multisets as explained in
+sections [#sec-sets] and [#sec-multisets].
+
+Note that `x ==#[k] y` is the prefix equality operator that compares
+co-inductive values for equality to a nesting level of k, as
+explained in section [#sec-co-equality].
+
+## Terms
+````
+Term(allowLemma, allowLambda) =
+ Factor(allowLemma, allowLambda)
+ { AddOp Factor(allowLemma, allowLambda) }
+AddOp = ( "+" | "-" )
+````
+
+`Terms` combine `Factors` by adding or subtracting.
+Addition has these meanings for different types:
+
+* Arithmetic addition for numeric types (section [#sec-numeric-types]).
+* Union for sets and multisets (sections [#sec-sets] and [#sec-multisets])
+* Concatenation for sequences (section [#sec-sequences])
+
+Subtraction is arithmetic subtraction for numeric types, and set or multiset
+difference for sets and multisets.
+
+## Factors
+````
+Factor(allowLemma, allowLambda) =
+ UnaryExpression(allowLemma, allowLambda)
+ { MulOp UnaryExpression(allowLemma, allowLambda) }
+MulOp = ( "*" | "/" | "%" )
+````
+
+A ``Factor`` combines ``UnaryExpression``s using multiplication,
+division, or modulus. For numeric types these are explained in
+section [#sec-numeric-types].
+
+Only `*` has a non-numeric application. It represents set or multiset
+intersection as explained in sections [#sec-sets] and [#sec-multisets].
+
+## Unary Expressions
+
+````
+UnaryExpression(allowLemma, allowLambda) =
+ ( "-" UnaryExpression(allowLemma, allowLambda)
+ | "!" UnaryExpression(allowLemma, allowLambda)
+ | PrimaryExpression_(allowLemma, allowLambda)
+ )
+
+````
+
+A ``UnaryExpression`` applies either numeric (section [#sec-numeric-types])
+or logical (section [#sec-booleans]) negation to its operand.
+
+## Primary Expressions
+<!-- These are introduced for explanatory purposes as are not in the grammar. -->
+````
+PrimaryExpression_(allowLemma, allowLambda) =
+ ( MapDisplayExpr { Suffix }
+ | LambdaExpression(allowLemma)
+ | EndlessExpression(allowLemma, allowLambda)
+ | NameSegment { Suffix }
+ | SeqDisplayExpr { Suffix }
+ | SetDisplayExpr { Suffix }
+ | MultiSetExpr { Suffix }
+ | ConstAtomExpression { Suffix }
+ )
+
+````
+
+After descending through all the binary and unary operators we arrive at
+the primary expressions which are explained in subsequent sections. As
+can be seen, a number of these can be followed by 0 or more ``Suffix``es
+to select a component of the value.
+
+If the `allowLambda` is false then ``LambdaExpression``s are not
+recognized in this context.
+
+## Lambda expressions
+````
+LambdaExpression(allowLemma) =
+ ( WildIdent
+ | "(" [ IdentTypeOptional { "," IdentTypeOptional } ] ")"
+ )
+ LambdaSpec_
+ LambdaArrow Expression(allowLemma, allowLambda: true)
+
+LambdaArrow = ( "=>" | "->" )
+````
+
+See section [#sec-lambda-specification] for a description of ``LambdaSpec``.
+
+In addition to named functions, Dafny supports expressions that define
+functions. These are called _lambda (expression)s_ (some languages
+know them as _anonymous functions_). A lambda expression has the
+form:
+```
+(\(_params_\)) \(_specification_\) => \(_body_\)
+```
+where `\(_params_\)` is a comma-delimited list of parameter
+declarations, each of which has the form `x` or `x: T`. The type `T`
+of a parameter can be omitted when it can be inferred. If the
+identifier `x` is not needed, it can be replaced by "`_`". If
+`\(_params_\)` consists of a single parameter `x` (or `_`) without an
+explicit type, then the parentheses can be dropped; for example, the
+function that returns the successor of a given integer can be written
+as the following lambda expression:
+```
+x => x + 1
+```
+
+The `\(_specification_\)` is a list of clauses `requires E` or
+`reads W`, where `E` is a boolean expression and `W` is a frame
+expression.
+
+`\(_body_\)` is an expression that defines the function's return
+value. The body must be well-formed for all possible values of the
+parameters that satisfy the precondition (just like the bodies of
+named functions and methods). In some cases, this means it is
+necessary to write explicit `requires` and `reads` clauses. For
+example, the lambda expression
+```
+x requires x != 0 => 100 / x
+```
+would not be well-formed if the `requires` clause were omitted,
+because of the possibility of division-by-zero.
+
+In settings where functions cannot be partial and there are no
+restrictions on reading the heap, the _eta expansion_ of a function
+`F: T -> U` (that is, the wrapping of `F` inside a lambda expression
+in such a way that the lambda expression is equivalent to `F`) would
+be written `x => F(x)`. In Dafny, eta expansion must also account for
+the precondition and reads set of the function, so the eta expansion
+of `F` looks like:
+```
+x requires F.requires(x) reads F.reads(x) => F(x)
+```
+
+## Left-Hand-Side Expressions
+````
+Lhs =
+ ( NameSegment { Suffix }
+ | ConstAtomExpression Suffix { Suffix }
+ )
+````
+
+A left-hand-side expression is only used on the left hand
+side of an ``UpdateStmt``.
+
+TODO: Try to give examples showing how these kinds of
+left-hand-sides are possible.
+
+## Right-Hand-Side Expressions
+````
+Rhs =
+ ( ArrayAllocation_
+ | ObjectAllocation_
+ | Expression(allowLemma: false, allowLambda: true)
+ | HavocRhs_
+ )
+ { Attribute }
+````
+
+An ``Rhs`` is either array allocation, an object allocation,
+an expression, or a havoc right-hand-side, optionally followed
+by one or more ``Attribute``s.
+
+Right-hand-side expressions appear in the following constructs:
+``ReturnStmt``, ``YieldStmt``, ``UpdateStmt``, or ``VarDeclStatement``.
+These are the only contexts in which arrays or objects may be
+allocated, or in which havoc may be produced.
+
+## Array Allocation
+````
+ArrayAllocation_ = "new" Type "[" Expressions "]"
+````
+
+This allocates a new single or multi-dimensional array as explained in
+section [#sec-array-types].
+
+## Object Allocation
+````
+ObjectAllocation_ = "new" Type [ "(" [ Expressions ] ")" ]
+````
+
+This allocated a new object of a class type as explained
+in section [#sec-class-types].
+
+## Havoc Right-Hand-Side
+````
+HavocRhs_ = "*"
+````
+A havoc right-hand-side produces an arbitrary value of its associated
+type. To get a more constrained arbitrary value the "assign-such-that"
+operator (`:|`) can be used. See section [#sec-update-statement].
+
+## Constant Or Atomic Expressions
+````
+ConstAtomExpression =
+ ( LiteralExpression_
+ | FreshExpression_
+ | OldExpression_
+ | CardinalityExpression_
+ | NumericConversionExpression_
+ | ParensExpression
+ )
+````
+A ``ConstAtomExpression`` represent either a constant of some type, or an
+atomic expression. A ``ConstAtomExpression`` is never an l-value. Also, a
+``ConstAtomExpression`` is never followed by an open parenthesis (but could
+very well have a suffix that starts with a period or a square bracket).
+(The "Also..." part may change if expressions in Dafny could yield
+functions.)
+
+## Literal Expressions
+````
+LiteralExpression_ =
+ ( "false" | "true" | "null" | Nat | Dec |
+ charToken | stringToken | "this")
+````
+A literal expression is a boolean literal, a null object reference,
+an unsigned integer or real literal, a character or string literal,
+or "this" which denote the current object in the context of
+an instance method or function.
+
+## Fresh Expressions
+````
+FreshExpression_ = "fresh" "(" Expression(allowLemma: true, allowLambda: true) ")"
+````
+
+`fresh(e)` returns a boolean value that is true if
+the objects referenced in expression `e` were all
+freshly allocated in the current method invocation.
+The argument of `fresh` must be either an object reference
+or a collection of object references.
+
+## Old Expressions
+````
+OldExpression_ = "old" "(" Expression(allowLemma: true, allowLambda: true) ")"
+````
+
+An _old expression_ is used in postconditions. `old(e)` evaluates to
+the value expression `e` had on entry to the current method.
+
+## Cardinality Expressions
+````
+CardinalityExpression_ = "|" Expression(allowLemma: true, allowLambda: true) "|"
+````
+
+For a collection expression `c`, `|c|` is the cardinality of `c`. For a
+set or sequence the cardinality is the number of elements. For
+a multiset the cardinality is the sum of the multiplicities of the
+elements. For a map the cardinality is the cardinality of the
+domain of the map. Cardinality is not defined for infinite maps.
+For more see section [#sec-collection-types].
+
+## Numeric Conversion Expressions
+````
+NumericConversionExpression_ =
+ ( "int" | "real" ) "(" Expression(allowLemma: true, allowLambda: true) ")"
+````
+Numeric conversion expressions give the name of the target type
+followed by the expression being converted in parentheses.
+This production is for `int` and `real` as the target types
+but this also applies more generally to other numeric types,
+e.g. `newtypes`. See section [#sec-numeric-conversion-operations].
+
+## Parenthesized Expression
+````
+ParensExpression =
+ "(" [ Expressions ] ")"
+````
+A ``ParensExpression`` is a list of zero or more expressions
+enclosed in parentheses.
+
+If there is exactly one expression enclosed then the value is just
+the value of that expression.
+
+If there are zero or more than one the result is a `tuple` value.
+See section [#sec-tuple-types].
+
+## Sequence Display Expression
+````
+SeqDisplayExpr = "[" [ Expressions ] "]"
+````
+A sequence display expression provide a way to constructing
+a sequence with given values. For example
+
+```
+[1, 2, 3]
+```
+is a sequence with three elements in it.
+See section [#sec-sequences] for more information on
+sequences.
+
+## Set Display Expression
+````
+SetDisplayExpr = [ "iset" ] "{" [ Expressions ] "}"
+````
+
+A set display expression provide a way to constructing
+a set with given elements. If the keyword `iset` is present
+then a potentially infinite set is constructed.
+
+For example
+
+```
+{1, 2, 3}
+```
+is a set with three elements in it.
+See section [#sec-sets] for more information on
+sets.
+
+## Multiset Display or Cast Expression
+````
+MultiSetExpr =
+ "multiset"
+ ( "{" [ Expressions ] "}"
+ | "(" Expression(allowLemma: true, allowLambda: true) ")"
+ )
+````
+
+A multiset display expression provide a way to constructing
+a multiset with given elements and multiplicity. For example
+
+```
+multiset{1, 1, 2, 3}
+```
+is a multiset with three elements in it. The number 1 has a multiplicity of 2,
+the others a multiplicity of 1.
+
+On the other hand, a multiset cast expression converts a set or a sequence
+into a multiset as shown here:
+
+```
+var s : set<int> := {1, 2, 3};
+var ms : multiset<int> := multiset(s);
+ms := ms + multiset{1};
+var sq : seq<int> := [1, 1, 2, 3];
+var ms2 : multiset<int> := multiset(sq);
+assert ms == ms2;
+```
+
+See section [#sec-multisets] for more information on
+multisets.
+
+## Map Display Expression
+````
+MapDisplayExpr = ("map" | "imap" ) "[" [ MapLiteralExpressions ] "]"
+MapLiteralExpressions =
+ Expression(allowLemma: true, allowLambda: true)
+ ":=" Expression(allowLemma: true, allowLambda: true)
+ { "," Expression(allowLemma: true, allowLambda: true)
+ ":=" Expression(allowLemma: true, allowLambda: true)
+ }
+````
+
+A map display expression builds a finite or potentially infinite
+map from explicit ``MapLiteralExpressions``. For example:
+
+```
+var m := map[1 := "a", 2 := "b"];
+ghost var im := imap[1 := "a", 2 := "b"];
+```
+
+Note that `imap`s may only appear in ghost contexts. See
+section [#sec-finite-and-infinite-maps] for more details on maps and imaps.
+
+## Endless Expression
+````
+EndlessExpression(allowLemma, allowLambda) =
+ ( IfExpression_(allowLemma, allowLambda)
+ | MatchExpression(allowLemma, allowLambda)
+ | QuantifierExpression(allowLemma, allowLambda)
+ | SetComprehensionExpr(allowLemma, allowLambda)
+ | StmtInExpr Expression(allowLemma, allowLambda)
+ | LetExpr(allowLemma, allowLambda)
+ | MapComprehensionExpr(allowLemma, allowLambda)
+ )
+````
+<!-- Experimental - do not document.
+ | NamedExpr(allowLemma, allowLambda)
+-->
+
+``EndlessExpression`` gets it name from the fact that all its alternate
+productions have no terminating symbol to end them, but rather they
+all end with an ``Expression`` at the end. The various
+``EndlessExpression`` alternatives are described below.
+
+## If Expression
+````
+IfExpression_(allowLemma, allowLambda) =
+ "if" Expression(allowLemma: true, allowLambda: true)
+ "then" Expression(allowLemma: true, allowLambda: true)
+ "else" Expression(allowLemma, allowLambda)
+````
+
+The ``IfExpression`` is a conditional expression. It first evaluates
+the expression following the `if`. If it evaluates to `true` then
+it evaluates the expression following the `then` and that is the
+result of the expression. If it evaluates to `false` then the
+expression following the `else` is evaluated and that is the result
+of the expression. It is important that only the selected expression
+is evaluated as the following example shows.
+
+```
+var k := 10 / x; // error, may divide by 0.
+var m := if x != 0 then 10 / x else 1; // ok, guarded
+```
+
+## Case Bindings and Patterns
+````
+CaseBinding_ =
+ "case"
+ ( Ident [ "(" CasePattern { "," CasePattern } ")" ]
+ | "(" CasePattern { "," CasePattern } ")"
+ )
+
+CasePattern =
+ ( Ident "(" [ CasePattern { "," CasePattern } ] ")"
+ | "(" [ CasePattern { "," Casepattern } ] ")"
+ | IdentTypeOptional
+ )
+````
+
+Case bindings and patterns are used for (possibly nested)
+pattern matching on inductive or coinductive values.
+The ``CaseBinding_`` construct is used in
+``CaseStatement`` and ``CaseExpression``s.
+Besides its use in ``CaseBinding_``, ``CasePattern``s are used
+in ``LetExpr``s and ``VarDeclStatement``s.
+
+When matching an inductive or coinductive value in
+a ``MatchStmt`` or ``MatchExpression``, there must be
+a ``CaseBinding_`` for each constructor. A tuple is
+considered to have a single constructor.
+The ``Ident`` of the ``CaseBinding_`` must match the name
+of a constructor (or in the case of a tuple the ``Ident`` is
+absent and the second alternative is chosen).
+The ``CasePattern``s inside the parenthesis are then
+matched against the argument that were given to the
+constructor when the value was constructed.
+The number of ``CasePattern``s must match the number
+of parameters to the constructor (or the arity of the
+tuple).
+
+The ``CasePattern``s may be nested. The set of non-constructor-name
+identifiers contained in a ``CaseBinding_`` must be distinct.
+They are bound to the corresponding values in the value being
+matched.
+
+## Match Expression
+
+````
+MatchExpression(allowLemma, allowLambda) =
+ "match" Expression(allowLemma, allowLambda)
+ ( "{" { CaseExpression(allowLemma: true, allowLambda: true) } "}"
+ | { CaseExpression(allowLemma, allowLambda) }
+ )
+
+CaseExpression(allowLemma, allowLambda) =
+ CaseBinding_ "=>" Expression(allowLemma, allowLambda)
+````
+
+A ``MatchExpression`` is used to conditionally evaluate and select an
+expression depending on the value of an algebraic type, i.e. an inductive
+type, or a co-inductive type.
+
+The ``Expression`` following the `match` keyword is called the
+_selector_. There must be a ``CaseExpression`` for each constructor of
+the type of the selector. The ``Ident`` following the `case` keyword in a
+``CaseExpression`` is the name of a constructor of the selector's type.
+It may be absent if the expression being matched is a tuple since these
+have no constructor name.
+
+If the constructor has parameters then in the ``CaseExpression`` the
+constructor name must be followed by a parenthesized list of ``CasePattern``s.
+If the constructor has no parameters then the
+``CaseExpression`` must not have a following ``CasePattern`` list.
+All of the identifiers in the ``CasePattern``s must be distinct.
+If types for the identifiers are not given then types are inferred
+from the types of the constructor's parameters. If types are
+given then they must agree with the types of the
+corresponding parameters.
+
+A ``MatchExpression`` is evaluated by first evaluating the selector.
+Then the ``CaseClause`` is selected for the constructor that was
+used to construct the evaluated selector. If the constructor had
+parameters then the actual values used to construct the selector
+value are bound to the identifiers in the identifier list.
+The expression to the right of the `=>` in the ``CaseClause`` is then
+evaluated in the environment enriched by this binding. The result
+of that evaluation is the result of the ``MatchExpression``.
+
+Note that the braces enclosing the ``CaseClause``s may be omitted.
+
+## Quantifier Expression
+````
+QuantifierExpression(allowLemma, allowLambda) =
+ ( "forall" | "exists" ) QuantifierDomain "::"
+ Expression(allowLemma, allowLambda)
+
+QuantifierDomain =
+ IdentTypeOptional { "," IdentTypeOptional } { Attribute }
+ [ "|" Expression(allowLemma: true, allowLambda: true) ]
+````
+
+A ``QuantifierExpression`` is a boolean expression that specifies that a
+given expression (the one following the "::") is true for all (for
+**forall**) or some (for **exists**) combination of values of the
+quantified variables, namely those in the ``QuantifierDomain``.
+
+Here are some examples:
+```
+assert forall x : nat | x <= 5 :: x * x <= 25;
+(forall n :: 2 <= n ==> (exists d :: n < d && d < 2*n))
+```
+
+or using the Unicode symbols:
+
+```
+assert \(&forall;\) x : nat | x <= 5 \(&bull;\) x * x <= 25;
+(\(&forall;\) n \(&bull;\) 2 <= n ==> (\(&exist;\) d \(&bull;\) n < d && d < 2*n))
+```
+
+The quantifier identifiers are _bound_ within the scope of the
+expressions in the ``QuantifierExpression``.
+
+It types are not given for the quantified identifiers then Dafny
+attempts to infer their types from the context of the expressions.
+It this is not possible the program is in error.
+
+
+## Set Comprehension Expressions
+````
+SetComprehensionExpr(allowLemma, allowLambda) =
+ [ "set" | "iset" ]
+ IdentTypeOptional { "," IdentTypeOptional } { Attribute }
+ "|" Expression(allowLemma, allowLambda)
+ [ "::" Expression(allowLemma, allowLambda) ]
+````
+
+A set comprehension expression is an expressions that yields a set
+(possibly infinite if `iset` is used) that
+satisfies specified conditions. There are two basic forms.
+
+If there is only one quantified variable the optional ``"::" Expression``
+need not be supplied, in which case it is as if it had been supplied
+and the expression consists solely of the quantified variable.
+That is,
+
+```
+set x : T | P(x)
+```
+
+is equivalent to
+
+```
+set x : T | P(x) :: x
+```
+
+For the full form
+
+```
+var S := set x1:T1, x2:T2 ... | P(x1, x2, ...) :: Q(x1, x2, ...)
+```
+
+the elements of `S` will be all values resulting from evaluation of `Q(x1, x2, ...)`
+for all combinations of quantified variables `x1, x2, ...` such that
+predicate `P(x1, x2, ...)` holds. For example,
+
+```
+var S := set x:nat, y:nat | x < 2 && y < 2 :: (x, y)
+```
+would yield `S == {(0, 0), (0, 1), (1, 0), (1,1) }`
+
+The types on the quantified variables are optional and if not given Dafny
+will attempt to infer them from the contexts in which they are used in the
+`P` or `Q` expressions.
+
+If a finite set was specified ("set" keyword used), Dafny must be able to prove that the
+result is finite otherwise the set comprehension expression will not be
+accepted.
+
+Set comprehensions involving reference types such as
+
+```
+set o: object | true
+```
+
+are allowed in ghost contexts. In particular, in ghost contexts, the
+check that the result is finite should allow any set comprehension
+where the bound variable is of a reference type. In non-ghost contexts,
+it is not allowed, because--even though the resulting set would be
+finite--it is not pleasant or practical to compute at run time.
+
+## Statements in an Expression
+````
+StmtInExpr = ( AssertStmt | AssumeStmt | CalcStmt )
+````
+
+A ``StmtInExpr`` is a kind of statement that is allowed to
+precede an expression in order to ensure that the expression
+can be evaluated without error. For example:
+
+```
+assume x != 0; 10/x
+```
+
+`Assert`, `assume` and `calc` statements can be used in this way.
+
+## Let Expression
+
+````
+LetExpr(allowLemma, allowLambda) =
+ [ "ghost" ] "var" CasePattern { "," CasePattern }
+ ( ":=" | { Attribute } ":|" )
+ Expression(allowLemma: false, allowLambda: true)
+ { "," Expression(allowLemma: false, allowLambda: true) } ";"
+ Expression(allowLemma, allowLambda)
+````
+
+A `let` expression allows binding of intermediate values to identifiers
+for use in an expression. The start of the `let` expression is
+signaled by the `var` keyword. They look much like a local variable
+declaration except the scope of the variable only extends to the
+enclosed expression.
+
+For example:
+```
+var sum := x + y; sum * sum
+```
+
+In the simple case the ``CasePattern`` is just an identifier with optional
+type (which if missing is inferred from the rhs).
+
+The more complex case allows destructuring of constructor expressions.
+For example:
+
+```
+datatype Stuff = SCons(x: int, y: int) | Other
+function GhostF(z: Stuff): int
+ requires z.SCons?
+{
+ var SCons(u, v) := z; var sum := u + v; sum * sum
+}
+```
+
+## Map Comprehension Expression
+````
+MapComprehensionExpr(allowLemma, allowLambda) =
+ ( "map" | "imap" ) IdentTypeOptional { Attribute }
+ [ "|" Expression(allowLemma: true, allowLambda: true) ]
+ "::" Expression(allowLemma, allowLambda)
+````
+
+A ``MapComprehensionExpr`` defines a finite or infinite map value
+by defining a domain (using the ``IdentTypeOptional`` and the optional
+condition following the "|") and for each value in the domain,
+giving the mapped value using the expression following the "::".
+
+For example:
+```
+function square(x : int) : int { x * x }
+method test()
+{
+ var m := map x : int | 0 <= x <= 10 :: x * x;
+ ghost var im := imap x : int :: x * x;
+ ghost var im2 := imap x : int :: square(x);
+}
+```
+
+Dafny maps must be finite, so the domain must be constrained to be finite.
+But imaps may be infinite as the example shows. The last example shows
+creation of an infinite map that gives the same results as a function.
+
+<!-- Experimental - do not document.
+
+## Named Expression
+````
+NamedExpr(allowLemma, allowLambda) =
+ "label" LabelName ":" Expression(allowLemma, allowLambda)
+````
+
+A ``NamedExpr`` is an expression that has been tagged with a name.
+For example:
+```
+label squareit: x * x
+```
+
+This is an experimental feature.
+TODO: When is this useful. Is there any way to refer to the label?
+Should we remove the description?
+-->
+
+## Name Segment
+````
+NameSegment = Ident [ GenericInstantiation | HashCall ]
+````
+
+A ``NameSegment`` names a Dafny entity by giving its declared
+name optionally followed by information to
+make the name more complete. For the simple case it is
+just an identifier.
+
+If the identifier is for a generic entity it is followed by
+a ``GenericInstantiation`` which provides actual types for
+the type parameters.
+
+To reference a prefix predicate (see section [#sec-copredicates]) or
+prefix lemma (see section [#sec-prefix-lemmas]), the identifier
+must be the name of the copredicate or colemma and it must be
+followed by a ``HashCall``.
+
+## Hash Call
+````
+HashCall = "#" [ GenericInstantiation ]
+ "[" Expression(allowLemma: true, allowLambda: true) "]"
+ "(" [ Expressions ] ")"
+````
+A ``HashCall`` is used to call the prefix for a copredicate or colemma.
+In the non-generic case it just insert `"#[k]"` before the call argument
+list where k is the number of recursion levels.
+
+In the case where the `colemma` is generic, the generic type
+argument is given before. Here is an example:
+
+```
+codatatype Stream<T> = Nil | Cons(head: int, stuff: T, tail: Stream)
+
+function append(M: Stream, N: Stream): Stream
+{
+ match M
+ case Nil => N
+ case Cons(t, s, M') => Cons(t, s, append(M', N))
+}
+
+function zeros<T>(s : T): Stream<T>
+{
+ Cons(0, s, zeros(s))
+}
+
+function ones<T>(s: T): Stream<T>
+{
+ Cons(1, s, ones(s))
+}
+
+copredicate atmost(a: Stream, b: Stream)
+{
+ match a
+ case Nil => true
+ case Cons(h,s,t) => b.Cons? && h <= b.head && atmost(t, b.tail)
+}
+
+colemma {:induction false} Theorem0<T>(s: T)
+ ensures atmost(zeros(s), ones(s))
+{
+ // the following shows two equivalent ways to getting essentially the
+ // co-inductive hypothesis
+ if (*) {
+ Theorem0#<T>[_k-1](s);
+ } else {
+ Theorem0(s);
+ }
+}
+
+```
+
+where the ``HashCall`` is `"Theorem0#<T>[_k-1](s);"`.
+See sections [#sec-copredicates] and [#sec-prefix-lemmas].
+
+## Suffix
+````
+Suffix =
+ ( AugmentedDotSuffix_
+ | DatatypeUpdateSuffix_
+ | SubsequenceSuffix_
+ | SlicesByLengthSuffix_
+ | SequenceUpdateSuffix_
+ | SelectionSuffix_
+ | ArgumentListSuffix_
+ )
+````
+
+The ``Suffix`` non-terminal describes ways of deriving a new value from
+the entity to which the suffix is appended. There are six kinds
+of suffixes which are described below.
+
+### Augmented Dot Suffix
+````
+AugmentedDotSuffix_ = ". " DotSuffix [ GenericInstantiation | HashCall ]
+````
+
+An augmented dot suffix consists of a simple ``DotSuffix`` optionally
+followed by either
+
+* a ``GenericInstantiation`` (for the case where the item
+selected by the ``DotSuffix`` is generic), or
+* a ``HashCall`` for the case where we want to call a prefix copredicate
+ or colemma. The result is the result of calling the prefix copredicate
+ or colemma.
+
+### Datatype Update Suffix
+
+````
+DatatypeUpdateSuffix_ =
+ "." "(" MemberBindingUpdate { "," MemberBindingUpdate } ")"
+
+MemberBindingUpdate =
+ ( ident | digits ) ":=" Expression(allowLemma: true, allowLambda: true)
+````
+
+A datatype update suffix is used to produce a new datatype value
+that is the same as an old datatype value except that the
+value corresponding to a given destructor has the specified value.
+In a ``MemberBindingUpdate``, the ``ident`` or ``digits`` is the
+name of a destructor (i.e. formal parameter name) for one of the
+constructors of the datatype. The expression to the right of the
+":=" is the new value for that formal.
+
+All of the destructors in a ``DatatypeUpdateSuffix_`` must be
+for the same constructor, and if they do not cover all of the
+destructors for that constructor then the datatype value being
+updated must have a value derived from that same constructor.
+
+Here is an example:
+
+```
+module NewSyntax {
+datatype MyDataType = MyConstructor(myint:int, mybool:bool)
+ | MyOtherConstructor(otherbool:bool)
+ | MyNumericConstructor(42:int)
+
+method test(datum:MyDataType, x:int)
+ returns (abc:MyDataType, def:MyDataType, ghi:MyDataType, jkl:MyDataType)
+ requires datum.MyConstructor?;
+ ensures abc == datum.(myint := x + 2);
+ ensures def == datum.(otherbool := !datum.mybool);
+ ensures ghi == datum.(myint := 2).(mybool := false);
+ // Resolution error: no non_destructor in MyDataType
+ //ensures jkl == datum.(non_destructor := 5);
+ ensures jkl == datum.(42 := 7);
+{
+ abc := MyConstructor(x + 2, datum.mybool);
+ abc := datum.(myint := x + 2);
+ def := MyOtherConstructor(!datum.mybool);
+ ghi := MyConstructor(2, false);
+ jkl := datum.(42 := 7);
+
+ assert abc.(myint := abc.myint - 2) == datum.(myint := x);
+}
+}
+```
+
+
+
+### Subsequence Suffix
+````
+SubsequenceSuffix_ =
+ "[" [ Expression(allowLemma: true, allowLambda: true) ]
+ ".." [ Expression(allowLemma: true, allowLambda: true) ]
+ "]"
+````
+A subsequence suffix applied to a sequence produces a new sequence whose
+elements are taken from a contiguous part of the original sequence. For
+example, expression `s[lo..hi]` for sequence `s`, and integer-based
+numerics `lo` and `hi` satisfying `0 <= lo <= hi <= |s|`. See
+section [#sec-other-sequence-expressions] for details.
+
+### Slices By Length Suffix
+````
+SlicesByLengthSuffix_ =
+ "[" Expression(allowLemma: true, allowLambda: true)
+ ":" Expression(allowLemma: true, allowLambda: true)
+ { ":" Expression(allowLemma: true, allowLambda: true) }
+ [ ":" ]
+ "]"
+````
+
+Applying a ``SlicesByLengthSuffix_`` to a sequence produces a
+sequence of subsequences of the original sequence.
+See section [#sec-other-sequence-expressions] for details.
+
+### Sequence Update Suffix
+````
+SequenceUpdateSuffix_ =
+ "[" Expression(allowLemma: true, allowLambda: true)
+ ":=" Expression(allowLemma: true, allowLambda: true)
+ "]"
+````
+
+For a sequence `s` and expressions `i` and `v`, the expression
+`s[i := v]` is the same as the sequence `s` except that at
+index `i` it has value `v`.
+
+### Selection Suffix
+````
+SelectionSuffix_ =
+ "[" Expression(allowLemma: true, allowLambda: true)
+ { "," Expression(allowLemma: true, allowLambda: true) }
+ "]"
+````
+
+If a ``SelectionSuffix_`` has only one expression in it, it is a
+zero-based index that may be used to select a single element of a
+sequence or from a single-dimensional array.
+
+If a ``SelectionSuffix_`` has more than one expression in it, then
+it is a list of indices to index into a multi-dimensional array.
+The rank of the array must be the same as the number of indices.
+
+### Argument List Suffix
+````
+ArgumentListSuffix_ = "(" [ Expressions ] ")"
+````
+
+An argument list suffix is a parenthesized list of expressions that
+are the arguments to pass to a method or function that is being
+called. Applying such a suffix caused the method or function
+to be called and the result is the result of the call.
+
+## Expression Lists
+````
+Expressions =
+ Expression(allowLemma: true, allowLambda: true)
+ { "," Expression(allowLemma: true, allowLambda: true) }
+````
+
+The ``Expressions`` non-terminal represents a list of
+one or more expressions separated by a comma.
+
+# Module Refinement
+TODO: Write this section.
+
+# Attributes
+````
+Attribute = "{" ":" AttributeName [ Expressions ] "}"
+````
+Dafny allows many of its entities to be annotated with _Attributes_.
+The grammar shows where the attribute annotations may appear.
+
+Here is an example of an attribute from the Dafny test suite:
+
+```
+{:MyAttribute "hello", "hi" + "there", 57}
+```
+
+In general an attribute may have any name the user chooses. It may be
+followed by a comma separated list of expressions. These expressions will
+be resolved and type-checked in the context where the attribute appears.
+
+## Dafny Attribute Implementation Details
+In the Dafny implementation the `Attributes` type holds the name of
+the attribute, a list of ``Expression`` arguments and a link to the
+previous Attributes object for that Dafny entity. So for each
+Dafny entity that has attributes we have a list of them.
+
+Dafny stores attributes on the following kinds of entities:
+Declaration (base class), ModuleDefinition, Statement,
+AssignmentRhs, LocalVariable, LetExpr, ComprehensionExpr,
+MaybeFreeExpression, Specification.
+
+TODO: Dafny internals information should go into a separate
+document on Dafny internals.
+
+## Dafny Attributes
+All entities that Dafny translates to Boogie have their attributes
+passed on to Boogie except for the `{:axiom}` attribute (which
+conflicts with Boogie usage) and the `{:trigger}` attribute which is
+instead converted into a Boogie quantifier _trigger_. See Section 11 of
+[@Leino:Boogie2-RefMan].
+
+Dafny has special processing for some attributes. For some attributes the
+setting is only looked for on the entity of interest. For others we start
+at the entity and if the attribute is not there, look up in the hierarchy
+(enclosing class and enclosing modules). The latter case is checked by
+the ContainsBoolAtAnyLevel method in the Dafny source. The attribute
+declaration closest to the entity overrides those further away.
+
+For attributes with a single boolean expression argument, the attribute
+with no argument is interpreted as if it were true.
+
+The attributes that are processed specially by Dafny are described in the
+following sections.
+
+### assumption
+This attribute can only be placed on a local ghost bool
+variable of a method. Its declaration cannot have a rhs, but it is
+allowed to participate as the lhs of exactly one assignment of the
+form: `b := b && expr;`. Such a variable declaration translates in the
+Boogie output to a declaration followed by an `assume b` command. TODO:
+What is the motivation for this?
+
+### autoReq boolExpr
+For a function declaration, if this attribute is set true at the nearest
+level, then its `requires` clause is strengthed sufficiently so that
+it may call the functions that it calls.
+
+For following example
+```
+function f(x:int) : bool
+ requires x > 3
+{
+ x > 7
+}
+
+// Should succeed thanks to auto_reqs
+function {:autoReq} g(y:int, b:bool) : bool
+{
+ if b then f(y + 2) else f(2*y)
+}
+```
+the `{:autoReq}` attribute causes Dafny to
+deduce a `requires` clause for g as if it had been
+declared
+```
+function g(y:int, b:bool) : bool
+ requires if b then y + 2 > 3 else 2 * y > 3
+{
+ if b then f(y + 2) else f(2*y)
+}
+```
+
+### autocontracts
+Dynamic frames [@Kassios:FM2006;@SmansEtAl:VeriCool;@SmansEtAl:ImplicitDynamicFrames;
+@LEINO:Dafny:DynamicFrames]
+are frame expressions that can vary dynamically during
+program execution. AutoContracts is an experimental feature that will
+fill much of the dynamic-frames boilerplate into a class.
+
+From the user's perspective, what needs to be done is simply:
+
+* mark the class with {:autocontracts}
+* declare a function (or predicate) called Valid()
+
+
+AutoContracts will then:
+
+* Declare:
+```
+ ghost var Repr: set(object);
+```
+
+* For function/predicate Valid(), insert:
+```
+ reads this, Repr
+```
+* Into body of Valid(), insert (at the beginning of the body):
+```
+ this in Repr && null !in Repr
+```
+* and also insert, for every array-valued field A declared in the class:
+```
+ (A != null ==> A in Repr) &&
+```
+* and for every field F of a class type T where T has a field called Repr, also insert:
+```
+ (F != null ==> F in Repr && F.Repr SUBSET Repr && this !in Repr)
+```
+* Except, if A or F is declared with {:autocontracts false}, then the implication will not
+be added.
+
+* For every constructor, add:
+```
+ modifies this
+ ensures Valid() && fresh(Repr - {this})
+```
+* At the end of the body of the constructor, add:
+```
+ Repr := {this};
+ if (A != null) { Repr := Repr + {A}; }
+ if (F != null) { Repr := Repr + {F} + F.Repr; }
+```
+* For every method, add:
+
+```
+ requires Valid()
+ modifies Repr
+ ensures Valid() && fresh(Repr - old(Repr))
+```
+* At the end of the body of the method, add:
+```
+ if (A != null) { Repr := Repr + {A}; }
+ if (F != null) { Repr := Repr + {F} + F.Repr; }
+```
+
+### axiom
+The `{:axiom}` attribute may be placed on a function or method.
+It means that the post-condition may be assumed to be true
+without proof. In that case also the body of the function or
+method may be omitted.
+
+The `{:axiom}` attribute is also used for generated `reveal_*`
+lemmas as shown in Section [#sec-opaque].
+
+### compile
+The `{:compile}` attribute takes a boolean argument. It may be applied to
+any top-level declaration. If that argument is false then that declaration
+will not be compiled into .Net code.
+
+### decl
+The `{:decl}` attribute may be placed on a method declaration. It
+inhibits the error message that has would be given when the method has a
+`ensures` clauses but no body.
+
+TODO: There are no examples of this in the Dafny tests. What is the motivation
+for this?
+
+### fuel
+The fuel attributes is used to specify how much "fuel" a function should have,
+i.e., how many times Z3 is permitted to unfold it's definition. The
+new {:fuel} annotation can be added to the function itself, it which
+case it will apply to all uses of that function, or it can overridden
+within the scope of a module, function, method, iterator, calc, forall,
+while, assert, or assume. The general format is:
+
+```
+{:fuel functionName,lowFuel,highFuel}
+```
+
+When applied as an annotation to the function itself, omit
+functionName. If highFuel is omitted, it defaults to lowFuel + 1.
+
+The default fuel setting for recursive functions is 1,2. Setting the
+fuel higher, say, to 3,4, will give more unfoldings, which may make
+some proofs go through with less programmer assistance (e.g., with
+fewer assert statements), but it may also increase verification time,
+so use it with care. Setting the fuel to 0,0 is similar to making the
+definition opaque, except when used with all literal arguments.
+
+### heapQuantifier
+The `{:heapQuantifier}` attribute may be used on a ``QuantifierExpression``.
+When it appears in a quantifier expression it is as if a new heap-valued
+quantifier variable was added to the quantification. Consider this code
+that is one of the invariants of a while loop.
+
+```
+invariant forall u {:heapQuantifier} :: f(u) == u + r
+```
+
+The quantifier is translated into the following Boogie:
+
+```
+(forall q$heap#8: Heap, u#5: int ::
+ {:heapQuantifier}
+ $IsGoodHeap(q$heap#8) && ($Heap == q$heap#8 || $HeapSucc($Heap, q$heap#8))
+ ==> $Unbox(Apply1(TInt, TInt, f#0, q$heap#8, $Box(u#5))): int == u#5 + r#0);
+```
+
+What this is saying is that the quantified expression, `f(u) == u + r`,
+which may depend on the heap, is also valid for any good heap that is either the
+same as the current heap, or that is derived from it by heap update operations.
+
+TODO: I think this means that the quantified expression is actually independent of the
+heap. Is that true?
+
+### imported
+If a ``MethodDecl`` or ``FunctionDecl`` has an `{:imported}` attribute,
+then it is allowed to have a empty body even though it has an **ensures**
+clause. Ordinarily a body would be required in order to provide the
+proof of the **ensures** clause (but the `(:axiom)` attribute also
+provides this facility, so the need for `(:imported)` is not clear.)
+A method or function declaration may be given the `(:imported)` attribute. This suppresses
+the error message that would be given if a method or function with an `ensures` clause
+does not have a body.
+
+TODO: When would this be used? An example would be helpful.
+
+TODO: When is this useful or valid?
+
+### induction
+The `{:induction}` attribute controls the application of
+proof by induction to two contexts. Given a list of
+variables on which induction might be applied, the
+`{:induction}` attribute selects a sub-list of those
+variables (in the same order) to which to apply induction.
+
+TODO: Would there be any advantage to taking the order
+from the attribute, rather than preserving the original
+order? That would seem to give the user more control.
+
+The two contexts are:
+
+* A method, in which case the bound variables are all the
+ in-parameters of the method.
+* A quantifier expression, in which case the bound variables
+ are the bound variables of the quantifier expression.
+
+The form of the `{:induction}` attribute is one of the following:
+
+* `{:induction}` -- apply induction to all bound variables
+* `{:induction false}` -- suppress induction, that is, don't apply it to any bound variable
+* `{:induction L}` where `L` is a list consisting entirely of bound variables
+-- apply induction to the specified bound variables
+* `{:induction X}` where `X` is anything else -- treat the same as
+{:induction}, that is, apply induction to all bound variables. For this
+usage conventionally `X` is `true`.
+
+Here is an example of using it on a quantifier expression:
+```
+ghost method Fill_J(s: seq<int>)
+ requires forall i :: 1 <= i < |s| ==> s[i-1] <= s[i]
+ ensures forall i,j {:induction j} :: 0 <= i < j < |s| ==> s[i] <= s[j]
+{
+}
+```
+
+### layerQuantifier
+When Dafny is translating a quantified expression, if it has
+a `{:layerQuantifier}` attribute an additional quantifier
+variable is added to the quantifier bound variables.
+This variable as the predefined _LayerType_.
+A `{:layerQuantifier}` attribute may be placed on a quantifier expression.
+Translation of Dafny into Boogie defines a _LayerType_ which has defined zero and
+successor constructors.
+
+The Dafny source has the comment that "if a function is recursive,
+then make the reveal lemma quantifier a layerQuantifier."
+And in that case it adds the attribute to the quantifier.
+
+There is no explicit user of the `{:layerQuantifier}` attribute
+in the Dafny tests. So I believe this attribute is only used
+internally by Dafny and not externally.
+
+TODO: Need more complete explanation of this attribute.
+
+### nativeType {#sec-nativetype}
+The `{:nativeType}` attribute may only be used on a ``NewtypeDecl``
+where the base type is an integral type. It can take one of the following
+forms:
+
+* `{:nativeType}` - With no parameters it has no effect and the ``NewtypeDecl``
+have its default behavior which is to choose a native type that can hold any
+value satisfying the constraints, if possible, otherwise BigInteger is used.
+* `{:nativeType true}` - Also gives default ``NewtypeDecl`` behavior,
+but gives an error if base type is not integral.
+* `{:nativeType false}` - Inhibits using a native type. BigInteger is used
+for integral types and BitRational for real types.
+* `{:nativeType "typename"}` - This form has an native integral
+type name as a string literal. Acceptable values are: "byte",
+"sbyte", "ushort", "short", "uint", "int", "ulong" and "long".
+An error is reported if the given data type cannot hold all the
+values that satisfy the constraint.
+
+
+### opaque {#sec-opaque}
+Ordinarily the body of a function is transparent to its users but
+sometimes it is useful to hide it. If a function `f` is given the
+`{:opaque}` attribute then Dafny hides the body of the function,
+so that it can only be seen within its recursive clique (if any),
+or if the programmer specifically asks to see it via the `reveal_f()` lemma.
+
+We create a lemma to allow the user to selectively reveal the function's body
+That is, given:
+
+```
+ function {:opaque} foo(x:int, y:int) : int
+ requires 0 <= x < 5
+ requires 0 <= y < 5
+ ensures foo(x, y) < 10
+ { x + y }
+```
+
+We produce:
+
+```
+ lemma {:axiom} reveal_foo()
+ ensures forall x:int, y:int {:trigger foo(x,y)} ::
+ 0 <= x < 5 && 0 <= y < 5 ==> foo(x,y) == foo_FULL(x,y)
+```
+
+where `foo_FULL` is a copy of `foo` which does not have its body
+hidden. In addition `foo_FULL` is given the
+`{:opaque_full}` and `{:auto_generated}` attributes in addition
+to the `{:opaque}` attribute (which it got because it is a copy of `foo`).
+
+### opaque full
+The `{:opaque_full}` attribute is used to mark the _full_ version
+of an opaque function. See Section [#sec-opaque].
+
+### prependAssertToken
+This is used internally in Dafny as part of module refinement.
+It is an attribute on an assert statement.
+The Dafny code has the following comment:
+
+```
+// Clone the expression, but among the new assert's attributes, indicate
+// that this assertion is supposed to be translated into a check. That is,
+// it is not allowed to be just assumed in the translation, despite the fact
+// that the condition is inherited.
+```
+
+TODO: Decide if we want to describe this in more detail, or whether
+the functionality is already adequately described where
+refinement is described.
+
+### tailrecursion
+This attribute is used on a method declarations. It has a boolean argument.
+
+If specified with a false value it means the user specifically
+requested no tail recursion, so none is done.
+
+If specified with a true value, or if not specified
+then tail recursive optimization will be attempted subject to
+the following conditions:
+
+* It is an error if the method is a ghost method and tail
+recursion was explicitly requested.
+* Only direct recursion is supported, not mutually recursive methods.
+* If `{:tailrecursion true}` was specified but the code does not allow it
+an error message is given.
+
+### timeLimitMultiplier
+This attribute may be placed on a method or function declaration
+and has an integer argument. If `{:timeLimitMultiplier X}` was
+specified a `{:timelimit Y}` attributed is passed on to Boogie
+where `Y` is `X` times either the default verification time limit
+for a function or method, or times the value specified by the
+Boogie `timelimit` command-line option.
+
+### trigger
+Trigger attributes are used on quantifiers and comprehensions.
+They are translated into Boogie triggers.
+
+### typeQuantifier
+The `{:typeQuantifier}` must be used on a quantifier if it
+quantifies over types.
+
+
+## Boogie Attributes
+Use the Boogie "/attrHelp" option to get the list of attributes
+that Boogie recognizes and their meaning. Here is the output at
+the time of this writing. Dafny passes attributes that have
+been specified to the Boogie.
+
+```
+Boogie: The following attributes are supported by this implementation.
+
+ ---- On top-level declarations ---------------------------------------------
+
+ {:ignore}
+ Ignore the declaration (after checking for duplicate names).
+
+ {:extern}
+ If two top-level declarations introduce the same name (for example, two
+ constants with the same name or two procedures with the same name), then
+ Boogie usually produces an error message. However, if at least one of
+ the declarations is declared with :extern, one of the declarations is
+ ignored. If both declarations are :extern, Boogie arbitrarily chooses
+ one of them to keep; otherwise, Boogie ignore the :extern declaration
+ and keeps the other.
+
+ {:checksum <string>}
+ Attach a checksum to be used for verification result caching.
+
+ ---- On implementations and procedures -------------------------------------
+
+ {:inline N}
+ Inline given procedure (can be also used on implementation).
+ N should be a non-negative number and represents the inlining depth.
+ With /inline:assume call is replaced with "assume false" once inlining depth is reached.
+ With /inline:assert call is replaced with "assert false" once inlining depth is reached.
+ With /inline:spec call is left as is once inlining depth is reached.
+ With the above three options, methods with the attribute {:inline N} are not verified.
+ With /inline:none the entire attribute is ignored.
+
+ {:verify false}
+ Skip verification of an implementation.
+
+ {:vcs_max_cost N}
+ {:vcs_max_splits N}
+ {:vcs_max_keep_going_splits N}
+ Per-implementation versions of
+ /vcsMaxCost, /vcsMaxSplits and /vcsMaxKeepGoingSplits.
+
+ {:selective_checking true}
+ Turn all asserts into assumes except for the ones reachable from
+ assumptions marked with the attribute {:start_checking_here}.
+ Thus, "assume {:start_checking_here} something;" becomes an inverse
+ of "assume false;": the first one disables all verification before
+ it, and the second one disables all verification after.
+
+ {:priority N}
+ Assign a positive priority 'N' to an implementation to control the order
+ in which implementations are verified (default: N = 1).
+
+ {:id <string>}
+ Assign a unique ID to an implementation to be used for verification
+ result caching (default: "<impl. name>:0").
+
+ {:timeLimit N}
+ Set the time limit for a given implementation.
+
+ ---- On functions ----------------------------------------------------------
+
+ {:builtin "spec"}
+ {:bvbuiltin "spec"}
+ Rewrite the function to built-in prover function symbol 'fn'.
+
+ {:inline}
+ {:inline true}
+ Expand function according to its definition before going to the prover.
+
+ {:never_pattern true}
+ Terms starting with this function symbol will never be
+ automatically selected as patterns. It does not prevent them
+ from being used inside the triggers, and does not affect explicit
+ trigger annotations. Internally it works by adding {:nopats ...}
+ annotations to quantifiers.
+
+ {:identity}
+ {:identity true}
+ If the function has 1 argument and the use of it has type X->X for
+ some X, then the abstract interpreter will treat the function as an
+ identity function. Note, the abstract interpreter trusts the
+ attribute--it does not try to verify that the function really is an
+ identity function.
+
+ ---- On variables ----------------------------------------------------------
+
+ {:existential true}
+ Marks a global Boolean variable as existentially quantified. If
+ used in combination with option /contractInfer Boogie will check
+ whether there exists a Boolean assignment to the existentials
+ that makes all verification conditions valid. Without option
+ /contractInfer the attribute is ignored.
+
+ ---- On assert statements --------------------------------------------------
+
+ {:subsumption n}
+ Overrides the /subsumption command-line setting for this assertion.
+
+ {:split_here}
+ Verifies code leading to this point and code leading from this point
+ to the next split_here as separate pieces. May help with timeouts.
+ May also occasionally double-report errors.
+
+ ---- The end ---------------------------------------------------------------
+
+```
+
+However a scan of Boogie's sources shows it checks for the
+following attributes.
+
+* `{:$}`
+* `{:$renamed$}`
+* `{:InlineAssume}`
+* `{:PossiblyUnreachable}`
+* `{:__dominator_enabled}`
+* `{:__enabled}`
+* `{:a##post##}`
+* `{:absdomain}`
+* `{:ah}`
+* `{:assumption}`
+* `{:assumption_variable_initialization}`
+* `{:atomic}`
+* `{:aux}`
+* `{:both}`
+* `{:bvbuiltin}`
+* `{:candidate}`
+* `{:captureState}`
+* `{:checksum}`
+* `{:constructor}`
+* `{:datatype}`
+* `{:do_not_predicate}`
+* `{:entrypoint}`
+* `{:existential}`
+* `{:exitAssert}`
+* `{:expand}`
+* `{:extern}`
+* `{:hidden}`
+* `{:ignore}`
+* `{:inline}`
+* `{:left}`
+* `{:linear}`
+* `{:linear_in}`
+* `{:linear_out}`
+* `{:msg}`
+* `{:name}`
+* `{:originated_from_invariant}`
+* `{:partition}`
+* `{:positive}`
+* `{:post}`
+* `{:pre}`
+* `{:precondition_previous_snapshot}`
+* `{:qid}`
+* `{:right}`
+* `{:selective_checking}`
+* `{:si_fcall}`
+* `{:si_unique_call}`
+* `{:sourcefile}`
+* `{:sourceline}`
+* `{:split_here}`
+* `{:stage_active}`
+* `{:stage_complete}`
+* `{:staged_houdini_tag}`
+* `{:start_checking_here}`
+* `{:subsumption}`
+* `{:template}`
+* `{:terminates}`
+* `{:upper}`
+* `{:verified_under}`
+* `{:weight}`
+* `{:yields}`
+
+# Dafny User's Guide
+## Installing Dafny From Binaries
+## Building Dafny from Source
+The current version of Dafny only works with Visual Studio 2012,
+so if you intend to run Dafny from withing Visual Studio you must
+install Visual Studio 2012.
+
+Dafny performs its verification by translating the Dafny source into
+the Boogie intermediate verification language. So Dafny references
+data structures defined in the Boogie project. So the first step
+is to clone and build Boogie from sources. See
+<https://github.com/boogie-org/boogie>.
+
+Follow these steps.
+
+Let _work_ be a working directory.
+
+Clone Boogie using
+
+```
+cd work
+git clone https://github.com/boogie-org/boogie.git
+```
+
+Build Boogie using the directions from the Boogie web site,
+which for Windows currently are:
+
+1. Open Source\Boogie.sln in Visual Studio
+2. Right click the Boogie solution in the Solution Explorer and click Enable NuGet Package Restore. You will probably get a prompt asking to confirm this. Choose Yes.
+3. Click BUILD > Build Solution.
+
+Clone Dafny using Mercurial. The Dafny directory must be a sibling
+of the Boogie directory in order for it to find the Boogie files it needs.
+
+```
+cd work
+hg clone https://hg.codeplex.com/dafny
+```
+
+Download and install the Visual Studio 2012 SDK from
+
+* <https://www.microsoft.com/en-us/download/details.aspx?id=30668>.
+
+This is needed to build the Visual Studio Extension that
+runs Dafny from within Visual Studio 2012.
+
+Build the command-line Dafny executables.
+1. Open dafny\Source\Dafny.sln in Visual Studio
+2. Click BUILD > Build Solution.
+
+Build and install the Dafny Visual Studio extensions
+
+1. Open dafny/Source/DafnyExtension.sln in Visual Studio
+2. Click BUILD > Build Solution.
+3. This builds DafnyLanguageService.vsix and DafnyMenu.vsix
+in the dafny/Binaries directory.
+4. Install these by clicking on them from Windows Explorer. When
+prompted, only check installing into Visual Studio 2012.
+
+## Using Dafny From Visual Studio
+To test your installation, you can open Dafny test files
+from the dafny/Test subdirectory in Visual Studio 2012.
+You will want to use "VIEW/Error List" to ensure that
+you see any errors that Dafny detects, and
+"VIEW/Output" to see the result of any compilation.
+
+An example of a valid Dafny test is
+
+```
+dafny\Test\vstte2012\Tree.dfy
+```
+
+You can choose "Dafny/Compile" to compile the Dafny
+program to C#. Doing that for the above test
+produces `Tree.cs` and `Tree.dll` (since this test does
+not have a main program).
+
+The following file:
+
+```
+D:\gh\dafny\Test\dafny0\Array.dfy
+```
+
+is an example of a Dafny file with verification errors.
+The source will show red squiggles or dots where there
+are errors, and the Error List window will describe the
+errors.
+
+## Using Dafny From the Command Line
+### Dafny Command Line Options
+The command `Dafny.exe /?` gives the following description of
+options that can be passed to Dafny.
+
+```
+ ---- Dafny options ---------------------------------------------------------
+
+ Multiple .dfy files supplied on the command line are concatenated into one
+ Dafny program.
+
+ /dprelude:<file>
+ choose Dafny prelude file
+ /dprint:<file>
+ print Dafny program after parsing it
+ (use - as <file> to print to console)
+ /printMode:<Everything|NoIncludes|NoGhost>
+ NoIncludes disables printing of {:verify false} methods incorporated via the
+ include mechanism, as well as datatypes and fields included from other files.
+ NoGhost disables printing of functions, ghost methods, and proof statements
+ in implementation methods. It also disables anything NoIncludes disables.
+ /rprint:<file>
+ print Dafny program after resolving it
+ (use - as <file> to print to console)
+ /dafnyVerify:<n>
+ 0 - stop after typechecking
+ 1 - continue on to translation, verification, and compilation
+ /compile:<n> 0 - do not compile Dafny program
+ 1 (default) - upon successful verification of the Dafny
+ program, compile Dafny program to .NET assembly
+ Program.exe (if the program has a Main method) or
+ Program.dll (othewise), where Program.dfy is the name
+ of the last .dfy file on the command line
+ 2 - always attempt to compile Dafny program to C# program
+ out.cs, regardless of verification outcome
+ 3 - if there is a Main method and there are no verification
+ errors, compiles program in memory (i.e., does not write
+ an output file) and runs it
+ /spillTargetCode:<n>
+ 0 (default) - don't write the compiled Dafny program (but
+ still compile it, if /compile indicates to do so)
+ 1 - write the compiled Dafny program as a .cs file
+ /dafnycc Disable features not supported by DafnyCC
+ /noCheating:<n>
+ 0 (default) - allow assume statements and free invariants
+ 1 - treat all assumptions as asserts, and drop free.
+ /induction:<n>
+ 0 - never do induction, not even when attributes request it
+ 1 - only apply induction when attributes request it
+ 2 - apply induction as requested (by attributes) and also
+ for heuristically chosen quantifiers
+ 3 (default) - apply induction as requested, and for
+ heuristically chosen quantifiers and ghost methods
+ /inductionHeuristic:<n>
+ 0 - least discriminating induction heuristic (that is, lean
+ toward applying induction more often)
+ 1,2,3,4,5 - levels in between, ordered as follows as far as
+ how discriminating they are: 0 < 1 < 2 < (3,4) < 5 < 6
+ 6 (default) - most discriminating
+ /noIncludes Ignore include directives
+ /noNLarith Reduce Z3's knowledge of non-linear arithmetic (*,/,%).
+ Results in more manual work, but also produces more predictable behavior.
+ /autoReqPrint:<file>
+ Print out requirements that were automatically generated by autoReq.
+ /noAutoReq Ignore autoReq attributes
+ /allowGlobals Allow the implicit class '_default' to contain fields, instance functions,
+ and instance methods. These class members are declared at the module scope,
+ outside of explicit classes. This command-line option is provided to simply
+ a transition from the behavior in the language prior to version 1.9.3, from
+ which point onward all functions and methods declared at the module scope are
+ implicitly static and fields declarations are not allowed at the module scope.
+ The reference manual is written assuming this option is not given.
+
+
+ /nologo suppress printing of version number, copyright message
+ /env:<n> print command line arguments
+ 0 - never, 1 (default) - during BPL print and prover log,
+ 2 - like 1 and also to standard output
+ /wait await Enter from keyboard before terminating program
+ /xml:<file> also produce output in XML format to <file>
+
+ ---- Boogie options --------------------------------------------------------
+
+ Multiple .bpl files supplied on the command line are concatenated into one
+ Boogie program.
+
+ /proc:<p> : limits which procedures to check
+ /noResolve : parse only
+ /noTypecheck : parse and resolve only
+
+ /print:<file> : print Boogie program after parsing it
+ (use - as <file> to print to console)
+ /pretty:<n>
+ 0 - print each Boogie statement on one line (faster).
+ 1 (default) - pretty-print with some line breaks.
+ /printWithUniqueIds : print augmented information that uniquely
+ identifies variables
+ /printUnstructured : with /print option, desugars all structured statements
+ /printDesugared : with /print option, desugars calls
+
+ /overlookTypeErrors : skip any implementation with resolution or type
+ checking errors
+
+ /loopUnroll:<n>
+ unroll loops, following up to n back edges (and then some)
+ /soundLoopUnrolling
+ sound loop unrolling
+ /printModel:<n>
+ 0 (default) - do not print Z3's error model
+ 1 - print Z3's error model
+ 2 - print Z3's error model plus reverse mappings
+ 4 - print Z3's error model in a more human readable way
+ /printModelToFile:<file>
+ print model to <file> instead of console
+ /mv:<file> Specify file where to save the model in BVD format
+ /enhancedErrorMessages:<n>
+ 0 (default) - no enhanced error messages
+ 1 - Z3 error model enhanced error messages
+
+ /printCFG:<prefix> : print control flow graph of each implementation in
+ Graphviz format to files named:
+ <prefix>.<procedure name>.dot
+
+ /useBaseNameForFileName : When parsing use basename of file for tokens instead
+ of the path supplied on the command line
+
+ ---- Inference options -----------------------------------------------------
+
+ /infer:<flags>
+ use abstract interpretation to infer invariants
+ The default is /infer:i
+ <flags> are as follows (missing <flags> means all)
+ i = intervals
+ c = constant propagation
+ d = dynamic type
+ n = nullness
+ p = polyhedra for linear inequalities
+ t = trivial bottom/top lattice (cannot be combined with
+ other domains)
+ j = stronger intervals (cannot be combined with other
+ domains)
+ or the following (which denote options, not domains):
+ s = debug statistics
+ 0..9 = number of iterations before applying a widen (default=0)
+ /noinfer turn off the default inference, and overrides the /infer
+ switch on its left
+ /checkInfer instrument inferred invariants as asserts to be checked by
+ theorem prover
+ /interprocInfer
+ perform interprocedural inference (deprecated, not supported)
+ /contractInfer
+ perform procedure contract inference
+ /instrumentInfer
+ h - instrument inferred invariants only at beginning of
+ loop headers (default)
+ e - instrument inferred invariants at beginning and end
+ of every block (this mode is intended for use in
+ debugging of abstract domains)
+ /printInstrumented
+ print Boogie program after it has been instrumented with
+ invariants
+
+ ---- Debugging and general tracing options ---------------------------------
+
+ /trace blurt out various debug trace information
+ /traceTimes output timing information at certain points in the pipeline
+ /tracePOs output information about the number of proof obligations
+ (also included in the /trace output)
+ /log[:method] Print debug output during translation
+
+ /break launch and break into debugger
+
+ ---- Verification-condition generation options -----------------------------
+
+ /liveVariableAnalysis:<c>
+ 0 = do not perform live variable analysis
+ 1 = perform live variable analysis (default)
+ 2 = perform interprocedural live variable analysis
+ /noVerify skip VC generation and invocation of the theorem prover
+ /verifySnapshots:<n>
+ verify several program snapshots (named <filename>.v0.bpl
+ to <filename>.vN.bpl) using verification result caching:
+ 0 - do not use any verification result caching (default)
+ 1 - use the basic verification result caching
+ 2 - use the more advanced verification result caching
+ /verifySeparately
+ verify each input program separately
+ /removeEmptyBlocks:<c>
+ 0 - do not remove empty blocks during VC generation
+ 1 - remove empty blocks (default)
+ /coalesceBlocks:<c>
+ 0 = do not coalesce blocks
+ 1 = coalesce blocks (default)
+ /vc:<variety> n = nested block (default for /prover:Simplify),
+ m = nested block reach,
+ b = flat block, r = flat block reach,
+ s = structured, l = local,
+ d = dag (default, except with /prover:Simplify)
+ doomed = doomed
+ /traceverify print debug output during verification condition generation
+ /subsumption:<c>
+ apply subsumption to asserted conditions:
+ 0 - never, 1 - not for quantifiers, 2 (default) - always
+ /alwaysAssumeFreeLoopInvariants
+ usually, a free loop invariant (or assume
+ statement in that position) is ignored in checking contexts
+ (like other free things); this option includes these free
+ loop invariants as assumes in both contexts
+ /inline:<i> use inlining strategy <i> for procedures with the :inline
+ attribute, see /attrHelp for details:
+ none
+ assume (default)
+ assert
+ spec
+ /printInlined
+ print the implementation after inlining calls to
+ procedures with the :inline attribute (works with /inline)
+ /lazyInline:1
+ Use the lazy inlining algorithm
+ /stratifiedInline:1
+ Use the stratified inlining algorithm
+ /fixedPointEngine:<engine>
+ Use the specified fixed point engine for inference
+ /recursionBound:<n>
+ Set the recursion bound for stratified inlining to
+ be n (default 500)
+ /inferLeastForUnsat:<str>
+ Infer the least number of constants (whose names
+ are prefixed by <str>) that need to be set to
+ true for the program to be correct. This turns
+ on stratified inlining.
+ /smoke Soundness Smoke Test: try to stick assert false; in some
+ places in the BPL and see if we can still prove it
+ /smokeTimeout:<n>
+ Timeout, in seconds, for a single theorem prover
+ invocation during smoke test, defaults to 10.
+ /causalImplies
+ Translate Boogie's A ==> B into prover's A ==> A && B.
+ /typeEncoding:<m>
+ how to encode types when sending VC to theorem prover
+ n = none (unsound)
+ p = predicates (default)
+ a = arguments
+ m = monomorphic
+ /monomorphize
+ Do not abstract map types in the encoding (this is an
+ experimental feature that will not do the right thing if
+ the program uses polymorphism)
+ /reflectAdd In the VC, generate an auxiliary symbol, elsewhere defined
+ to be +, instead of +.
+
+ ---- Verification-condition splitting --------------------------------------
+
+ /vcsMaxCost:<f>
+ VC will not be split unless the cost of a VC exceeds this
+ number, defaults to 2000.0. This does NOT apply in the
+ keep-going mode after first round of splitting.
+ /vcsMaxSplits:<n>
+ Maximal number of VC generated per method. In keep
+ going mode only applies to the first round.
+ Defaults to 1.
+ /vcsMaxKeepGoingSplits:<n>
+ If set to more than 1, activates the keep
+ going mode, where after the first round of splitting,
+ VCs that timed out are split into <n> pieces and retried
+ until we succeed proving them, or there is only one
+ assertion on a single path and it timeouts (in which
+ case error is reported for that assertion).
+ Defaults to 1.
+ /vcsKeepGoingTimeout:<n>
+ Timeout in seconds for a single theorem prover
+ invocation in keep going mode, except for the final
+ single-assertion case. Defaults to 1s.
+ /vcsFinalAssertTimeout:<n>
+ Timeout in seconds for the single last
+ assertion in the keep going mode. Defaults to 30s.
+ /vcsPathJoinMult:<f>
+ If more than one path join at a block, by how much
+ multiply the number of paths in that block, to accomodate
+ for the fact that the prover will learn something on one
+ paths, before proceeding to another. Defaults to 0.8.
+ /vcsPathCostMult:<f1>
+ /vcsAssumeMult:<f2>
+ The cost of a block is
+ (<assert-cost> + <f2>*<assume-cost>) *
+ (1.0 + <f1>*<entering-paths>)
+ <f1> defaults to 1.0, <f2> defaults to 0.01.
+ The cost of a single assertion or assumption is
+ currently always 1.0.
+ /vcsPathSplitMult:<f>
+ If the best path split of a VC of cost A is into
+ VCs of cost B and C, then the split is applied if
+ A >= <f>*(B+C), otherwise assertion splitting will be
+ applied. Defaults to 0.5 (always do path splitting if
+ possible), set to more to do less path splitting
+ and more assertion splitting.
+ /vcsDumpSplits
+ For split #n dump split.n.dot and split.n.bpl.
+ Warning: Affects error reporting.
+ /vcsCores:<n>
+ Try to verify <n> VCs at once. Defaults to 1.
+ /vcsLoad:<f> Sets vcsCores to the machine's ProcessorCount * f,
+ rounded to the nearest integer (where 0.0 <= f <= 3.0),
+ but never to less than 1.
+
+ ---- Prover options --------------------------------------------------------
+
+ /errorLimit:<num>
+ Limit the number of errors produced for each procedure
+ (default is 5, some provers may support only 1)
+ /timeLimit:<num>
+ Limit the number of seconds spent trying to verify
+ each procedure
+ /errorTrace:<n>
+ 0 - no Trace labels in the error output,
+ 1 (default) - include useful Trace labels in error output,
+ 2 - include all Trace labels in the error output
+ /vcBrackets:<b>
+ bracket odd-charactered identifier names with |'s. <b> is:
+ 0 - no (default with non-/prover:Simplify),
+ 1 - yes (default with /prover:Simplify)
+ /prover:<tp> use theorem prover <tp>, where <tp> is either the name of
+ a DLL containing the prover interface located in the
+ Boogie directory, or a full path to a DLL containing such
+ an interface. The standard interfaces shipped include:
+ SMTLib (default, uses the SMTLib2 format and calls Z3)
+ Z3 (uses Z3 with the Simplify format)
+ Simplify
+ ContractInference (uses Z3)
+ Z3api (Z3 using Managed .NET API)
+ /proverOpt:KEY[=VALUE]
+ Provide a prover-specific option (short form /p).
+ /proverLog:<file>
+ Log input for the theorem prover. Like filenames
+ supplied as arguments to other options, <file> can use the
+ following macros:
+ @TIME@ expands to the current time
+ @PREFIX@ expands to the concatenation of strings given
+ by /logPrefix options
+ @FILE@ expands to the last filename specified on the
+ command line
+ In addition, /proverLog can also use the macro '@PROC@',
+ which causes there to be one prover log file per
+ verification condition, and the macro then expands to the
+ name of the procedure that the verification condition is for.
+ /logPrefix:<str>
+ Defines the expansion of the macro '@PREFIX@', which can
+ be used in various filenames specified by other options.
+ /proverLogAppend
+ Append (not overwrite) the specified prover log file
+ /proverWarnings
+ 0 (default) - don't print, 1 - print to stdout,
+ 2 - print to stderr
+ /proverMemoryLimit:<num>
+ Limit on the virtual memory for prover before
+ restart in MB (default:100MB)
+ /restartProver
+ Restart the prover after each query
+ /proverShutdownLimit<num>
+ Time between closing the stream to the prover and
+ killing the prover process (default: 0s)
+ /platform:<ptype>,<location>
+ ptype = v11,v2,cli1
+ location = platform libraries directory
+
+ Simplify specific options:
+ /simplifyMatchDepth:<num>
+ Set Simplify prover's matching depth limit
+
+ Z3 specific options:
+ /z3opt:<arg> specify additional Z3 options
+ /z3multipleErrors
+ report multiple counterexamples for each error
+ /useArrayTheory
+ use Z3's native theory (as opposed to axioms). Currently
+ implies /monomorphize.
+ /useSmtOutputFormat
+ Z3 outputs a model in the SMTLIB2 format.
+ /z3types generate multi-sorted VC that make use of Z3 types
+ /z3lets:<n> 0 - no LETs, 1 - only LET TERM, 2 - only LET FORMULA,
+ 3 - (default) any
+ /z3exe:<path>
+ path to Z3 executable
+
+ CVC4 specific options:
+ /cvc4exe:<path>
+ path to CVC4 executable
+
+```
+
+
+# References
+[BIB]
+
diff --git a/Docs/DafnyRef/css.sty b/Docs/DafnyRef/css.sty
new file mode 100644
index 00000000..aab82e69
--- /dev/null
+++ b/Docs/DafnyRef/css.sty
@@ -0,0 +1,803 @@
+%---------------------------------------------------------------------------
+% Copyright 2013 Microsoft Corporation.
+%
+% This is free software; you can redistribute it and/or modify it under the
+% terms of the Apache License, Version 2.0. A copy of the License can be
+% found in the file "license.txt" at the root of this distribution.
+%---------------------------------------------------------------------------
+\NeedsTeXFormat{LaTeX2e}[1995/12/01]
+
+\RequirePackage{iftex}
+\RequirePackage{etoolbox}
+\RequirePackage{xkeyval}
+\RequirePackage[table]{xcolor}
+\RequirePackage{mdframed}
+\RequirePackage{graphicx}
+\RequirePackage{tablefootnote}
+
+% font selection
+\ifXeTeX\RequirePackage{fontspec}\else
+\ifLuaTeX\RequirePackage{fontspec}\else
+\providecommand{\fontspec}[2][]{}
+\fi\fi
+
+
+% Define CSS 17 standard colors
+\definecolor{Red}{HTML}{FF0000}
+\definecolor{Lime}{HTML}{00FF00}
+\definecolor{Blue}{HTML}{0000FF}
+
+\definecolor{Yellow}{HTML}{FFFF00}
+\definecolor{Cyan}{HTML}{00FFFF}
+\definecolor{Magenta}{HTML}{FF00FF}
+
+\definecolor{Navy}{HTML}{000080}
+\definecolor{Maroon}{HTML}{800000}
+\definecolor{Green}{HTML}{008000}
+
+\definecolor{Teal}{HTML}{008080}
+\definecolor{Purple}{HTML}{800080}
+\definecolor{Olive}{HTML}{808000}
+
+\definecolor{Black}{HTML}{000000}
+\definecolor{Dimgray}{HTML}{696969}
+\definecolor{Gray}{HTML}{808080}
+\definecolor{Darkgray}{HTML}{A9A9A9}
+\definecolor{Silver}{HTML}{C0C0C0}
+\definecolor{Lightgray}{HTML}{D3D3D3}
+\definecolor{Gainsboro}{HTML}{DCDCDC}
+\definecolor{Floralwhite}{HTML}{FFFAF0}
+\definecolor{Ivory}{HTML}{FFFFF0}
+\definecolor{White}{HTML}{FFFFFF}
+
+\definecolor{Orange}{HTML}{FFA500}
+\definecolor{Aqua}{HTML}{00FFFF}
+\definecolor{Fuchsia}{HTML}{FF00FF}
+
+\newcommand{\@swap}[2]{#2{#1}}
+\newcommand{\@expandafter}[2]{\expandafter\@swap\expandafter{#2}{#1}}
+
+\newcommand{\eifstrequal}{\expandafter\ifstrequal\expandafter}
+\newcommand{\eeifstrequal}[2]{\@expandafter{\eifstrequal{#1}}{#2}}
+
+\providecommand\providelength[1]{%
+ \begingroup
+ \escapechar\m@ne
+ \xdef\@gtempa{\string#1}%
+ \endgroup
+ \@ifundefined{\@gtempa}%
+ {\newskip#1}%
+ {}%
+}
+
+% is a string an element of a list of (comma separated) strings
+\newcommand{\eifstrelement}[4]{%
+ \def\@found{}%
+ \@for\@ii:=#2\do{%
+ \eeifstrequal{\@ii}{#1}{\def\@found{true}}{}%
+ }%
+ \ifdefvoid{\@found}{#4}{#3}%
+}
+
+% do two lists of strings intersect?
+\newcommand{\ifintersect}[4]{%
+ \def\@intersect{}%
+ \@for\@sname:=#1\do{%
+ \ifdefvoid{\@intersect}{%
+ \eifstrelement{\@sname}{#2}{\def\@intersect{true}}{}%
+ }{}%
+ }%
+ \ifdefvoid{\@intersect}{#4}{#3}%
+}
+
+% get string head and tail
+\def\strsplit#1{\expandafter\strsplitx#1\empty\empty\empty}
+\def\strsplitx#1#2\empty{%
+ \edef\strhead{#1}%
+ \edef\strtail{#2}%
+}
+
+% normalize colors: to lowercase and then capitalize
+\newcommand{\cssDefNormalizeColor}[2]{%
+ \expandafter\@cssNormColor#2\empty{#1}\empty%
+}
+\def\@cssNormColor#1#2\empty#3\empty{%
+ \uppercase{\def\@hd{#1}}\lowercase{\def\@tl{#2}}%
+ \expandafter\global\expandafter\edef\csname #3\endcsname{\@hd\@tl}%
+}
+
+
+% ---------------------------------------------------
+% Some TeX stuff to compose functions
+% ---------------------------------------------------
+\newcommand{\apptox}[2]{% apptox{\cmd1}{\cmd2} == newcommand{\cmd1'}[1]{\cmd1{\cmd2{#1}}}
+ \providecommand{#1}[1]{##1}% define it if necessary (as identity)
+ \protected@edef#1##1{#1{\protect #2{##1}}}%
+}
+
+\newcommand{\pretox}[2]{% pretox{\cmd1}{\cmd2} == newcommand{\cmd1'}[1]{\cmd2{\cmd1{#1}}}
+ \providecommand{#1}[1]{##1}%
+ \protected@edef#1##1{\protect #2{#1{##1}}}%
+}
+
+%-------------------------------------------------------------
+% Save footnotes inside mdframed and minipage environments
+%-------------------------------------------------------------
+\newif\if@saveFootnotes
+\newcommand{\cssSaveFootnotes}%
+ {\if@saveFootnotes\else%
+ \let\footnote\tablefootnote%
+ \fi%
+ \@saveFootnotestrue}%
+\newcommand{\cssRestoreFootnotes}%
+ {\if@saveFootnotes\else%
+ \tfn@tablefootnoteprintout%
+ \gdef\tfn@fnt{0}%
+ \fi}%
+
+%-------------------------------------------------------------
+% Setup mdframed with default values
+%-------------------------------------------------------------
+\newlength{\cssPixel}\setlength{\cssPixel}{0.4pt}% assume 180 dpi
+\mdfsetup{%
+ leftmargin=0pt,%
+ rightmargin=0pt,%
+ skipabove=0pt,%
+ skipbelow=0pt,%
+ innertopmargin=0pt,%
+ innerbottommargin=0pt,%
+ innerleftmargin=0pt,%
+ innerrightmargin=0pt,%
+ middlelinewidth=0pt,%
+ linewidth=0pt,%
+ outerlinewidth=0pt,innerlinewidth=0pt%
+}
+
+
+% ---------------------------------------------------
+% Basic command to process attributes passed to TeX
+% ---------------------------------------------------
+\newif\if@usewrap
+\newcommand{\@doBefore}{}
+\newcommand{\@doAfter}{}
+\newcommand{\@wrapCmd}[1]{#1}
+
+\newcommand{\@cssUseCmd}{\renewcommand{\@wrapCmd}[1]{##1}\@usewraptrue}
+\newcommand{\@cssUseEnv}{\renewcommand{\@doBefore}{}\renewcommand{\@doAfter}{}\@usewrapfalse}
+
+\newcommand{\@cssApplyCmd}[1]{{\@wrapCmd{#1}}}
+\newcommand{\@cssApplyBefore}{\@doBefore{}}
+\newcommand{\@cssApplyAfter}{\@doAfter{}}
+
+\newcommand{\@cssProcessAttrs}[2]{%
+ \setkeys*{cssx}{#1}\setrmkeys*{csspre}\setrmkeys*{css}\setrmkeys*{csspost}% defaults
+ \@cssApplyRulesFor{parentclass}{css}{\cssParentClass}%
+ \setkeys*{cssx}{#2}\setrmkeys*{csspre}\setrmkeys*{css}\setrmkeys*{csspost}% regular
+ \protected@edef\cssParentClass{\cssClass}%
+}
+
+
+\newcommand{\@cmdBefore}[2]{#1#2}
+\newcommand{\@cmdAfter}[2]{#2#1}
+
+\newcommand{\cssWrapCmd}[1]{\apptox{\@wrapCmd}{#1}}
+\newcommand{\cssDoBefore}[1]{\if@usewrap\cssWrapCmd{\@cmdBefore{#1}}\else #1\fi}
+\newcommand{\cssDoAfter}[1]{\if@usewrap\cssWrapCmd{\@cmdAfter{#1}}\else\preto\@doAfter{#1}\fi}
+
+\newcommand{\cssDoEnv}[1]{\cssDoBefore{\protect\begin{#1}}\cssDoAfter{\protect\end{#1}}}
+\newcommand{\cssDoEnvOpt}[2]{\cssDoBefore{\begin{#1}[#2]}\cssDoAfter{\end{#1}}}
+\newcommand{\cssDoEnvArg}[2]{\cssDoBefore{\begin{#1}{#2}}\cssDoAfter{\end{#1}}}
+\newcommand{\cssDoEnvArgII}[3]{\cssDoBefore{\begin{#1}{#2}{#3}}\cssDoAfter{\end{#1}}}
+
+\newcommand{\newKey}[4][]{\define@key{#2}{#3}[#1]{#4}}
+\newcommand{\newLength}[2]{\providelength{#1}\setlength{#1}{#2}}
+
+
+\newcommand{\@cssReset}{}
+\newcommand{\cssAddReset}[1]{\appto{\@cssReset}{#1}}
+\newcommand{\cssNewResetCommand}[2]{\newcommand{#1}{#2}\cssAddReset{\renewcommand{#1}{#2}}}
+
+\newlength{\cssFill}
+\setlength{\cssFill}{2sp plus 1fill minus 2sp} % make \fill unequal to 0pt, and detectable as 2sp
+
+\newcommand{\cssNewLengthKey}[4][0pt]{%
+ \newLength{#4}{#1}%
+ \newKey{#2}{#3}{%
+ \ifstrequal{##1}{auto}{\setlength{#4}{\cssFill}}{\setlength{#4}{##1}}%
+ }%
+ \cssAddReset{\setlength{#4}{#1}}%
+}
+
+
+\newcommand{\cssNewKeyNoReset}[4]{%
+ \newcommand{#3}{#4}%
+ \newKey{#1}{#2}{\renewcommand{#3}{##1}}%
+}
+
+\newcommand{\cssNewKey}[4]{%
+ \cssNewResetCommand{#3}{#4}%
+ \newKey{#1}{#2}{%(#2=##1)%debug key setting
+ \renewcommand{#3}{##1}}%
+}
+\newcommand{\cssNewKeyX}[5]{%
+ \cssNewResetCommand{#3}{#4}%
+ \newKey{#1}{#2}{\renewcommand{#3}{##1}#5{##1}}%
+}
+\newcommand{\cssNewListKey}[4]{%
+ \cssNewResetCommand{#3}{#4}%
+ \newKey{#1}{#2}{\appto{#3}{,##1}}%
+}
+\newcommand{\cssNewListKeyX}[5]{%
+ \cssNewResetCommand{#3}{#4}%
+ \newKey{#1}{#2}{\appto{#3}{,##1}#5{##1}}%
+}
+\newcommand{\cssNewPseudoKey}[3]{%
+ \newKey{#1}{#2}{\setkeys{#1}{#3}}%
+}
+
+%-------------------------------------------------------------
+% css: display
+%-------------------------------------------------------------
+\cssNewKey{css}{display}{\cssDisplay}{block}
+
+%-------------------------------------------------------------
+% css: width, height, and margins
+%-------------------------------------------------------------
+
+\cssNewLengthKey{css}{margin-left}{\cssMarginLeft}
+\cssNewLengthKey{css}{margin-right}{\cssMarginRight}
+\cssNewLengthKey{css}{margin-top}{\cssMarginTop}
+\cssNewLengthKey{css}{margin-bottom}{\cssMarginBottom}
+\cssNewLengthKey[1sp]{css}{width}{\cssWidth}
+\cssNewLengthKey[1sp]{css}{height}{\cssHeight}
+\cssNewKey{css}{vertical-align}{\cssVerticalAlign}{}
+
+\cssNewPseudoKey{css}{margin}{margin-top=#1,margin-bottom=#1,margin-left=#1,margin-right=#1}
+
+
+\newcommand{\@cssProcessMargins}{%
+ \eifstrequal{\cssDisplay}{block}%
+ {\@cssBlockEndPar\@cssBlockMargins}%
+ {\eifstrequal{\cssDisplay}{block-inline}%
+ {\@cssBlockMargins}%
+ {\@cssInlineMargins}%
+ }%
+}
+
+\newcommand{\@cssBlockEndPar}{%
+ \cssIfHasClass{para-continue,para-block}{}{\cssDoAfter{\relax\ifhmode\par\global\hangindent=0pt\fi}}%
+}
+
+\newif\if@hasdim
+
+\newlength{\cssHeightFull} % height including padding and border
+\newlength{\cssWidthFull} % width including padding and border
+\newLength{\@cssMarginAfter}{0pt}
+\newLength{\@cssParSkip}{\parskip}
+\newLength{\@cssParIndent}{\parindent}
+\newcommand{\@cssFixMathSpacing}{\strut\vspace{-\baselineskip}} % fixes weird abovedisplay skip spacing
+\newcommand{\@cssBlockMargins}{%
+ \@hasdimfalse
+ \ifdim\cssWidth=1sp\setlength{\cssWidthFull}{1sp}\else\@hasdimtrue\fi
+ \ifdim\cssHeight=1sp\setlength{\cssHeightFull}{1sp}\else\@hasdimtrue\fi
+ \if@hasdim%
+ % set full height and width
+ \setlength{\cssWidthFull}{\dimexpr\cssWidth+\cssPaddingLeft+\cssPaddingRight\relax}%
+ \eifstrequal{\cssBorderLeftStyle}{none}{}{\addtolength{\cssWidthFull}{\cssBorderWidth}}%
+ \eifstrequal{\cssBorderRightStyle}{none}{}{\addtolength{\cssWidthFull}{\cssBorderWidth}}%
+ \setlength{\cssHeightFull}{\dimexpr\cssHeight+\cssPaddingTop+\cssPaddingBottom\relax}%
+ \eifstrequal{\cssBorderTopStyle}{none}{}{\addtolength{\cssHeightFull}{\cssBorderWidth}}%
+ \eifstrequal{\cssBorderBottomStyle}{none}{}{\addtolength{\cssHeightFull}{\cssBorderWidth}}%
+ % set default width?
+ \ifdim\cssWidth=1sp% in this case, cssWidthFull is just padding and borders
+ \setlength{\cssWidth}{\dimexpr\linewidth-\cssWidthFull-\cssMarginLeft-\cssMarginRight}%
+ \addtolength{\cssWidthFull}{\cssWidth}%
+ \fi%
+ %minipage
+ \ifdim\cssMarginTop=0pt\else\cssDoBefore{\vspace{\cssMarginTop}}\fi
+ \ifdim\cssMarginLeft=0pt\else\cssDoBefore{\hspace*{\cssMarginLeft}}\fi
+ \setlength{\@cssParIndent}{\parindent}% save parskip and parindent since minipage resets it
+ \setlength{\@cssParSkip}{\parskip}%
+ \eifstrequal{\cssVerticalAlign}{bottom}{\def\@cssValign{b}}%
+ {\eifstrequal{\cssVerticalAlign}{center}{\def\@cssValign{c}}%
+ {\eifstrequal{\cssVerticalAlign}{top}{\def\@cssValign{t}}%
+ {\def\@cssValign{c}}}}% including `middle`
+ \ifdim\cssHeight=1sp%
+ \cssDoBefore{\begin{minipage}[\@cssValign]{\cssWidthFull}}%
+ \else
+ \cssDoBefore{\begin{minipage}[\@cssValign][\cssHeightFull]{\cssWidthFull}}%
+ \fi
+ \cssDoBefore{\cssSaveFootnotes\setlength{\parskip}{\@cssParSkip}\setlength{\parindent}{\@cssParIndent}}%
+ %note: DoAfter prepends, so in opposite order
+ \ifdim\cssMarginBottom=0pt\else\cssDoAfter{\vspace{\cssMarginBottom}}\fi
+ \ifdim\cssMarginRight=0pt\else\cssDoAfter{\hspace*{\cssMarginRight}}\fi
+ \cssDoAfter{\end{minipage}\cssRestoreFootnotes}%
+ \else
+ % no height/width: trivlist
+ \@hasdimfalse
+ \ifdim\cssMarginLeft=0pt\else\@hasdimtrue\fi
+ \ifdim\cssMarginRight=0pt\else\@hasdimtrue\fi
+ \ifdim\cssMarginTop=0pt\else\@hasdimtrue\fi
+ \ifdim\cssMarginBottom=0pt\else\@hasdimtrue\fi
+ \if@hasdim
+ \setlength{\@cssMarginAfter}{\dimexpr\cssMarginBottom-\cssMarginTop\relax}%
+ \cssDoEnvArgII{list}{}{%
+ \leftmargin=\cssMarginLeft%
+ \rightmargin=\cssMarginRight%
+ \topsep=\cssMarginTop%
+ \itemsep=0pt%
+ \parsep=0pt%
+ \parskip=0pt%
+ \partopsep=0pt%
+ \listparindent=\parindent%
+ }%
+ \ifdim\@cssMarginAfter=0pt\else\cssDoAfter{\vspace{\@cssMarginAfter}}\fi%
+ \cssIfHasClass{math-display}%
+ {\cssDoBefore{\item\@cssFixMathSpacing}}%
+ {\cssDoBefore{\item}}% \fi
+ \fi
+ \fi
+}
+
+\newcommand{\@cssHide}[1]{}
+\newcommand{\@cssInlineMargins}{%
+ \ifdim\cssMarginLeft=0pt\else\cssDoBefore{\hspace*{\cssMarginLeft}}\fi
+ \ifdim\cssMarginRight=0pt\else\cssDoAfter{\hspace*{\cssMarginRight}}\fi
+ \ifdim\cssMarginBottom=0pt\else\cssDoBefore{\rule[-\cssMarginBottom]{0pt}{\cssMarginBottom}}\fi
+ \ifdim\cssMarginTop=0pt\else\cssDoBefore{\rule{0pt}{\dimexpr\baselineskip*0.7+\cssMarginTop\relax}}\fi
+ \eifstrequal{\cssDisplay}{hidden}{%
+ \cssWrapCmd{\@cssHide}%
+ }{}%
+}
+
+%-------------------------------------------------------------
+% css: Borders and padding
+%-------------------------------------------------------------
+
+\cssNewLengthKey{css}{padding-left}{\cssPaddingLeft}
+\cssNewLengthKey{css}{padding-right}{\cssPaddingRight}
+\cssNewLengthKey{css}{padding-top}{\cssPaddingTop}
+\cssNewLengthKey{css}{padding-bottom}{\cssPaddingBottom}
+
+\newlength{\cssBorderWidthTotal}
+\cssNewLengthKey[\cssPixel]{css}{border-width}{\cssBorderWidth}
+\cssNewKey{css}{border-color}{\cssBorderColor}{black}
+\cssNewKey{css}{border-top-style}{\cssBorderTopStyle}{none}
+\cssNewKey{css}{border-bottom-style}{\cssBorderBottomStyle}{none}
+\cssNewKey{css}{border-left-style}{\cssBorderLeftStyle}{none}
+\cssNewKey{css}{border-right-style}{\cssBorderRightStyle}{none}
+\cssNewKey{css}{background-color}{\cssBackgroundColor}{white}
+
+\cssNewPseudoKey{css}{padding}{padding-top=#1,padding-bottom=#1,padding-right=#1,padding-left=#1}
+
+\cssNewPseudoKey{css}{border-style}%
+ {border-top-style=#1,border-bottom-style=#1,border-left-style=#1,border-right-style=#1}
+
+\newcommand{\@cssProcessPadding}{%
+ \eifstrequal{\cssDisplay}{block}%
+ {\@cssBlockPadding}%
+ {\eifstrequal{\cssDisplay}{block-inline}%
+ {\@cssBlockPadding}%
+ {\@cssInlinePadding}%
+ }}
+
+% Special math-framed environment that fixes vertical spacing around math display
+\newenvironment{mdmathframed}[1][]%
+ {\begin{mdframed}[#1]\@cssFixMathSpacing}%
+ {\@cssFixMathSpacing\end{mdframed}}
+
+
+\newif\if@needframe
+\newLength{\@cssPaddingLength}{0pt}
+\newcommand{\@cssFramedArgs}{}
+\newcommand{\@cssBorderStyleAll}{}
+\newcommand{\@cssBlockPadding}{%
+ \@needframefalse%
+ \eifstrequal{\cssBorderTopStyle}{none}{}{\@needframetrue}%
+ \eifstrequal{\cssBorderBottomStyle}{none}{}{\@needframetrue}%
+ \eifstrequal{\cssBorderLeftStyle}{none}{}{\@needframetrue}%
+ \eifstrequal{\cssBorderRightStyle}{none}{}{\@needframetrue}%
+ \eifstrequal{\cssBackgroundColor}{white}{}{\@needframetrue}%
+ \ifdim\cssPaddingTop=0pt\else\@needframetrue\fi
+ \ifdim\cssPaddingBottom=0pt\else\@needframetrue\fi
+ \ifdim\cssPaddingLeft=0pt\else\@needframetrue\fi
+ \ifdim\cssPaddingRight=0pt\else\@needframetrue\fi
+ \strsplit{\cssBackgroundColor}%
+ \eifstrequal{\strhead}{\#}%
+ {\definecolor{Temp}{HTML}{\strtail}\edef\@@bcolor{Temp}}%
+ {\cssDefNormalizeColor{@bcolor}{\cssBackgroundColor}\edef\@@bcolor{\@bcolor}}%
+ %\expandafter\lowercase\expandafter{\expandafter\def\expandafter\bcolor\expandafter{\cssBackgroundColor}}%
+ \if@needframe%
+ \cssDoAfter{\cssRestoreFootnotes}% first, because post commands are pre-pended
+ \renewcommand{\@cssFramedArgs}{%
+ innertopmargin=\the\cssPaddingTop,%
+ innerbottommargin=\the\cssPaddingBottom,%
+ innerleftmargin=\the\cssPaddingLeft,%
+ innerrightmargin=\the\cssPaddingRight,%
+ linewidth=\the\cssBorderWidth,%
+ linecolor=\cssBorderColor,%
+ backgroundcolor=\@@bcolor%
+ }%
+ \setlength{\cssBorderWidthTotal}{0pt}%
+ \eifstrequal{\cssBorderTopStyle}{none}{\appto{\@cssFramedArgs}{,topline=false}}{}%
+ \eifstrequal{\cssBorderBottomStyle}{none}{\appto{\@cssFramedArgs}{,bottomline=false}}{}%
+ \eifstrequal{\cssBorderLeftStyle}{none}{\appto{\@cssFramedArgs}{,leftline=false}}%
+ {\addtolength{\cssBorderWidthTotal}{\cssBorderWidth}}%
+ \eifstrequal{\cssBorderRightStyle}{none}{\appto{\@cssFramedArgs}{,rightline=false}}%
+ {\addtolength{\cssBorderWidthTotal}{\cssBorderWidth}}%
+ \cssIfHasClass{math-display}%
+ {\@expandafter{\cssDoEnvOpt{mdmathframed}}{\@cssFramedArgs}}%
+ {\@expandafter{\cssDoEnvOpt{mdframed}}{\@cssFramedArgs}}%
+ % insert a minipage if height or width was set so the frame is as large
+ \@hasdimfalse
+ \ifdim\cssWidth=1sp\else\@hasdimtrue\fi
+ \ifdim\cssHeight=1sp\else\@hasdimtrue\fi
+ \if@hasdim%
+ \ifdim\cssHeight=1sp%
+ \cssDoBefore{\begin{minipage}{\cssWidth}}%
+ \else
+ \cssDoBefore{\begin{minipage}[t][\cssHeight]{\cssWidth}}%
+ \fi
+ \cssDoBefore{\setlength{\parskip}{\@cssParSkip}\setlength{\parindent}{\@cssParIndent}}%
+ %note: DoAfter prepends, so in opposite order
+ \cssDoAfter{\end{minipage}}%
+ \fi
+ \cssDoBefore{\cssSaveFootnotes}%
+ \fi
+}
+
+\newcommand{\@robustFramebox}[2]{%
+ \eifstrequal{\cssTextAlign}{center}{\framebox[#1][c]{#2}}%
+ {\eifstrequal{\cssTextAlign}{right}{\framebox[#1][r]{#2}}%
+ {\framebox[#1][l]{#2}}}%
+}
+
+\newcommand{\@robustMakebox}[2]{%
+ \eifstrequal{\cssDisplay}{table-cell}%
+ {\@robustTableParbox{#1}{#2}}%
+ {\eifstrequal{\cssTextAlign}{center}{\makebox[#1][c]{#2}}%
+ {\eifstrequal{\cssTextAlign}{right}{\makebox[#1][r]{#2}}%
+ {\makebox[#1][l]{#2}}}}%
+}
+
+\newcommand{\@robustRaisebox}[2]{%
+ \raisebox{#1}{#2}%
+}
+
+\newcommand{\@robustHeight}[1]{%
+ \eifstrequal{\cssVerticalAlign}{top}%
+ {\raisebox{0pt}[0pt][\cssHeight]{#1}}%
+ {\eifstrequal{\cssVerticalAlign}{middle}%
+ {\raisebox{0pt}[0.5\cssHeight][0.5\cssHeight]{#1}}%
+ {\eifstrequal{\cssVerticalAlign}{baseline}%
+ {\raisebox{0pt}[\dimexpr\cssHeight-\depth\relax][\depth]{#1}}%
+ {\raisebox{0pt}[\cssHeight][0pt]{#1}}% bottom
+ }}%
+}
+
+\newcommand{\@robustTableParbox}[2]{%
+ \eifstrequal{\cssVerticalAlign}{top}{\def\@cssValign{t}}%
+ {\eifstrequal{\cssVerticalAlign}{center}{\def\@cssValign{c}}%
+ {\eifstrequal{\cssVerticalAlign}{middle}{\def\@cssValign{c}}}%
+ {\def\@cssValign{b}}}%
+ \ifdim\cssHeight=1sp%
+ \parbox[\@cssValign]{#1}{#2}%
+ \else%
+ \parbox[\@cssValign][\cssHeight]{#1}{#2}%
+ \fi%
+}
+
+\newcommand{\@cssInlinePadding}{%
+ \eifstrequal{\cssBackgroundColor}{}{}%
+ {\eifstrequal{\cssBackgroundColor}{white}{}%
+ {\strsplit{\cssBackgroundColor}%
+ \eifstrequal{\strhead}{\#}%
+ {\cssWrapCmd{\protect\colorbox[HTML]{\strtail}}}%
+ {\cssWrapCmd{\@robustColorbox{\cssBackgroundColor}}}%
+ }%
+ }%
+ \@needframefalse%
+ \eifstrequal{\cssBorderTopStyle}{none}{}{\@needframetrue}%
+ \eifstrequal{\cssBorderBottomStyle}{none}{}{\@needframetrue}%
+ \eifstrequal{\cssBorderLeftStyle}{none}{}{\@needframetrue}%
+ \eifstrequal{\cssBorderRightStyle}{none}{}{\@needframetrue}%
+ \if@needframe%
+ \setlength{\fboxrule}{\cssBorderWidth}%
+ \ifdim\cssWidth=1sp%
+ \cssWrapCmd{\fbox}%
+ \else
+ \cssWrapCmd{\@robustFramebox{\cssWidth}}%
+ \fi
+ \else
+ \ifdim\cssWidth=1sp\else
+ \cssWrapCmd{\@robustMakebox{\cssWidth}}%
+ \fi
+ \fi
+ % height?
+ \ifdim\cssHeight=1sp\else\cssWrapCmd{\@robustHeight}\fi
+ % raisebox?
+ \eifstrequal{\cssDisplay}{inline}{%
+ \eifstrequal{\cssVerticalAlign}{}{}%
+ {\eifstrequal{\cssVerticalAlign}{top}{}%
+ {\eifstrequal{\cssVerticalAlign}{bottom}{}%
+ {\eifstrequal{\cssVerticalAlign}{middle}{}%
+ {\eifstrequal{\cssVerticalAlign}{baseline}{}%
+ {\cssWrapCmd{\@robustRaisebox{\cssVerticalAlign}}%
+ }}}}}%
+ }{}%
+ % padding
+ \if@needframe
+ \setlength{\fboxsep}{\cssPaddingTop}% todo: define our own box so we can set paddingtop/bot separately
+ \ifdim\cssPaddingBottom>\fboxsep\setlength{\fboxsep}{\cssPaddingBottom}\fi
+ \ifdim\cssPaddingLeft=\fboxsep\else\hspace*{\dimexpr\cssPaddingLeft-\fboxsep\relax}\fi
+ \ifdim\cssPaddingRight=\fboxsep\else\hspace*{\dimexpr\cssPaddingRight-\fboxsep\relax}\fi
+ \else
+ \ifdim\cssPaddingLeft=0pt\else\cssDoBefore{\hspace*{\cssPaddingLeft}}\fi
+ \ifdim\cssPaddingRight=0pt\else\cssDoAfter{\hspace*{\cssPaddingRight}}\fi
+ \ifdim\cssPaddingBottom=0pt\else\cssDoBefore{\protect\rule[-\cssPaddingBottom]{0pt}{\cssPaddingBottom}}\fi
+ \ifdim\cssPaddingTop=0pt\else\cssDoBefore{\protect\rule{0pt}{\dimexpr\cssPaddingTop+0.8em\relax}}\fi
+ \fi
+}
+
+%-------------------------------------------------------------
+% css: Textalign, textindent etc
+%-------------------------------------------------------------
+
+\cssNewLengthKey{css}{text-indent}{\cssTextIndent}
+\cssNewKey{css}{text-align}{\cssTextAlign}{justify}
+\cssNewLengthKey{css}{line-height}{\cssLineHeight}
+\cssNewKey{css}{float}{\cssFloat}{}
+
+\DeclareRobustCommand{\@robustColor}[1]{%
+ \cssDefNormalizeColor{@fcolor}{#1}\color{\@fcolor}%
+}
+\DeclareRobustCommand{\@robustColorbox}[2]{%
+ \cssDefNormalizeColor{@bcolor}{#1}\colorbox{\@bcolor}{#2}%
+}
+
+
+\newcommand{\@cssProcessText}{%
+ \eifstrequal{\cssDisplay}{block}%
+ {\@cssBlockText}%
+ {\eifstrequal{\cssDisplay}{block-inline}%
+ {\@cssBlockText}%
+ {\eifstrequal{\cssDisplay}{table-cell}%
+ {\@cssBlockText}%
+ {\@cssInlineText}%
+ }}}
+
+\newcommand{\@cssBlockText}{%
+ \eifstrequal{\cssId}{}{}{\label{\cssId}}% set label
+ \eifstrequal{\cssTextAlign}{left}%
+ {\cssDoBefore{\protect\raggedright}}%
+ {\eifstrequal{\cssTextAlign}{right}%
+ {\cssDoBefore{\protect\raggedleft}}%
+ {\eifstrequal{\cssTextAlign}{center}%
+ {\cssDoBefore{\protect\centering}}%
+ {}}}%
+ \ifdim\cssLineHeight=0pt\else\setlength{\baselineskip}{\cssLineHeight}\fi
+ \noindent\ifdim\cssTextIndent=0pt\else\hspace*{\cssTextIndent}\fi
+}
+
+\newcommand{\@cssInlineText}{%
+ \eifstrequal{\cssId}{}{}{\label{\cssId}}% set label
+ \eifstrequal{\cssFloat}{left}%
+ {\cssDoAfter{\hspace*{\fill}}}%
+ {\eifstrequal{\cssFloat}{right}%
+ {\cssDoBefore{\hspace*{\fill}}}%
+ {\eifstrequal{\cssFloat}{center}%
+ {\cssDoAfter{\hspace*{\fill}}\cssDoBefore{\hspace*{\fill}}}%
+ {}}}%
+ \ifdim\cssLineHeight=0pt\else\cssDoBefore{\rule{0pt}{\cssLineHeight}}\fi
+}
+
+%-------------------------------------------------------------
+% css: Font attributes
+%-------------------------------------------------------------
+
+\cssNewKey{css}{font-weight}{\cssFontWeight}{}
+\cssNewKey{css}{font-variant}{\cssFontVariant}{}
+\cssNewKey{css}{font-style}{\cssFontStyle}{}
+\cssNewKey{css}{font-size}{\cssFontSize}{}
+\cssNewKey{css}{font-family}{\cssFontFamily}{}
+\cssNewKey{css}{color}{\cssColor}{}
+\cssNewKey{css}{penalty}{\cssPenalty}{}
+
+\newcommand{\@cssProcessFont}{%
+ % font family
+ \edef\@fontFamily{\cssFontFamily}%
+ \@for\@ii:=\cssFontFamily\do{% find the last argument in a comma separated list
+ \edef\@fontFamily{\@ii}%
+ }%
+ \eifstrequal{\@fontFamily}{}{}% quick test
+ {\eifstrequal{\@fontFamily}{monospace}%
+ {\cssDoBefore\ttfamily}%
+ {\eifstrequal{\@fontFamily}{serif}%
+ {\cssDoBefore\rmfamily}%
+ {\eifstrequal{\@fontFamily}{sans-serif}%
+ {\cssDoBefore\sffamily}%
+ {\eifstrequal{\@fontFamily}{normal}%
+ {\cssDoBefore\rmfamily}%
+ {\cssDoBefore{\fontspec{\@fontFamily}}}%
+ }}}}%
+ %
+ \eifstrequal{\cssFontWeight}{bold}%
+ {\cssDoBefore\bfseries}%
+ {\eifstrequal{\cssFontWeight}{normal}%
+ {\cssDoBefore\mdseries}%
+ {}}%
+ \eifstrequal{\cssFontVariant}{small-caps}%
+ {\cssDoBefore\scshape}%
+ {\eifstrequal{\cssFontVariant}{normal}%
+ {\cssDoBefore\upshape}%
+ {}}%
+ \eifstrequal{\cssFontStyle}{italic}%
+ {\cssDoBefore\itshape\hspace{-0.2ex}}%
+ {\eifstrequal{\cssFontStyle}{oblique}%
+ {\cssDoBefore\slshape}%
+ {\eifstrequal{\cssFontStyle}{normal}%
+ {\cssDoBefore\upshape}%
+ {}}}%
+ \eifstrequal{\cssFontSize}{}{}% quick test
+ {\eifstrequal{\cssFontSize}{xx-small}%
+ {\cssDoBefore\tiny}%
+ {\eifstrequal{\cssFontSize}{x-small}%
+ {\cssDoBefore\scriptsize}%
+ {\eifstrequal{\cssFontSize}{small}%
+ {\cssDoBefore\small}%
+ {\eifstrequal{\cssFontSize}{medium}%
+ {\cssDoBefore\normalsize}%
+ {\eifstrequal{\cssFontSize}{large}%
+ {\cssDoBefore\large}%
+ {\eifstrequal{\cssFontSize}{x-large}%
+ {\cssDoBefore\Large}%
+ {\eifstrequal{\cssFontSize}{xx-large}%
+ {\cssDoBefore\LARGE}%
+ {}}}}}}}}%
+ %
+ \eifstrequal{\cssColor}{}{}%
+ {\strsplit{\cssColor}%
+ \eifstrequal{\strhead}{\#}%
+ {\cssDoBefore{\protect\color[HTML]{\strtail}}}%
+ {\cssDoBefore{\@robustColor{\cssColor}}}%
+ }%
+ %
+ \eifstrequal{\cssPenalty}{}{}%
+ {\penalty \cssPenalty\relax}%
+}
+
+
+
+
+%-------------------------------------------------------------
+% Generic css rules for certain classes, ids, or elements
+%-------------------------------------------------------------
+\newcommand{\cssRule}[3]{%
+ \@for\@ii:=#2\do{%
+ \csappto{@rule@#1@\@ii}{,#3}%
+ }%
+}%
+
+\newcommand{\cssRuleDo}[3]{%
+ \@for\@ii:=#2\do{%
+ \csappto{@ruleDo@#1@\@ii}{#3}%
+ }%
+}%
+
+
+\newcommand{\@cssApplyRulesFor}[3]{%
+ \@for\@ii:=#3\do{%
+ \ifcsmacro{@rule@#1@\@ii}{%
+ \edef\@args{\csname @rule@#1@\@ii\endcsname}%
+ \@expandafter{\setkeys{#2}}{\@args}%
+ }{}%
+ }%
+}
+
+\newcommand{\@cssApplyDoRulesFor}[3]{%
+ \@for\@ii:=#3\do{%
+ \ifcsmacro{@ruleDo@#1@\@ii}{%
+ \csname @ruleDo@#1@\@ii\endcsname%
+ }{}%
+ }%
+}
+
+\newcommand{\cssIfHasClass}[3]{%
+ \def\@found{}%
+ \@for\@ii:=\cssClass\do{%
+ \@for\@cname:=#1\do{%
+ \eeifstrequal{\@ii}{\@cname}{%
+ \def\@found{true}%
+ }{}%
+ }%
+ }%
+ \ifdefvoid{\@found}{#3}{#2}%
+}
+
+
+\newcommand{\cssClassRule}[2]{\cssRule{class}{#1}{#2}}
+\newcommand{\cssElemRule}[2]{\cssRule{elem}{#1}{#2}}
+\newcommand{\cssIdRule}[2]{\cssRule{id}{#1}{#2}}
+
+\cssNewListKeyX{cssx}{class}{\cssClass}{}{\@cssApplyRulesFor{class}{css}}
+\cssNewKeyX{cssx}{elem}{\cssElem}{}{\@cssApplyRulesFor{elem}{css}}
+\cssNewKeyX{cssx}{id}{\cssId}{}{\@cssApplyRulesFor{id}{css}}
+
+
+\newcommand{\cssClassRuleDo}[2]{\cssRuleDo{class}{#1}{#2}}
+\newcommand{\cssClassRuleCmd}[2]{\cssClassRuleDo{#1}{\cssWrapCmd{#2}}}
+\newcommand{\cssClassRuleDoBefore}[2]{\cssClassRuleDo{#1}{\cssDoBefore{#2}}}
+\newcommand{\cssClassRuleDoAfter}[2]{\cssClassRuleDo{#1}{\cssDoAfter{#2}}}
+\newcommand{\cssClassRuleEnv}[2]{\cssClassRuleDoBefore{#1}{\begin{#2}}\cssClassRuleDoAfter{#1}{#2}}
+
+\newcommand{\cssElemRuleDo}[2]{\cssRuleDo{class}{#1}{#2}}
+\newcommand{\cssElemRuleCmd}[2]{\cssElemRuleDo{#1}{\cssWrapCmd{#2}}}
+\newcommand{\cssElemRuleDoBefore}[2]{\cssElemRuleDo{#1}{\cssDoBefore{#2}}}
+\newcommand{\cssElemRuleDoAfter}[2]{\cssElemRuleDo{#1}{\cssDoAfter{#2}}}
+\newcommand{\cssElemRuleEnv}[2]{\cssElemRuleDo{#1}{\cssDoEnv{#2}}}
+
+\newcommand{\@cssClassDoRules}{\@cssApplyDoRulesFor{class}{css}{\cssClass}}
+\newcommand{\@cssElemDoRules}{%
+ \@cssApplyDoRulesFor{elem}{css}{\cssElem}%
+ \@cssApplyDoRulesFor{id}{css}{\cssId}%
+}
+
+\newcommand{\cssParentClass}{}
+\newcommand{\cssParentClassRule}[2]{\cssRule{parentclass}{#1}{#2}}
+
+
+%-------------------------------------------------------------
+%
+%-------------------------------------------------------------
+
+\newenvironment{cssBlockX}[2]%
+ {\@cssReset\@cssUseEnv\@cssProcessAttrs{#1}{#2}%
+ \@cssElemDoRules%
+ \@cssProcessMargins\@cssProcessPadding%
+ \@cssClassDoRules%
+ \@cssProcessText\@cssProcessFont%
+ \@cssApplyBefore}%
+ {\@cssApplyAfter}
+
+\newenvironment{cssBlock}[1][]%
+ {\begin{cssBlockX}{}{#1}}{\end{cssBlockX}}
+
+\newcommand{\cssInlineX}[4]%
+ {\@cssReset\@cssUseCmd\@cssProcessAttrs{display=inline,#1}{#2}%
+ \@cssElemDoRules%
+ \@cssProcessMargins\@cssProcessPadding%
+ \@cssClassDoRules%
+ #3%
+ \@cssProcessText\@cssProcessFont%
+ \@cssApplyCmd{#4}%
+ }%
+
+\newcommand{\cssInline}[2][]{\cssInlineX{}{#1}{}{#2}}
+\newcommand{\cssInlineCmd}[3][]{\cssInlineX{}{#1}{\cssWrapCmd{#2}}{#3}}
+
+\newcommand{\cssNewBlockElem}[3]{%
+ \newenvironment{#1}[1][]{\begin{cssBlockX}{elem=#2,#3}{##1}}{\end{cssBlockX}}}
+
+\newcommand{\cssNewInlineElem}[3]{%
+ \newcommand{#1}[2][]{\cssInlineX{elem=#2,#3}{##1}{}{##2}}}
+
+
+\newcommand{\cssNewInlineElemCmd}[4]{%
+ \newcommand{#1}[2][]{\cssInlineX{elem=#2,#3}{##1}{\cssWrapCmd{#4}}{##2}}}
+
+\newcommand{\cssInitKeys}[1]{%
+ \@cssReset\@cssUseCmd\@cssProcessAttrs{display=inline}{#1}%
+}
+
+% cssText is just for font attributes; no padding or margins
+\newcommand{\cssTextX}[2]%
+ {\@cssReset\@cssUseCmd\@cssProcessAttrs{display=inline}{#1}%
+ \@cssElemDoRules%
+ %\@cssProcessMargins\@cssProcessPadding%
+ \@cssClassDoRules%
+ \@cssProcessText\@cssProcessFont%
+ \@cssApplyCmd{#2}%
+ }%
+
+
+\newcommand{\cssText}[2][]{\cssTextX{#1}{#2}}
diff --git a/Docs/DafnyRef/dafnyx.json b/Docs/DafnyRef/dafnyx.json
new file mode 100644
index 00000000..be06bfa1
--- /dev/null
+++ b/Docs/DafnyRef/dafnyx.json
@@ -0,0 +1,4 @@
+{ "name": "dafnyx",
+ "extend": "dafny",
+ "extraKeywords": ["inductive"]
+} \ No newline at end of file
diff --git a/Docs/DafnyRef/ignores.dic b/Docs/DafnyRef/ignores.dic
new file mode 100644
index 00000000..f492d28b
--- /dev/null
+++ b/Docs/DafnyRef/ignores.dic
@@ -0,0 +1,83 @@
+Dafny
+lexer
+Datatypes
+initializable
+Multisets
+multiset
+multisets
+disequality
+maplets
+maplet
+datatypes
+datatype
+datatype's
+Dafny's
+destructors
+nullable
+subarray
+indices
+mixin
+supertype
+CLU
+Async
+async
+Newtypes
+newtype
+newtype's
+pre
+BNF
+Polikarpova
+Paqui
+backticks
+colorizer
+Daan's
+Btw
+Codeplex
+formedness
+forall
+newtypes
+TODO
+updatable
+toplevel
+bodyless
+bool
+calc
+codatatype
+colemma
+comethod
+copredicate
+nat
+wildcard
+Builtin
+builtin
+inline
+NoUSIdent
+iff
+timeLimitMultiplier
+prependAssertToken
+ModuleDefinition
+AssignmentRhs
+LocalVariable
+LetExpr
+MaybeFreeExpression
+attrHelp
+EXE
+IDE
+SkippingLemma
+deconstructing
+Leino
+Moskal
+Agda
+Coq
+pointwise
+SMT
+BelowSquare
+CoFixpoint
+Copredicates
+prepending
+unrollings
+Colemmas
+Explies
+imaps
+NamedExpr
+strengthed \ No newline at end of file
diff --git a/Docs/DafnyRef/krml250.bib b/Docs/DafnyRef/krml250.bib
new file mode 100644
index 00000000..f30547f7
--- /dev/null
+++ b/Docs/DafnyRef/krml250.bib
@@ -0,0 +1,2026 @@
+@string{lncs = "LNCS"}
+
+@InCollection{Leino:Dafny:MOD2008,
+ author = {K. Rustan M. Leino},
+ title = {Specification and verification of object-oriented software},
+ booktitle = {Engineering Methods and Tools for Software Safety and Security},
+ pages = {231-266},
+ publisher = {IOS Press},
+ year = {2009},
+ editor = {Manfred Broy and Wassiou Sitou and Tony Hoare},
+ volume = {22},
+ series = {NATO Science for Peace and Security Series D: Information and Communication Security},
+ note = {Summer School Marktoberdorf 2008 lecture notes},
+}
+
+@inproceedings{Why:Platform,
+ author = {Jean-Christophe Filli{\^a}tre and Claude March{\'e}},
+ title = {The {Why}/{Krakatoa}/{Caduceus} Platform for Deductive Program Verification},
+ booktitle = {Computer Aided Verification, 19th International Conference, CAV 2007},
+ editor = {Werner Damm and Holger Hermanns},
+ volume = {4590},
+ series = lncs,
+ publisher = {Springer},
+ month = jul,
+ year = {2007},
+ pages = {173--177}
+}
+
+@InProceedings{BarrettTinelli:CVC3,
+ author = {Clark Barrett and Cesare Tinelli},
+ title = {{CVC3}},
+ booktitle = {Computer Aided Verification, 19th International Conference, CAV 2007},
+ editor = {Werner Damm and Holger Hermanns},
+ volume = {4590},
+ series = lncs,
+ publisher = {Springer},
+ month = jul,
+ year = {2007},
+ pages = {298-302},
+}
+
+@InProceedings{HubertMarche:SchorrWaite,
+ author = {Thierry Hubert and Claude March{\'e}},
+ title = {A case study of {C} source code verification: the
+ {S}chorr-{W}aite algorithm},
+ booktitle = {Third IEEE International Conference on Software
+ Engineering and Formal Methods (SEFM 2005)},
+ editor = {Bernhard K. Aichernig and Bernhard Beckert},
+ publisher = {IEEE Computer Society },
+ month = sep,
+ year = {2005},
+ pages = {190-199},
+}
+
+@Article{BroyPepper:SchorrWaite,
+ author = {Manfred Broy and Peter Pepper},
+ title = {Combining Algebraic and Algorithmic Reasoning: An
+ Approach to the {S}chorr-{W}aite Algorithm},
+ journal = toplas,
+ volume = {4},
+ number = {3},
+ month = jul,
+ year = {1982},
+ pages = {362-381},
+}
+
+@Article{MehtaNipkow:SchorrWaite,
+ author = {Farhad Mehta and Tobias Nipkow},
+ title = {Proving pointer programs in higher-order logic},
+ journal = {Information and Computation},
+ year = {2005},
+ volume = {199},
+ number = {1--2},
+ pages = {200-227},
+ month = may # "--" # jun,
+}
+
+@InProceedings{BallEtAll:ScalableChecking,
+ author = {Thomas Ball and Brian Hackett and Shuvendu K. Lahiri
+ and Shaz Qadeer and Julien Vanegue},
+ title = {Towards Scalable Modular Checking of User-Defined Properties},
+ booktitle = {Verified Software: Theories, Tools, Experiments,
+ (VSTTE 2010)},
+ editor = {Gary T. Leavens and Peter O'Hearn and Sriram K. Rajamani},
+ volume = {6217},
+ series = lncs,
+ publisher = {Springer},
+ month = aug,
+ year = {2010},
+ pages = {1-24},
+}
+
+@InProceedings{RegisGianasPottier:FunctionalHoare,
+ author = {Yann R{\'e}gis-Gianas and Fran{\,c}ois Pottier},
+ title = {A {H}oare Logic for Call-by-Value Functional Programs},
+ booktitle = {Mathematics of Program Construction, 9th International Conference, MPC 2008},
+ pages = {305-335},
+ year = {2008},
+ editor = {Philippe Audebaud and Christine Paulin-Mohring},
+ volume = {5133},
+ series = lncs,
+ month = jul,
+ publisher = {Springer},
+}
+
+@InProceedings{VeanesEtAl:SpecExplorer,
+ author = {Margus Veanes and Colin Campbell and Wolfgang
+ Grieskamp and Wolfram Schulte and Nikolai Tillmann
+ and Lev Nachmanson},
+ title = {Model-Based Testing of Object-Oriented Reactive
+ Systems with {Spec} {Explorer}},
+ booktitle = {Formal Methods and Testing},
+ pages = {39-76},
+ year = {2008},
+ editor = {Robert M. Hierons and Jonathan P. Bowen and Mark Harman},
+ volume = {4949},
+ series = lncs,
+ publisher = {Springer},
+}
+
+@book{Dijkstra:Discipline,
+ author = "Edsger W. Dijkstra",
+ title = "A Discipline of Programming",
+ publisher = "Prentice Hall",
+ address = "Englewood Cliffs, NJ",
+ year = 1976
+}
+
+@InProceedings{LeinoMueller:ESOP2009,
+ author = {K. Rustan M. Leino and Peter M{\"u}ller},
+ title = {A Basis for Verifying Multi-threaded Programs},
+ booktitle = {Programming Languages and Systems, 18th European
+ Symposium on Programming, ESOP 2009},
+ editor = {Giuseppe Castagna},
+ volume = {5502},
+ series = lncs,
+ publisher = {Springer},
+ month = mar,
+ year = 2009,
+ pages = {378-393},
+}
+
+@InProceedings{LeinoRuemmer:Boogie2,
+ author = {K. Rustan M. Leino and Philipp R{\"u}mmer},
+ title = {A Polymorphic Intermediate Verification Language:
+ Design and Logical Encoding},
+ booktitle = {Tools and Algorithms for the Construction and
+ Analysis of Systems, 16th International Conference,
+ TACAS 2010},
+ editor = {Javier Esparza and Rupak Majumdar},
+ series = lncs,
+ volume = 6015,
+ publisher = {Springer},
+ month = mar,
+ year = 2010,
+ pages = {312-327},
+}
+
+@book{LiskovGuttag:book,
+ author = "Barbara Liskov and John Guttag",
+ title = "Abstraction and Specification in Program Development",
+ publisher = "MIT Press",
+ series = "MIT Electrical Engineering and Computer Science Series",
+ year = 1986
+}
+
+@TechReport{DahlEtAl:Simula67,
+ author = {Ole-Johan Dahl and Bj{\o}rn Myhrhaug and Kristen Nygaard},
+ title = {Common Base Language},
+ institution = {Norwegian Computing Center},
+ type = {Publication},
+ number = {S-22},
+ month = oct,
+ year = 1970,
+}
+
+@inproceedings{LeinoMueller:ModelFields,
+ author = {K. Rustan M. Leino and
+ Peter M{\"u}ller},
+ title = {A Verification Methodology for Model Fields},
+ booktitle = "Programming Languages and Systems, 15th European Symposium on Programming, ESOP 2006",
+ editor = "Peter Sestoft",
+ series = lncs,
+ volume = 3924,
+ publisher = "Springer",
+ month = mar,
+ year = 2006,
+ pages = {115-130},
+}
+
+@InProceedings{CarterEtAl:UsingPerfectDeveloper,
+ author = {Gareth Carter and Rosemary Monahan and Joseph M. Morris},
+ title = {Software Refinement with {P}erfect {D}eveloper},
+ booktitle = {Third IEEE International Conference on Software
+ Engineering and Formal Methods (SEFM 2005)},
+ pages = {363-373},
+ editor = {Bernhard K. Aichernig and Bernhard Beckert},
+ month = sep,
+ year = {2005},
+ publisher = {IEEE Computer Society},
+}
+
+@InProceedings{Abrial:SchorrWaite,
+ author = {Jean-Raymond Abrial},
+ title = {Event Based Sequential Program Development:
+ Application to Constructing a Pointer Program},
+ booktitle = {FME 2003: Formal Methods, International Symposium of
+ Formal Methods Europe},
+ editor = {Keijiro Araki and Stefania Gnesi and Dino Mandrioli},
+ volume = {2805},
+ series = lncs,
+ publisher = {Springer},
+ month = sep,
+ year = {2003},
+ pages = {51-74},
+}
+
+@article{Barnett-etal04,
+ author = {Mike Barnett and Robert DeLine and Manuel F{\"a}hndrich and
+ K. Rustan M. Leino and Wolfram Schulte},
+ title = {Verification of Object-Oriented Programs with Invariants},
+ journal = {Journal of Object Technology},
+ volume = 3,
+ number = 6,
+ year = 2004,
+ pages = {27-56},
+}
+
+@InProceedings{SmansEtAl:ImplicitDynamicFrames,
+ author = {Jan Smans and Bart Jacobs and Frank Piessens},
+ title = {Implicit Dynamic Frames: Combining Dynamic Frames
+ and Separation Logic},
+ booktitle = {ECOOP 2009 --- Object-Oriented Programming, 23rd
+ European Conference},
+ editor = {Sophia Drossopoulou},
+ volume = {5653},
+ series = lncs,
+ publisher = {Springer},
+ month = jul,
+ year = {2009},
+ pages = {148-172},
+}
+
+@inproceedings{GriesPrins:Encapsulation,
+ author = "David Gries and Jan Prins",
+ title = "A New Notion of Encapsulation",
+ booktitle = "Proceedings of the {ACM} {SIGPLAN} 85
+ Symposium on Language Issues in Programming Environments",
+ publisher = "ACM",
+ series = "SIGPLAN Notices 20",
+ number = 7,
+ month = jul,
+ year = 1985,
+ pages = "131-139"
+}
+
+@InProceedings{YangHawblitzel:Verve,
+ author = {Jean Yang and Chris Hawblitzel},
+ title = {Safe to the last instruction: automated verification of a type-safe operating system},
+ booktitle = {Proceedings of the 2010 ACM SIGPLAN Conference on
+ Programming Language Design and Implementation, PLDI
+ 2010},
+ editor = {Benjamin G. Zorn and Alexander Aiken},
+ month = jun,
+ year = {2010},
+ publisher = {ACM},
+ pages = {99-110},
+}
+
+@Book{BoyerMoore:book,
+ author = {Robert S. Boyer and J Strother Moore},
+ title = {A Computational Logic},
+ publisher = {Academic Press},
+ series = {ACM Monograph Series},
+ year = {1979},
+}
+
+@article{HoareWirth:Pascal,
+ author = "C. A. R. Hoare and N. Wirth",
+ title = "An axiomatic definition of the programming language {PASCAL}",
+ journal = acta,
+ volume = 2,
+ number = 4,
+ year = 1973,
+ pages = "335-355"
+}
+
+@article{Hoare:AxiomaticBasis,
+ author = "C. A. R. Hoare",
+ title = "An axiomatic basis for computer programming",
+ journal = cacm,
+ volume = 12,
+ number = 10,
+ year = 1969,
+ month = oct,
+ pages = "576--580,583"
+}
+
+@InProceedings{LeinoMoskal:vacid0-notYetConfirmed,
+ author = {K. Rustan M. Leino and Micha{\l} Moskal},
+ title = {{VACID-0}: {V}erification of {A}mple {C}orrectness
+ of {I}nvariants of {D}ata-structures, Edition 0},
+ booktitle = {VS-Tools & Experiments},
+ year = 2010,
+ editor = {Rajeev Joshi and Tiziana Margaria and Peter
+ M{\"u}ller and David Naumann and Hongseok Yang},
+ series = {VSTTE 2010 Workshop Proceedings},
+ publisher = {ETH Zurich Technical Report 676},
+ month = aug,
+}
+
+@InCollection{Chalice:tutorial,
+ author = {K. Rustan M. Leino and Peter M{\"u}ller and Jan Smans},
+ title = {Verification of Concurrent Programs with {C}halice},
+ booktitle = {Foundations of Security Analysis and Design {V}: {FOSAD} 2007/2008/2009 Tutorial Lectures},
+ editor = {Alessandro Aldini and Gilles Barthe and Roberto Gorrieri},
+ volume = {5705},
+ series = lncs,
+ publisher = {Springer},
+ year = {2009},
+ pages = {195-222}
+}
+
+@inproceedings{LeinoMuellerSmans10,
+ author = {K. Rustan M. Leino and Peter M{\"u}ller and Jan Smans},
+ title = {Deadlock-Free Channels and Locks},
+ booktitle = {Programming Languages and Systems, 19th European Symposium on Programming, ESOP 2010},
+ editor = {Andrew D. Gordon},
+ volume = {6012},
+ series = lncs,
+ publisher = {Springer},
+ month = mar,
+ year = {2010},
+ pages = {407-426}
+}
+
+@Book{BundyEtAl:Rippling,
+ author = {Alan Bundy and David Basin and Dieter Hutter and Andrew Ireland},
+ title = {Rippling: Meta-level Guidance for Mathematical Reasoning},
+ publisher = {Cambridge University Press},
+ volume = {56},
+ series = {Cambridge Tracts in Theoretical Computer Science},
+ year = {2005},
+}
+
+@book{Gries:Science,
+ author = "David Gries",
+ title = "The Science of Programming",
+ publisher = "Springer-Verlag",
+ series = "Texts and Monographs in Computer Science",
+ year = 1981
+}
+
+@Book{DijkstraFeijen:Book,
+ author = "Edsger W. Dijkstra and W. H. J. Feijen",
+ title = "A Method of Programming",
+ publisher = "Addison-Wesley",
+ month = jul,
+ year = 1988,
+}
+
+@book{Kaldewaij:Programming,
+ author = "Anne Kaldewaij",
+ title = "Programming: The Derivation of Algorithms",
+ publisher = "Prentice-Hall International",
+ year = 1990,
+ series = "Series in Computer Science",
+}
+
+@InProceedings{LeinoMonahan:VSTTE2010,
+ author = {K. Rustan M. Leino and Rosemary Monahan},
+ title = {Dafny Meets the Verification Benchmarks Challenge},
+ booktitle = {Verified Software: Theories, Tools, Experiments,
+ Third International Conference, VSTTE 2010},
+ pages = {112-126},
+ year = {2010},
+ editor = {Gary T. Leavens and Peter W. O'Hearn and Sriram K. Rajamani},
+ volume = {6217},
+ series = lncs,
+ month = aug,
+ publisher = {Springer},
+}
+
+@InProceedings{VSComp2010:report,
+ author = {Vladimir Klebanov and Peter M{\"u}ller and Natarajan Shankar and
+ Gary T. Leavens and Valentin W{\"u}stholz and Eyad Alkassar and
+ Rob Arthan and Derek Bronish and Rod Chapman and Ernie Cohen and
+ Mark Hillebrand and Bart Jacobs and K. Rustan M. Leino and
+ Rosemary Monahan and Frank Piessens and Nadia Polikarpova and
+ Tom Ridge and Jan Smans and Stephan Tobies and Thomas Tuerk and
+ Mattias Ulbrich and Benjamin Wei{\ss}},
+ title = {The 1st Verified Software Competition: Experience Report},
+ booktitle = {FM 2011: Formal Methods --- 17th International
+ Symposium on Formal Methods},
+ pages = {154-168},
+ year = {2011},
+ editor = {Michael Butler and Wolfram Schulte},
+ volume = {6664},
+ series = lncs,
+ month = jun,
+ publisher = {Springer},
+}
+
+@InProceedings{Leino:Dafny:LPAR16,
+ author = {K. Rustan M. Leino},
+ title = {Dafny: An Automatic Program Verifier for Functional Correctness},
+ booktitle = {LPAR-16},
+ year = {2010},
+ volume = {6355},
+ series = lncs,
+ publisher = {Springer},
+ pages = {348-370},
+}
+
+@book{BackVonWright:Book,
+ author = "Ralph-Johan Back and von Wright, Joakim",
+ title = "Refinement Calculus: A Systematic Introduction",
+ series = "Graduate Texts in Computer Science",
+ publisher = "Springer-Verlag",
+ year = 1998
+}
+
+@Article{BalzerCheathamGreen:1990s,
+ author = {Robert Balzer and {Cheatham, Jr.}, Thomas E. and Cordell Green},
+ title = {Software Technology in the 1990's: Using a New Paradigm},
+ journal = {IEEE Computer},
+ year = {1983},
+ volume = {16},
+ number = {11},
+ pages = {39-45 },
+ month = nov,
+}
+
+@InProceedings{Zloof:QBE,
+ author = {Mosh{\'e} M. Zloof},
+ title = {Query by Example},
+ booktitle = {American Federation of Information Processing
+ Societies: 1975 National Computer Conference},
+ pages = {431-438},
+ year = {1975},
+ month = may,
+ publisher = {AFIPS Press },
+}
+
+@InProceedings{HarrisGulwani:PLDI2011,
+ author = {William R. Harris and Sumit Gulwani},
+ title = {Spreadsheet table transformations from examples},
+ booktitle = {Proceedings of the 32nd ACM SIGPLAN Conference on
+ Programming Language Design and Implementation, PLDI
+ 2011},
+ pages = {317-328},
+ year = {2011},
+ editor = {Mary W. Hall and David A. Padua},
+ month = jun,
+ publisher = {ACM},
+}
+
+@Article{Smith:KIDS-overview,
+ author = "Douglas R. Smith",
+ title = "{KIDS}: A Semi-Automatic Program Development System",
+ journal = {IEEE Transactions on Software Engineering },
+ volume = 16,
+ number = 9,
+ month = sep,
+ year = 1990,
+ pages = "1024-1043",
+}
+
+@Article{RodinToolset,
+ author = {Jean-Raymond Abrial and Michael Butler and Stefan
+ Hallerstede and Thai Son Hoang and Farhad Mehta and
+ Laurent Voisin},
+ title = {Rodin: An Open Toolset for Modelling and Reasoning in {Event-B}},
+ journal = {International Journal on Software Tools for Technology Transfer},
+ year = {2010},
+ month = apr,
+}
+
+@Article{Summers:LISP-from-examples,
+ author = {Phillip D. Summers},
+ title = {A Methodology for {LISP} Program Construction from Examples},
+ journal = jacm,
+ year = {1977},
+ volume = {24},
+ number = {1},
+ pages = {161-175},
+ month = jan,
+}
+
+@InProceedings{Pex:overview,
+ author = {Nikolai Tillmann and de Halleux, Jonathan},
+ title = {Pex---White Box Test Generation for {.NET}},
+ booktitle = {Tests and Proofs, Second International Conference, TAP 2008},
+ pages = {134-153},
+ year = {2008},
+ editor = {Bernhard Beckert and Reiner H{\"a}hnle},
+ series = lncs,
+ volume = {4966},
+ month = apr,
+ publisher = {Springer},
+}
+
+@InProceedings{GodefroidKlarlundSen:DART,
+ author = {Patrice Godefroid and Nils Klarlund and Koushik Sen},
+ title = {{DART}: directed automated random testing},
+ booktitle = {Proceedings of the ACM SIGPLAN 2005 Conference on
+ Programming Language Design and Implementation},
+ pages = {213-223},
+ year = {2005},
+ editor = {Vivek Sarkar and Mary W. Hall},
+ month = jun,
+ publisher = {ACM},
+}
+
+@PhdThesis{Monahan:thesis,
+ author = {Rosemary Monahan},
+ title = {Data Refinement in Object-Oriented Verification},
+ school = {Dublin City University},
+ year = {2010},
+}
+
+@InProceedings{Denali:pldi2002,
+ author = {Rajeev Joshi and Greg Nelson and Keith H. Randall},
+ title = {Denali: A Goal-directed Superoptimizer},
+ booktitle = {Proceedings of the 2002 ACM SIGPLAN Conference on
+ Programming Language Design and Implementation
+ (PLDI)},
+ pages = {304-314},
+ year = {2002},
+ month = jun,
+ publisher = {ACM},
+}
+@Book{SETL,
+ author = {J. T. Schwartz and R. B. K. Dewar and E. Dubinsky and E. Schonberg},
+ title = {Programming with Sets: An Introduction to {SETL}},
+ series = {Texts and Monographs in Computer Science},
+ publisher = {Springer},
+ year = {1986},
+}
+
+@InProceedings{KuncakEtAl:PLDI2010,
+ author = {Viktor Kuncak and Mika{\"e}l Mayer and Ruzica Piskac
+ and Philippe Suter},
+ title = {Complete functional synthesis},
+ booktitle = {Proceedings of the 2010 ACM SIGPLAN Conference on
+ Programming Language Design and Implementation, PLDI
+ 2010},
+ pages = {316-329},
+ year = {2010},
+ editor = {Benjamin G. Zorn and Alexander Aiken},
+ month = jun,
+ publisher = {ACM},
+}
+
+@Article{JML:ToolSuite:STTT,
+ author = {Lilian Burdy and Yoonsik Cheon and David R. Cok and
+ Michael D. Ernst and Joseph R. Kiniry and Gary T. Leavens and
+ K. Rustan M. Leino and Erik Poll},
+ title = {An overview of {JML} tools and applications},
+ journal = {International Journal on Software Tools
+ for Technology Transfer},
+ volume = 7,
+ number = 3,
+ publisher = {Springer},
+ month = jun,
+ year = 2005,
+ pages = {212-232},
+}
+
+@InProceedings{Green:ProblemSolving,
+ author = {Cordell Green},
+ title = {Application of Theorem Proving to Problem Solving},
+ booktitle = {Proceedings of the 1st International Joint Conference on Artificial Intelligence},
+ editor = {Donald E. Walker and Lewis M. Norton},
+ pages = {219-240},
+ year = {1969},
+ month = may,
+ publisher = {William Kaufmann},
+}
+
+@Article{MannaWaldinger:CACM1971,
+ author = {Zohar Manna and Richard J. Waldinger},
+ title = {Towards automatic program synthesis},
+ journal = cacm,
+ year = {1971},
+ volume = {14},
+ number = {3},
+ pages = {151-165},
+ month = mar,
+}
+
+@Article{RichWaters:ProgAppren,
+ author = {Charles Rich and Richard C. Waters},
+ title = {The {P}rogrammer's {A}pprentice: A Research Overview},
+ journal = {IEEE Computer},
+ year = {1988},
+ volume = {21},
+ number = {11},
+ pages = {10-25},
+ month = nov,
+}
+
+@InProceedings{Green:PSI,
+ author = {Cordell Green},
+ title = {The Design of the {PSI} Program Synthesis System},
+ booktitle = {Proceedings of the 2nd International Conference on Software Engineering},
+ pages = {4-18},
+ year = {1976},
+ month = oct,
+ publisher = {IEEE Computer Society},
+}
+
+@Article{SpecSharp:Retrospective:CACM,
+ author = {Mike Barnett and Manuel F{\"a}hndrich and
+ K. Rustan M. Leino and Peter M{\"u}ller and
+ Wolfram Schulte and Herman Venter},
+ title = {Specification and Verification: The {Spec\#} Experience},
+ journal = cacm,
+ volume = {54},
+ number = {6},
+ pages = {81-91},
+ month = jun,
+ year = 2011,
+}
+
+@article{Filipovic:SepLogicRefinement,
+ author = {Ivana Filipovi{\'c} and Peter O'Hearn and
+ Noah Torp-Smith and Hongseok Yang},
+ title = {Blaming the client: on data refinement in the presence of pointers},
+ journal = {Formal Aspects of Computing},
+ volume = {22},
+ number = {5},
+ month = sep,
+ year = {2010},
+ pages = {547-583},
+}
+
+@inproceedings{Grandy:JavaRefinement,
+ author = {Grandy, Holger and Stenzel, Kurt and Reif, Wolfgang},
+ title = {A refinement method for {J}ava programs},
+ booktitle = {Formal Methods for Open Object-Based Distributed Systems, 9th IFIP WG 6.1 International Conference, FMOODS 2007},
+ editor = {Marcello M. Bonsangue and Einar Broch Johnsen},
+ series = lncs,
+ number = {4468},
+ month = jun,
+ year = {2007},
+ publisher = {Springer},
+ pages = {221--235},
+}
+
+@InCollection{KoenigLeino:MOD2011,
+ author = {Jason Koenig and K. Rustan M. Leino},
+ title = {Getting Started with {D}afny: A Guide},
+ booktitle = {Software Safety and Security: Tools for Analysis and Verification},
+ pages = {152-181},
+ publisher = {IOS Press},
+ year = {2012},
+ editor = {Tobias Nipkow and Orna Grumberg and Benedikt Hauptmann},
+ volume = {33},
+ series = {NATO Science for Peace and Security Series D: Information and Communication Security},
+ note = {Summer School Marktoberdorf 2011 lecture notes},
+}
+
+@InProceedings{VonWright:ExtendingWindowInference,
+ author = {von Wright, Joakim},
+ title = {Extending Window Inference},
+ booktitle = {Theorem Proving in Higher Order Logics, 11th International Conference, TPHOLs'98},
+ pages = {17-32},
+ year = {1998},
+ editor = {Jim Grundy and Malcolm C. Newey},
+ volume = {1479},
+ series = lncs,
+ publisher = {Springer},
+}
+
+@InProceedings{BauerWenzel:IsarExperience,
+ author = {Gertrud Bauer and Markus Wenzel},
+ title = {Calculational reasoning revisited: an {I}sabelle/{I}sar experience},
+ booktitle = {Theorem Proving in Higher Order Logics, 14th International Conference, TPHOLs 2001},
+ pages = {75-90},
+ year = {2001},
+ editor = {Richard J. Boulton and Paul B. Jackson},
+ volume = {2152},
+ series = lncs,
+ month = sep,
+ publisher = {Springer},
+}
+
+@InProceedings{Leino:induction,
+ author = {K. Rustan M. Leino},
+ title = {Automating Induction with an {SMT} Solver},
+ booktitle = {VMCAI 2012},
+ pages = {315-331},
+ year = {2012},
+ volume = {7148},
+ series = lncs,
+ month = jan,
+ publisher = {Springer},
+}
+
+@InProceedings{LGLM:BVD,
+ author = {Le Goues, Claire and K. Rustan M. Leino and Micha{\l} Moskal},
+ title = {The {B}oogie {V}erification {D}ebugger (Tool Paper)},
+ booktitle = {Software Engineering and Formal Methods --- 9th International Conference, SEFM 2011},
+ pages = {407-414},
+ year = {2011},
+ editor = {Gilles Barthe and Alberto Pardo and Gerardo Schneider},
+ volume = {7041},
+ series = lncs,
+ month = nov,
+ publisher = {Springer},
+}
+
+@InProceedings{Filliatre:2lines,
+ author = {Jean-Christophe Filli{\^a}tre},
+ title = {Verifying two lines of {C} with {Why3}: an exercise in
+ program verification},
+ booktitle = {Verified Software: Theories, Tools, Experiments ---
+ 4th International Conference, VSTTE 2012},
+ pages = {83-97},
+ year = {2012},
+ editor = {Rajeev Joshi and Peter M{\"u}ller and Andreas Podelski},
+ volume = {7152},
+ series = lncs,
+ month = jan,
+ publisher = {Springer},
+}
+
+@InCollection{LeinoMoskal:UsableProgramVerification,
+ author = {K. Rustan M. Leino and Micha{\l} Moskal},
+ title = {Usable Auto-Active Verification},
+ booktitle = {UV10 (Usable Verification) workshop},
+ year = {2010},
+ editor = {Tom Ball and Lenore Zuck and N. Shankar},
+ month = nov,
+ publisher = {\url{http://fm.csl.sri.com/UV10/}},
+}
+
+@InProceedings{LeinoMonahan:Comprehensions,
+ author = {K. Rustan M. Leino and Rosemary Monahan},
+ title = {Reasoning about Comprehensions with First-Order {SMT} Solvers},
+ booktitle = {Proceedings of the 2009 ACM Symposium on Applied Computing (SAC)},
+ editor = {Sung Y. Shin and Sascha Ossowski},
+ publisher = {ACM},
+ month = mar,
+ year = 2009,
+ pages = {615-622},
+}
+
+@TechReport{VeriFast:TR,
+ author = {Bart Jacobs and Frank Piessens},
+ title = {The {VeriFast} program verifier},
+ institution = {Dept. of Computer Science, Katholieke Universiteit Leuven},
+ year = {2008},
+ number = {CW-520},
+}
+
+@book{DijkstraScholten:book,
+ author = "Edsger W. Dijkstra and Carel S. Scholten",
+ title = "Predicate Calculus and Program Semantics",
+ publisher = "Springer-Verlag",
+ series = "Texts and Monographs in Computer Science",
+ year = 1990
+}
+
+@Book{Coq:book,
+ author = {Yves Bertot and Pierre Cast{\'e}ran},
+ title = {{C}oq'{A}rt: The Calculus of Inductive Constructions},
+ publisher = {Springer},
+ year = {2004},
+ series = {Texts in Theoretical Comp. Sci.},
+}
+
+@Book{ACL2:book,
+ author = {Matt Kaufmann and Panagiotis Manolios and J Strother Moore},
+ title = {Computer-Aided Reasoning: An Approach},
+ publisher = {Kluwer Academic Publishers},
+ year = {2000},
+}
+
+@InProceedings{Coq:Coinduction,
+ author = {Eduardo Gim{\'e}nez},
+ title = {An Application of Co-inductive Types in {Coq}: Verification of the Alternating Bit Protocol},
+ booktitle = {Types for Proofs and Programs, International Workshop TYPES'95},
+ pages = {135-152},
+ year = {1996},
+ editor = {Stefano Berardi and Mario Coppo},
+ volume = 1158,
+ series = lncs,
+ publisher = {Springer},
+}
+
+@InCollection{JacobsRutten:IntroductionCoalgebra,
+ author = {Bart Jacobs and Jan Rutten},
+ title = {An Introduction to (Co)Algebra and (Co)Induction},
+ booktitle = {Advanced Topics in Bisimulation and Coinduction},
+ series = {Cambridge Tracts in Theoretical Comp. Sci.},
+ number = {52},
+ publisher = {Cambridge Univ. Press},
+ year = {2011},
+ pages = {38-99},
+}
+
+@InProceedings{SonnexEtAl:Zeno,
+ author = {William Sonnex and Sophia Drossopoulou and Susan Eisenbach},
+ title = {Zeno: An Automated Prover for Properties of Recursive
+ Data Structures},
+ booktitle = {Tools and Algorithms for the Construction and Analysis of
+ Systems --- 18th International Conference, TACAS 2012},
+ editor = {Cormac Flanagan and Barbara K{\"o}nig},
+ volume = {7214},
+ series = lncs,
+ year = {2012},
+ month = mar # "--" # apr,
+ publisher = {Springer},
+ pages = {407-421},
+}
+
+@InProceedings{JohanssonEtAl:IPT2010,
+ author = {Moa Johansson and Lucas Dixon and Alan Bundy},
+ title = {Case-Analysis for {R}ippling and Inductive Proof},
+ booktitle = {Interactive Theorem Proving, First International Conference, ITP 2010},
+ editor = {Matt Kaufmann and Lawrence C. Paulson},
+ volume = {6172},
+ series = lncs,
+ publisher = {Springer},
+ month = jul,
+ year = {2010},
+ pages = {291-306},
+}
+
+@Article{HatcliffEtAl:BISL,
+ author = {John Hatcliff and Gary T. Leavens and
+ K. Rustan M. Leino and Peter M{\"u}ller and Matthew Parkinson},
+ title = {Behavioral interface specification languages},
+ journal = {ACM Computing Surveys},
+ volume = {44},
+ number = {3},
+ note = {Article 16},
+ month = jun,
+ year = {2012},
+}
+
+@InProceedings{BoehmeNipkow:Sledgehammer,
+ author = {Sascha B{\"o}hme and Tobias Nipkow},
+ title = {Sledgehammer: {J}udgement {D}ay},
+ booktitle = {Automated Reasoning, 5th International Joint Conference, IJCAR 2010},
+ editor = {J{\"u}rgen Giesl and Reiner H{\"a}hnle},
+ year = {2010},
+ pages = {107-121},
+ volume = {6173},
+ series = lncs,
+ month = jul,
+ publisher = {Springer},
+}
+
+@InProceedings{Dafny:LASER2011,
+ author = {Luke Herbert and K. Rustan M. Leino and Jose Quaresma},
+ title = {Using {Dafny}, an Automatic Program Verifier},
+ booktitle = {Tools for Practical Software Verification, {LASER}, International Summer School 2011},
+ editor = {Bertrand Meyer and Martin Nordio},
+ volume = {7682},
+ series = lncs,
+ year = {2012},
+ pages = {156-181},
+ publisher = {Springer},
+}
+
+@Article{Leroy:CompCert:CACM,
+ author = {Xavier Leroy},
+ title = {Formal verification of a realistic compiler},
+ journal = cacm,
+ volume = {52},
+ number = {7},
+ year = {2009},
+ pages = {107-115},
+}
+
+@InProceedings{Leino:ITP2013,
+ author = {K. Rustan M. Leino},
+ title = {Automating Theorem Proving with {SMT}},
+ booktitle = {ITP 2013},
+ year = {2013},
+ volume = {7998},
+ series = lncs,
+ pages = {2-16},
+ month = jul,
+ publisher = {Springer},
+}
+
+@techreport{Nelson:thesis,
+ author = "Charles Gregory Nelson",
+ title = "Techniques for Program Verification",
+ institution = "Xerox PARC",
+ month = jun,
+ year = 1981,
+ number = "CSL-81-10",
+ note = "The author's PhD thesis"
+}
+
+@InProceedings{LernerMillsteinChambers:VerifiedOptimizations,
+ author = {Sorin Lerner and Todd Millstein and Craig Chambers},
+ title = {Automatically proving the correctness of compiler optimizations},
+ booktitle = {Proceedings of the ACM SIGPLAN 2003 Conference on
+ Programming Language Design and Implementation 2003},
+ year = {2003},
+ editor = {Ron Cytron and Rajiv Gupta},
+ pages = {220-231},
+ month = jun,
+ publisher = {ACM},
+}
+
+@InProceedings{BoyerHunt:ACL2,
+ author = {Robert S. Boyer and Hunt, Jr., Warren A.},
+ title = {Function Memoization and Unique Object Representation for {ACL2} Functions},
+ booktitle = {Proceedings of the Sixth International Workshop on
+ the ACL2 Theorem Prover and its Applications, ACL2 2006},
+ editor = {Panagiotis Manolios and Matthew Wilding},
+ month = aug,
+ year = {2006},
+ pages = {81--89},
+ publisher = {ACM},
+}
+
+@inproceedings{LeinoWuestholz:DafnyIDE,
+ author = {K. Rustan M. Leino and
+ Valentin W{\"{u}}stholz},
+ title = {The {D}afny Integrated Development Environment},
+ booktitle = {Proceedings 1st Workshop on Formal Integrated Development Environment,
+ {F-IDE} 2014},
+ month = apr,
+ year = {2014},
+ pages = {3--15},
+ editor = {Catherine Dubois and
+ Dimitra Giannakopoulou and
+ Dominique M{\'{e}}ry},
+ series = {{EPTCS}},
+ volume = {149},
+}
+
+@inproceedings{BarnettLeino:Weakest,
+ author = {Mike Barnett and K. Rustan M. Leino},
+ title = {Weakest-precondition of unstructured programs},
+ booktitle = {Proceedings of the 2005 ACM SIGPLAN-SIGSOFT Workshop on
+ Program Analysis For Software Tools and Engineering,
+ PASTE'05},
+ editor = {Michael D. Ernst and Thomas P. Jensen},
+ month = sep,
+ year = {2005},
+ pages = {82-87},
+ publisher = {ACM},
+}
+
+@InProceedings{AutoProof:TACAS2015,
+ author = {Julian Tschannen and Carlo A. Furia and Martin Nordio and Nadia Polikarpova},
+ title = {{AutoProof}: Auto-Active Functional Verification of Object-Oriented Programs},
+ booktitle = {Tools and Algorithms for the Construction and
+ Analysis of Systems --- 21st International Conference,
+ TACAS 2015},
+ OPTyear = {2015},
+ editor = {Christel Baier and Cesare Tinelli},
+ volume = {9035},
+ series = lncs,
+ pages = {566-580},
+ month = apr,
+ publisher = {Springer},
+}
+
+@Article{Doyle:TMS,
+ author = {Jon Doyle},
+ title = {A Truth Maintenance System},
+ journal = {Artificial Intelligence},
+ year = {1979},
+ month = nov,
+ volume = {12},
+ number = {3},
+ pages = {231-272},
+}
+
+@InProceedings{LeinoMueller:SpecSharp:Tutorial,
+ author = {K. Rustan M. Leino and Peter M{\"u}ller},
+ title = {Using the {Spec\#} Language, Methodology, and Tools to Write Bug-Free Programs},
+ booktitle = {LASER Summer School 2007/2008},
+ editor = {Peter M{\"u}ller},
+ series = lncs,
+ volume = 6029,
+ year = 2010,
+ publisher = {Springer},
+ pages = {91-139},
+}
+
+@inproceedings{TFNP-TACAS15,
+ author = {Julian Tschannen and Carlo A. Furia and Martin Nordio and Nadia Polikarpova},
+ title = {{AutoProof}: Auto-active Functional Verification of Object-oriented Programs},
+ booktitle = {Tools and Algorithms for the Construction and Analysis of Systems --- 21st International Conference, TACAS 2015},
+ editor = {Christel Baier and Cesare Tinelli},
+ series = lncs,
+ volume = {9035},
+ month = apr,
+ year = {2015},
+ publisher = {Springer},
+ pages = {566-580},
+}
+
+@inproceedings{PTFM-FM14,
+ author = {Nadia Polikarpova and Julian Tschannen and Carlo A. Furia and Bertrand Meyer},
+ title = {Flexible Invariants Through Semantic Collaboration},
+ booktitle = {FM 2014},
+ series = lncs,
+ volume = {8442},
+ publisher = {Springer},
+ month = may,
+ year = {2014},
+ pages = {514-530}
+}
+
+@InProceedings{VeriFast:Java:tutorial,
+ author = {Jan Smans and Bart Jacobs and Frank Piessens},
+ title = {{VeriFast} for {J}ava: A Tutorial},
+ booktitle = {Aliasing in Object-Oriented Programming. Types, Analysis and Verification},
+ year = {2013},
+ editor = {Dave Clarke and James Noble and Tobias Wrigstad},
+ volume = {7850},
+ series = lncs,
+ pages = {407-442},
+ publisher = {Springer},
+}
+
+@InProceedings{Traits:ECOOP2003,
+ author = {Nathanael Sch{\"a}rli and St{\'e}phane Ducasse and Oscar Nierstrasz and Andrew P. Black},
+ title = {Traits: Composable Units of Behaviour},
+ booktitle = {ECOOP 2003 --- Object-Oriented Programming, 17th European Conference},
+ editor = {Luca Cardelli},
+ series = lncs,
+ volume = {2743},
+ pages = {248-274},
+ month = jul,
+ year = {2003},
+ publisher = {Springer},
+}
+
+@Article{Traits:logic,
+ author = {Ferruccio Damiani and Johan Dovland and Einar Broch Johnsen and Ina Schaefer},
+ title = {Verifying traits: an incremental proof system for fine-grained reuse},
+ journal = {Formal Aspects of Computing},
+ volume = {26},
+ number = {4},
+ pages = {761-793},
+ month = jul,
+ year = {2014},
+}
+
+@inproceedings{LeinoPolikarpova:calc,
+ author = {K. Rustan M. Leino and
+ Nadia Polikarpova},
+ title = {Verified Calculations},
+ booktitle = {VSTTE 2013},
+ series = lncs,
+ volume = 8164,
+ year = {2014},
+ pages = {170-190},
+ publisher = {Springer},
+}
+
+@Article{LeinoYessenov:ChaliceRefinement,
+ author = {K. Rustan M. Leino and Kuat Yessenov},
+ title = {Stepwise refinement of heap-manipulating code in {C}halice},
+ journal = {Formal Aspects of Computing},
+ year = {2012},
+ volume = {24},
+ number = {4--6},
+ pages = {519--535},
+ month = jul,
+}
+
+@article{Wirth:StepwiseRefinment,
+ author = "N. Wirth",
+ title = "{Program Development by Stepwise Refinement}",
+ journal = cacm,
+ volume = 14,
+ year = 1971,
+ pages = "221-227"
+}
+
+@article{Dijkstra:Refinement,
+ author = "E. W. Dijkstra",
+ title = "A constructive approach to the problem of program correctness",
+ journal = "BIT",
+ volume = 8,
+ year = 1968,
+ pages = "174-186"
+}
+
+@phdthesis{Back:thesis,
+ author = "R.-J. R. Back",
+ title = "On the Correctness of Refinement Steps in Program Development",
+ school = "University of Helsinki",
+ year = 1978,
+ note = "Report A-1978-4"
+}
+
+@article{Morgan:SpecStmt,
+ author = "Carroll Morgan",
+ title = "The Specification Statement",
+ journal = toplas,
+ volume = 10,
+ number = 3,
+ year = 1988,
+ month = jul,
+ pages = "403-419"
+}
+
+@book{Morgan:book,
+ author = "Carroll Morgan",
+ title = "Programming from Specifications",
+ publisher = "Prentice-Hall International",
+ series = "Series in Computer Science",
+ year = 1990
+}
+
+@article{Morris:Refinement,
+ author = "Joseph M. Morris",
+ title = "A theoretical basis for stepwise refinement and the
+ programming calculus",
+ journal = scp,
+ volume = 9,
+ number = 3,
+ month = dec,
+ year = 1987,
+ pages = "287-306"
+}
+
+@article{GriesVolpano:Transform,
+ author = "David Gries and Dennis Volpano",
+ title = "The Transform --- a New Language Construct",
+ journal = "Structured Programming",
+ volume = 11,
+ number = 1,
+ year = 1990,
+ pages = "1-10"
+}
+
+@Book{Abrial:BBook,
+ author = "J.-R. Abrial",
+ title = "The {B}-Book: Assigning Programs to Meanings",
+ publisher = "Cambridge University Press",
+ year = 1996
+}
+
+@Book{Jones:VDM:book,
+ Author = "Cliff B. Jones",
+ Title = "Systematic Software Development Using {VDM}",
+ Publisher = "Prentice Hall",
+ Series = "International Series in Computer Science",
+ Address = "Englewood Cliffs, N.J.",
+ Edition = "Second",
+ Year = 1990
+}
+
+@Book{Abrial:EventB:book,
+ author = {Jean-Raymond Abrial},
+ title = {Modeling in {Event-B}: System and Software Engineering},
+ publisher = {Cambridge University Press},
+ year = {2010},
+}
+
+@Misc{ClearSy:AtelierB,
+ author = {ClearSy},
+ title = {Atelier {B}},
+ howpublished = {\url{http://www.atelierb.eu/}},
+}
+
+@InProceedings{Abrial:FM-in-practice,
+ author = {Jean-Raymond Abrial},
+ title = {Formal methods in industry: achievements, problems, future},
+ booktitle = {28th International Conference on Software Engineering (ICSE 2006)},
+ editor = {Leon J. Osterweil and H. Dieter Rombach and Mary Lou Soffa},
+ month = may,
+ year = {2006},
+ publisher = {ACM},
+ pages = {761-768},
+}
+
+@InProceedings{MartinEtAl:AsynchMIPS,
+ author = {Alain J. Martin and Andrew Lines and Rajit Manohar
+ and Mika Nystr{\"o}m and Paul I. P{\'e}nzes and
+ Robert Southworth and Uri Cummings},
+ title = {The Design of an Asynchronous {MIPS} {R3000} Microprocessor},
+ booktitle = {17th Conference on Advanced Research in VLSI {ARVLSI '97}},
+ month = sep,
+ year = {1997},
+ publisher = {IEEE Computer Society},
+ pages = {164-181},
+}
+
+@Book{Abrial:EventB-book,
+ author = {Jean-Raymond Abrial},
+ title = {Modeling in {Event-B}: System and Software Engineering},
+ publisher = {Cambridge University Press},
+ year = {2010},
+}
+
+@Article{BackSere:ActionSystems,
+ author = {Ralph-Johan Back and Kaisa Sere},
+ title = {Stepwise Refinement of Action Systems},
+ journal = {Structured Programming},
+ year = {1991},
+ volume = {12},
+ number = {1},
+ pages = {17-30},
+}
+
+@InProceedings{VCC:overview,
+ author = {Ernie Cohen and Markus Dahlweid and Mark Hillebrand and Dirk Leinenbach and
+ Micha{\l} Moskal and Thomas Santen and Wolfram Schulte and Stephan Tobies},
+ title = {{VCC}: A Practical System for Verifying Concurrent {C}},
+ booktitle = {Theorem Proving in Higher Order Logics, 22nd International Conference, TPHOLs 2009},
+ editor = {Stefan Berghofer and Tobias Nipkow and Christian Urban and Makarius Wenzel},
+ volume = {5674},
+ series = LNCS,
+ publisher = {Springer},
+ month = aug,
+ year = {2009},
+ pages = {23-42},
+}
+
+@InProceedings{BallEtAll:ScalableChecking,
+ author = {Thomas Ball and Brian Hackett and Shuvendu K. Lahiri
+ and Shaz Qadeer and Julien Vanegue},
+ title = {Towards Scalable Modular Checking of User-Defined Properties},
+ booktitle = {Verified Software: Theories, Tools, Experiments,
+ (VSTTE 2010)},
+ editor = {Gary T. Leavens and Peter O'Hearn and Sriram K. Rajamani},
+ volume = {6217},
+ series = lncs,
+ publisher = {Springer},
+ month = aug,
+ year = {2010},
+ pages = {1-24},
+}
+
+@techreport{ESC:rr,
+ author = "David L. Detlefs and K. Rustan M. Leino and Greg Nelson
+ and James B. Saxe",
+ title = "Extended static checking",
+ institution = "Compaq Systems Research Center",
+ month = dec,
+ year = 1998,
+ type = "Research Report",
+ number = 159
+}
+
+@InProceedings{VeanesEtAl:SpecExplorer,
+ author = {Margus Veanes and Colin Campbell and Wolfgang
+ Grieskamp and Wolfram Schulte and Nikolai Tillmann
+ and Lev Nachmanson},
+ title = {Model-Based Testing of Object-Oriented Reactive
+ Systems with {Spec} {Explorer}},
+ booktitle = {Formal Methods and Testing},
+ pages = {39-76},
+ year = {2008},
+ editor = {Robert M. Hierons and Jonathan P. Bowen and Mark Harman},
+ volume = {4949},
+ series = lncs,
+ publisher = {Springer},
+}
+
+@article{Hoare:DataRepresentations,
+ author = "C. A. R. Hoare",
+ title = "Proof of correctness of data representations",
+ journal = acta,
+ volume = 1,
+ number = 4,
+ year = 1972,
+ pages = "271-281"
+}
+
+@manual{baudin09acsl,
+ title = {{ACSL}: {ANSI}/{ISO} {C} Specification Language, version 1.4},
+ author = {Patrick Baudin and Jean-Christophe Filli{\^a}tre and
+ Claude March{\'e} and Benjamin Monate and Yannick
+ Moy and Virgile Prevosto},
+ year = 2009,
+ note = {\url{http://frama-c.com/}}
+}
+
+@InProceedings{BarnettEtAl:Boogie,
+ author = "Mike Barnett and Bor-Yuh Evan Chang and Robert DeLine and
+ Bart Jacobs and K. Rustan M. Leino",
+ title = "{B}oogie: A Modular Reusable Verifier for Object-Oriented Programs",
+ booktitle = "Formal Methods for Components and Objects: 4th
+ International Symposium, FMCO 2005",
+ editor = "de Boer, Frank S. and Marcello M. Bonsangue and
+ Susanne Graf and de Roever, Willem-Paul",
+ series = lncs,
+ volume = 4111,
+ publisher = "Springer",
+ month = sep,
+ year = 2006,
+ pages = "364-387"
+}
+
+@inproceedings{deMouraBjorner:Z3:overview,
+ author = "de Moura, Leonardo and Nikolaj Bj{\o}rner",
+ title = {{Z3}: An efficient {SMT} solver},
+ booktitle = {TACAS 2008},
+ series = lncs,
+ volume = 4963,
+ publisher = {Springer},
+ month = mar # "--" # apr,
+ year = 2008,
+ pages = {337-340},
+}
+
+@Article{Back-Mikhajlova-vonWright:ClassRefinement,
+ author = {Ralph-Johan Back and Anna Mikhaljova and von Wright, Joakim},
+ title = {Class Refinement as Semantics of Correct Object Substitutability},
+ journal = {Formal Aspects of Computing},
+ volume = {12},
+ number = {1},
+ year = {2000},
+ month = oct,
+ pages = {18-40},
+}
+
+@InProceedings{MikhajlovaSekerinski:ClassRefinement,
+ author = {Anna Mikhaljova and Emil Sekerinski},
+ title = {Class Refinement and Interface Refinement in Object-Oriented Programs},
+ booktitle = {FME '97: Industrial Applications and Strengthened
+ Foundations of Formal Methods, 4th International
+ Symposium of Formal Methods Europe},
+ editor = {John S. Fitzgerald and Cliff B. Jones and Peter Lucas},
+ volume = {1313 },
+ series = lncs,
+ publisher = {Springer},
+ month = sep,
+ year = {1997},
+ pages = {82-101},
+}
+
+@InProceedings{LeinoMueller:ESOP2009,
+ author = {K. Rustan M. Leino and Peter M{\"u}ller},
+ title = {A Basis for Verifying Multi-threaded Programs},
+ booktitle = {Programming Languages and Systems, 18th European
+ Symposium on Programming, ESOP 2009},
+ editor = {Giuseppe Castagna},
+ volume = {5502},
+ series = lncs,
+ publisher = {Springer},
+ month = mar,
+ year = 2009,
+ pages = {378-393},
+}
+
+@InCollection{Chalice:tutorial,
+ author = {K. Rustan M. Leino and Peter M{\"u}ller and Jan Smans},
+ title = {Verification of Concurrent Programs with {C}halice},
+ booktitle = {Foundations of Security Analysis and Design {V}: {FOSAD} 2007/2008/2009 Tutorial Lectures},
+ editor = {Alessandro Aldini and Gilles Barthe and Robert Gorrieri},
+ volume = {5705},
+ series = lncs,
+ publisher = {Springer},
+ year = {2009},
+ pages = {195-222},
+}
+
+@InProceedings{LeinoRuemmer:Boogie2,
+ author = {K. Rustan M. Leino and Philipp R{\"u}mmer},
+ title = {A Polymorphic Intermediate Verification Language:
+ Design and Logical Encoding},
+ booktitle = {Tools and Algorithms for the Construction and
+ Analysis of Systems, 16th International Conference,
+ TACAS 2010},
+ editor = {Javier Esparza and Rupak Majumdar},
+ series = lncs,
+ volume = 6015,
+ publisher = {Springer},
+ month = mar,
+ year = 2010,
+ pages = {312-327},
+}
+
+@book{LiskovGuttag:book,
+ author = "Barbara Liskov and John Guttag",
+ title = "Abstraction and Specification in Program Development",
+ publisher = "MIT Press",
+ series = "MIT Electrical Engineering and Computer Science Series",
+ year = 1986
+}
+
+@TechReport{DahlEtAl:Simula67,
+ author = {Ole-Johan Dahl and Bj{\o}rn Myhrhaug and Kristen Nygaard},
+ title = {Common Base Language},
+ institution = {Norwegian Computing Center},
+ type = {Publication},
+ number = {S-22},
+ month = oct,
+ year = 1970,
+}
+
+@InProceedings{tafat10foveoos,
+ author = {Asma Tafat and Sylvain Boulm\'e and Claude March\'e},
+ title = {A Refinement Methodology for Object-Oriented Programs},
+ booktitle = {Formal Verification of Object-Oriented Software, Papers
+ Presented at the International Conference},
+ editor = {Bernhard Beckert and Claude March\'e},
+ month = jun,
+ year = 2010,
+ pages = {143--159},
+}
+
+@inproceedings{LeinoMueller:ModelFields,
+ author = {K. Rustan M. Leino and
+ Peter M{\"u}ller},
+ title = {A Verification Methodology for Model Fields},
+ booktitle = "Programming Languages and Systems, 15th European Symposium on Programming, ESOP 2006",
+ editor = "Peter Sestoft",
+ series = lncs,
+ volume = 3924,
+ publisher = "Springer",
+ month = mar,
+ year = 2006,
+ pages = {115-130},
+}
+
+@InProceedings{CarterEtAl:UsingPerfectDeveloper,
+ author = {Gareth Carter and Rosemary Monahan and Joseph M. Morris},
+ title = {Software Refinement with {P}erfect {D}eveloper},
+ booktitle = {Third IEEE International Conference on Software
+ Engineering and Formal Methods (SEFM 2005)},
+ pages = {363-373},
+ editor = {Bernhard K. Aichernig and Bernhard Beckert},
+ month = sep,
+ year = {2005},
+ publisher = {IEEE Computer Society},
+}
+
+@InProceedings{Abrial:SchorrWaite,
+ author = {Jean-Raymond Abrial},
+ title = {Event Based Sequential Program Development:
+ Application to Constructing a Pointer Program},
+ booktitle = {FME 2003: Formal Methods, International Symposium of
+ Formal Methods Europe},
+ editor = {Keijiro Araki and Stefania Gnesi and Dino Mandrioli},
+ volume = {2805},
+ series = lncs,
+ publisher = {Springer},
+ month = sep,
+ year = {2003},
+ pages = {51-74},
+}
+
+@article{Barnett-etal04,
+ author = {Mike Barnett and Robert DeLine and Manuel F{\"a}hndrich and
+ K. Rustan M. Leino and Wolfram Schulte},
+ title = {Verification of Object-Oriented Programs with Invariants},
+ journal = {Journal of Object Technology},
+ volume = 3,
+ number = 6,
+ year = 2004,
+ pages = {27-56},
+}
+
+@TechReport{HatcliffEtAl:survey-tr,
+ author = {John Hatcliff and Gary T. Leavens and K. Rustan M. Leino and
+ Peter M{\"u}ller and Matthew Parkinson},
+ title = {Behavioral Interface Specification Languages},
+ institution = {University of Central Florida, School of EECS},
+ month = oct,
+ year = {2010},
+ number = {CS-TR-09-01a},
+}
+
+@Article{HatcliffEtAl:survey:journal:tentativeInfo,
+ author = {John Hatcliff and Gary T. Leavens and K. Rustan M. Leino and
+ Peter M{\"u}ller and Matthew Parkinson},
+ title = {Behavioral Interface Specification Languages},
+ journal = {ACM Computing Surveys},
+ year = {2012},
+ volume = {44},
+ number = {3},
+ month = may,
+}
+
+@inproceedings{Boyland:SAS2003,
+ author = {John Boyland},
+ title = {Checking Interference with Fractional Permissions},
+ booktitle = "Static Analysis, 10th International Symposium, SAS 2003",
+ editor = {Radhia Cousot},
+ series = lncs,
+ volume = 2694,
+ publisher = "Springer",
+ year = 2003,
+ pages = {55-72}
+}
+
+@InProceedings{SmansEtAl:ImplicitDynamicFrames,
+ author = {Jan Smans and Bart Jacobs and Frank Piessens},
+ title = {Implicit Dynamic Frames: Combining Dynamic Frames
+ and Separation Logic},
+ booktitle = {ECOOP 2009 --- Object-Oriented Programming, 23rd
+ European Conference},
+ editor = {Sophia Drossopoulou},
+ volume = {5653},
+ series = lncs,
+ publisher = {Springer},
+ month = jul,
+ year = {2009},
+ pages = {148-172},
+}
+
+@Misc{Escher,
+ author = "{Escher Technologies, Inc.}",
+ title = "Getting started with {P}erfect",
+ howpublished = "\url{http://www.eschertech.com}",
+ year = 2001
+}
+
+@Article{LeinoNelson:tome,
+ author = "K. Rustan M. Leino and Greg Nelson",
+ title = "Data abstraction and information hiding",
+ journal = toplas,
+ month = sep,
+ year = 2002,
+ volume = 24,
+ number = 5,
+ pages = "491-553"
+}
+
+@InProceedings{Clarke-Drossopoulou02,
+ author = {Dave Clarke and Sophia Drossopoulou},
+ title = {Ownership, encapsulation and the disjointness of
+ type and effect},
+ booktitle = {Proceedings of the 2002 ACM SIGPLAN Conference on
+ Object-Oriented Programming Systems, Languages and
+ Applications, OOPSLA 2002},
+ publisher = {ACM},
+ Month = nov,
+ Year = 2002,
+ pages = {292--310},
+}
+
+@InProceedings{Reynolds:SepLogic,
+ author = {John C. Reynolds},
+ title = {Separation Logic: A Logic for Shared Mutable Data Structures},
+ booktitle = {17th IEEE Symposium on Logic in Computer Science (LICS 2002)},
+ publisher = {IEEE Computer Society},
+ year = {2002},
+ month = jul,
+ pages = {55-74},
+}
+
+# References supplied by the reviewers.
+# Added 12/20/11.
+
+@incollection{Potet:BComposition,
+ author = {Potet, Marie and Rouzaud, Yann},
+ affiliation = {LSR-IMAG Grenoble France},
+ title = {Composition and refinement in the B-method},
+ booktitle = {Bï¿¿ï¿¿98: Recent Advances in the Development and Use of the B Method},
+ series = lncs,
+ editor = {Bert, Didier},
+ publisher = {Springer Berlin / Heidelberg},
+ isbn = {978-3-540-64405-7},
+ keyword = {Computer Science},
+ pages = {46-65},
+ volume = {1393},
+ url = {http://dx.doi.org/10.1007/BFb0053355},
+ note = {10.1007/BFb0053355},
+ year = {1998}
+}
+
+@inproceedings{Grandy:JavaRefinement,
+ author = {Grandy, Holger and Stenzel, Kurt and Reif, Wolfgang},
+ title = {A refinement method for {J}ava programs},
+ booktitle = {Formal Methods for Open Object-Based Distributed Systems, 9th IFIP WG 6.1 International Conference, FMOODS 2007},
+ editor = {Marcello M. Bonsangue and Einar Broch Johnsen},
+ series = lncs,
+ number = {4468},
+ month = jun,
+ year = {2007},
+ publisher = {Springer},
+ pages = {221--235},
+}
+
+@inproceedings{Wehrheim:Subtypes,
+ author = {Heike Wehrheim},
+ title = {Checking Behavioural Subtypes via Refinement},
+ booktitle = {FMOODS},
+ year = {2002},
+ pages = {79-93},
+ crossref = {DBLP:conf/fmoods/2002},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+@article{Banerjee:ownership,
+ author = {Banerjee, Anindya and Naumann, David A.},
+ title = {Ownership confinement ensures representation independence for object-oriented programs},
+ journal = jacm,
+ volume = {52},
+ issue = {6},
+ month = {November},
+ year = {2005},
+ issn = {0004-5411},
+ pages = {894--960},
+ numpages = {67},
+ url = {http://doi.acm.org/10.1145/1101821.1101824},
+ doi = {http://doi.acm.org/10.1145/1101821.1101824},
+ acmid = {1101824},
+ publisher = {ACM},
+ address = {New York, NY, USA},
+ keywords = {Alias control, confinement, data refinement, relational parametricity, simulation},
+}
+
+@Article{SpecSharp:Retrospective:CACM,
+ author = {Mike Barnett and Manuel F{\"a}hndrich and
+ K. Rustan M. Leino and Peter M{\"u}ller and
+ Wolfram Schulte and Herman Venter},
+ title = {Specification and Verification: The {Spec\#} Experience},
+ journal = cacm,
+ volume = {54},
+ number = {6},
+ pages = {81-91},
+ month = jun,
+ year = 2011,
+}
+
+@InProceedings{Heule:FractionsWithoutFractions,
+ author = {Stefan Heule and K. Rustan M. Leino and Peter
+ M{\"u}ller and Alexander J. Summers},
+ title = {Fractional Permissions without the Fractions},
+ booktitle = {13th Workshop on Formal Techniques for Java-like
+ Programs, FTfJP 2011},
+ year = {2011},
+ month = jul,
+}
+
+@incollection{Morgan:Capjunctive,
+ author = "Carroll Morgan",
+ title = "The Cuppest Capjunctive Capping, and {G}alois",
+ editor = "A. W. Roscoe",
+ booktitle = "A Classical Mind: Essays in Honour of C.A.R. Hoare",
+ publisher = "Prentice-Hall",
+ series = "International Series in Computer Science",
+ pages = "317-332",
+ year = 1994
+}
+
+@Article{Morgan:CompositionalNoninterference,
+ author = {Carroll Morgan},
+ title = {Compositional noninterference from first principles},
+ journal = fac,
+ year = {2012},
+ volume = {24},
+ number = {1},
+ pages = {3-26},
+}
+
+@article{DenningDenning:Certification,
+ author = "Dorothy E. Denning and Peter J. Denning",
+ title = "Certification of Programs for Secure Information Flow",
+ journal = cacm,
+ volume = 20,
+ number = 7,
+ month = jul,
+ year = 1977,
+ pages = "504-513"
+}
+
+@article{Jones:Interference,
+ author = "C. B. Jones",
+ title = "Accommodating interference in the formal design of
+ concurrent object-based programs",
+ journal = "Formal Methods in System Design",
+ volume = 8,
+ number = 2,
+ pages = "105-122",
+ month = mar,
+ year = 1996
+}
+
+@Book{Jackson:Alloy:book,
+ author = {Daniel Jackson},
+ title = {Software Abstractions: Logic, Language, and Analysis},
+ publisher = {MIT Press},
+ year = {2006},
+}
+
+@inproceedings{LeuschelButler:FME03,
+ author = {Michael Leuschel and Michael Butler},
+ title = {Pro{B}: A Model Checker for {B}},
+ booktitle = {FME 2003: Formal Methods},
+ editor = {Araki, Keijiro and Gnesi, Stefania and Mandrioli, Dino},
+ publisher = {Springer},
+ series = lncs,
+ number = {2805},
+ year = 2003,
+ pages = {855-874},
+}
+
+@InProceedings{ParkinsonBierman:POPL2005,
+ author = {Matthew J. Parkinson and Gavin M. Bierman},
+ title = {Separation logic and abstraction},
+ booktitle = {Proceedings of the 32nd ACM SIGPLAN-SIGACT Symposium
+ on Principles of Programming Languages, POPL 2005},
+ publisher = {ACM},
+ month = jan,
+ year = {2005},
+ pages = {247-258},
+}
+
+@Article{LiskovWing94,
+ author = "Barbara Liskov and Jeannette M. Wing",
+ title = "A Behavioral Notion of Subtyping",
+ journal = toplas,
+ year = 1994,
+ volume = 16,
+ number = 6
+}
+
+@book{WoodcockDavies:UsingZ,
+ title = "Using {Z}: Specification, Refinement, and Proof",
+ author = "Jim Woodcock and Jim Davies",
+ year = "1996",
+ publisher = "Prentice Hall International",
+}
+
+@Article{Leavens:ModularOOSpecs,
+ author = {Gary T. Leavens},
+ title = {Modular Specification and Verification of Object-Oriented Programs},
+ journal = {IEEE Software},
+ year = {1991},
+ volume = {8},
+ number = {4},
+ pages = {72-80},
+}
+
+@InProceedings{ShieldHayes:InvsAndDynConstraints,
+ author = {Jamie Shield and Ian J. Hayes},
+ title = {Refining Object-Oriented Invariants and Dynamic Constraints},
+ booktitle = {9th Asia-Pacific Software Engineering Conference (APSEC 2002)},
+ pages = {52-61},
+ year = {2002},
+ publisher = {IEEE Computer Society},
+}
+
+@TechReport{Chalice:predicates:TR,
+ author = {S. Heule and I. T. Kassios and P. M\"uller and A. J. Summers},
+ title = {Verification Condition Generation for Permission Logics with Abstraction Functions},
+ institution = {ETH Zurich},
+ year = {2012},
+ number = {761}
+}
+
+@InCollection{KleinEtAl:DataRefinement,
+ author = {Gerwin Klein and Thomas Sewell and Simon Winwood},
+ title = {Refinement in the formal verification of {seL4}},
+ booktitle = {Design and Verification of Microprocessor Systems for High-Assurance Applications},
+ editor = {David S. Hardin},
+ pages = {323--339},
+ month = Mar,
+ year = {2010},
+ publisher = {Springer},
+}
+
+@InProceedings{DharaLeavens:forcing,
+ author = {Krishna Kishore Dhara and Gary T. Leavens},
+ title = {Forcing Behavioral Subtyping through Specification Inheritance},
+ booktitle = {18th International Conference on Software Engineering},
+ year = {1996},
+ editor = {H. Dieter Rombach and T. S. E. Maibaum and Marvin V. Zelkowitz},
+ pages = {258-267},
+ month = mar,
+ publisher = {IEEE Computer Society},
+}
+
+@InProceedings{SpiritOfGhostCode,
+ author = {Jean-Christophe Filli{\^a}tre and L{\'e}on Gondelman and Andrei Paskevich},
+ title = {The Spirit of Ghost Code},
+ booktitle = {Computer Aided Verification --- 26th International Conference, CAV 2014},
+ year = {2014},
+ editor = {Armin Biere and Roderick Bloem},
+ series = lncs,
+ volume = {8559},
+ pages = {1-16},
+ month = jul,
+ publisher = {Springer},
+}
+
+@InProceedings{Dafny:traits,
+ author = {Reza Ahmadi and K. Rustan M. Leino and Jyrki Nummenmaa},
+ title = {Automatic Verification of {D}afny Programs with Traits},
+ booktitle = {Formal Techniques for {J}ava-like Programs, FTfJP 2015},
+ year = {2015},
+ editor = {Rosemary Monahan},
+ publisher = {ACM},
+}
+
+@InProceedings{Dafny:Cloudmake,
+ author = {Maria Christakis and K. Rustan M. Leino and Wolfram Schulte},
+ title = {Formalizing and Verifying a Modern Build Language},
+ booktitle = {FM 2014: Formal Methods --- 19th International Symposium},
+ year = {2014},
+ editor = {Cliff B. Jones and Pekka Pihlajasaari and Jun Sun},
+ volume = {8442},
+ series = lncs,
+ pages = {643-657},
+ month = may,
+ publisher = {Springer},
+}
+
+@Misc{Leino:SPLASH2012:keynote,
+ author = {K. Rustan M. Leino},
+ title = {Staged Program Development},
+ howpublished = {SPLASH 2012 keynote},
+ note = {InfoQ video, \url{http://www.infoq.com/presentations/Staged-Program-Development}},
+ month = oct,
+ year = {2012},
+}
+
+@article{Parnas:secret,
+ author = "D. L. Parnas",
+ title = "On the criteria to be used in decomposing systems into modules",
+ journal = cacm,
+ volume = 15,
+ number = 12,
+ month = dec,
+ year = 1972,
+ pages = "1053-1058",
+ note = "Reprinted as {\tt www.acm.org/classics/may96/}"
+}
+
+@InProceedings{KIV:overview,
+ author = {Wolfgang Reif},
+ title = {The {KIV} System: Systematic Construction of Verified Software},
+ booktitle = {Automated Deduction --- CADE-11, 11th International Conference
+ on Automated Deduction},
+ editor = {Deepak Kapur},
+ series = lncs,
+ volume = {607},
+ publisher = {Springer},
+ pages = {753-757},
+ month = jun,
+ year = {1992},
+}
+
+@InProceedings{LeinoMoskal:Coinduction,
+ author = {K. Rustan M. Leino and Micha{\l} Moskal},
+ title = {Co-induction Simply --- Automatic Co-inductive Proofs in a Program Verifier},
+ booktitle = {FM 2014},
+ series = lncs,
+ volume = {8442},
+ publisher = {Springer},
+ month = may,
+ year = {2014},
+ pages = {382-398},
+}
+
+@Article{Tarski:theorem,
+ author = "Alfred Tarski",
+ title = "A lattice-theoretical fixpoint theorem and its applications",
+ journal = "Pacific Journal of Mathematics",
+ year = 1955,
+ volume = 5,
+ pages = "285-309"
+}
+
+@TechReport{KozenSilva:Coinduction,
+ author = {Dexter Kozen and Alexandra Silva},
+ title = {Practical coinduction},
+ institution = {Comp. and Inf. Science, Cornell Univ.},
+ year = {2012},
+ number = {\url{http://hdl.handle.net/1813/30510}},
+}
+
+@book{Milner:CCS,
+ author = "Robin Milner",
+ title = {A Calculus of Communicating Systems},
+ year = {1982},
+ publisher = {Springer},
+}
+
+@Book{NipkowKlein:ConcreteSemantics,
+ author = {Tobias Nipkow and Gerwin Klein},
+ title = {Concrete Semantics with {I}sabelle/{HOL}},
+ publisher = {Springer},
+ year = {2014},
+}
+
+@Book{Pierce:SoftwareFoundations,
+ author = {Benjamin C. Pierce and Chris Casinghino and
+ Marco Gaboardi and Michael Greenberg and
+ C{\u{a}}t{\u{a}}lin Hri\c{t}cu and Vilhelm Sj{\"o}berg and
+ Brent Yorgey},
+ title = {Software Foundations},
+ publisher = {\url{http://www.cis.upenn.edu/~bcpierce/sf}},
+ year = {2015},
+ edition = {version 3.2},
+ month = jan,
+}
+
+@InProceedings{ClochardEtAl:SemanticsInWhy3,
+ author = {Martin Clochard and Jean-Christophe
+ Filli\^atre and Claude March\'e and Andrei Paskevich},
+ title = {Formalizing Semantics with an Automatic Program Verifier},
+ booktitle = {VSTTE 2014},
+ volume = {8471},
+ series = lncs,
+ publisher = {Springer},
+ month = jul,
+ year = {2014},
+ pages = {37--51},
+}
+
+@Article{LeroyGrall:CoinductiveBigStep,
+ author = {Xavier Leroy and Herv\'e Grall},
+ title = {Coinductive big-step operational semantics},
+ journal = {Information and Computation},
+ volume = {207},
+ number = {2},
+ pages = {284-304},
+ month = feb,
+ year = {2009},
+}
+
+@InProceedings{SwamyEtAl:Fstar2011,
+ author = {Nikhil Swamy and Juan Chen and C{\'e}dric Fournet and
+ Pierre-Yves Strub and Karthikeyan Bhargavan and Jean Yang},
+ title = {Secure distributed programming with value-dependent types},
+ booktitle = {ICFP 2011},
+ publisher = {ACM},
+ month = sep,
+ year = {2011},
+ pages = {266-278},
+}
+
+@Book{Nipkow-Paulson-Wenzel02,
+ author = {Tobias Nipkow and Lawrence C. Paulson and Markus Wenzel},
+ title = {{Isabelle/HOL} --- A Proof Assistant for Higher-Order Logic},
+ publisher = {Springer},
+ year = 2002,
+ volume = 2283,
+ series = LNCS,
+}
+
+@InProceedings{BoveDybjerNorell:BriefAgda,
+ author = {Ana Bove and Peter Dybjer and Ulf Norell},
+ title = {A Brief Overview of {A}gda --- A Functional Language with Dependent Types},
+ booktitle = {TPHOLs 2009},
+ series = lncs,
+ volume = {5674},
+ publisher = {Springer},
+ month = aug,
+ year = {2009},
+ pages = {73-78},
+}
+
+@InProceedings{PaulinMohring:InductiveCoq,
+ author = {Christine Paulin-Mohring},
+ title = {Inductive Definitions in the system {C}oq --- Rules and Properties},
+ booktitle = {TLCA '93},
+ series = lncs,
+ volume = {664},
+ pages = {328-345},
+ year = {1993},
+ publisher = {Springer},
+}
+
+@TechReport{CamilleriMelham:InductiveRelations,
+ author = {Juanito Camilleri and Tom Melham},
+ title = {Reasoning with Inductively Defined Relations
+ in the {HOL} Theorem Prover},
+ institution = {University of Cambridge Computer Laboratory},
+ year = {1992},
+ number = {265},
+ OPTmonth = aug,
+}
+
+@Book{Winskel:FormalSemantics,
+ author = {Glynn Winskel},
+ title = {The Formal Semantics of Programming Languages: An Introduction},
+ publisher = {MIT Press},
+ year = {1993},
+}
+
+@inproceedings{Paulson:CADE1994,
+ author = {Lawrence C. Paulson},
+ title = {A Fixedpoint Approach to Implementing (Co)Inductive Definitions},
+ booktitle = {CADE-12},
+ editor = {Alan Bundy},
+ volume = {814},
+ series = lncs,
+ publisher = {Springer},
+ year = {1994},
+ pages = {148-161},
+}
+
+@InProceedings{Harrison:InductiveDefs,
+ author = {John Harrison},
+ title = {Inductive Definitions: Automation and Application},
+ booktitle = {TPHOLs 1995},
+ year = {1995},
+ editor = {E. Thomas Schubert and Phillip J. Windley and Jim Alves-Foss},
+ volume = {971},
+ series = lncs,
+ pages = {200-213},
+ publisher = {Springer},
+}
+
+@Article{ManoliosMoore:PartialFunctions,
+ author = {Panagiotis Manolios and J Strother Moore},
+ title = {Partial Functions in {ACL2}},
+ journal = {Journal of Automated Reasoning},
+ year = {2003},
+ volume = {31},
+ number = {2},
+ pages = {107-127},
+}
+
+@PhdThesis{Krauss:PhD,
+ author = {Alexander Krauss},
+ title = {Automating Recursive Definitions and Termination Proofs in Higher-Order Logic},
+ school = {Technische Universit{\"a}t M{\"u}nchen},
+ year = {2009},
+}
+
diff --git a/Docs/DafnyRef/madoko.css b/Docs/DafnyRef/madoko.css
new file mode 100644
index 00000000..2a2d7279
--- /dev/null
+++ b/Docs/DafnyRef/madoko.css
@@ -0,0 +1,471 @@
+/* ---------------------------------------------------
+ Various settings to display madoko elements correctly.
+ For example, lines in tables or a table of contents.
+
+ All rules use specific madoko classes and never just
+ a generic element. This means one can safely include
+ this CSS into any web page without affecting non-madoko
+ content.
+----------------------------------------------------*/
+
+/* The table of contents */
+.madoko .toc>.tocblock .tocblock .tocblock {
+ margin-left: 2.5em;
+}
+
+.madoko .toc>.tocblock .tocblock {
+ margin-left: 1.7em;
+}
+
+.madoko .toc>.tocblock>.tocitem {
+ font-weight: bold;
+}
+
+.madoko .toc {
+ margin-top: 1em;
+}
+
+/* Paragraphs */
+.madoko p.para-continue {
+ margin-bottom: 0pt;
+}
+
+.madoko .para-block+p {
+ margin-top: 0pt;
+}
+
+.madoko ul.para-block, .madoko ol.para-block {
+ margin-top: 0pt;
+ margin-bottom: 0pt;
+}
+
+.madoko ul.para-end, .madoko ol.para-end {
+ margin-bottom: 1em;
+}
+
+.madoko dl {
+ margin-left: 0em;
+}
+
+.madoko blockquote {
+ border-left: 5px Gainsboro solid;
+ padding-left: 1ex;
+ margin-left: 1em;
+}
+
+/* Local page links do not get an underline unless hovering */
+.madoko a.localref {
+ text-decoration: none;
+}
+.madoko a.localref:hover {
+ text-decoration: underline;
+}
+
+/* Footnotes */
+.madoko .footnotes {
+ font-size: smaller;
+ margin-top: 2em;
+}
+
+.madoko .footnotes hr {
+ width: 50%;
+ text-align: left;
+}
+
+.madoko .footnote {
+ margin-left: 1em;
+}
+.madoko .footnote-before {
+ margin-left: -1em;
+ width: 1em;
+ display: inline-block;
+}
+
+/* Alignment */
+.madoko .align-center, .madoko .align-center>p {
+ text-align: center !important;
+}
+
+.madoko .align-center pre {
+ text-align: left;
+}
+
+.madoko .align-center>* {
+ margin-left: auto !important;
+ margin-right: auto !important;
+}
+
+.madoko .align-left, .madoko .align-left>p {
+ text-align: left !important;
+}
+
+.madoko .align-left>* {
+ margin-left: 0pt !important;
+ margin-right: auto !important;
+}
+
+.madoko .align-right, .madoko .align-right>p {
+ text-align: right !important;
+}
+
+.madoko .align-right>* {
+ margin-left: auto !important;
+ margin-right: 0pt !important;
+}
+
+.madoko .align-center>table,
+.madoko .align-left>table,
+.madoko .align-right>table {
+ text-align: left !important;
+}
+
+
+/* Equations, Figure's etc. */
+.madoko .equation-before {
+ float: right;
+}
+
+
+/* Bibliography */
+.madoko .bibitem {
+ font-size: smaller;
+}
+
+.madoko .bib-numeric .bibitem {
+ margin-left: 3em;
+ text-indent: -3em;
+}
+
+.madoko .bibitem-before {
+ display: none;
+}
+
+.madoko .bib-numeric .bibitem-before {
+ display: inline-block;
+ width: 3em;
+ text-align: right;
+}
+
+.madoko .bibliography {
+}
+
+.madoko .bibsearch {
+ font-size: x-small;
+ text-decoration:none;
+ color: black;
+ font-family: "Segoe UI Symbol", Symbola;
+}
+
+/* General */
+.madoko .block, .madoko .figure, .madoko .bibitem, .madoko .equation, .madoko div.math {
+ margin-top: 1ex;
+ margin-bottom: 1ex;
+}
+
+.madoko .figure {
+ padding: 0.5em;
+ margin-left: 0pt;
+ margin-right: 0pt;
+}
+
+.madoko .hidden {
+ display: none;
+}
+
+.madoko .invisible {
+ visibility: hidden;
+}
+
+.madoko.preview .invisible {
+ visibility: visible;
+ opacity: 0.5;
+}
+
+.madoko code.code, .madoko span.code {
+ white-space: pre-wrap;
+}
+
+.madoko hr, hr.madoko {
+ border: none;
+ border-bottom: black solid 1px;
+ margin-bottom: 0.5ex;
+}
+
+.madoko .framed>*:first-child {
+ margin-top: 0pt;
+}
+.madoko .framed>*:last-child {
+ margin-bottom: 0pt;
+}
+
+
+/* Title, authors */
+.madoko .title {
+ font-size: xx-large;
+ font-weight: bold;
+ margin-bottom: 1ex;
+}
+
+.madoko .subtitle {
+ font-size: x-large;
+ margin-bottom: 1ex;
+ margin-top: -1ex;
+}
+
+.madoko .titleblock>* {
+ margin-left: auto;
+ margin-right: auto;
+ text-align: center;
+}
+
+.madoko .titleblock table {
+ width: 80%;
+}
+
+.madoko .authorblock .author {
+ font-size: large;
+}
+
+.madoko .titlenote {
+ margin-top: -0.5ex;
+ margin-bottom: 1.5ex;
+}
+
+/* Lists */
+
+.madoko ul.list-star {
+ list-style-type: disc;
+}
+
+.madoko ul.list-dash {
+ list-style-type: none !important;
+}
+
+.madoko ul.list-dash > li:before {
+ content: "\2013";
+ position: absolute;
+ margin-left: -1em;
+}
+
+.madoko ul.list-plus {
+ list-style-type: square;
+}
+
+/* Tables */
+.madoko table.madoko {
+ border-collapse: collapse;
+}
+.madoko td, .madoko th {
+ padding: 0ex 0.5ex;
+ margin: 0pt;
+ vertical-align: top;
+}
+
+.madoko .cell-border-left {
+ border-left: 1px solid black;
+}
+.madoko .cell-border-right {
+ border-right: 1px solid black;
+}
+
+
+.madoko thead>tr:first-child>.cell-line,
+.madoko tbody:first-child>tr:first-child>.cell-line {
+ border-top: 1px solid black;
+ border-bottom: none;
+}
+
+.madoko .cell-line, .madoko .cell-double-line {
+ border-bottom: 1px solid black;
+ border-top: none;
+}
+
+.madoko .cell-double-line {
+ border-top: 1px solid black;
+ padding-top: 1.5px !important;
+}
+
+
+/* Math Pre */
+.madoko .input-mathpre .MathJax_Display {
+ text-align: left !important;
+}
+
+.madoko div.input-mathpre {
+ text-align: left;
+ margin-top: 1.5ex;
+ margin-bottom: 1ex;
+}
+
+.madoko .math-rendering {
+ color: gray;
+}
+
+/* Math */
+.madoko .mathdisplay {
+ text-align: center;
+}
+
+
+/*---------------------------------------------------------------------------
+ Default style for syntax highlighting
+---------------------------------------------------------------------------*/
+
+.highlighted { color: black; }
+.highlighted .token.identifier { }
+.highlighted .token.operators { }
+.highlighted .token.keyword { color: blue }
+.highlighted .token.string { color: maroon }
+.highlighted .token.string.escape { color: gray }
+.highlighted .token.comment { color: darkgreen }
+.highlighted .token.comment.doc { font-style: normal }
+.highlighted .token.constant { color: purple; }
+.highlighted .token.entity { }
+.highlighted .token.tag { color: blue }
+.highlighted .token.info-token { color: black }
+.highlighted .token.warn-token { color: black }
+.highlighted .token.error-token { color: darkred }
+.highlighted .token.debug-token { color: gray }
+.highlighted .token.regexp { color: maroon }
+.highlighted .token.attribute.name { color: navy }
+.highlighted .token.attribute.value { color: maroon }
+.highlighted .token.constructor { color: purple }
+.highlighted .token.namespace { color: navy }
+.highlighted .token.header { color: navy }
+.highlighted .token.type { color: teal }
+.highlighted .token.type.delimiter { color: teal; }
+.highlighted .token.predefined { color: navy }
+.highlighted .token.invalid { border-bottom: red dotted 1px }
+.highlighted .token.code { color: maroon }
+.highlighted .token.code.keyword { color: navy }
+.highlighted .token.typevar { font-style: italic; }
+
+.highlighted .token.delimiter { } /* .[curly,square,parenthesis,angle,array,bracket] */
+.highlighted .token.number { } /* .[hex,octal,binary,float] */
+.highlighted .token.variable { } /* .[name,value] */
+.highlighted .token.meta { color: navy } /* .[content] */
+
+.highlighted .token.bold { font-weight: bold; }
+.highlighted .token.italic { font-style: italic; }
+
+
+/* Pretty formatting of code */
+.madoko pre.pretty, .madoko code.pretty {
+ font-family: Cambria,Times,Georgia,serif;
+ font-size: 100%;
+}
+
+.madoko .pretty table {
+ border-collapse: collapse;
+}
+.madoko .pretty td {
+ padding: 0em;
+}
+.madoko .pretty td.empty {
+ min-width: 1.5ex;
+}
+.madoko .pretty td.expander {
+ width: 100em;
+}
+.madoko .pretty .token.identifier { font-style: italic }
+.madoko .pretty .token.constructor { font-style: italic }
+
+
+/* ---------------------------------------------------
+ Styling for full documents
+----------------------------------------------------*/
+body.madoko, .madoko-body {
+ font-family: Cambria,"Times New Roman","Liberation Serif","Times",serif;
+ -webkit-text-size-adjust: 100%; /* so math displays well on mobile devices */
+}
+
+body.madoko, .madoko-body {
+ padding: 0em 2em;
+ max-width: 88ex; /* about 88 characters */
+ margin: 1em auto;
+}
+
+body.madoko.preview {
+ padding: 0em 1em;
+}
+
+.madoko p,
+.madoko li {
+ text-align: justify;
+}
+
+/* style headings nicer, especially h5 and h6 */
+.madoko h1, .madoko h2, .madoko h3, .madoko h4 {
+ margin-top: 1.22em;
+ margin-bottom: 1ex;
+}
+.madoko h1+p, .madoko h2+p, .madoko h3+p, .madoko h4+p, .madoko h5+p {
+ margin-top: 1ex;
+}
+.madoko h5, .madoko h6 {
+ margin-top: 1ex;
+ font-size: 1em;
+}
+.madoko h5 {
+ margin-bottom: 0.5ex;
+}
+.madoko h5 + p {
+ margin-top: 0.5ex;
+}
+.madoko h6 {
+ margin-bottom: 0pt;
+}
+.madoko h6 + p {
+ margin-top: 0pt;
+}
+
+
+/* Fix monospace display (see http://code.stephenmorley.org/html-and-css/fixing-browsers-broken-monospace-font-handling/) */
+.madoko pre, .madoko code, .madoko kbd, .madoko samp, .madoko tt, .madoko .monospace, .madoko .token.indent, .madoko .reveal pre, .madoko .reveal code, .madoko .email {
+ font-family: Consolas,"Andale Mono WT","Andale Mono",Lucida Console,Monaco,monospace,monospace;
+ font-size: 0.85em;
+}
+.madoko pre code, .madoko .token.indent {
+ font-size: 0.95em;
+}
+
+.madoko pre code {
+ font-family: inherit !important;
+}
+
+/* Code prettify */
+.madoko ol.linenums li {
+ background-color: white;
+ list-style-type: decimal;
+}
+
+/* Merging */
+.madoko .remote {
+ background-color: #F0FFF0;
+}
+.madoko .remote + * {
+ margin-top: 0pt;
+}
+
+/* ---------------------------------------------------
+ Print settings
+----------------------------------------------------*/
+
+@media print {
+ body.madoko, .madoko-body {
+ font-size: 10pt;
+ }
+ @page {
+ margin: 1in 1.5in;
+ }
+}
+
+/* ---------------------------------------------------
+ Mobile device settings
+----------------------------------------------------*/
+
+@media only screen and (max-device-width:1024px) {
+ body.madoko, .madoko-body {
+ padding: 0em 1em;
+ }
+}
diff --git a/Docs/DafnyRef/out/DafnyRef.html b/Docs/DafnyRef/out/DafnyRef.html
new file mode 100644
index 00000000..80843e5f
--- /dev/null
+++ b/Docs/DafnyRef/out/DafnyRef.html
@@ -0,0 +1,8287 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta name="generator" content="Madoko, version 1.0.0-rc3" />
+ <meta name="viewport" content="initial-scale=1.0" />
+ <meta name="author" content="K. Rustan M. Leino" />
+ <meta name="description" content="Leino mode: Start numbering at 0" />
+ <title>Draft Dafny Reference Manual</title>
+ <style type="text/css" class="link">
+ /*# sourceURL=madoko.css */
+
+ .madoko .toc>.tocblock .tocblock .tocblock {
+ margin-left: 2.5em;
+ }
+ .madoko .toc>.tocblock .tocblock {
+ margin-left: 1.7em;
+ }
+ .madoko .toc>.tocblock>.tocitem {
+ font-weight: bold;
+ }
+ .madoko .toc {
+ margin-top: 1em;
+ }
+ .madoko p.para-continue {
+ margin-bottom: 0pt;
+ }
+ .madoko .para-block+p {
+ margin-top: 0pt;
+ }
+ .madoko ul.para-block, .madoko ol.para-block {
+ margin-top: 0pt;
+ margin-bottom: 0pt;
+ }
+ .madoko ul.para-end, .madoko ol.para-end {
+ margin-bottom: 1em;
+ }
+ .madoko dl {
+ margin-left: 0em;
+ }
+ .madoko blockquote {
+ border-left: 5px Gainsboro solid;
+ padding-left: 1ex;
+ margin-left: 1em;
+ }
+ .madoko a.localref {
+ text-decoration: none;
+ }
+ .madoko a.localref:hover {
+ text-decoration: underline;
+ }
+ .madoko .footnotes {
+ font-size: smaller;
+ margin-top: 2em;
+ }
+ .madoko .footnotes hr {
+ width: 50%;
+ text-align: left;
+ }
+ .madoko .footnote {
+ margin-left: 1em;
+ }
+ .madoko .footnote-before {
+ margin-left: -1em;
+ width: 1em;
+ display: inline-block;
+ }
+ .madoko .align-center, .madoko .align-center>p {
+ text-align: center !important;
+ }
+ .madoko .align-center pre {
+ text-align: left;
+ }
+ .madoko .align-center>* {
+ margin-left: auto !important;
+ margin-right: auto !important;
+ }
+ .madoko .align-left, .madoko .align-left>p {
+ text-align: left !important;
+ }
+ .madoko .align-left>* {
+ margin-left: 0pt !important;
+ margin-right: auto !important;
+ }
+ .madoko .align-right, .madoko .align-right>p {
+ text-align: right !important;
+ }
+ .madoko .align-right>* {
+ margin-left: auto !important;
+ margin-right: 0pt !important;
+ }
+ .madoko .align-center>table,
+ .madoko .align-left>table,
+ .madoko .align-right>table {
+ text-align: left !important;
+ }
+ .madoko .equation-before {
+ float: right;
+ }
+ .madoko .bibitem {
+ font-size: smaller;
+ }
+ .madoko .bib-numeric .bibitem {
+ margin-left: 3em;
+ text-indent: -3em;
+ }
+ .madoko .bibitem-before {
+ display: none;
+ }
+ .madoko .bib-numeric .bibitem-before {
+ display: inline-block;
+ width: 3em;
+ text-align: right;
+ }
+ .madoko .bibliography {
+ }
+ .madoko .bibsearch {
+ font-size: x-small;
+ text-decoration:none;
+ color: black;
+ font-family: "Segoe UI Symbol", Symbola;
+ }
+ .madoko .block, .madoko .figure, .madoko .bibitem, .madoko .equation, .madoko div.math {
+ margin-top: 1ex;
+ margin-bottom: 1ex;
+ }
+ .madoko .figure {
+ padding: 0.5em;
+ margin-left: 0pt;
+ margin-right: 0pt;
+ }
+ .madoko .hidden {
+ display: none;
+ }
+ .madoko .invisible {
+ visibility: hidden;
+ }
+ .madoko.preview .invisible {
+ visibility: visible;
+ opacity: 0.5;
+ }
+ .madoko code.code, .madoko span.code {
+ white-space: pre-wrap;
+ }
+ .madoko hr, hr.madoko {
+ border: none;
+ border-bottom: black solid 1px;
+ margin-bottom: 0.5ex;
+ }
+ .madoko .framed>*:first-child {
+ margin-top: 0pt;
+ }
+ .madoko .framed>*:last-child {
+ margin-bottom: 0pt;
+ }
+ .madoko .title {
+ font-size: xx-large;
+ font-weight: bold;
+ margin-bottom: 1ex;
+ }
+ .madoko .subtitle {
+ font-size: x-large;
+ margin-bottom: 1ex;
+ margin-top: -1ex;
+ }
+ .madoko .titleblock>* {
+ margin-left: auto;
+ margin-right: auto;
+ text-align: center;
+ }
+ .madoko .titleblock table {
+ width: 80%;
+ }
+ .madoko .authorblock .author {
+ font-size: large;
+ }
+ .madoko .titlenote {
+ margin-top: -0.5ex;
+ margin-bottom: 1.5ex;
+ }
+ .madoko ul.list-star {
+ list-style-type: disc;
+ }
+ .madoko ul.list-dash {
+ list-style-type: none !important;
+ }
+ .madoko ul.list-dash > li:before {
+ content: "\2013";
+ position: absolute;
+ margin-left: -1em;
+ }
+ .madoko ul.list-plus {
+ list-style-type: square;
+ }
+ .madoko table.madoko {
+ border-collapse: collapse;
+ }
+ .madoko td, .madoko th {
+ padding: 0ex 0.5ex;
+ margin: 0pt;
+ vertical-align: top;
+ }
+ .madoko .cell-border-left {
+ border-left: 1px solid black;
+ }
+ .madoko .cell-border-right {
+ border-right: 1px solid black;
+ }
+ .madoko thead>tr:first-child>.cell-line,
+ .madoko tbody:first-child>tr:first-child>.cell-line {
+ border-top: 1px solid black;
+ border-bottom: none;
+ }
+ .madoko .cell-line, .madoko .cell-double-line {
+ border-bottom: 1px solid black;
+ border-top: none;
+ }
+ .madoko .cell-double-line {
+ border-top: 1px solid black;
+ padding-top: 1.5px !important;
+ }
+ .madoko .input-mathpre .MathJax_Display {
+ text-align: left !important;
+ }
+ .madoko div.input-mathpre {
+ text-align: left;
+ margin-top: 1.5ex;
+ margin-bottom: 1ex;
+ }
+ .madoko .math-rendering {
+ color: gray;
+ }
+ .madoko .mathdisplay {
+ text-align: center;
+ }
+ .highlighted { color: black; }
+ .highlighted .token.identifier { }
+ .highlighted .token.operators { }
+ .highlighted .token.keyword { color: blue }
+ .highlighted .token.string { color: maroon }
+ .highlighted .token.string.escape { color: gray }
+ .highlighted .token.comment { color: darkgreen }
+ .highlighted .token.comment.doc { font-style: normal }
+ .highlighted .token.constant { color: purple; }
+ .highlighted .token.entity { }
+ .highlighted .token.tag { color: blue }
+ .highlighted .token.info-token { color: black }
+ .highlighted .token.warn-token { color: black }
+ .highlighted .token.error-token { color: darkred }
+ .highlighted .token.debug-token { color: gray }
+ .highlighted .token.regexp { color: maroon }
+ .highlighted .token.attribute.name { color: navy }
+ .highlighted .token.attribute.value { color: maroon }
+ .highlighted .token.constructor { color: purple }
+ .highlighted .token.namespace { color: navy }
+ .highlighted .token.header { color: navy }
+ .highlighted .token.type { color: teal }
+ .highlighted .token.type.delimiter { color: teal; }
+ .highlighted .token.predefined { color: navy }
+ .highlighted .token.invalid { border-bottom: red dotted 1px }
+ .highlighted .token.code { color: maroon }
+ .highlighted .token.code.keyword { color: navy }
+ .highlighted .token.typevar { font-style: italic; }
+ .highlighted .token.delimiter { }
+ .highlighted .token.number { }
+ .highlighted .token.variable { }
+ .highlighted .token.meta { color: navy }
+ .highlighted .token.bold { font-weight: bold; }
+ .highlighted .token.italic { font-style: italic; }
+ .madoko pre.pretty, .madoko code.pretty {
+ font-family: Cambria,Times,Georgia,serif;
+ font-size: 100%;
+ }
+ .madoko .pretty table {
+ border-collapse: collapse;
+ }
+ .madoko .pretty td {
+ padding: 0em;
+ }
+ .madoko .pretty td.empty {
+ min-width: 1.5ex;
+ }
+ .madoko .pretty td.expander {
+ width: 100em;
+ }
+ .madoko .pretty .token.identifier { font-style: italic }
+ .madoko .pretty .token.constructor { font-style: italic }
+ body.madoko, .madoko-body {
+ font-family: Cambria,"Times New Roman","Liberation Serif","Times",serif;
+ -webkit-text-size-adjust: 100%;
+ }
+ body.madoko, .madoko-body {
+ padding: 0em 2em;
+ max-width: 88ex;
+ margin: 1em auto;
+ }
+ body.madoko.preview {
+ padding: 0em 1em;
+ }
+ .madoko p,
+ .madoko li {
+ text-align: justify;
+ }
+ .madoko h1, .madoko h2, .madoko h3, .madoko h4 {
+ margin-top: 1.22em;
+ margin-bottom: 1ex;
+ }
+ .madoko h1+p, .madoko h2+p, .madoko h3+p, .madoko h4+p, .madoko h5+p {
+ margin-top: 1ex;
+ }
+ .madoko h5, .madoko h6 {
+ margin-top: 1ex;
+ font-size: 1em;
+ }
+ .madoko h5 {
+ margin-bottom: 0.5ex;
+ }
+ .madoko h5 + p {
+ margin-top: 0.5ex;
+ }
+ .madoko h6 {
+ margin-bottom: 0pt;
+ }
+ .madoko h6 + p {
+ margin-top: 0pt;
+ }
+ .madoko pre, .madoko code, .madoko kbd, .madoko samp, .madoko tt, .madoko .monospace, .madoko .token.indent, .madoko .reveal pre, .madoko .reveal code, .madoko .email {
+ font-family: Consolas,"Andale Mono WT","Andale Mono",Lucida Console,Monaco,monospace,monospace;
+ font-size: 0.85em;
+ }
+ .madoko pre code, .madoko .token.indent {
+ font-size: 0.95em;
+ }
+ .madoko pre code {
+ font-family: inherit !important;
+ }
+ .madoko ol.linenums li {
+ background-color: white;
+ list-style-type: decimal;
+ }
+ .madoko .remote {
+ background-color: #F0FFF0;
+ }
+ .madoko .remote + * {
+ margin-top: 0pt;
+ }
+ @media print {
+ body.madoko, .madoko-body {
+ font-size: 10pt;
+ }
+ @page {
+ margin: 1in 1.5in;
+ }
+ }
+ @media only screen and (max-device-width:1024px) {
+ body.madoko, .madoko-body {
+ padding: 0em 1em;
+ }
+ }
+
+ </style>
+
+ <style>
+ body { text-rendering=optimizeLegibility }
+ </style>
+ </head>
+<body class="madoko">
+
+<div class="body madoko" style="line-adjust:0">
+<style>
+.code-escaped .comment-color { color: darkgreen }
+
+.token.java.string { color: black; font-weight: bold }
+.token.java.type { color: black; font-weight: normal }
+.token.java.operator,
+.token.java.delimiter { color: blue; }
+</style>
+
+
+
+<div class="mathdefs input-mathdefs"></div>
+<div class="titleblock align-center para-block" style="text-align:center;line-adjust:0">
+<div class="titleheader align-center" style="text-align:center;line-adjust:0">
+<div class="title para-block" style="font-size:xx-large;font-weight:bold;margin-bottom:0.5ex;line-adjust:0">Draft Dafny Reference Manual</div>
+<div class="titlenote para-block" style="line-adjust:0">Manuscript Dafny Reference<br>
+2016-01-27 21:22</div></div>
+<div class="authors align-center" style="text-align:center;width:80%;line-adjust:0"><table class="authorrow columns block" style="margin-top:2ex;width:100%;line-adjust:0">
+<tbody><tr><td class="author column" style="text-align:center;line-adjust:0">
+<div class="authorname" style="font-size:large;line-adjust:0">Richard L. Ford</div>
+<div class="authoremail email" style="line-adjust:0">richford@microsoft.com</div></td><td class="author column" style="text-align:center;line-adjust:0">
+<div class="authorname" style="font-size:large;line-adjust:0">K. Rustan M. Leino</div>
+<div class="authoremail email" style="line-adjust:0">leino@microsoft.com</div></td></tr></tbody></table></div></div>
+<div class="abstract" style="margin-left:3em;margin-right:3em;font-size:small">
+<p class="p noindent"><strong class="strong-star2">Abstract.</strong> This is the Dafny reference manual which describes the Dafny programming
+language and how to use the Dafny verification system.
+Parts of this manual are more tutorial in nature in order to help the
+user understand how to do proofs with Dafny.</p></div><span data-line=""></span>
+<nav class="toc toc-contents"><h2 id="sec-contents" class="clearnum h1 heading-contents" data-heading-depth="1" style="display:block">Contents</h2>
+<div class="tocblock tocblock1">
+<div class="tocitem tocitem1" data-toc-target="sec-introduction" data-toc-depth="1" data-toc-line="[0]{.heading-label}.&#8194;Introduction" style="toctarget:sec-introduction"><a href="#sec-introduction" class="localref"><span class="heading-label">0</span>.&#8194;Introduction</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-dafny-example" data-toc-depth="2" data-toc-line="[0.0]{.heading-label}.&#8194;Dafny Example" style="toctarget:sec-dafny-example"><a href="#sec-dafny-example" class="localref"><span class="heading-label">0.0</span>.&#8194;Dafny Example</a></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-lexical-and-low-level-grammar" data-toc-depth="1" data-toc-line="[1]{.heading-label}.&#8194;Lexical and Low Level Grammar" style="toctarget:sec-lexical-and-low-level-grammar"><a href="#sec-lexical-and-low-level-grammar" class="localref"><span class="heading-label">1</span>.&#8194;Lexical and Low Level Grammar</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-character-classes" data-toc-depth="2" data-toc-line="[1.0]{.heading-label}.&#8194;Character Classes" style="toctarget:sec-character-classes"><a href="#sec-character-classes" class="localref"><span class="heading-label">1.0</span>.&#8194;Character Classes</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-comments" data-toc-depth="3" data-toc-line="[1.0.0]{.heading-label}.&#8194;Comments" style="toctarget:sec-comments"><a href="#sec-comments" class="localref"><span class="heading-label">1.0.0</span>.&#8194;Comments</a></div></div>
+<div class="tocitem tocitem2" data-toc-target="sec-tokens" data-toc-depth="2" data-toc-line="[1.1]{.heading-label}.&#8194;Tokens" style="toctarget:sec-tokens"><a href="#sec-tokens" class="localref"><span class="heading-label">1.1</span>.&#8194;Tokens</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-reserved-words" data-toc-depth="3" data-toc-line="[1.1.0]{.heading-label}.&#8194;Reserved Words" style="toctarget:sec-reserved-words"><a href="#sec-reserved-words" class="localref"><span class="heading-label">1.1.0</span>.&#8194;Reserved Words</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-identifiers" data-toc-depth="3" data-toc-line="[1.1.1]{.heading-label}.&#8194;Identifiers" style="toctarget:sec-identifiers"><a href="#sec-identifiers" class="localref"><span class="heading-label">1.1.1</span>.&#8194;Identifiers</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-digits" data-toc-depth="3" data-toc-line="[1.1.2]{.heading-label}.&#8194;Digits" style="toctarget:sec-digits"><a href="#sec-digits" class="localref"><span class="heading-label">1.1.2</span>.&#8194;Digits</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-escaped-character" data-toc-depth="3" data-toc-line="[1.1.3]{.heading-label}.&#8194;Escaped Character" style="toctarget:sec-escaped-character"><a href="#sec-escaped-character" class="localref"><span class="heading-label">1.1.3</span>.&#8194;Escaped Character</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-character-constant-token" data-toc-depth="3" data-toc-line="[1.1.4]{.heading-label}.&#8194;Character Constant Token" style="toctarget:sec-character-constant-token"><a href="#sec-character-constant-token" class="localref"><span class="heading-label">1.1.4</span>.&#8194;Character Constant Token</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-string-constant-token" data-toc-depth="3" data-toc-line="[1.1.5]{.heading-label}.&#8194;String Constant Token" style="toctarget:sec-string-constant-token"><a href="#sec-string-constant-token" class="localref"><span class="heading-label">1.1.5</span>.&#8194;String Constant Token</a></div></div>
+<div class="tocitem tocitem2" data-toc-target="sec-low-level-grammar-productions" data-toc-depth="2" data-toc-line="[1.2]{.heading-label}.&#8194;Low Level Grammar Productions" style="toctarget:sec-low-level-grammar-productions"><a href="#sec-low-level-grammar-productions" class="localref"><span class="heading-label">1.2</span>.&#8194;Low Level Grammar Productions</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-identifier-variations" data-toc-depth="3" data-toc-line="[1.2.0]{.heading-label}.&#8194;Identifier Variations" style="toctarget:sec-identifier-variations"><a href="#sec-identifier-variations" class="localref"><span class="heading-label">1.2.0</span>.&#8194;Identifier Variations</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-nousident-synonyms" data-toc-depth="3" data-toc-line="[1.2.1]{.heading-label}.&#8194;NoUSIdent Synonyms" style="toctarget:sec-nousident-synonyms"><a href="#sec-nousident-synonyms" class="localref"><span class="heading-label">1.2.1</span>.&#8194;NoUSIdent Synonyms</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-qualified-names" data-toc-depth="3" data-toc-line="[1.2.2]{.heading-label}.&#8194;Qualified Names" style="toctarget:sec-qualified-names"><a href="#sec-qualified-names" class="localref"><span class="heading-label">1.2.2</span>.&#8194;Qualified Names</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-identifier-type-combinations" data-toc-depth="3" data-toc-line="[1.2.3]{.heading-label}.&#8194;Identifier-Type Combinations" style="toctarget:sec-identifier-type-combinations"><a href="#sec-identifier-type-combinations" class="localref"><span class="heading-label">1.2.3</span>.&#8194;Identifier-Type Combinations</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-numeric-literals" data-toc-depth="3" data-toc-line="[1.2.4]{.heading-label}.&#8194;Numeric Literals" style="toctarget:sec-numeric-literals"><a href="#sec-numeric-literals" class="localref"><span class="heading-label">1.2.4</span>.&#8194;Numeric Literals</a></div></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-programs" data-toc-depth="1" data-toc-line="[2]{.heading-label}.&#8194;Programs" style="toctarget:sec-programs"><a href="#sec-programs" class="localref"><span class="heading-label">2</span>.&#8194;Programs</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-include-directives" data-toc-depth="2" data-toc-line="[2.0]{.heading-label}.&#8194;Include Directives" style="toctarget:sec-include-directives"><a href="#sec-include-directives" class="localref"><span class="heading-label">2.0</span>.&#8194;Include Directives</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-top-level-declarations" data-toc-depth="2" data-toc-line="[2.1]{.heading-label}.&#8194;Top Level Declarations" style="toctarget:sec-top-level-declarations"><a href="#sec-top-level-declarations" class="localref"><span class="heading-label">2.1</span>.&#8194;Top Level Declarations</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-declaration-modifiers" data-toc-depth="2" data-toc-line="[2.2]{.heading-label}.&#8194;Declaration Modifiers" style="toctarget:sec-declaration-modifiers"><a href="#sec-declaration-modifiers" class="localref"><span class="heading-label">2.2</span>.&#8194;Declaration Modifiers</a></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-modules" data-toc-depth="1" data-toc-line="[3]{.heading-label}.&#8194;Modules" style="toctarget:sec-modules"><a href="#sec-modules" class="localref"><span class="heading-label">3</span>.&#8194;Modules</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-declaring-new-modules" data-toc-depth="2" data-toc-line="[3.0]{.heading-label}.&#8194;Declaring New Modules" style="toctarget:sec-declaring-new-modules"><a href="#sec-declaring-new-modules" class="localref"><span class="heading-label">3.0</span>.&#8194;Declaring New Modules</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-importing-modules" data-toc-depth="2" data-toc-line="[3.1]{.heading-label}.&#8194;Importing Modules" style="toctarget:sec-importing-modules"><a href="#sec-importing-modules" class="localref"><span class="heading-label">3.1</span>.&#8194;Importing Modules</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-opening-modules" data-toc-depth="2" data-toc-line="[3.2]{.heading-label}.&#8194;Opening Modules" style="toctarget:sec-opening-modules"><a href="#sec-opening-modules" class="localref"><span class="heading-label">3.2</span>.&#8194;Opening Modules</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-module-abstraction" data-toc-depth="2" data-toc-line="[3.3]{.heading-label}.&#8194;Module Abstraction" style="toctarget:sec-module-abstraction"><a href="#sec-module-abstraction" class="localref"><span class="heading-label">3.3</span>.&#8194;Module Abstraction</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-module-ordering-and-dependencies" data-toc-depth="2" data-toc-line="[3.4]{.heading-label}.&#8194;Module Ordering and Dependencies" style="toctarget:sec-module-ordering-and-dependencies"><a href="#sec-module-ordering-and-dependencies" class="localref"><span class="heading-label">3.4</span>.&#8194;Module Ordering and Dependencies</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-name-resolution" data-toc-depth="2" data-toc-line="[3.5]{.heading-label}.&#8194;Name Resolution" style="toctarget:sec-name-resolution"><a href="#sec-name-resolution" class="localref"><span class="heading-label">3.5</span>.&#8194;Name Resolution</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-expression-context-name-resolution" data-toc-depth="3" data-toc-line="[3.5.0]{.heading-label}.&#8194;Expression Context Name Resolution" style="toctarget:sec-expression-context-name-resolution"><a href="#sec-expression-context-name-resolution" class="localref"><span class="heading-label">3.5.0</span>.&#8194;Expression Context Name Resolution</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-type-context-name-resolution" data-toc-depth="3" data-toc-line="[3.5.1]{.heading-label}.&#8194;Type Context Name Resolution" style="toctarget:sec-type-context-name-resolution"><a href="#sec-type-context-name-resolution" class="localref"><span class="heading-label">3.5.1</span>.&#8194;Type Context Name Resolution</a></div></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-specifications" data-toc-depth="1" data-toc-line="[4]{.heading-label}.&#8194;Specifications" style="toctarget:sec-specifications"><a href="#sec-specifications" class="localref"><span class="heading-label">4</span>.&#8194;Specifications</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-specification-clauses" data-toc-depth="2" data-toc-line="[4.0]{.heading-label}.&#8194;Specification Clauses" style="toctarget:sec-specification-clauses"><a href="#sec-specification-clauses" class="localref"><span class="heading-label">4.0</span>.&#8194;Specification Clauses</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-requires-clause" data-toc-depth="3" data-toc-line="[4.0.0]{.heading-label}.&#8194;Requires Clause" style="toctarget:sec-requires-clause"><a href="#sec-requires-clause" class="localref"><span class="heading-label">4.0.0</span>.&#8194;Requires Clause</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-ensures-clause" data-toc-depth="3" data-toc-line="[4.0.1]{.heading-label}.&#8194;Ensures Clause" style="toctarget:sec-ensures-clause"><a href="#sec-ensures-clause" class="localref"><span class="heading-label">4.0.1</span>.&#8194;Ensures Clause</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-decreases-clause" data-toc-depth="3" data-toc-line="[4.0.2]{.heading-label}.&#8194;Decreases Clause" style="toctarget:sec-decreases-clause"><a href="#sec-decreases-clause" class="localref"><span class="heading-label">4.0.2</span>.&#8194;Decreases Clause</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-framing" data-toc-depth="3" data-toc-line="[4.0.3]{.heading-label}.&#8194;Framing" style="toctarget:sec-framing"><a href="#sec-framing" class="localref"><span class="heading-label">4.0.3</span>.&#8194;Framing</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-reads-clause" data-toc-depth="3" data-toc-line="[4.0.4]{.heading-label}.&#8194;Reads Clause" style="toctarget:sec-reads-clause"><a href="#sec-reads-clause" class="localref"><span class="heading-label">4.0.4</span>.&#8194;Reads Clause</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-modifies-clause" data-toc-depth="3" data-toc-line="[4.0.5]{.heading-label}.&#8194;Modifies Clause" style="toctarget:sec-modifies-clause"><a href="#sec-modifies-clause" class="localref"><span class="heading-label">4.0.5</span>.&#8194;Modifies Clause</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-invariant-clause" data-toc-depth="3" data-toc-line="[4.0.6]{.heading-label}.&#8194;Invariant Clause" style="toctarget:sec-invariant-clause"><a href="#sec-invariant-clause" class="localref"><span class="heading-label">4.0.6</span>.&#8194;Invariant Clause</a></div></div>
+<div class="tocitem tocitem2" data-toc-target="sec-method-specification" data-toc-depth="2" data-toc-line="[4.1]{.heading-label}.&#8194;Method Specification" style="toctarget:sec-method-specification"><a href="#sec-method-specification" class="localref"><span class="heading-label">4.1</span>.&#8194;Method Specification</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-function-specification" data-toc-depth="2" data-toc-line="[4.2]{.heading-label}.&#8194;Function Specification" style="toctarget:sec-function-specification"><a href="#sec-function-specification" class="localref"><span class="heading-label">4.2</span>.&#8194;Function Specification</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-lambda-specification" data-toc-depth="2" data-toc-line="[4.3]{.heading-label}.&#8194;Lambda Specification" style="toctarget:sec-lambda-specification"><a href="#sec-lambda-specification" class="localref"><span class="heading-label">4.3</span>.&#8194;Lambda Specification</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-iterator-specification" data-toc-depth="2" data-toc-line="[4.4]{.heading-label}.&#8194;Iterator Specification" style="toctarget:sec-iterator-specification"><a href="#sec-iterator-specification" class="localref"><span class="heading-label">4.4</span>.&#8194;Iterator Specification</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-loop-specification" data-toc-depth="2" data-toc-line="[4.5]{.heading-label}.&#8194;Loop Specification" style="toctarget:sec-loop-specification"><a href="#sec-loop-specification" class="localref"><span class="heading-label">4.5</span>.&#8194;Loop Specification</a></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-types" data-toc-depth="1" data-toc-line="[5]{.heading-label}.&#8194;Types" style="toctarget:sec-types"><a href="#sec-types" class="localref"><span class="heading-label">5</span>.&#8194;Types</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-value-types" data-toc-depth="2" data-toc-line="[5.0]{.heading-label}.&#8194;Value Types" style="toctarget:sec-value-types"><a href="#sec-value-types" class="localref"><span class="heading-label">5.0</span>.&#8194;Value Types</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-reference-types" data-toc-depth="2" data-toc-line="[5.1]{.heading-label}.&#8194;Reference Types" style="toctarget:sec-reference-types"><a href="#sec-reference-types" class="localref"><span class="heading-label">5.1</span>.&#8194;Reference Types</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-named-types" data-toc-depth="2" data-toc-line="[5.2]{.heading-label}.&#8194;Named Types" style="toctarget:sec-named-types"><a href="#sec-named-types" class="localref"><span class="heading-label">5.2</span>.&#8194;Named Types</a></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-basic-types" data-toc-depth="1" data-toc-line="[6]{.heading-label}.&#8194;Basic types" style="toctarget:sec-basic-types"><a href="#sec-basic-types" class="localref"><span class="heading-label">6</span>.&#8194;Basic types</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-booleans" data-toc-depth="2" data-toc-line="[6.0]{.heading-label}.&#8194;Booleans" style="toctarget:sec-booleans"><a href="#sec-booleans" class="localref"><span class="heading-label">6.0</span>.&#8194;Booleans</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-equivalence-operator" data-toc-depth="3" data-toc-line="[6.0.0]{.heading-label}.&#8194;Equivalence Operator" style="toctarget:sec-equivalence-operator"><a href="#sec-equivalence-operator" class="localref"><span class="heading-label">6.0.0</span>.&#8194;Equivalence Operator</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-conjunction-and-disjunction" data-toc-depth="3" data-toc-line="[6.0.1]{.heading-label}.&#8194;Conjunction and Disjunction" style="toctarget:sec-conjunction-and-disjunction"><a href="#sec-conjunction-and-disjunction" class="localref"><span class="heading-label">6.0.1</span>.&#8194;Conjunction and Disjunction</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-implication-and-reverse-implication" data-toc-depth="3" data-toc-line="[6.0.2]{.heading-label}.&#8194;Implication and Reverse Implication" style="toctarget:sec-implication-and-reverse-implication"><a href="#sec-implication-and-reverse-implication" class="localref"><span class="heading-label">6.0.2</span>.&#8194;Implication and Reverse Implication</a></div></div>
+<div class="tocitem tocitem2" data-toc-target="sec-numeric-types" data-toc-depth="2" data-toc-line="[6.1]{.heading-label}.&#8194;Numeric types" style="toctarget:sec-numeric-types"><a href="#sec-numeric-types" class="localref"><span class="heading-label">6.1</span>.&#8194;Numeric types</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-characters" data-toc-depth="2" data-toc-line="[6.2]{.heading-label}.&#8194;Characters" style="toctarget:sec-characters"><a href="#sec-characters" class="localref"><span class="heading-label">6.2</span>.&#8194;Characters</a></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-type-parameters" data-toc-depth="1" data-toc-line="[7]{.heading-label}.&#8194;Type parameters" style="toctarget:sec-type-parameters"><a href="#sec-type-parameters" class="localref"><span class="heading-label">7</span>.&#8194;Type parameters</a></div>
+<div class="tocitem tocitem1" data-toc-target="sec-generic-instantiation" data-toc-depth="1" data-toc-line="[8]{.heading-label}.&#8194;Generic Instantiation" style="toctarget:sec-generic-instantiation"><a href="#sec-generic-instantiation" class="localref"><span class="heading-label">8</span>.&#8194;Generic Instantiation</a></div>
+<div class="tocitem tocitem1" data-toc-target="sec-collection-types" data-toc-depth="1" data-toc-line="[9]{.heading-label}.&#8194;Collection types" style="toctarget:sec-collection-types"><a href="#sec-collection-types" class="localref"><span class="heading-label">9</span>.&#8194;Collection types</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-sets" data-toc-depth="2" data-toc-line="[9.0]{.heading-label}.&#8194;Sets" style="toctarget:sec-sets"><a href="#sec-sets" class="localref"><span class="heading-label">9.0</span>.&#8194;Sets</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-multisets" data-toc-depth="2" data-toc-line="[9.1]{.heading-label}.&#8194;Multisets" style="toctarget:sec-multisets"><a href="#sec-multisets" class="localref"><span class="heading-label">9.1</span>.&#8194;Multisets</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-sequences" data-toc-depth="2" data-toc-line="[9.2]{.heading-label}.&#8194;Sequences" style="toctarget:sec-sequences"><a href="#sec-sequences" class="localref"><span class="heading-label">9.2</span>.&#8194;Sequences</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-sequence-displays" data-toc-depth="3" data-toc-line="[9.2.0]{.heading-label}.&#8194;Sequence Displays" style="toctarget:sec-sequence-displays"><a href="#sec-sequence-displays" class="localref"><span class="heading-label">9.2.0</span>.&#8194;Sequence Displays</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-sequence-relational-operators" data-toc-depth="3" data-toc-line="[9.2.1]{.heading-label}.&#8194;Sequence Relational Operators" style="toctarget:sec-sequence-relational-operators"><a href="#sec-sequence-relational-operators" class="localref"><span class="heading-label">9.2.1</span>.&#8194;Sequence Relational Operators</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-sequence-concatenation" data-toc-depth="3" data-toc-line="[9.2.2]{.heading-label}.&#8194;Sequence Concatenation" style="toctarget:sec-sequence-concatenation"><a href="#sec-sequence-concatenation" class="localref"><span class="heading-label">9.2.2</span>.&#8194;Sequence Concatenation</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-other-sequence-expressions" data-toc-depth="3" data-toc-line="[9.2.3]{.heading-label}.&#8194;Other Sequence Expressions" style="toctarget:sec-other-sequence-expressions"><a href="#sec-other-sequence-expressions" class="localref"><span class="heading-label">9.2.3</span>.&#8194;Other Sequence Expressions</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-strings" data-toc-depth="3" data-toc-line="[9.2.4]{.heading-label}.&#8194;Strings" style="toctarget:sec-strings"><a href="#sec-strings" class="localref"><span class="heading-label">9.2.4</span>.&#8194;Strings</a></div></div>
+<div class="tocitem tocitem2" data-toc-target="sec-finite-and-infinite-maps" data-toc-depth="2" data-toc-line="[9.3]{.heading-label}.&#8194;Finite and Infinite Maps" style="toctarget:sec-finite-and-infinite-maps"><a href="#sec-finite-and-infinite-maps" class="localref"><span class="heading-label">9.3</span>.&#8194;Finite and Infinite Maps</a></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-types-that-stand-for-other-types" data-toc-depth="1" data-toc-line="[10]{.heading-label}.&#8194;Types that stand for other types" style="toctarget:sec-types-that-stand-for-other-types"><a href="#sec-types-that-stand-for-other-types" class="localref"><span class="heading-label">10</span>.&#8194;Types that stand for other types</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-type-synonyms" data-toc-depth="2" data-toc-line="[10.0]{.heading-label}.&#8194;Type synonyms" style="toctarget:sec-type-synonyms"><a href="#sec-type-synonyms" class="localref"><span class="heading-label">10.0</span>.&#8194;Type synonyms</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-opaque-types" data-toc-depth="2" data-toc-line="[10.1]{.heading-label}.&#8194;Opaque types" style="toctarget:sec-opaque-types"><a href="#sec-opaque-types" class="localref"><span class="heading-label">10.1</span>.&#8194;Opaque types</a></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-well-founded-functions-and-extreme-predicates" data-toc-depth="1" data-toc-line="[11]{.heading-label}.&#8194;Well-founded Functions and Extreme Predicates" style="toctarget:sec-well-founded-functions-and-extreme-predicates"><a href="#sec-well-founded-functions-and-extreme-predicates" class="localref"><span class="heading-label">11</span>.&#8194;Well-founded Functions and Extreme Predicates</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-function-definitions" data-toc-depth="2" data-toc-line="[11.0]{.heading-label}.&#8194;Function Definitions" style="toctarget:sec-function-definitions"><a href="#sec-function-definitions" class="localref"><span class="heading-label">11.0</span>.&#8194;Function Definitions</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-well-founded-functions" data-toc-depth="3" data-toc-line="[11.0.0]{.heading-label}.&#8194;Well-founded Functions" style="toctarget:sec-well-founded-functions"><a href="#sec-well-founded-functions" class="localref"><span class="heading-label">11.0.0</span>.&#8194;Well-founded Functions</a></div>
+<div class="tocblock tocblock4">
+<div class="tocitem tocitem4" data-toc-target="sec-fib-example" data-toc-depth="4" data-toc-line="[11.0.0.0]{.heading-label}.&#8194;Example with Well-founded Functions" style="toctarget:sec-fib-example"><a href="#sec-fib-example" class="localref"><span class="heading-label">11.0.0.0</span>.&#8194;Example with Well-founded Functions</a></div></div>
+<div class="tocitem tocitem3" data-toc-target="sec-extreme-solutions" data-toc-depth="3" data-toc-line="[11.0.1]{.heading-label}.&#8194;Extreme Solutions" style="toctarget:sec-extreme-solutions"><a href="#sec-extreme-solutions" class="localref"><span class="heading-label">11.0.1</span>.&#8194;Extreme Solutions</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-working-with-extreme-predicates" data-toc-depth="3" data-toc-line="[11.0.2]{.heading-label}.&#8194;Working with Extreme Predicates" style="toctarget:sec-working-with-extreme-predicates"><a href="#sec-working-with-extreme-predicates" class="localref"><span class="heading-label">11.0.2</span>.&#8194;Working with Extreme Predicates</a></div>
+<div class="tocblock tocblock4">
+<div class="tocitem tocitem4" data-toc-target="sec-example-least-solution" data-toc-depth="4" data-toc-line="[11.0.2.0]{.heading-label}.&#8194;Example with Least Solution" style="toctarget:sec-example-least-solution"><a href="#sec-example-least-solution" class="localref"><span class="heading-label">11.0.2.0</span>.&#8194;Example with Least Solution</a></div>
+<div class="tocitem tocitem4" data-toc-target="sec-example-greatest-solution" data-toc-depth="4" data-toc-line="[11.0.2.1]{.heading-label}.&#8194;Example with Greatest Solution" style="toctarget:sec-example-greatest-solution"><a href="#sec-example-greatest-solution" class="localref"><span class="heading-label">11.0.2.1</span>.&#8194;Example with Greatest Solution</a></div></div>
+<div class="tocitem tocitem3" data-toc-target="sec-other-techniques" data-toc-depth="3" data-toc-line="[11.0.3]{.heading-label}.&#8194;Other Techniques" style="toctarget:sec-other-techniques"><a href="#sec-other-techniques" class="localref"><span class="heading-label">11.0.3</span>.&#8194;Other Techniques</a></div></div>
+<div class="tocitem tocitem2" data-toc-target="sec-functions-in-dafny" data-toc-depth="2" data-toc-line="[11.1]{.heading-label}.&#8194;Functions in Dafny" style="toctarget:sec-functions-in-dafny"><a href="#sec-functions-in-dafny" class="localref"><span class="heading-label">11.1</span>.&#8194;Functions in Dafny</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-well-founded-functions-in-dafny" data-toc-depth="3" data-toc-line="[11.1.0]{.heading-label}.&#8194;Well-founded Functions in Dafny" style="toctarget:sec-well-founded-functions-in-dafny"><a href="#sec-well-founded-functions-in-dafny" class="localref"><span class="heading-label">11.1.0</span>.&#8194;Well-founded Functions in Dafny</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-proofs-in-dafny" data-toc-depth="3" data-toc-line="[11.1.1]{.heading-label}.&#8194;Proofs in Dafny" style="toctarget:sec-proofs-in-dafny"><a href="#sec-proofs-in-dafny" class="localref"><span class="heading-label">11.1.1</span>.&#8194;Proofs in Dafny</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-friendliness" data-toc-depth="3" data-toc-line="[11.1.2]{.heading-label}.&#8194;Extreme Predicates in Dafny" style="toctarget:sec-friendliness"><a href="#sec-friendliness" class="localref"><span class="heading-label">11.1.2</span>.&#8194;Extreme Predicates in Dafny</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-proofs-about-extreme-predicates" data-toc-depth="3" data-toc-line="[11.1.3]{.heading-label}.&#8194;Proofs about Extreme Predicates" style="toctarget:sec-proofs-about-extreme-predicates"><a href="#sec-proofs-about-extreme-predicates" class="localref"><span class="heading-label">11.1.3</span>.&#8194;Proofs about Extreme Predicates</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-nicer-proofs-of-extreme-predicates" data-toc-depth="3" data-toc-line="[11.1.4]{.heading-label}.&#8194;Nicer Proofs of Extreme Predicates" style="toctarget:sec-nicer-proofs-of-extreme-predicates"><a href="#sec-nicer-proofs-of-extreme-predicates" class="localref"><span class="heading-label">11.1.4</span>.&#8194;Nicer Proofs of Extreme Predicates</a></div></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-class-types" data-toc-depth="1" data-toc-line="[12]{.heading-label}.&#8194;Class Types" style="toctarget:sec-class-types"><a href="#sec-class-types" class="localref"><span class="heading-label">12</span>.&#8194;Class Types</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-field-declarations" data-toc-depth="2" data-toc-line="[12.0]{.heading-label}.&#8194;Field Declarations" style="toctarget:sec-field-declarations"><a href="#sec-field-declarations" class="localref"><span class="heading-label">12.0</span>.&#8194;Field Declarations</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-method-declarations" data-toc-depth="2" data-toc-line="[12.1]{.heading-label}.&#8194;Method Declarations" style="toctarget:sec-method-declarations"><a href="#sec-method-declarations" class="localref"><span class="heading-label">12.1</span>.&#8194;Method Declarations</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-constructors" data-toc-depth="3" data-toc-line="[12.1.0]{.heading-label}.&#8194;Constructors" style="toctarget:sec-constructors"><a href="#sec-constructors" class="localref"><span class="heading-label">12.1.0</span>.&#8194;Constructors</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-lemmas" data-toc-depth="3" data-toc-line="[12.1.1]{.heading-label}.&#8194;Lemmas" style="toctarget:sec-lemmas"><a href="#sec-lemmas" class="localref"><span class="heading-label">12.1.1</span>.&#8194;Lemmas</a></div></div>
+<div class="tocitem tocitem2" data-toc-target="sec-function-declarations" data-toc-depth="2" data-toc-line="[12.2]{.heading-label}.&#8194;Function Declarations" style="toctarget:sec-function-declarations"><a href="#sec-function-declarations" class="localref"><span class="heading-label">12.2</span>.&#8194;Function Declarations</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-function-transparency" data-toc-depth="3" data-toc-line="[12.2.0]{.heading-label}.&#8194;Function Transparency" style="toctarget:sec-function-transparency"><a href="#sec-function-transparency" class="localref"><span class="heading-label">12.2.0</span>.&#8194;Function Transparency</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-predicates" data-toc-depth="3" data-toc-line="[12.2.1]{.heading-label}.&#8194;Predicates" style="toctarget:sec-predicates"><a href="#sec-predicates" class="localref"><span class="heading-label">12.2.1</span>.&#8194;Predicates</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-inductive-predicates-and-lemmas" data-toc-depth="3" data-toc-line="[12.2.2]{.heading-label}.&#8194;Inductive Predicates and Lemmas" style="toctarget:sec-inductive-predicates-and-lemmas"><a href="#sec-inductive-predicates-and-lemmas" class="localref"><span class="heading-label">12.2.2</span>.&#8194;Inductive Predicates and Lemmas</a></div></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-trait-types" data-toc-depth="1" data-toc-line="[13]{.heading-label}.&#8194;Trait Types" style="toctarget:sec-trait-types"><a href="#sec-trait-types" class="localref"><span class="heading-label">13</span>.&#8194;Trait Types</a></div>
+<div class="tocitem tocitem1" data-toc-target="sec-array-types" data-toc-depth="1" data-toc-line="[14]{.heading-label}.&#8194;Array Types" style="toctarget:sec-array-types"><a href="#sec-array-types" class="localref"><span class="heading-label">14</span>.&#8194;Array Types</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-one-dimensional-arrays" data-toc-depth="2" data-toc-line="[14.0]{.heading-label}.&#8194;One-dimensional arrays" style="toctarget:sec-one-dimensional-arrays"><a href="#sec-one-dimensional-arrays" class="localref"><span class="heading-label">14.0</span>.&#8194;One-dimensional arrays</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-multi-dimensional-arrays" data-toc-depth="2" data-toc-line="[14.1]{.heading-label}.&#8194;Multi-dimensional arrays" style="toctarget:sec-multi-dimensional-arrays"><a href="#sec-multi-dimensional-arrays" class="localref"><span class="heading-label">14.1</span>.&#8194;Multi-dimensional arrays</a></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-type-object" data-toc-depth="1" data-toc-line="[15]{.heading-label}.&#8194;Type object" style="toctarget:sec-type-object"><a href="#sec-type-object" class="localref"><span class="heading-label">15</span>.&#8194;Type object</a></div>
+<div class="tocitem tocitem1" data-toc-target="sec-iterator-types" data-toc-depth="1" data-toc-line="[16]{.heading-label}.&#8194;Iterator types" style="toctarget:sec-iterator-types"><a href="#sec-iterator-types" class="localref"><span class="heading-label">16</span>.&#8194;Iterator types</a></div>
+<div class="tocitem tocitem1" data-toc-target="sec-function-types" data-toc-depth="1" data-toc-line="[17]{.heading-label}.&#8194;Function types" style="toctarget:sec-function-types"><a href="#sec-function-types" class="localref"><span class="heading-label">17</span>.&#8194;Function types</a></div>
+<div class="tocitem tocitem1" data-toc-target="sec-algebraic-datatypes" data-toc-depth="1" data-toc-line="[18]{.heading-label}.&#8194;Algebraic Datatypes" style="toctarget:sec-algebraic-datatypes"><a href="#sec-algebraic-datatypes" class="localref"><span class="heading-label">18</span>.&#8194;Algebraic Datatypes</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-inductive-datatypes" data-toc-depth="2" data-toc-line="[18.0]{.heading-label}.&#8194;Inductive datatypes" style="toctarget:sec-inductive-datatypes"><a href="#sec-inductive-datatypes" class="localref"><span class="heading-label">18.0</span>.&#8194;Inductive datatypes</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-tuple-types" data-toc-depth="2" data-toc-line="[18.1]{.heading-label}.&#8194;Tuple types" style="toctarget:sec-tuple-types"><a href="#sec-tuple-types" class="localref"><span class="heading-label">18.1</span>.&#8194;Tuple types</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-co-inductive-datatypes" data-toc-depth="2" data-toc-line="[18.2]{.heading-label}.&#8194;Co-inductive datatypes" style="toctarget:sec-co-inductive-datatypes"><a href="#sec-co-inductive-datatypes" class="localref"><span class="heading-label">18.2</span>.&#8194;Co-inductive datatypes</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-well-founded-functionmethod-definitions" data-toc-depth="3" data-toc-line="[18.2.0]{.heading-label}.&#8194;Well-Founded Function/Method Definitions" style="toctarget:sec-well-founded-functionmethod-definitions"><a href="#sec-well-founded-functionmethod-definitions" class="localref"><span class="heading-label">18.2.0</span>.&#8194;Well-Founded Function/Method Definitions</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-defining-co-inductive-datatypes" data-toc-depth="3" data-toc-line="[18.2.1]{.heading-label}.&#8194;Defining Co-inductive Datatypes" style="toctarget:sec-defining-co-inductive-datatypes"><a href="#sec-defining-co-inductive-datatypes" class="localref"><span class="heading-label">18.2.1</span>.&#8194;Defining Co-inductive Datatypes</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-creating-values-of-co-datatypes" data-toc-depth="3" data-toc-line="[18.2.2]{.heading-label}.&#8194;Creating Values of Co-datatypes" style="toctarget:sec-creating-values-of-co-datatypes"><a href="#sec-creating-values-of-co-datatypes" class="localref"><span class="heading-label">18.2.2</span>.&#8194;Creating Values of Co-datatypes</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-copredicates" data-toc-depth="3" data-toc-line="[18.2.3]{.heading-label}.&#8194;Copredicates" style="toctarget:sec-copredicates"><a href="#sec-copredicates" class="localref"><span class="heading-label">18.2.3</span>.&#8194;Copredicates</a></div>
+<div class="tocblock tocblock4">
+<div class="tocitem tocitem4" data-toc-target="sec-co-equality" data-toc-depth="4" data-toc-line="[18.2.3.0]{.heading-label}.&#8194;Co-Equality" style="toctarget:sec-co-equality"><a href="#sec-co-equality" class="localref"><span class="heading-label">18.2.3.0</span>.&#8194;Co-Equality</a></div></div>
+<div class="tocitem tocitem3" data-toc-target="sec-co-inductive-proofs" data-toc-depth="3" data-toc-line="[18.2.4]{.heading-label}.&#8194;Co-inductive Proofs" style="toctarget:sec-co-inductive-proofs"><a href="#sec-co-inductive-proofs" class="localref"><span class="heading-label">18.2.4</span>.&#8194;Co-inductive Proofs</a></div>
+<div class="tocblock tocblock4">
+<div class="tocitem tocitem4" data-toc-target="sec-properties-about-prefix-predicates" data-toc-depth="4" data-toc-line="[18.2.4.0]{.heading-label}.&#8194;Properties About Prefix Predicates" style="toctarget:sec-properties-about-prefix-predicates"><a href="#sec-properties-about-prefix-predicates" class="localref"><span class="heading-label">18.2.4.0</span>.&#8194;Properties About Prefix Predicates</a></div>
+<div class="tocitem tocitem4" data-toc-target="sec-colemmas" data-toc-depth="4" data-toc-line="[18.2.4.1]{.heading-label}.&#8194;Colemmas" style="toctarget:sec-colemmas"><a href="#sec-colemmas" class="localref"><span class="heading-label">18.2.4.1</span>.&#8194;Colemmas</a></div>
+<div class="tocitem tocitem4" data-toc-target="sec-prefix-lemmas" data-toc-depth="4" data-toc-line="[18.2.4.2]{.heading-label}.&#8194;Prefix Lemmas" style="toctarget:sec-prefix-lemmas"><a href="#sec-prefix-lemmas" class="localref"><span class="heading-label">18.2.4.2</span>.&#8194;Prefix Lemmas</a></div></div></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-newtypes" data-toc-depth="1" data-toc-line="[19]{.heading-label}.&#8194;Newtypes" style="toctarget:sec-newtypes"><a href="#sec-newtypes" class="localref"><span class="heading-label">19</span>.&#8194;Newtypes</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-numeric-conversion-operations" data-toc-depth="2" data-toc-line="[19.0]{.heading-label}.&#8194;Numeric conversion operations" style="toctarget:sec-numeric-conversion-operations"><a href="#sec-numeric-conversion-operations" class="localref"><span class="heading-label">19.0</span>.&#8194;Numeric conversion operations</a></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-subset-types" data-toc-depth="1" data-toc-line="[20]{.heading-label}.&#8194;Subset types" style="toctarget:sec-subset-types"><a href="#sec-subset-types" class="localref"><span class="heading-label">20</span>.&#8194;Subset types</a></div>
+<div class="tocitem tocitem1" data-toc-target="sec-statements" data-toc-depth="1" data-toc-line="[21]{.heading-label}.&#8194;Statements" style="toctarget:sec-statements"><a href="#sec-statements" class="localref"><span class="heading-label">21</span>.&#8194;Statements</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-labeled-statement" data-toc-depth="2" data-toc-line="[21.0]{.heading-label}.&#8194;Labeled Statement" style="toctarget:sec-labeled-statement"><a href="#sec-labeled-statement" class="localref"><span class="heading-label">21.0</span>.&#8194;Labeled Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-break-statement" data-toc-depth="2" data-toc-line="[21.1]{.heading-label}.&#8194;Break Statement" style="toctarget:sec-break-statement"><a href="#sec-break-statement" class="localref"><span class="heading-label">21.1</span>.&#8194;Break Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-block-statement" data-toc-depth="2" data-toc-line="[21.2]{.heading-label}.&#8194;Block Statement" style="toctarget:sec-block-statement"><a href="#sec-block-statement" class="localref"><span class="heading-label">21.2</span>.&#8194;Block Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-return-statement" data-toc-depth="2" data-toc-line="[21.3]{.heading-label}.&#8194;Return Statement" style="toctarget:sec-return-statement"><a href="#sec-return-statement" class="localref"><span class="heading-label">21.3</span>.&#8194;Return Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-yield-statement" data-toc-depth="2" data-toc-line="[21.4]{.heading-label}.&#8194;Yield Statement" style="toctarget:sec-yield-statement"><a href="#sec-yield-statement" class="localref"><span class="heading-label">21.4</span>.&#8194;Yield Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-update-statement" data-toc-depth="2" data-toc-line="[21.5]{.heading-label}.&#8194;Update Statement" style="toctarget:sec-update-statement"><a href="#sec-update-statement" class="localref"><span class="heading-label">21.5</span>.&#8194;Update Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-variable-declaration-statement" data-toc-depth="2" data-toc-line="[21.6]{.heading-label}.&#8194;Variable Declaration Statement" style="toctarget:sec-variable-declaration-statement"><a href="#sec-variable-declaration-statement" class="localref"><span class="heading-label">21.6</span>.&#8194;Variable Declaration Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-guards" data-toc-depth="2" data-toc-line="[21.7]{.heading-label}.&#8194;Guards" style="toctarget:sec-guards"><a href="#sec-guards" class="localref"><span class="heading-label">21.7</span>.&#8194;Guards</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-binding-guards" data-toc-depth="2" data-toc-line="[21.8]{.heading-label}.&#8194;Binding Guards" style="toctarget:sec-binding-guards"><a href="#sec-binding-guards" class="localref"><span class="heading-label">21.8</span>.&#8194;Binding Guards</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-if-statement" data-toc-depth="2" data-toc-line="[21.9]{.heading-label}.&#8194;If Statement" style="toctarget:sec-if-statement"><a href="#sec-if-statement" class="localref"><span class="heading-label">21.9</span>.&#8194;If Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-while-statement" data-toc-depth="2" data-toc-line="[21.10]{.heading-label}.&#8194;While Statement" style="toctarget:sec-while-statement"><a href="#sec-while-statement" class="localref"><span class="heading-label">21.10</span>.&#8194;While Statement</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-loop-specifications" data-toc-depth="3" data-toc-line="[21.10.0]{.heading-label}.&#8194;Loop Specifications" style="toctarget:sec-loop-specifications"><a href="#sec-loop-specifications" class="localref"><span class="heading-label">21.10.0</span>.&#8194;Loop Specifications</a></div>
+<div class="tocblock tocblock4">
+<div class="tocitem tocitem4" data-toc-target="sec-loop-invariants" data-toc-depth="4" data-toc-line="[21.10.0.0]{.heading-label}.&#8194;Loop Invariants" style="toctarget:sec-loop-invariants"><a href="#sec-loop-invariants" class="localref"><span class="heading-label">21.10.0.0</span>.&#8194;Loop Invariants</a></div>
+<div class="tocitem tocitem4" data-toc-target="sec-loop-termination" data-toc-depth="4" data-toc-line="[21.10.0.1]{.heading-label}.&#8194;Loop Termination" style="toctarget:sec-loop-termination"><a href="#sec-loop-termination" class="localref"><span class="heading-label">21.10.0.1</span>.&#8194;Loop Termination</a></div>
+<div class="tocitem tocitem4" data-toc-target="sec-loop-framing" data-toc-depth="4" data-toc-line="[21.10.0.2]{.heading-label}.&#8194;Loop Framing" style="toctarget:sec-loop-framing"><a href="#sec-loop-framing" class="localref"><span class="heading-label">21.10.0.2</span>.&#8194;Loop Framing</a></div></div></div>
+<div class="tocitem tocitem2" data-toc-target="sec-match-statement" data-toc-depth="2" data-toc-line="[21.11]{.heading-label}.&#8194;Match Statement" style="toctarget:sec-match-statement"><a href="#sec-match-statement" class="localref"><span class="heading-label">21.11</span>.&#8194;Match Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-assert-statement" data-toc-depth="2" data-toc-line="[21.12]{.heading-label}.&#8194;Assert Statement" style="toctarget:sec-assert-statement"><a href="#sec-assert-statement" class="localref"><span class="heading-label">21.12</span>.&#8194;Assert Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-assume-statement" data-toc-depth="2" data-toc-line="[21.13]{.heading-label}.&#8194;Assume Statement" style="toctarget:sec-assume-statement"><a href="#sec-assume-statement" class="localref"><span class="heading-label">21.13</span>.&#8194;Assume Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-print-statement" data-toc-depth="2" data-toc-line="[21.14]{.heading-label}.&#8194;Print Statement" style="toctarget:sec-print-statement"><a href="#sec-print-statement" class="localref"><span class="heading-label">21.14</span>.&#8194;Print Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-forall-statement" data-toc-depth="2" data-toc-line="[21.15]{.heading-label}.&#8194;Forall Statement" style="toctarget:sec-forall-statement"><a href="#sec-forall-statement" class="localref"><span class="heading-label">21.15</span>.&#8194;Forall Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-modify-statement" data-toc-depth="2" data-toc-line="[21.16]{.heading-label}.&#8194;Modify Statement" style="toctarget:sec-modify-statement"><a href="#sec-modify-statement" class="localref"><span class="heading-label">21.16</span>.&#8194;Modify Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-calc-statement" data-toc-depth="2" data-toc-line="[21.17]{.heading-label}.&#8194;Calc Statement" style="toctarget:sec-calc-statement"><a href="#sec-calc-statement" class="localref"><span class="heading-label">21.17</span>.&#8194;Calc Statement</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-skeleton-statement" data-toc-depth="2" data-toc-line="[21.18]{.heading-label}.&#8194;Skeleton Statement" style="toctarget:sec-skeleton-statement"><a href="#sec-skeleton-statement" class="localref"><span class="heading-label">21.18</span>.&#8194;Skeleton Statement</a></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-expressions" data-toc-depth="1" data-toc-line="[22]{.heading-label}.&#8194;Expressions" style="toctarget:sec-expressions"><a href="#sec-expressions" class="localref"><span class="heading-label">22</span>.&#8194;Expressions</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-top-level-expressions" data-toc-depth="2" data-toc-line="[22.0]{.heading-label}.&#8194;Top-level expressions" style="toctarget:sec-top-level-expressions"><a href="#sec-top-level-expressions" class="localref"><span class="heading-label">22.0</span>.&#8194;Top-level expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-equivalence-expressions" data-toc-depth="2" data-toc-line="[22.1]{.heading-label}.&#8194;Equivalence Expressions" style="toctarget:sec-equivalence-expressions"><a href="#sec-equivalence-expressions" class="localref"><span class="heading-label">22.1</span>.&#8194;Equivalence Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-implies-or-explies-expressions" data-toc-depth="2" data-toc-line="[22.2]{.heading-label}.&#8194;Implies or Explies Expressions" style="toctarget:sec-implies-or-explies-expressions"><a href="#sec-implies-or-explies-expressions" class="localref"><span class="heading-label">22.2</span>.&#8194;Implies or Explies Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-logical-expressions" data-toc-depth="2" data-toc-line="[22.3]{.heading-label}.&#8194;Logical Expressions" style="toctarget:sec-logical-expressions"><a href="#sec-logical-expressions" class="localref"><span class="heading-label">22.3</span>.&#8194;Logical Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-relational-expressions" data-toc-depth="2" data-toc-line="[22.4]{.heading-label}.&#8194;Relational Expressions" style="toctarget:sec-relational-expressions"><a href="#sec-relational-expressions" class="localref"><span class="heading-label">22.4</span>.&#8194;Relational Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-terms" data-toc-depth="2" data-toc-line="[22.5]{.heading-label}.&#8194;Terms" style="toctarget:sec-terms"><a href="#sec-terms" class="localref"><span class="heading-label">22.5</span>.&#8194;Terms</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-factors" data-toc-depth="2" data-toc-line="[22.6]{.heading-label}.&#8194;Factors" style="toctarget:sec-factors"><a href="#sec-factors" class="localref"><span class="heading-label">22.6</span>.&#8194;Factors</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-unary-expressions" data-toc-depth="2" data-toc-line="[22.7]{.heading-label}.&#8194;Unary Expressions" style="toctarget:sec-unary-expressions"><a href="#sec-unary-expressions" class="localref"><span class="heading-label">22.7</span>.&#8194;Unary Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-primary-expressions" data-toc-depth="2" data-toc-line="[22.8]{.heading-label}.&#8194;Primary Expressions" style="toctarget:sec-primary-expressions"><a href="#sec-primary-expressions" class="localref"><span class="heading-label">22.8</span>.&#8194;Primary Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-lambda-expressions" data-toc-depth="2" data-toc-line="[22.9]{.heading-label}.&#8194;Lambda expressions" style="toctarget:sec-lambda-expressions"><a href="#sec-lambda-expressions" class="localref"><span class="heading-label">22.9</span>.&#8194;Lambda expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-left-hand-side-expressions" data-toc-depth="2" data-toc-line="[22.10]{.heading-label}.&#8194;Left-Hand-Side Expressions" style="toctarget:sec-left-hand-side-expressions"><a href="#sec-left-hand-side-expressions" class="localref"><span class="heading-label">22.10</span>.&#8194;Left-Hand-Side Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-right-hand-side-expressions" data-toc-depth="2" data-toc-line="[22.11]{.heading-label}.&#8194;Right-Hand-Side Expressions" style="toctarget:sec-right-hand-side-expressions"><a href="#sec-right-hand-side-expressions" class="localref"><span class="heading-label">22.11</span>.&#8194;Right-Hand-Side Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-array-allocation" data-toc-depth="2" data-toc-line="[22.12]{.heading-label}.&#8194;Array Allocation" style="toctarget:sec-array-allocation"><a href="#sec-array-allocation" class="localref"><span class="heading-label">22.12</span>.&#8194;Array Allocation</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-object-allocation" data-toc-depth="2" data-toc-line="[22.13]{.heading-label}.&#8194;Object Allocation" style="toctarget:sec-object-allocation"><a href="#sec-object-allocation" class="localref"><span class="heading-label">22.13</span>.&#8194;Object Allocation</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-havoc-right-hand-side" data-toc-depth="2" data-toc-line="[22.14]{.heading-label}.&#8194;Havoc Right-Hand-Side" style="toctarget:sec-havoc-right-hand-side"><a href="#sec-havoc-right-hand-side" class="localref"><span class="heading-label">22.14</span>.&#8194;Havoc Right-Hand-Side</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-constant-or-atomic-expressions" data-toc-depth="2" data-toc-line="[22.15]{.heading-label}.&#8194;Constant Or Atomic Expressions" style="toctarget:sec-constant-or-atomic-expressions"><a href="#sec-constant-or-atomic-expressions" class="localref"><span class="heading-label">22.15</span>.&#8194;Constant Or Atomic Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-literal-expressions" data-toc-depth="2" data-toc-line="[22.16]{.heading-label}.&#8194;Literal Expressions" style="toctarget:sec-literal-expressions"><a href="#sec-literal-expressions" class="localref"><span class="heading-label">22.16</span>.&#8194;Literal Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-fresh-expressions" data-toc-depth="2" data-toc-line="[22.17]{.heading-label}.&#8194;Fresh Expressions" style="toctarget:sec-fresh-expressions"><a href="#sec-fresh-expressions" class="localref"><span class="heading-label">22.17</span>.&#8194;Fresh Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-old-expressions" data-toc-depth="2" data-toc-line="[22.18]{.heading-label}.&#8194;Old Expressions" style="toctarget:sec-old-expressions"><a href="#sec-old-expressions" class="localref"><span class="heading-label">22.18</span>.&#8194;Old Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-cardinality-expressions" data-toc-depth="2" data-toc-line="[22.19]{.heading-label}.&#8194;Cardinality Expressions" style="toctarget:sec-cardinality-expressions"><a href="#sec-cardinality-expressions" class="localref"><span class="heading-label">22.19</span>.&#8194;Cardinality Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-numeric-conversion-expressions" data-toc-depth="2" data-toc-line="[22.20]{.heading-label}.&#8194;Numeric Conversion Expressions" style="toctarget:sec-numeric-conversion-expressions"><a href="#sec-numeric-conversion-expressions" class="localref"><span class="heading-label">22.20</span>.&#8194;Numeric Conversion Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-parenthesized-expression" data-toc-depth="2" data-toc-line="[22.21]{.heading-label}.&#8194;Parenthesized Expression" style="toctarget:sec-parenthesized-expression"><a href="#sec-parenthesized-expression" class="localref"><span class="heading-label">22.21</span>.&#8194;Parenthesized Expression</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-sequence-display-expression" data-toc-depth="2" data-toc-line="[22.22]{.heading-label}.&#8194;Sequence Display Expression" style="toctarget:sec-sequence-display-expression"><a href="#sec-sequence-display-expression" class="localref"><span class="heading-label">22.22</span>.&#8194;Sequence Display Expression</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-set-display-expression" data-toc-depth="2" data-toc-line="[22.23]{.heading-label}.&#8194;Set Display Expression" style="toctarget:sec-set-display-expression"><a href="#sec-set-display-expression" class="localref"><span class="heading-label">22.23</span>.&#8194;Set Display Expression</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-multiset-display-or-cast-expression" data-toc-depth="2" data-toc-line="[22.24]{.heading-label}.&#8194;Multiset Display or Cast Expression" style="toctarget:sec-multiset-display-or-cast-expression"><a href="#sec-multiset-display-or-cast-expression" class="localref"><span class="heading-label">22.24</span>.&#8194;Multiset Display or Cast Expression</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-map-display-expression" data-toc-depth="2" data-toc-line="[22.25]{.heading-label}.&#8194;Map Display Expression" style="toctarget:sec-map-display-expression"><a href="#sec-map-display-expression" class="localref"><span class="heading-label">22.25</span>.&#8194;Map Display Expression</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-endless-expression" data-toc-depth="2" data-toc-line="[22.26]{.heading-label}.&#8194;Endless Expression" style="toctarget:sec-endless-expression"><a href="#sec-endless-expression" class="localref"><span class="heading-label">22.26</span>.&#8194;Endless Expression</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-if-expression" data-toc-depth="2" data-toc-line="[22.27]{.heading-label}.&#8194;If Expression" style="toctarget:sec-if-expression"><a href="#sec-if-expression" class="localref"><span class="heading-label">22.27</span>.&#8194;If Expression</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-case-bindings-and-patterns" data-toc-depth="2" data-toc-line="[22.28]{.heading-label}.&#8194;Case Bindings and Patterns" style="toctarget:sec-case-bindings-and-patterns"><a href="#sec-case-bindings-and-patterns" class="localref"><span class="heading-label">22.28</span>.&#8194;Case Bindings and Patterns</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-match-expression" data-toc-depth="2" data-toc-line="[22.29]{.heading-label}.&#8194;Match Expression" style="toctarget:sec-match-expression"><a href="#sec-match-expression" class="localref"><span class="heading-label">22.29</span>.&#8194;Match Expression</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-quantifier-expression" data-toc-depth="2" data-toc-line="[22.30]{.heading-label}.&#8194;Quantifier Expression" style="toctarget:sec-quantifier-expression"><a href="#sec-quantifier-expression" class="localref"><span class="heading-label">22.30</span>.&#8194;Quantifier Expression</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-set-comprehension-expressions" data-toc-depth="2" data-toc-line="[22.31]{.heading-label}.&#8194;Set Comprehension Expressions" style="toctarget:sec-set-comprehension-expressions"><a href="#sec-set-comprehension-expressions" class="localref"><span class="heading-label">22.31</span>.&#8194;Set Comprehension Expressions</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-statements-in-an-expression" data-toc-depth="2" data-toc-line="[22.32]{.heading-label}.&#8194;Statements in an Expression" style="toctarget:sec-statements-in-an-expression"><a href="#sec-statements-in-an-expression" class="localref"><span class="heading-label">22.32</span>.&#8194;Statements in an Expression</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-let-expression" data-toc-depth="2" data-toc-line="[22.33]{.heading-label}.&#8194;Let Expression" style="toctarget:sec-let-expression"><a href="#sec-let-expression" class="localref"><span class="heading-label">22.33</span>.&#8194;Let Expression</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-map-comprehension-expression" data-toc-depth="2" data-toc-line="[22.34]{.heading-label}.&#8194;Map Comprehension Expression" style="toctarget:sec-map-comprehension-expression"><a href="#sec-map-comprehension-expression" class="localref"><span class="heading-label">22.34</span>.&#8194;Map Comprehension Expression</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-name-segment" data-toc-depth="2" data-toc-line="[22.35]{.heading-label}.&#8194;Name Segment" style="toctarget:sec-name-segment"><a href="#sec-name-segment" class="localref"><span class="heading-label">22.35</span>.&#8194;Name Segment</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-hash-call" data-toc-depth="2" data-toc-line="[22.36]{.heading-label}.&#8194;Hash Call" style="toctarget:sec-hash-call"><a href="#sec-hash-call" class="localref"><span class="heading-label">22.36</span>.&#8194;Hash Call</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-suffix" data-toc-depth="2" data-toc-line="[22.37]{.heading-label}.&#8194;Suffix" style="toctarget:sec-suffix"><a href="#sec-suffix" class="localref"><span class="heading-label">22.37</span>.&#8194;Suffix</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-augmented-dot-suffix" data-toc-depth="3" data-toc-line="[22.37.0]{.heading-label}.&#8194;Augmented Dot Suffix" style="toctarget:sec-augmented-dot-suffix"><a href="#sec-augmented-dot-suffix" class="localref"><span class="heading-label">22.37.0</span>.&#8194;Augmented Dot Suffix</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-datatype-update-suffix" data-toc-depth="3" data-toc-line="[22.37.1]{.heading-label}.&#8194;Datatype Update Suffix" style="toctarget:sec-datatype-update-suffix"><a href="#sec-datatype-update-suffix" class="localref"><span class="heading-label">22.37.1</span>.&#8194;Datatype Update Suffix</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-subsequence-suffix" data-toc-depth="3" data-toc-line="[22.37.2]{.heading-label}.&#8194;Subsequence Suffix" style="toctarget:sec-subsequence-suffix"><a href="#sec-subsequence-suffix" class="localref"><span class="heading-label">22.37.2</span>.&#8194;Subsequence Suffix</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-slices-by-length-suffix" data-toc-depth="3" data-toc-line="[22.37.3]{.heading-label}.&#8194;Slices By Length Suffix" style="toctarget:sec-slices-by-length-suffix"><a href="#sec-slices-by-length-suffix" class="localref"><span class="heading-label">22.37.3</span>.&#8194;Slices By Length Suffix</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-sequence-update-suffix" data-toc-depth="3" data-toc-line="[22.37.4]{.heading-label}.&#8194;Sequence Update Suffix" style="toctarget:sec-sequence-update-suffix"><a href="#sec-sequence-update-suffix" class="localref"><span class="heading-label">22.37.4</span>.&#8194;Sequence Update Suffix</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-selection-suffix" data-toc-depth="3" data-toc-line="[22.37.5]{.heading-label}.&#8194;Selection Suffix" style="toctarget:sec-selection-suffix"><a href="#sec-selection-suffix" class="localref"><span class="heading-label">22.37.5</span>.&#8194;Selection Suffix</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-argument-list-suffix" data-toc-depth="3" data-toc-line="[22.37.6]{.heading-label}.&#8194;Argument List Suffix" style="toctarget:sec-argument-list-suffix"><a href="#sec-argument-list-suffix" class="localref"><span class="heading-label">22.37.6</span>.&#8194;Argument List Suffix</a></div></div>
+<div class="tocitem tocitem2" data-toc-target="sec-expression-lists" data-toc-depth="2" data-toc-line="[22.38]{.heading-label}.&#8194;Expression Lists" style="toctarget:sec-expression-lists"><a href="#sec-expression-lists" class="localref"><span class="heading-label">22.38</span>.&#8194;Expression Lists</a></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-module-refinement" data-toc-depth="1" data-toc-line="[23]{.heading-label}.&#8194;Module Refinement" style="toctarget:sec-module-refinement"><a href="#sec-module-refinement" class="localref"><span class="heading-label">23</span>.&#8194;Module Refinement</a></div>
+<div class="tocitem tocitem1" data-toc-target="sec-attributes" data-toc-depth="1" data-toc-line="[24]{.heading-label}.&#8194;Attributes" style="toctarget:sec-attributes"><a href="#sec-attributes" class="localref"><span class="heading-label">24</span>.&#8194;Attributes</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-dafny-attribute-implementation-details" data-toc-depth="2" data-toc-line="[24.0]{.heading-label}.&#8194;Dafny Attribute Implementation Details" style="toctarget:sec-dafny-attribute-implementation-details"><a href="#sec-dafny-attribute-implementation-details" class="localref"><span class="heading-label">24.0</span>.&#8194;Dafny Attribute Implementation Details</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-dafny-attributes" data-toc-depth="2" data-toc-line="[24.1]{.heading-label}.&#8194;Dafny Attributes" style="toctarget:sec-dafny-attributes"><a href="#sec-dafny-attributes" class="localref"><span class="heading-label">24.1</span>.&#8194;Dafny Attributes</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-assumption" data-toc-depth="3" data-toc-line="[24.1.0]{.heading-label}.&#8194;assumption" style="toctarget:sec-assumption"><a href="#sec-assumption" class="localref"><span class="heading-label">24.1.0</span>.&#8194;assumption</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-autoreq-boolexpr" data-toc-depth="3" data-toc-line="[24.1.1]{.heading-label}.&#8194;autoReq boolExpr" style="toctarget:sec-autoreq-boolexpr"><a href="#sec-autoreq-boolexpr" class="localref"><span class="heading-label">24.1.1</span>.&#8194;autoReq boolExpr</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-autocontracts" data-toc-depth="3" data-toc-line="[24.1.2]{.heading-label}.&#8194;autocontracts" style="toctarget:sec-autocontracts"><a href="#sec-autocontracts" class="localref"><span class="heading-label">24.1.2</span>.&#8194;autocontracts</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-axiom" data-toc-depth="3" data-toc-line="[24.1.3]{.heading-label}.&#8194;axiom" style="toctarget:sec-axiom"><a href="#sec-axiom" class="localref"><span class="heading-label">24.1.3</span>.&#8194;axiom</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-compile" data-toc-depth="3" data-toc-line="[24.1.4]{.heading-label}.&#8194;compile" style="toctarget:sec-compile"><a href="#sec-compile" class="localref"><span class="heading-label">24.1.4</span>.&#8194;compile</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-decl" data-toc-depth="3" data-toc-line="[24.1.5]{.heading-label}.&#8194;decl" style="toctarget:sec-decl"><a href="#sec-decl" class="localref"><span class="heading-label">24.1.5</span>.&#8194;decl</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-fuel" data-toc-depth="3" data-toc-line="[24.1.6]{.heading-label}.&#8194;fuel" style="toctarget:sec-fuel"><a href="#sec-fuel" class="localref"><span class="heading-label">24.1.6</span>.&#8194;fuel</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-heapquantifier" data-toc-depth="3" data-toc-line="[24.1.7]{.heading-label}.&#8194;heapQuantifier" style="toctarget:sec-heapquantifier"><a href="#sec-heapquantifier" class="localref"><span class="heading-label">24.1.7</span>.&#8194;heapQuantifier</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-imported" data-toc-depth="3" data-toc-line="[24.1.8]{.heading-label}.&#8194;imported" style="toctarget:sec-imported"><a href="#sec-imported" class="localref"><span class="heading-label">24.1.8</span>.&#8194;imported</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-induction" data-toc-depth="3" data-toc-line="[24.1.9]{.heading-label}.&#8194;induction" style="toctarget:sec-induction"><a href="#sec-induction" class="localref"><span class="heading-label">24.1.9</span>.&#8194;induction</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-layerquantifier" data-toc-depth="3" data-toc-line="[24.1.10]{.heading-label}.&#8194;layerQuantifier" style="toctarget:sec-layerquantifier"><a href="#sec-layerquantifier" class="localref"><span class="heading-label">24.1.10</span>.&#8194;layerQuantifier</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-nativetype" data-toc-depth="3" data-toc-line="[24.1.11]{.heading-label}.&#8194;nativeType" style="toctarget:sec-nativetype"><a href="#sec-nativetype" class="localref"><span class="heading-label">24.1.11</span>.&#8194;nativeType</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-opaque" data-toc-depth="3" data-toc-line="[24.1.12]{.heading-label}.&#8194;opaque" style="toctarget:sec-opaque"><a href="#sec-opaque" class="localref"><span class="heading-label">24.1.12</span>.&#8194;opaque</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-opaque-full" data-toc-depth="3" data-toc-line="[24.1.13]{.heading-label}.&#8194;opaque full" style="toctarget:sec-opaque-full"><a href="#sec-opaque-full" class="localref"><span class="heading-label">24.1.13</span>.&#8194;opaque full</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-prependasserttoken" data-toc-depth="3" data-toc-line="[24.1.14]{.heading-label}.&#8194;prependAssertToken" style="toctarget:sec-prependasserttoken"><a href="#sec-prependasserttoken" class="localref"><span class="heading-label">24.1.14</span>.&#8194;prependAssertToken</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-tailrecursion" data-toc-depth="3" data-toc-line="[24.1.15]{.heading-label}.&#8194;tailrecursion" style="toctarget:sec-tailrecursion"><a href="#sec-tailrecursion" class="localref"><span class="heading-label">24.1.15</span>.&#8194;tailrecursion</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-timelimitmultiplier" data-toc-depth="3" data-toc-line="[24.1.16]{.heading-label}.&#8194;timeLimitMultiplier" style="toctarget:sec-timelimitmultiplier"><a href="#sec-timelimitmultiplier" class="localref"><span class="heading-label">24.1.16</span>.&#8194;timeLimitMultiplier</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-trigger" data-toc-depth="3" data-toc-line="[24.1.17]{.heading-label}.&#8194;trigger" style="toctarget:sec-trigger"><a href="#sec-trigger" class="localref"><span class="heading-label">24.1.17</span>.&#8194;trigger</a></div>
+<div class="tocitem tocitem3" data-toc-target="sec-typequantifier" data-toc-depth="3" data-toc-line="[24.1.18]{.heading-label}.&#8194;typeQuantifier" style="toctarget:sec-typequantifier"><a href="#sec-typequantifier" class="localref"><span class="heading-label">24.1.18</span>.&#8194;typeQuantifier</a></div></div>
+<div class="tocitem tocitem2" data-toc-target="sec-boogie-attributes" data-toc-depth="2" data-toc-line="[24.2]{.heading-label}.&#8194;Boogie Attributes" style="toctarget:sec-boogie-attributes"><a href="#sec-boogie-attributes" class="localref"><span class="heading-label">24.2</span>.&#8194;Boogie Attributes</a></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-dafny-users-guide" data-toc-depth="1" data-toc-line="[25]{.heading-label}.&#8194;Dafny User\&#39;s Guide" style="toctarget:sec-dafny-users-guide"><a href="#sec-dafny-users-guide" class="localref"><span class="heading-label">25</span>.&#8194;Dafny User&#39;s Guide</a></div>
+<div class="tocblock tocblock2">
+<div class="tocitem tocitem2" data-toc-target="sec-installing-dafny-from-binaries" data-toc-depth="2" data-toc-line="[25.0]{.heading-label}.&#8194;Installing Dafny From Binaries" style="toctarget:sec-installing-dafny-from-binaries"><a href="#sec-installing-dafny-from-binaries" class="localref"><span class="heading-label">25.0</span>.&#8194;Installing Dafny From Binaries</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-building-dafny-from-source" data-toc-depth="2" data-toc-line="[25.1]{.heading-label}.&#8194;Building Dafny from Source" style="toctarget:sec-building-dafny-from-source"><a href="#sec-building-dafny-from-source" class="localref"><span class="heading-label">25.1</span>.&#8194;Building Dafny from Source</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-using-dafny-from-visual-studio" data-toc-depth="2" data-toc-line="[25.2]{.heading-label}.&#8194;Using Dafny From Visual Studio" style="toctarget:sec-using-dafny-from-visual-studio"><a href="#sec-using-dafny-from-visual-studio" class="localref"><span class="heading-label">25.2</span>.&#8194;Using Dafny From Visual Studio</a></div>
+<div class="tocitem tocitem2" data-toc-target="sec-using-dafny-from-the-command-line" data-toc-depth="2" data-toc-line="[25.3]{.heading-label}.&#8194;Using Dafny From the Command Line" style="toctarget:sec-using-dafny-from-the-command-line"><a href="#sec-using-dafny-from-the-command-line" class="localref"><span class="heading-label">25.3</span>.&#8194;Using Dafny From the Command Line</a></div>
+<div class="tocblock tocblock3">
+<div class="tocitem tocitem3" data-toc-target="sec-dafny-command-line-options" data-toc-depth="3" data-toc-line="[25.3.0]{.heading-label}.&#8194;Dafny Command Line Options" style="toctarget:sec-dafny-command-line-options"><a href="#sec-dafny-command-line-options" class="localref"><span class="heading-label">25.3.0</span>.&#8194;Dafny Command Line Options</a></div></div></div>
+<div class="tocitem tocitem1" data-toc-target="sec-references" data-toc-depth="1" data-toc-line="[26]{.heading-label}.&#8194;References" style="toctarget:sec-references"><a href="#sec-references" class="localref"><span class="heading-label">26</span>.&#8194;References</a></div>
+<div class="tocitem tocitem1" data-toc-target="sec-references" data-toc-depth="1" data-toc-line="References" style="toctarget:sec-references"><a href="#sec-references" class="localref">References</a></div></div></nav><h2 id="sec-introduction" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">0</span>.&#8194;</span>Introduction</h2>
+<p class="p noindent">Dafny&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#leino:dafny:lpar16" title="K.&#160;Rustan&#160;M. Leino.
+Dafny: An automatic program verifier for functional correctness." class="bibref localref" style="target-element:bibitem"><span class="cite-number">18</span></a>]</span> is a programming language with built-in specification constructs.
+The Dafny static program verifier can be used to verify the functional
+correctness of programs.
+</p>
+<p class="p indent">The Dafny programming language is designed to support the static
+verification of programs. It is imperative, sequential, supports generic
+classes, methods and functions, dynamic allocation, inductive and
+co-inductive datatypes, and specification constructs. The
+specifications include pre- and postconditions, frame specifications
+(read and write sets), and termination metrics. To further support
+specifications, the language also offers updatable ghost variables,
+recursive functions, and types like sets and sequences. Specifications
+and ghost constructs are used only during verification; the compiler
+omits them from the executable code.
+</p>
+<p class="p indent">The Dafny verifier is run as part of the compiler. As such, a programmer
+interacts with it much in the same way as with the static type
+checkerâ€â€when the tool produces errors, the programmer responds by
+changing the program’s type declarations, specifications, and statements.
+</p>
+<p class="p indent">The easiest way to try out&nbsp;<a href="http://rise4fun.com/Dafny">Dafny is in your web browser at
+rise4fun</a><span class="citations" style="target-element:bibitem">[<a href="#rise4fun:dafny" title="K.&#160;Rustan&#160;M. Leino.
+Try dafny in your browser, c." class="bibref localref" style="target-element:bibitem"><span class="cite-number">15</span></a>]</span>. Once you get a bit
+more serious, you may prefer to&nbsp;<a href="http://dafny.codeplex.com/">download</a> it
+to run it on your machine. Although Dafny can be run from the command
+line (on Windows or other platforms), the preferred way to run it is in
+Microsoft Visual Studio 2012 (or newer) or using emacs, where the Dafny
+verifier runs in the background while the programmer is editing the
+program.
+</p>
+<p class="p indent">The Dafny verifier is powered
+by&nbsp;<a href="http://research.microsoft.com/boogie">Boogie</a>
+<span class="citations" style="target-element:bibitem">[<a href="#boogie:architecture" title="Mike Barnett, Bor-Yuh&#160;Evan Chang, Robert DeLine, Bart Jacobs, and K.&#160;Rustan&#160;M. Leino.
+Boogie: A modular reusable verifier for object-oriented programs.
+In Frank&#160;S. de&#160;Boer, Marcello&#160;M. Bonsangue, Susanne Graf, and Willem-Paul de&#160;Roever, editors, Formal Methods for Components and Objects: 4th International Symposium, FMCO 2005, volume 4111, pages 364&#8211;387. Springer, September 2006." class="bibref localref" style="target-element:bibitem"><span class="cite-number">0</span></a>, <a href="#leino:boogie2-refman" title="K.&#160;Rustan&#160;M. Leino.
+This is Boogie 2." class="bibref localref" style="target-element:bibitem"><span class="cite-number">16</span></a>, <a href="#leinoruemmer:boogie2" title="K.&#160;Rustan&#160;M. Leino and Philipp R&#252;mmer.
+A polymorphic intermediate verification language: Design and logical encoding.
+In Javier Esparza and Rupak Majumdar, editors, Tools and Algorithms for the Construction and Analysis of Systems, 16th International Conference, TACAS 2010, volume 6015, pages 312&#8211;327. Springer, March 2010." class="bibref localref" style="target-element:bibitem"><span class="cite-number">23</span></a>]</span>
+and&nbsp;<a href="https://github.com/z3prover">Z3</a><span class="citations" style="target-element:bibitem">[<a href="#demourabjorner:z3:overview" title="Leonardo de&#160;Moura and Nikolaj Bj&#248;rner.
+Z3: An efficient SMT solver." class="bibref localref" style="target-element:bibitem"><span class="cite-number">4</span></a>]</span>.
+</p>
+<p class="p indent">From verified programs, the Dafny compiler produces code (<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">.dll</code> or
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">.exe</code>) for the .NET platform via intermediate C# files. However, the
+facilities for interfacing with other .NET code are minimal.
+</p>
+<p class="p indent">This is the reference manual for the Dafny verification system. It is
+based on the following references:
+<span class="citations" style="target-element:bibitem">[<a href="#msr:dafny:source" title="K.&#160;Rustan M.&#160;Leino et&#160;al.
+Dafny source code." class="bibref localref" style="target-element:bibitem"><span class="cite-number">5</span></a>, <a href="#msr:dafny:main" title="K.&#160;Rustan&#160;M. Leino.
+Main microsoft research dafny web page, a." class="bibref localref" style="target-element:bibitem"><span class="cite-number">13</span></a>&#8211;<a href="#rise4fun:dafny" title="K.&#160;Rustan&#160;M. Leino.
+Try dafny in your browser, c." class="bibref localref" style="target-element:bibitem"><span class="cite-number">15</span></a>, <a href="#leino:dafny:lpar16" title="K.&#160;Rustan&#160;M. Leino.
+Dafny: An automatic program verifier for functional correctness." class="bibref localref" style="target-element:bibitem"><span class="cite-number">18</span></a>, <a href="#leino:dafny:coinduction" title="K.&#160;Rustan&#160;M. Leino and Michal Moskal.
+Co-induction simply: Automatic co-inductive proofs in a program verifier.
+Manuscript KRML 230, 2014a.
+Available at http://research.microsoft.com/en-us/um/people/leino/papers/krml230.pdf." class="bibref localref" style="target-element:bibitem"><span class="cite-number">20</span></a>, <a href="#leino:dafny:calc" title="K.&#160;Rustan&#160;M. Leino and Nadia Polikarpova.
+Verified calculations." class="bibref localref" style="target-element:bibitem"><span class="cite-number">22</span></a>]</span>
+</p>
+<p class="p indent">The main part of the reference manual is in top down order except for an
+initial section that deals with the lowest level constructs.
+</p><h3 id="sec-dafny-example" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">0.0</span>.&#8194;</span>Dafny Example</h3>
+<p class="p noindent">To give a flavor of Dafny, here is the solution to a competition problem.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:darkgreen">// VSComp 2010, problem 3, find a 0 in a linked list and return how many</span>
+<span style="color:darkgreen">// nodes were skipped until the first 0 (or end-of-list) was found.</span>
+<span style="color:darkgreen">// Rustan Leino, 18 August 2010.</span>
+<span style="color:darkgreen">// </span>
+<span style="color:darkgreen">// The difficulty in this problem lies in specifying what the return</span>
+<span style="color:darkgreen">// value &#39;r&#39; denotes and in proving that the program terminates. Both of</span>
+<span style="color:darkgreen">// these are addressed by declaring a ghost field &#39;List&#39; in each</span>
+<span style="color:darkgreen">// linked-list node, abstractly representing the linked-list elements</span>
+<span style="color:darkgreen">// from the node to the end of the linked list. The specification can</span>
+<span style="color:darkgreen">// now talk about that sequence of elements and can use &#39;r&#39; as an index</span>
+<span style="color:darkgreen">// into the sequence, and termination can be proved from the fact that</span>
+<span style="color:darkgreen">// all sequences in Dafny are finite.</span>
+<span style="color:darkgreen">// </span>
+<span style="color:darkgreen">// We only want to deal with linked lists whose &#39;List&#39; field is properly</span>
+<span style="color:darkgreen">// filled in (which can only happen in an acyclic list, for example). To</span>
+<span style="color:darkgreen">// that avail, the standard idiom in Dafny is to declare a predicate</span>
+<span style="color:darkgreen">// &#39;Valid()&#39; that is true of an object when the data structure</span>
+<span style="color:darkgreen">// representing object&#39;s abstract value is properly formed. The</span>
+<span style="color:darkgreen">// definition of &#39;Valid()&#39; is what one intuitively would think of as the</span>
+<span style="color:darkgreen">// &#39;&#39;object invariant&#39;&#39;, and it is mentioned explicitly in method pre-</span>
+<span style="color:darkgreen">// and postconditions. As part of this standard idiom, one also declared</span>
+<span style="color:darkgreen">// a ghost variable &#39;Repr&#39; that is maintained as the set of objects that</span>
+<span style="color:darkgreen">// make up the representation of the aggregate object--in this case, the</span>
+<span style="color:darkgreen">// Node itself and all its successors.</span>
+
+<span style="color:blue">class</span> Node {
+ <span style="color:blue">ghost</span> <span style="color:blue">var</span> List: <span style="color:teal">seq</span>&lt;<span style="color:teal">int</span>&gt;
+ <span style="color:blue">ghost</span> <span style="color:blue">var</span> Repr: <span style="color:teal">set</span>&lt;Node&gt;
+ <span style="color:blue">var</span> head: <span style="color:teal">int</span>
+ <span style="color:blue">var</span> next: Node
+
+ <span style="color:blue">predicate</span> Valid()
+ <span style="color:purple">reads</span> <span style="color:blue">this</span>, Repr
+ {
+ <span style="color:blue">this</span> <span style="color:blue">in</span> Repr &amp;&amp;
+ <span class="constant" style="color:purple">1</span> &lt;= |List| &amp;&amp; List[<span class="constant" style="color:purple">0</span>] == head &amp;&amp;
+ (next == <span style="color:blue">null</span> ==&gt; |List| == <span class="constant" style="color:purple">1</span>) &amp;&amp;
+ (next != <span style="color:blue">null</span> ==&gt;
+ next <span style="color:blue">in</span> Repr &amp;&amp; next.Repr &lt;= Repr &amp;&amp; <span style="color:blue">this</span> !<span style="color:blue">in</span> next.Repr &amp;&amp;
+ next.Valid() &amp;&amp; next.List == List[<span class="constant" style="color:purple">1</span>..])
+ }
+
+ <span style="color:blue">static</span> <span style="color:blue">method</span> Cons(x: <span style="color:teal">int</span>, tail: Node) <span style="color:blue">returns</span> (n: Node)
+ <span style="color:purple">requires</span> tail == <span style="color:blue">null</span> || tail.Valid()
+ <span style="color:purple">ensures</span> n != <span style="color:blue">null</span> &amp;&amp; n.Valid()
+ <span style="color:purple">ensures</span> <span style="color:blue">if</span> tail == <span style="color:blue">null</span> <span style="color:blue">then</span> n.List == [x]
+ <span style="color:blue">else</span> n.List == [x] + tail.List
+ {
+ n <span style="color:blue">:=</span> <span style="color:blue">new</span> Node;
+ n.head, n.next <span style="color:blue">:=</span> x, tail;
+ <span style="color:blue">if</span> (tail == <span style="color:blue">null</span>) {
+ n.List <span style="color:blue">:=</span> [x];
+ n.Repr <span style="color:blue">:=</span> {n};
+ } <span style="color:blue">else</span> {
+ n.List <span style="color:blue">:=</span> [x] + tail.List;
+ n.Repr <span style="color:blue">:=</span> {n} + tail.Repr;
+ }
+ }
+}
+
+<span style="color:blue">method</span> Search(ll: Node) <span style="color:blue">returns</span> (r: <span style="color:teal">int</span>)
+ <span style="color:purple">requires</span> ll == <span style="color:blue">null</span> || ll.Valid()
+ <span style="color:purple">ensures</span> ll == <span style="color:blue">null</span> ==&gt; r == <span class="constant" style="color:purple">0</span>
+ <span style="color:purple">ensures</span> ll != <span style="color:blue">null</span> ==&gt;
+ <span class="constant" style="color:purple">0</span> &lt;= r &amp;&amp; r &lt;= |ll.List| &amp;&amp;
+ (r &lt; |ll.List| ==&gt; ll.List[r] == <span class="constant" style="color:purple">0</span> &amp;&amp; <span class="constant" style="color:purple">0</span> !<span style="color:blue">in</span> ll.List[..r]) &amp;&amp;
+ (r == |ll.List| ==&gt; <span class="constant" style="color:purple">0</span> !<span style="color:blue">in</span> ll.List)
+{
+ <span style="color:blue">if</span> (ll == <span style="color:blue">null</span>) {
+ r <span style="color:blue">:=</span> <span class="constant" style="color:purple">0</span>;
+ } <span style="color:blue">else</span> {
+ <span style="color:blue">var</span> jj,i <span style="color:blue">:=</span> ll,<span class="constant" style="color:purple">0</span>;
+ <span style="color:blue">while</span> (jj != <span style="color:blue">null</span> &amp;&amp; jj.head != <span class="constant" style="color:purple">0</span>)
+ <span style="color:purple">invariant</span> jj != <span style="color:blue">null</span> ==&gt; jj.Valid() &amp;&amp; i + |jj.List| == |ll.List| &amp;&amp;
+ ll.List[i..] == jj.List
+ <span style="color:purple">invariant</span> jj == <span style="color:blue">null</span> ==&gt; i == |ll.List|
+ <span style="color:purple">invariant</span> <span class="constant" style="color:purple">0</span> !<span style="color:blue">in</span> ll.List[..i]
+ <span style="color:purple">decreases</span> |ll.List| - i
+ {
+ jj <span style="color:blue">:=</span> jj.next;
+ i <span style="color:blue">:=</span> i + <span class="constant" style="color:purple">1</span>;
+ }
+ r <span style="color:blue">:=</span> i;
+ }
+}
+
+<span style="color:blue">method</span> Main()
+{
+ <span style="color:blue">var</span> list: Node <span style="color:blue">:=</span> <span style="color:blue">null</span>;
+ list <span style="color:blue">:=</span> list.Cons(<span class="constant" style="color:purple">0</span>, list);
+ list <span style="color:blue">:=</span> list.Cons(<span class="constant" style="color:purple">5</span>, list);
+ list <span style="color:blue">:=</span> list.Cons(<span class="constant" style="color:purple">0</span>, list);
+ list <span style="color:blue">:=</span> list.Cons(<span class="constant" style="color:purple">8</span>, list);
+ <span style="color:blue">var</span> r <span style="color:blue">:=</span> Search(list);
+ <span style="color:blue">print</span> <span style="color:maroon">&quot;</span><span style="color:maroon">Search returns </span><span style="color:maroon">&quot;</span>, r, <span style="color:maroon">&quot;</span><span style="color:gray">\n</span><span style="color:maroon">&quot;</span>;
+ <span style="color:blue">assert</span> r == <span class="constant" style="color:purple">1</span>;
+}</code></pre><h2 id="sec-lexical-and-low-level-grammar" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">1</span>.&#8194;</span>Lexical and Low Level Grammar</h2>
+<p class="p noindent">Dafny uses the Coco/R lexer and parser generator for its lexer and parser
+(<a href="http://www.ssw.uni-linz.ac.at/Research/Projects/Coco">http://www.ssw.uni-linz.ac.at/Research/Projects/Coco</a>)<span class="citations" style="target-element:bibitem">[<a href="#linz:coco" title="Hanspeter M&#246;ssenb&#246;ck, Markus L&#246;berbauer, and Albrecht W&#246;&#223;.
+The compiler generator coco/r.
+Open source from University of Linz, 2013.
+Available at http://www.ssw.uni-linz.ac.at/Research/Projects/Coco/." class="bibref localref" style="target-element:bibitem"><span class="cite-number">27</span></a>]</span>.
+The Dafny input file to Coco/R is the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Dafny.atg</code> file in the source tree.
+A Coco/R input file consists of code written in the target language
+(e.g. C#) intermixed with these special sections:
+</p>
+<ol class="ol compact" start="0">
+<li class="li ol-li compact-li">The Characters section which defines classes of characters that are used
+in defining the lexer (Section&nbsp;<a href="#sec-character-classes" title="1.0.&#8194;Character Classes" class="localref" style="target-element:h2"><span class="heading-label">1.0</span></a>).
+</li>
+<li class="li ol-li compact-li">The Tokens section which defines the lexical tokens (Section&nbsp;<a href="#sec-tokens" title="1.1.&#8194;Tokens" class="localref" style="target-element:h2"><span class="heading-label">1.1</span></a>).
+</li>
+<li class="li ol-li compact-li">The Productions section which defines the grammar. The grammar productions
+are distributed in the later parts of this document in the parts where
+those constructs are explained.
+</li></ol>
+
+<p class="p noindent">The grammar presented in this document was derived from the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Dafny.atg</code>
+file but has been simplified by removing details that, though needed by
+the parser, are not needed to understand the grammar. In particular, the
+following transformation have been performed.
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">The semantics actions, enclosed by &#8220;(.&#8221; and &#8220;.)&#8221;, where removed.
+</li>
+<li class="li ul-li list-star-li compact-li">There are some elements in the grammar used for error recovery
+(&#8220;SYNC&#8221;). These were removed.
+</li>
+<li class="li ul-li list-star-li compact-li">There are some elements in the grammar for resolving conflicts
+(&#8220;IF(b)&#8221;). These have been removed.
+</li>
+<li class="li ul-li list-star-li compact-li">Some comments related to Coco/R parsing details have been removed.
+</li>
+<li class="li ul-li list-star-li compact-li">A Coco/R grammar is an attributed grammar where the attributes enable
+the productions to have input and output parameters. These attributes
+were removed except that boolean input parameters that affect
+the parsing are kept.
+
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">In our representation we represent these
+in a definition by giving the names of the parameters following
+the non-terminal name. For example <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">entity1(allowsX)</code>.
+</li>
+<li class="li ul-li list-star-li compact-li">In the case of uses of the parameter, the common case is that the
+parameter is just passed to a lower-level non-terminal. In that
+case we just give the name, e.g. <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">entity2(allowsX)</code>.
+</li>
+<li class="li ul-li list-star-li compact-li">If we want to given an explicit value to a parameter, we specify it in
+a keyword notation like this: <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">entity2(allowsX: <span style="color:blue">true</span>)</code>.
+</li>
+<li class="li ul-li list-star-li compact-li">
+<p>In some cases the value to be passed depends on the grammatical context.
+In such cases we give a description of the conditions under which the
+parameter is true, enclosed in parenthesis. For example:
+</p>
+<p> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">FunctionSignatureOrEllipsis_(allowGhostKeyword: (<span style="color:maroon">&quot;</span><span style="color:maroon">method</span><span style="color:maroon">&quot;</span> present))</code>
+</p>means that the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">allowGhostKeyword</code> parameter is true if the
+&#8220;method&#8221; keyword was given in the associated <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_functiondecl" class="ntref localref" style="color:maroon">FunctionDecl</a></span></code>.
+</li>
+<li class="li ul-li list-star-li compact-li">Where a parameter affects the parsing of a non-terminal we will
+explain the effect of the parameter.
+</li></ul></li></ul>
+
+<p class="p noindent">The names of character sets and tokens start with a lower case
+letter but the names of grammar non-terminals start with
+an upper-case letter.
+</p>
+<p class="p indent">The grammar uses Extended BNF notation. See the&nbsp;<a href="http://www.ssw.uni-linz.ac.at/Research/Projects/Coco/Doc/UserManual.pdf">Coco/R Referenced
+manual</a>
+for details. But in summary:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">identifiers starting with a lower case letter denote
+terminal symbols,
+</li>
+<li class="li ul-li list-star-li compact-li">identifiers starting with an upper case letter denote nonterminal
+symbols.
+</li>
+<li class="li ul-li list-star-li compact-li">Strings denote themselves.
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">=</code> separates the sides of a production, e.g. <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A = a b c</code>
+</li>
+<li class="li ul-li list-star-li compact-li">In the Coco grammars &#8220;.&#8221; terminates a production, but for readability
+in this document a production starts with the defined identifier in
+the left margin and may be continued on subsequent lines if they
+are indented.
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">|</code> separates alternatives, e.g. <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a b | c | d e</code> means <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a b</code> or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">c or d e</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(</code> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">)</code> groups alternatives, e.g. (a | b) c means a c or b c
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">[ ]</code> option, e.g. <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">[a] b</code> means <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a b</code> or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">b</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{ }</code> iteration (0 or more times), e.g. <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{a} b</code> means <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">b</code> or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a b</code> or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a a b</code> or &#8230;
+</li>
+<li class="li ul-li list-star-li compact-li">We allow <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">|</code> inside <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">[ ]</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{ }</code>. So <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">[a | b]</code> is short for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">[(a | b)]</code>
+and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{a | b}</code> is short for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{(a | b)}</code>.
+</li>
+<li class="li ul-li list-star-li compact-li">The first production defines the name of the grammar, in this case <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Dafny</code>.
+</li></ul>
+
+<p class="p noindent">In addition to the Coco rules, for the sake of readability we have adopted
+these additional conventions.
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">We allow <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">-</code> to be used. <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a - b</code> means it matches if it matches <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a</code> but not <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">b</code>.
+</li>
+<li class="li ul-li list-star-li compact-li">To aid in explaining the grammar we have added some additional productions
+that are not present in the original grammar. We name these with a trailing
+underscore. If you inline these where they are referenced, the result should
+let you reconstruct the original grammar.
+</li></ul>
+
+<p class="p noindent"><strong class="strong-star2">For the convenience of the reader, any references to character sets,
+tokens, or grammar non-terminals in this document are hyper-links that
+will link to the definition of the entity.</strong>
+</p><h3 id="sec-character-classes" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">1.0</span>.&#8194;</span>Character Classes</h3>
+<p class="p noindent">This section defines character classes used later in the token definitions.
+In this section backslash is used to start an escape sequence, so for example
+&#8216;\n&#8217; denotes the single linefeed character.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_letter" class="ntdef" style="color:olive">letter</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"</span></span></code></pre>
+<p class="p noindent para-continued">At present, a letter is an ASCII upper or lowercase letter. Other Unicode letters
+are not supported.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_digit" class="ntdef" style="color:olive">digit</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"0123456789"</span></span></code></pre>
+<p class="p noindent para-continued">A digit is just one of the base-10 digits.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_posdigit" class="ntdef" style="color:olive">posDigit</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"123456789"</span></span></code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#2_posdigit" class="ntref localref" style="color:maroon">posDigit</a></span></code> is a digit, excluding 0.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_hexdigit" class="ntdef" style="color:olive">hexdigit</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"0123456789ABCDEFabcdef"</span></span></code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#2_hexdigit" class="ntref localref" style="color:maroon">hexdigit</a></span></code> character is a digit or one of the letters from &#8216;A&#8217; to &#8216;F&#8217; in either case.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_special" class="ntdef" style="color:olive">special</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&#39;_?"</span></span></code></pre>
+<p class="p noindent para-continued">The <em class="em-low1">special</em> characters are the characters in addition to alphanumeric characters
+that are allowed to appear in a Dafny identifier. These are
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">&#39;</span><span style="color:maroon">&quot;</span></code> because mathematicians like to put primes on identifiers and some ML
+programmers like to start names of type parameters with a &#8220;&#39;&#8221;.
+</li>
+<li class="li ul-li list-star-li compact-li">&#8220;_&#8221; because computer scientists expect to be able to have underscores in identifiers.
+</li>
+<li class="li ul-li list-star-li compact-li">&#8220;?&#8221; because it is useful to have &#8220;?&#8221; at the end of names of predicates,
+e.g. &#8220;Cons?&#8221;.
+</li></ul>
+
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_cr" class="ntdef" style="color:olive">cr</span></span> = <span style="color:maroon">&#39;</span><span style="color:gray">\r</span><span style="color:maroon">&#39;</span></code></pre>
+<p class="p noindent para-continued">A carriage return character.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_lf" class="ntdef" style="color:olive">lf</span></span> = <span style="color:maroon">&#39;</span><span style="color:gray">\n</span><span style="color:maroon">&#39;</span></code></pre>
+<p class="p noindent para-continued">A line feed character.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_tab" class="ntdef" style="color:olive">tab</span></span> = <span style="color:maroon">&#39;</span><span style="color:gray">\t</span><span style="color:maroon">&#39;</span></code></pre>
+<p class="p noindent para-continued">A tab character.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_space" class="ntdef" style="color:olive">space</span></span> = <span style="color:maroon">&#39; &#39;</span></code></pre>
+<p class="p noindent para-continued">A space character.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_nondigitidchar" class="ntdef" style="color:olive">nondigitIdChar</span></span> = <span class="code-escaped"><a href="#2_letter" class="ntref localref" style="color:maroon">letter</a></span> + <span class="code-escaped"><a href="#2_special" class="ntref localref" style="color:maroon">special</a></span></code></pre>
+<p class="p noindent para-continued">The characters that can be used in an identifier minus the digits.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_idchar" class="ntdef" style="color:olive">idchar</span></span> = <span class="code-escaped"><a href="#2_nondigitidchar" class="ntref localref" style="color:maroon">nondigitIdChar</a></span> + <span class="code-escaped"><a href="#2_digit" class="ntref localref" style="color:maroon">digit</a></span></code></pre>
+<p class="p noindent para-continued">The characters that can be used in an identifier.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_nonidchar" class="ntdef" style="color:olive">nonidchar</span></span> = <span class="code-escaped"><a href="#1_any" class="ntref localref" style="color:maroon">ANY</a></span> - <span class="code-escaped"><a href="#2_idchar" class="ntref localref" style="color:maroon">idchar</a></span></code></pre>
+<p class="p noindent para-continued">Any character except those that can be used in an identifier.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_charchar" class="ntdef" style="color:olive">charChar</span></span> = <span class="code-escaped"><a href="#1_any" class="ntref localref" style="color:maroon">ANY</a></span> - <span style="color:maroon">&#39;</span><span style="color:gray">\&#39;</span><span style="color:maroon">&#39;</span> - <span style="color:maroon">&#39;</span><span style="color:gray">\\</span><span style="color:maroon">&#39;</span> - <span class="code-escaped"><a href="#2_cr" class="ntref localref" style="color:maroon">cr</a></span> - <span class="code-escaped"><a href="#2_lf" class="ntref localref" style="color:maroon">lf</a></span></code></pre>
+<p class="p noindent para-continued">Characters that can appear in a character constant.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_stringchar" class="ntdef" style="color:olive">stringChar</span></span> = <span class="code-escaped"><a href="#1_any" class="ntref localref" style="color:maroon">ANY</a></span> - <span style="color:maroon">&#39;&quot;&#39;</span> - <span style="color:maroon">&#39;</span><span style="color:gray">\\</span><span style="color:maroon">&#39;</span> - <span class="code-escaped"><a href="#2_cr" class="ntref localref" style="color:maroon">cr</a></span> - <span class="code-escaped"><a href="#2_lf" class="ntref localref" style="color:maroon">lf</a></span></code></pre>
+<p class="p noindent para-continued">Characters that can appear in a string constant.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_verbatimstringchar" class="ntdef" style="color:olive">verbatimStringChar</span></span> = <span class="code-escaped"><a href="#1_any" class="ntref localref" style="color:maroon">ANY</a></span> - <span style="color:maroon">&#39;&quot;&#39;</span></code></pre>
+<p class="p noindent para-continued">Characters that can appear in a verbatim string.
+</p><h4 id="sec-comments" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">1.0.0</span>.&#8194;</span>Comments</h4>
+<p class="p noindent">Comments are in two forms.
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">They may go from &#8220;/*&#8221; to &#8220;*/&#8221; and be nested.
+</li>
+<li class="li ul-li list-star-li compact-li">They may go from &#8220;//&#8221; to the end of the line.
+</li></ul>
+<h3 id="sec-tokens" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">1.1</span>.&#8194;</span>Tokens</h3>
+<p class="p noindent">As with most languages, Dafny syntax is defined in two levels. First the stream
+of input characters is broken up into <em class="em-low1">tokens</em>. Then these tokens are parsed
+using the Dafny grammar. The Dafny tokens are defined in this section.
+</p><h4 id="sec-reserved-words" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">1.1.0</span>.&#8194;</span>Reserved Words</h4>
+<p class="p noindent">The following reserved words appear in the Dafny grammar and may not be used
+as identifiers of user-defined entities:
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_reservedword" class="ntdef" style="color:olive">reservedword</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"abstract"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"array"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"as"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"assert"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"assume"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"bool"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"break"</span></span> |
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"calc"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"case"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"char"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"class"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"codatatype"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"colemma"</span></span> |
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"constructor"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"copredicate"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"datatype"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"decreases"</span></span> |
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"default"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"else"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"ensures"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"exists"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"extends"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"false"</span></span> |
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"forall"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"free"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"fresh"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"function"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"ghost"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"if"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"imap"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"import"</span></span> |
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"in"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"include"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"inductive"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"int"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"invariant"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"iset"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"iterator"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"label"</span></span> |
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"lemma"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"map"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"match"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"method"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"modifies"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"modify"</span></span> |
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"module"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"multiset"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"nat"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"new"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"newtype"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"null"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"object"</span></span> |
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"old"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"opened"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"predicate"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"print"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"protected"</span></span> |
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"reads"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"real"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"refines"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"requires"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"return"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"returns"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"seq"</span></span> |
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"set"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"static"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"string"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"then"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"this"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"trait"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"true"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"type"</span></span> |
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"var"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"where"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"while"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"yield"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"yields"</span></span> | <span class="code-escaped"><a href="#2_arraytoken" class="ntref localref" style="color:maroon">arrayToken</a></span>
+
+<span class="code-escaped"><span id="2_arraytoken" class="ntdef" style="color:olive">arrayToken</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"array"</span></span> [ <span class="code-escaped"><a href="#2_posdigit" class="ntref localref" style="color:maroon">posDigit</a></span> { <span class="code-escaped"><a href="#2_digit" class="ntref localref" style="color:maroon">digit</a></span> }]</code></pre>
+<p class="p noindent para-continued">An <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#2_arraytoken" class="ntref localref" style="color:maroon">arrayToken</a></span></code> is a reserved word that denotes an array type of
+given rank. <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array</span></code> is an array type of rank 1 (aka a vector). <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array2</span></code>
+is the type of two-dimensional arrays, etc.
+</p>
+<p class="p indent">TODO: Is &#8220;_&#8221; is reserved word?
+</p><h4 id="sec-identifiers" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">1.1.1</span>.&#8194;</span>Identifiers</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_ident" class="ntdef" style="color:olive">ident</span></span> = <span class="code-escaped"><a href="#2_nondigitidchar" class="ntref localref" style="color:maroon">nondigitIdChar</a></span> { <span class="code-escaped"><a href="#2_idchar" class="ntref localref" style="color:maroon">idchar</a></span> } - <span class="code-escaped"><a href="#2_arraytoken" class="ntref localref" style="color:maroon">arraytoken</a></span> - <span class="code-escaped"><a href="#2_chartoken" class="ntref localref" style="color:maroon">chartoken</a></span> - <span class="code-escaped"><a href="#2_reservedword" class="ntref localref" style="color:maroon">reservedword</a></span> </code></pre>
+<p class="p noindent para-continued">In general Dafny identifiers are sequences of <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#2_idchar" class="ntref localref" style="color:maroon">idChar</a></span></code> characters where
+the first character is a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#2_nondigitidchar" class="ntref localref" style="color:maroon">nondigitIdChar</a></span></code>. However tokens that fit this pattern
+are not identifiers if they look like an array type token, a character literal,
+or a reserved work.
+</p><h4 id="sec-digits" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">1.1.2</span>.&#8194;</span>Digits</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_digits" class="ntdef" style="color:olive">digits</span></span> = <span class="code-escaped"><a href="#2_digit" class="ntref localref" style="color:maroon">digit</a></span> {[<span style="color:maroon">&#39;_&#39;</span>] <span class="code-escaped"><a href="#2_digit" class="ntref localref" style="color:maroon">digit</a></span>}</code></pre>
+<p class="p noindent para-continued">A sequence of decimal digits, possibly interspersed with underscores for readability. Example: <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">1_234_567</span></code>.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_hexdigits" class="ntdef" style="color:olive">hexdigits</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"0x"</span></span> <span class="code-escaped"><a href="#2_hexdigit" class="ntref localref" style="color:maroon">hexdigit</a></span> {[<span style="color:maroon">&#39;_&#39;</span>] <span class="code-escaped"><a href="#2_hexdigit" class="ntref localref" style="color:maroon">hexdigit</a></span>}</code></pre>
+<p class="p noindent para-continued">A hexadecimal constant, possibly interspersed with underscores for readability.
+Example: <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0xffff_ffff</span></code>.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_decimaldigits" class="ntdef" style="color:olive">decimaldigits</span></span> = <span class="code-escaped"><a href="#2_digit" class="ntref localref" style="color:maroon">digit</a></span> {[<span style="color:maroon">&#39;_&#39;</span>] <span class="code-escaped"><a href="#2_digit" class="ntref localref" style="color:maroon">digit</a></span>} <span style="color:maroon">&#39;.&#39;</span> <span class="code-escaped"><a href="#2_digit" class="ntref localref" style="color:maroon">digit</a></span> {[<span style="color:maroon">&#39;_&#39;</span>] <span class="code-escaped"><a href="#2_digit" class="ntref localref" style="color:maroon">digit</a></span>}</code></pre>
+<p class="p noindent para-continued">A decimal fraction constant, possibly interspersed with underscores for readability.
+Example: <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">123_456.789_123</span></code>.
+</p><h4 id="sec-escaped-character" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">1.1.3</span>.&#8194;</span>Escaped Character</h4>
+<p class="p noindent">In this section the &#8220;\&#8221; characters are literal.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_escapedchar" class="ntdef" style="color:olive">escapedChar</span></span> =
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"\'"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"\&#34;"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"\\"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"\0"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"\n"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"\r"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"\t"</span></span>
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"\u"</span></span> <span class="code-escaped"><a href="#2_hexdigit" class="ntref localref" style="color:maroon">hexdigit</a></span> <span class="code-escaped"><a href="#2_hexdigit" class="ntref localref" style="color:maroon">hexdigit</a></span> <span class="code-escaped"><a href="#2_hexdigit" class="ntref localref" style="color:maroon">hexdigit</a></span> <span class="code-escaped"><a href="#2_hexdigit" class="ntref localref" style="color:maroon">hexdigit</a></span>
+ )</code></pre>
+<p class="p noindent para-continued">In Dafny character or string literals escaped characters may be used
+to specify the presence of the delimiting quote, or back slash,
+or null, or new line, or carriage return or tab, or the
+Unicode character with given hexadecimal representation.
+
+</p><h4 id="sec-character-constant-token" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">1.1.4</span>.&#8194;</span>Character Constant Token</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_chartoken" class="ntdef" style="color:olive">charToken</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&#39;"</span></span> ( <span class="code-escaped"><a href="#2_charchar" class="ntref localref" style="color:maroon">charChar</a></span> | <span class="code-escaped"><a href="#2_escapedchar" class="ntref localref" style="color:maroon">escapedChar</a></span> ) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&#39;"</span></span> </code></pre>
+<p class="p noindent para-continued">A character constant is enclosed by &#8220;&#39;&#8221; and includes either a character
+from the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#2_charchar" class="ntref localref" style="color:maroon">charChar</a></span></code> set, or an escaped character. Note that although Unicode
+letters are not allowed in Dafny identifiers, Dafny does support Unicode
+in its character and string constants and in its data. A character
+constant has type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">char</span></code>.
+</p><h4 id="sec-string-constant-token" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">1.1.5</span>.&#8194;</span>String Constant Token</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_stringtoken" class="ntdef" style="color:olive">stringToken</span></span> =
+ <span style="color:maroon">&#39;&quot;&#39;</span> { <span class="code-escaped"><a href="#2_stringchar" class="ntref localref" style="color:maroon">stringChar</a></span> | <span class="code-escaped"><a href="#2_escapedchar" class="ntref localref" style="color:maroon">escapedChar</a></span> } <span style="color:maroon">&#39;&quot;&#39;</span>
+ | <span style="color:maroon">&#39;@&#39;</span> <span style="color:maroon">&#39;&quot;&#39;</span> { <span class="code-escaped"><a href="#2_verbatimstringchar" class="ntref localref" style="color:maroon">verbatimStringChar</a></span> | <span style="color:maroon">&#39;&quot;&#39;</span> <span style="color:maroon">&#39;&quot;&#39;</span> } <span style="color:maroon">&#39;&quot;&#39;</span></code></pre>
+<p class="p noindent para-continued">A string constant is either a normal string constant or a verbatim string constant.
+A normal string constant is enclosed by &#8216;&quot;&#8217; and can contain characters from the
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#2_stringchar" class="ntref localref" style="color:maroon">stringChar</a></span></code> set and escapes.
+</p>
+<p class="p indent">A verbatim string constant is enclosed between &#8216;@&quot;&#8217; and &#8216;&quot;&#8217; and can
+consists of any characters (including newline characters) except that two
+successive double quotes give a way to escape one quote character inside
+the string.
+</p><h3 id="sec-low-level-grammar-productions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">1.2</span>.&#8194;</span>Low Level Grammar Productions</h3><h4 id="sec-identifier-variations" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">1.2.0</span>.&#8194;</span>Identifier Variations</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_ident" class="ntdef" style="color:olive">Ident</span></span> = <span class="code-escaped"><a href="#2_ident" class="ntref localref" style="color:maroon">ident</a></span> </code></pre>
+<p class="p noindent para-continued">The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_ident" class="ntref localref" style="color:maroon">Ident</a></span></code> non-terminal is just an <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#2_ident" class="ntref localref" style="color:maroon">ident</a></span></code> token and represents an ordinary
+identifier.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_dotsuffix" class="ntdef" style="color:olive">DotSuffix</span></span> =
+ ( <span class="code-escaped"><a href="#2_ident" class="ntref localref" style="color:maroon">ident</a></span> | <span class="code-escaped"><a href="#2_digits" class="ntref localref" style="color:maroon">digits</a></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"requires"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"reads"</span></span> ) </code></pre>
+<p class="p noindent para-continued">When using the <em class="em-low1">dot</em> notation to denote a component of a compound entity
+the token following the &#8220;.&#8221;, in addition to being an identifier,
+can also be a natural number, or one of the keywords <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">requires</span></code> or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">reads</span></code>.
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">Digits can be used to name fields of classes and destructors of
+datatypes. For example, the built-in tuple datatypes have destructors
+named 0, 1, 2, etc. Note that as a field or destructor name, internal
+underscores matter, so 10 is different from 1_0.
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m.<span style="color:purple">requires</span></code> is used to denote the precondition for method m.
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m.<span style="color:purple">reads</span></code> is used to denote the things that method m may read.
+</li></ul>
+
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_nousident" class="ntdef" style="color:olive">NoUSIdent</span></span> = <span class="code-escaped"><a href="#2_ident" class="ntref localref" style="color:maroon">ident</a></span> - <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"_"</span></span> { <span class="code-escaped"><a href="#2_idchar" class="ntref localref" style="color:maroon">idChar</a></span> }</code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span></code> is an identifier except that identifiers with a <strong class="strong-star2">leading</strong>
+underscore are not allowed. The names of user-defined entities are
+required to be <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span></code>s. We introduce more mnemonic names
+for these below (e.g. <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_classname" class="ntref localref" style="color:maroon">ClassName</a></span></code>).
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_wildident" class="ntdef" style="color:olive">WildIdent</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"_"</span></span> </code></pre>
+<p class="p noindent para-continued">Identifier, disallowing leading underscores, except the &#8220;wildcard&#8221;
+identifier &#8220;_&#8221;. When &#8220;_&#8221; appears it is replaced by a unique generated
+identifier distinct from user identifiers.
+</p><h4 id="sec-nousident-synonyms" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">1.2.1</span>.&#8194;</span>NoUSIdent Synonyms</h4>
+<p class="p noindent">In the productions for the declaration of user-defined entities the name of the
+user-defined entity is required to be an identifier that does not start
+with an underscore, i.e., a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span></code>. To make the productions more
+mnemonic, we introduce the following synonyms for <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span></code>.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_modulename" class="ntdef" style="color:olive">ModuleName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_classname" class="ntdef" style="color:olive">ClassName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_traitname" class="ntdef" style="color:olive">TraitName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_datatypename" class="ntdef" style="color:olive">DatatypeName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_datatypemembername" class="ntdef" style="color:olive">DatatypeMemberName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_newtypename" class="ntdef" style="color:olive">NewtypeName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_numerictypename" class="ntdef" style="color:olive">NumericTypeName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_synonymtypename" class="ntdef" style="color:olive">SynonymTypeName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_iteratorname" class="ntdef" style="color:olive">IteratorName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_typevariablename" class="ntdef" style="color:olive">TypeVariableName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_methodname" class="ntdef" style="color:olive">MethodName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_functionname" class="ntdef" style="color:olive">FunctionName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_predicatename" class="ntdef" style="color:olive">PredicateName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_copredicatename" class="ntdef" style="color:olive">CopredicateName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_labelname" class="ntdef" style="color:olive">LabelName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_attributename" class="ntdef" style="color:olive">AttributeName</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span>
+<span class="code-escaped"><span id="1_fieldident" class="ntdef" style="color:olive">FieldIdent</span></span> = <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span> </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_fieldident" class="ntref localref" style="color:maroon">FieldIdent</a></span></code> is one of the ways to identify a field. The other is
+using digits.
+</p><h4 id="sec-qualified-names" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">1.2.2</span>.&#8194;</span>Qualified Names</h4>
+<p class="p noindent">A qualified name starts with the name of the top-level entity and then is followed by
+zero or more <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_dotsuffix" class="ntref localref" style="color:maroon">DotSuffix</a></span></code>s which denote a component. Examples:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Module.MyType1</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MyTuple.<span class="constant" style="color:purple">1</span></code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MyMethod.<span style="color:purple">requires</span></code>
+</li></ul>
+
+<p class="p noindent">The grammar does not actually have a production for qualified names
+except in the special case of a qualified name that is known to be
+a module name, i.e. a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_qualifiedmodulename" class="ntref localref" style="color:maroon">QualifiedModuleName</a></span></code>.
+</p><h4 id="sec-identifier-type-combinations" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">1.2.3</span>.&#8194;</span>Identifier-Type Combinations</h4>
+<p class="p noindent">In this section, we describe some nonterminals that combine an identifier and a type.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_identtype" class="ntdef" style="color:olive">IdentType</span></span> = <span class="code-escaped"><a href="#1_wildident" class="ntref localref" style="color:maroon">WildIdent</a></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":"</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span></code></pre>
+<p class="p noindent para-continued">In Dafny, a variable or field is typically declared by giving its name followed by
+a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#2_colon" class="ntref localref" style="color:maroon">colon</a></span></code> and its type. An <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_identtype" class="ntref localref" style="color:maroon">IdentType</a></span></code> is such a construct.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_gidenttype" class="ntdef" style="color:olive">GIdentType</span></span>(allowGhostKeyword) = [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"ghost"</span></span> ] <span class="code-escaped"><a href="#1_identtype" class="ntref localref" style="color:maroon">IdentType</a></span></code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_gidenttype" class="ntref localref" style="color:maroon">GIdentType</a></span></code> is a typed entity declaration optionally preceded by &#8220;ghost&#8221;. The <em class="em-low1">ghost</em>
+qualifier means the entity is only used during verification but not in the generated code.
+Ghost variables are useful for abstractly representing internal state in specifications.
+If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">allowGhostKeyword</code> is false then &#8220;ghost&#8221; is not allowed.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_localidenttypeoptional" class="ntdef" style="color:olive">LocalIdentTypeOptional</span></span> = <span class="code-escaped"><a href="#1_wildident" class="ntref localref" style="color:maroon">WildIdent</a></span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":"</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span> ] </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_localidenttypeoptional" class="ntref localref" style="color:maroon">LocalIdentTypeOptional</a></span></code> is used when declaring local variables. In
+such a case a value may be specified for the variable in which case the
+type may be omitted because it can be inferred from the initial value.
+The initial value value may also be omitted.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_identtypeoptional" class="ntdef" style="color:olive">IdentTypeOptional</span></span> = <span class="code-escaped"><a href="#1_wildident" class="ntref localref" style="color:maroon">WildIdent</a></span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":"</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span> ] </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_identtypeoptional" class="ntref localref" style="color:maroon">IdentTypeOptional</a></span></code> is typically used in a context where the type of the identifier
+may be inferred from the context. Examples are in pattern matching or quantifiers.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_typeidentoptional" class="ntdef" style="color:olive">TypeIdentOptional</span></span> = [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"ghost"</span></span> ] ( <span class="code-escaped"><a href="#1_nousident" class="ntref localref" style="color:maroon">NoUSIdent</a></span> | <span class="code-escaped"><a href="#2_digits" class="ntref localref" style="color:maroon">digits</a></span> ) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":"</span></span> ] <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span></code></pre>
+<p class="p noindent para-continued"><code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_typeidentoptional" class="ntref localref" style="color:maroon">TypeIdentOptional</a></span></code>s are used in <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_formalsoptionalids" class="ntref localref" style="color:maroon">FormalsOptionalIds</a></span></code>. This represents situations
+where a type is given but there may not be an identifier.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_formalsoptionalids" class="ntdef" style="color:olive">FormalsOptionalIds</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> [<span style="color:teal">TypeIdentOptional</span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_typeidentoptional" class="ntref localref" style="color:maroon">TypeIdentOptional</a></span> } ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_formalsoptionalids" class="ntref localref" style="color:maroon">FormalsOptionalIds</a></span></code> is a formal parameter list in which the types are required
+but the names of the parameters is optional. This is used in algebraic
+datatype definitions.
+</p><h4 id="sec-numeric-literals" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">1.2.4</span>.&#8194;</span>Numeric Literals</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_nat" class="ntdef" style="color:olive">Nat</span></span> = ( <span class="code-escaped"><a href="#2_digits" class="ntref localref" style="color:maroon">digits</a></span> | <span class="code-escaped"><a href="#2_hexdigits" class="ntref localref" style="color:maroon">hexdigits</a></span> ) </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_nat" class="ntref localref" style="color:maroon">Nat</a></span></code> represents a natural number expressed in either decimal or hexadecimal.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_dec" class="ntdef" style="color:olive">Dec</span></span> = (decimaldigits ) </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_dec" class="ntref localref" style="color:maroon">Dec</a></span></code> represents a decimal fraction literal.
+</p><h2 id="sec-programs" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">2</span>.&#8194;</span>Programs</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_dafny" class="ntdef" style="color:olive">Dafny</span></span> = { <span class="code-escaped"><a href="#1_includedirective_" class="ntref localref" style="color:maroon">IncludeDirective_</a></span> } { <span class="code-escaped"><a href="#1_topdecl" class="ntref localref" style="color:maroon">TopDecl</a></span> } <span class="code-escaped"><a href="#1_eof" class="ntref localref" style="color:maroon">EOF</a></span> </code></pre>
+<p class="p noindent para-continued">At the top level, a Dafny program (stored as files with extension <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">.dfy</code>)
+is a set of declarations. The declarations introduce (module-level)
+methods and functions, as well as types (classes, traits, inductive and
+co-inductive datatypes, new_types, type synonyms, opaque types, and
+iterators) and modules, where the order of introduction is irrelevant. A
+class also contains a set of declarations, introducing fields, methods,
+and functions.
+</p>
+<p class="p indent">When asked to compile a program, Dafny looks for the existence of a
+Main() method. If a legal Main() method is found, the compiler will emit
+a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">.EXE</code>; otherwise, it will emit a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">.DLL</code>.
+</p>
+<p class="p indent"> (If there is more than one Main(), Dafny will try to emit an .EXE, but
+ this may cause the C# compiler to complain. One could imagine improving
+ this functionality so that Dafny will produce a polite error message in
+ this case.)
+</p>
+<p class="p indent">In order to be a legal Main() method, the following must be true:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">The method takes no parameters
+</li>
+<li class="li ul-li list-star-li compact-li">The method is not a ghost method
+</li>
+<li class="li ul-li list-star-li compact-li">The method has no requires clause
+</li>
+<li class="li ul-li list-star-li compact-li">The method has no modifies clause
+</li>
+<li class="li ul-li list-star-li compact-li">If the method is an instance (that is, non-static) method in a class,
+then the enclosing class must not declare any constructor
+</li></ul>
+
+<p class="p noindent">Note, however, that the following are allowed:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">The method is allowed to be an instance method as long as the enclosing
+class does not declare any constructor. In this case, the runtime
+system will allocate an object of the enclosing class and will invoke
+Main() on it.
+</li>
+<li class="li ul-li list-star-li compact-li">The method is allowed to have <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">ensures</span></code> clauses
+</li>
+<li class="li ul-li list-star-li compact-li">The method is allowed to have <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">decreases</span></code> clauses, including a
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">decreases</span> *</code>. (If Main() has a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">decreases</span> *</code>, then its execution may
+go on forever, but in the absence of a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">decreases</span> *</code> on Main(), Dafny
+will have verified that the entire execution will eventually
+terminate.)
+</li></ul>
+
+<p class="p noindent">An invocation of Dafny may specify a number of source files.
+Each Dafny file follows the grammar of the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_dafny" class="ntref localref" style="color:maroon">Dafny</a></span></code> non-terminal.
+</p>
+<p class="p indent">It consists of a sequence of optional <em class="em-low1">include</em> directives followed by top
+level declarations followed by the end of the file.
+</p><h3 id="sec-include-directives" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">2.0</span>.&#8194;</span>Include Directives</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_includedirective_" class="ntdef" style="color:olive">IncludeDirective_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"include"</span></span> <span class="code-escaped"><a href="#2_stringtoken" class="ntref localref" style="color:maroon">stringToken</a></span> </code></pre>
+<p class="p noindent para-continued">Include directives have the form <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"include"</span></span> <span class="code-escaped"><a href="#2_stringtoken" class="ntref localref" style="color:maroon">stringToken</a></span></code> where
+the string token is either a normal string token or a
+verbatim string token. The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#2_stringtoken" class="ntref localref" style="color:maroon">stringToken</a></span></code> is interpreted as the name of
+a file that will be included in the Dafny source. These included
+files also obey the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_dafny" class="ntref localref" style="color:maroon">Dafny</a></span></code> grammar. Dafny parses and processes the
+transitive closure of the original source files and all the included files,
+but will not invoke the verifier on these unless they have been listed
+explicitly on the command line.
+</p><h3 id="sec-top-level-declarations" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">2.1</span>.&#8194;</span>Top Level Declarations</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_topdecl" class="ntdef" style="color:olive">TopDecl</span></span> = { { <span class="code-escaped"><a href="#1_declmodifier" class="ntref localref" style="color:maroon">DeclModifier</a></span> }
+ ( <span class="code-escaped"><a href="#1_submoduledecl" class="ntref localref" style="color:maroon">SubModuleDecl</a></span>
+ | <span class="code-escaped"><a href="#1_classdecl" class="ntref localref" style="color:maroon">ClassDecl</a></span>
+ | <span class="code-escaped"><a href="#1_datatypedecl" class="ntref localref" style="color:maroon">DatatypeDecl</a></span>
+ | <span class="code-escaped"><a href="#1_newtypedecl" class="ntref localref" style="color:maroon">NewtypeDecl</a></span>
+ | <span class="code-escaped"><a href="#1_synonymtypedecl" class="ntref localref" style="color:maroon">SynonymTypeDecl</a></span>
+ | <span class="code-escaped"><a href="#1_iteratordecl" class="ntref localref" style="color:maroon">IteratorDecl</a></span>
+ | <span class="code-escaped"><a href="#1_traitdecl" class="ntref localref" style="color:maroon">TraitDecl</a></span>
+ | <span class="code-escaped"><a href="#1_classmemberdecl" class="ntref localref" style="color:maroon">ClassMemberDecl</a></span>(moduleLevelDecl: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ } </code></pre>
+<p class="p noindent para-continued">Top-level declarations may appear either at the top level of a Dafny file,
+or within a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_submoduledecl" class="ntref localref" style="color:maroon">SubModuleDecl</a></span></code>. A top-level declaration is one of the following
+types of declarations which are described later.
+</p>
+<p class="p indent">The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_classdecl" class="ntref localref" style="color:maroon">ClassDecl</a></span></code>, <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_datatypedecl" class="ntref localref" style="color:maroon">DatatypeDecl</a></span></code>, <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_newtypedecl" class="ntref localref" style="color:maroon">NewtypeDecl</a></span></code>,
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_synonymtypedecl" class="ntref localref" style="color:maroon">SynonymTypeDecl</a></span></code>, <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_iteratordecl" class="ntref localref" style="color:maroon">IteratorDecl</a></span></code>, and <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_traitdecl" class="ntref localref" style="color:maroon">TraitDecl</a></span></code> declarations are
+type declarations and are describe in Section&nbsp;<a href="#sec-types" title="5.&#8194;Types" class="localref" style="target-element:h1"><span class="heading-label">5</span></a>. Ordinarily
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_classmemberdecl" class="ntref localref" style="color:maroon">ClassMemberDecl</a></span></code>s appear in class declarations but they can also
+appear at the top level. In that case they are included as part of an
+implicit top-level class and are implicitly <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">static</span></code> (but cannot be
+declared as static). In addition a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_classmemberdecl" class="ntref localref" style="color:maroon">ClassMemberDecl</a></span></code> that appears at
+the top level cannot be a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_fielddecl" class="ntref localref" style="color:maroon">FieldDecl</a></span></code>.
+</p><h3 id="sec-declaration-modifiers" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">2.2</span>.&#8194;</span>Declaration Modifiers</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_declmodifier" class="ntdef" style="color:olive">DeclModifier</span></span> =
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"abstract"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"ghost"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"static"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"protected"</span></span>
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"extern"</span></span> [ <span class="code-escaped"><a href="#2_stringtoken" class="ntref localref" style="color:maroon">stringToken</a></span>]
+ )</code></pre>
+<p class="p noindent para-continued">Top level declarations may be preceded by zero or more declaration
+modifiers. Not all of these are allowed in all contexts.
+</p>
+<p class="p indent">The &#8220;abstract&#8221; modifiers may only be used for module declarations.
+An abstract module can leave some entities underspecified.
+Abstract modules are not compiled to C#.
+</p>
+<p class="p indent">The ghost modifier is used to mark entities as being used for
+specification only, not for compilation to code.
+</p>
+<p class="p indent">The static modifier is used for class members that that
+are associated with the class as a whole rather than with
+an instance of the class.
+</p>
+<p class="p indent">The protected modifier is used to control the visibility of the
+body of functions.
+</p>
+<p class="p indent">The extern modifier is used to alter the CompileName of
+entities. The CompileName is the name for the entity
+when translating to Boogie or C#.
+</p>
+<p class="p indent">The following table shows modifiers that are available
+for each of the kinds of declaration. In the table
+we use already-ghost to denote that the item is not
+allowed to have the ghost modifier because it is already
+implicitly ghost.
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> Declaration </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> allowed modifiers </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> module </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> abstract </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> class </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> extern </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> trait </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> - </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="4" data-col="1"> datatype or codatatype </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> - </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="5" data-col="1"> field </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="5" data-col="2"> ghost </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="6" data-col="1"> newtype </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="6" data-col="2"> - </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="7" data-col="1"> synonym types </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="7" data-col="2"> - </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="8" data-col="1"> iterators </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="8" data-col="2"> - </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="9" data-col="1"> method </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="9" data-col="2"> ghost static extern </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="10" data-col="1"> lemma, colemma, comethod </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="10" data-col="2"> already-ghost static protected </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="11" data-col="1"> inductive lemma </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="11" data-col="2"> already-ghost static </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="12" data-col="1"> constructor </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="12" data-col="2"> - </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="13" data-col="1"> function (non-method) </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="13" data-col="2"> already-ghost static protected </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="14" data-col="1"> function method </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="14" data-col="2"> already-ghost static protected extern </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="15" data-col="1"> predicate (non-method) </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="15" data-col="2"> already-ghost static protected </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="16" data-col="1"> predicate method </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="16" data-col="2"> already-ghost static protected extern </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="17" data-col="1"> inductive predicate </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="17" data-col="2"> already-ghost static protected </td></tr>
+<tr><td class="tbody tr-even tr-last col cell-border-left col-odd col-first" data-row="18" data-col="1"> copredicate </td><td class="tbody tr-even tr-last col cell-border-left cell-border-right col-even col-last" data-row="18" data-col="2"> already-ghost static protected </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="18" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="18" data-col="2"></td></tr></tbody></table><h2 id="sec-modules" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">3</span>.&#8194;</span>Modules</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_submoduledecl" class="ntdef" style="color:olive">SubModuleDecl</span></span> = ( <span class="code-escaped"><a href="#1_moduledefinition_" class="ntref localref" style="color:maroon">ModuleDefinition_</a></span> | <span class="code-escaped"><a href="#1_moduleimport_" class="ntref localref" style="color:maroon">ModuleImport_</a></span> ) </code></pre>
+<p class="p noindent para-continued">Structuring a program by breaking it into parts is an important part of
+creating large programs. In Dafny, this is accomplished via <em class="em-low1">modules</em>.
+Modules provide a way to group together related types, classes, methods,
+functions, and other modules together, as well as control the scope of
+declarations. Modules may import each other for code reuse, and it is
+possible to abstract over modules to separate an implementation from an
+interface.
+</p><h3 id="sec-declaring-new-modules" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">3.0</span>.&#8194;</span>Declaring New Modules</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_moduledefinition_" class="ntdef" style="color:olive">ModuleDefinition_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"module"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span class="code-escaped"><a href="#1_modulename" class="ntref localref" style="color:maroon">ModuleName</a></span>
+ [ [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"exclusively"</span></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"refines"</span></span> <span class="code-escaped"><a href="#1_qualifiedmodulename" class="ntref localref" style="color:maroon">QualifiedModuleName</a></span> ]
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"{"</span></span> { <span class="code-escaped"><a href="#1_topdecl" class="ntref localref" style="color:maroon">TopDecl</a></span> } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"}"</span></span>
+<span class="code-escaped"><span id="1_qualifiedmodulename" class="ntdef" style="color:olive">QualifiedModuleName</span></span> = <span class="code-escaped"><a href="#1_ident" class="ntref localref" style="color:maroon">Ident</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"."</span></span> <span class="code-escaped"><a href="#1_ident" class="ntref localref" style="color:maroon">Ident</a></span> } </code></pre>
+<p class="p noindent para-continued">A qualified name that is known to refer to a module.
+</p>
+<p class="p indent">A new module is declared with the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">module</span></code> keyword, followed by the name
+of the new module, and a pair of curly braces ({}) enclosing the body
+of the module:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">module</span> Mod {
+ ...
+}</code></pre>
+<p class="p noindent para-continued">A module body can consist of anything that you could put at the top
+level. This includes classes, datatypes, types, methods, functions, etc.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">module</span> Mod {
+ <span style="color:blue">class</span> C {
+ <span style="color:blue">var</span> f: <span style="color:teal">int</span>
+ <span style="color:blue">method</span> m()
+ }
+ <span style="color:blue">datatype</span> Option = A(<span style="color:teal">int</span>) | B(<span style="color:teal">int</span>)
+ <span style="color:blue">type</span> T
+ <span style="color:blue">method</span> m()
+ <span style="color:blue">function</span> f(): <span style="color:teal">int</span>
+}</code></pre>
+<p class="p noindent para-continued">You can also put a module inside another, in a nested fashion:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">module</span> Mod {
+ <span style="color:blue">module</span> Helpers {
+ <span style="color:blue">class</span> C {
+ <span style="color:blue">method</span> doIt()
+ <span style="color:blue">var</span> f: <span style="color:teal">int</span>
+ }
+ }
+}</code></pre>
+<p class="p noindent para-continued">Then you can refer to the members of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Helpers</code> module within the
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Mod</code> module by prefixing them with &#8220;Helpers.&#8221;. For example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">module</span> Mod {
+ <span style="color:blue">module</span> Helpers { ... }
+ <span style="color:blue">method</span> m() {
+ <span style="color:blue">var</span> x <span style="color:blue">:=</span> <span style="color:blue">new</span> Helpers.C;
+ x.doIt();
+ x.f <span style="color:blue">:=</span> <span class="constant" style="color:purple">4</span>;
+ }
+}</code></pre>
+<p class="p noindent para-continued">Methods and functions defined at the module level are available like
+classes, with just the module name prefixing them. They are also
+available in the methods and functions of the classes in the same
+module.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">module</span> Mod {
+ <span style="color:blue">module</span> Helpers {
+ <span style="color:blue">function</span> <span style="color:blue">method</span> addOne(n: <span style="color:teal">nat</span>): <span style="color:teal">nat</span> {
+ n + <span class="constant" style="color:purple">1</span>
+ }
+ }
+ <span style="color:blue">method</span> m() {
+ <span style="color:blue">var</span> x <span style="color:blue">:=</span> <span class="constant" style="color:purple">5</span>;
+ x <span style="color:blue">:=</span> Helpers.addOne(x); <span style="color:darkgreen">// x is now 6</span>
+ }
+}</code></pre><h3 id="sec-importing-modules" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">3.1</span>.&#8194;</span>Importing Modules</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_moduleimport_" class="ntdef" style="color:olive">ModuleImport_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"import"</span></span> [<span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"opened"</span></span> ] <span class="code-escaped"><a href="#1_modulename" class="ntref localref" style="color:maroon">ModuleName</a></span>
+ [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"="</span></span> <span class="code-escaped"><a href="#1_qualifiedmodulename" class="ntref localref" style="color:maroon">QualifiedModuleName</a></span>
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"as"</span></span> <span class="code-escaped"><a href="#1_qualifiedmodulename" class="ntref localref" style="color:maroon">QualifiedModuleName</a></span> [<span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"default"</span></span> <span class="code-escaped"><a href="#1_qualifiedmodulename" class="ntref localref" style="color:maroon">QualifiedModuleName</a></span> ]
+ ]
+ [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span> ] </code></pre>
+<p class="p noindent para-continued">Declaring new submodules is useful, but sometimes you want to refer to
+things from an existing module, such as a library. In this case, you
+can <em class="em-low1">import</em> one module into another. This is done via the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">import</span></code>
+keyword, and there are a few different forms, each of which has a
+different meaning. The simplest kind is the concrete import, and has
+the form <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">import</span> A = B</code>. This declaration creates a reference to the
+module <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code> (which must already exist), and binds it to the new name
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code>. Note this new name, i.e. <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code>, is only bound in the module containing
+the import declaration; it does not create a global alias. For
+example, if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Helpers</code> was defined outside of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Mod</code>, then we could import
+it:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">module</span> Helpers {
+ ...
+}
+<span style="color:blue">module</span> Mod {
+ <span style="color:blue">import</span> A = Helpers
+ <span style="color:blue">method</span> m() {
+ <span style="color:blue">assert</span> A.addOne(<span class="constant" style="color:purple">5</span>) == <span class="constant" style="color:purple">6</span>;
+ }
+}</code></pre>
+<p class="p noindent para-continued">Note that inside <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m()</code>, we have to use <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code> instead of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Helpers</code>, as we bound
+it to a different name. The name <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Helpers</code> is not available inside <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m()</code>,
+as only names that have been bound inside <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Mod</code> are available. In order
+to use the members from another module, it either has to be declared
+there with <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">module</span></code> or imported with <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">import</span></code>.
+</p>
+<p class="p indent">We don&#39;t have to give <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Helpers</code> a new name, though, if we don&#39;t want
+to. We can write <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">import</span> Helpers = Helpers</code> if we want to, and Dafny
+even provides the shorthand <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">import</span> Helpers</code> for this behavior. You
+can&#39;t bind two modules with the same name at the same time, so
+sometimes you have to use the = version to ensure the names do not
+clash.
+</p>
+<p class="p indent">The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_qualifiedmodulename" class="ntref localref" style="color:maroon">QualifiedModuleName</a></span></code> in the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_moduleimport_" class="ntref localref" style="color:maroon">ModuleImport_</a></span></code> starts with a
+sibling module of the importing module, or with a submodule of the
+importing module. There is no wya to refer to the parent module, only
+sibling modules (and their submodules).
+</p><h3 id="sec-opening-modules" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">3.2</span>.&#8194;</span>Opening Modules</h3>
+<p class="p noindent">Sometimes, prefixing the members of the module you imported with the
+name is tedious and ugly, even if you select a short name when
+importing it. In this case, you can import the module as <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">opened</span></code>,
+which causes all of its members to be available without adding the
+module name. The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">opened</span></code> keyword must immediately follow <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">import</span></code>, if it
+is present. For example, we could write the previous example as:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">module</span> Mod {
+ <span style="color:blue">import</span> <span style="color:blue">opened</span> Helpers
+ <span style="color:blue">method</span> m() {
+ <span style="color:blue">assert</span> addOne(<span class="constant" style="color:purple">5</span>) == <span class="constant" style="color:purple">6</span>;
+ }
+}</code></pre>
+<p class="p noindent para-continued">When opening modules, the newly bound members will have low priority,
+so they will be hidden by local definitions. This means if you define
+a local function called <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">addOne</code>, the function from <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Helpers</code> will no
+longer be available under that name. When modules are opened, the
+original name binding is still present however, so you can always use
+the name that was bound to get to anything that is hidden.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">module</span> Mod {
+ <span style="color:blue">import</span> <span style="color:blue">opened</span> Helpers
+ <span style="color:blue">function</span> addOne(n: <span style="color:teal">nat</span>): <span style="color:teal">nat</span> {
+ n - <span class="constant" style="color:purple">1</span>
+ }
+ <span style="color:blue">method</span> m() {
+ <span style="color:blue">assert</span> addOne(<span class="constant" style="color:purple">5</span>) == <span class="constant" style="color:purple">6</span>; <span style="color:darkgreen">// this is now false,</span>
+ <span style="color:darkgreen">// as this is the function just defined</span>
+ <span style="color:blue">assert</span> Helpers.addOne(<span class="constant" style="color:purple">5</span>) == <span class="constant" style="color:purple">6</span>; <span style="color:darkgreen">// this is still true</span>
+ }
+}</code></pre>
+<p class="p noindent para-continued">If you open two modules that both declare members with the same name,
+then neither member can be referred to without a module prefix, as it
+would be ambiguous which one was meant. Just opening the two modules
+is not an error, however, as long as you don&#39;t attempt to use members
+with common names. The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">opened</span></code> keyword can be used with any kind of
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">import</span></code> declaration, including the module abstraction form.
+</p><h3 id="sec-module-abstraction" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">3.3</span>.&#8194;</span>Module Abstraction</h3>
+<p class="p noindent">Sometimes, using a specific implementation is unnecessary; instead,
+all that is needed is a module that implements some interface. In
+that case, you can use an <em class="em-low1">abstract</em> module import. In Dafny, this is
+written <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">import</span> A <span style="color:blue">as</span> B</code>. This means bind the name <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code> as before, but
+instead of getting the exact module <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code>, you get any module which is a
+<em class="em-low1">adheres</em> of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code>. Typically, the module <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code> may have abstract type
+definitions, classes with bodyless methods, or otherwise be unsuitable
+to use directly. Because of the way refinement is defined, any
+refinement of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code> can be used safely. For example, if we start with:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">module</span> Interface {
+ <span style="color:blue">function</span> <span style="color:blue">method</span> addSome(n: <span style="color:teal">nat</span>): <span style="color:teal">nat</span>
+ <span style="color:purple">ensures</span> addSome(n) &gt; n
+}
+<span style="color:blue">module</span> Mod {
+ <span style="color:blue">import</span> A <span style="color:blue">as</span> Interface
+ <span style="color:blue">method</span> m() {
+ <span style="color:blue">assert</span> <span class="constant" style="color:purple">6</span> &lt;= A.addSome(<span class="constant" style="color:purple">5</span>);
+ }
+}</code></pre>
+<p class="p noindent para-continued">then we can be more precise if we know that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">addSome</code> actually adds
+exactly one. The following module has this behavior. Further, the
+postcondition is stronger, so this is actually a refinement of the
+Interface module.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">module</span> Implementation {
+ <span style="color:blue">function</span> <span style="color:blue">method</span> addSome(n: <span style="color:teal">nat</span>): <span style="color:teal">nat</span>
+ <span style="color:purple">ensures</span> addSome(n) == n + <span class="constant" style="color:purple">1</span>
+ {
+ n + <span class="constant" style="color:purple">1</span>
+ }
+}</code></pre>
+<p class="p noindent para-continued">We can then substitute <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Implementation</code> for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code> in a new module, by
+declaring a refinement of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Mod</code> which defines <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code> to be <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Implementation</code>.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">module</span> Mod2 <span style="color:blue">refines</span> Mod {
+ <span style="color:blue">import</span> A = Implementation
+ ...
+}</code></pre>
+<p class="p noindent para-continued">You can also give an implementation directly, without introducing a
+refinement, by giving a default to the abstract import:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">module</span> Interface {
+ <span style="color:blue">function</span> <span style="color:blue">method</span> addSome(n: <span style="color:teal">nat</span>): <span style="color:teal">nat</span>
+ <span style="color:purple">ensures</span> addSome(n) &gt; n
+}
+<span style="color:blue">module</span> Mod {
+ <span style="color:blue">import</span> A <span style="color:blue">as</span> Interface <span style="color:blue">default</span> Implementation
+ <span style="color:blue">method</span> m() {
+ <span style="color:blue">assert</span> <span class="constant" style="color:purple">6</span> &lt;= A.addSome(<span class="constant" style="color:purple">5</span>);
+ }
+}
+<span style="color:blue">module</span> Implementation {
+ <span style="color:blue">function</span> <span style="color:blue">method</span> addSome(n: <span style="color:teal">nat</span>): <span style="color:teal">nat</span>
+ <span style="color:purple">ensures</span> addSome(n) == n + <span class="constant" style="color:purple">1</span>
+ {
+ n + <span class="constant" style="color:purple">1</span>
+ }
+}
+<span style="color:blue">module</span> Mod2 <span style="color:blue">refines</span> Mod {
+ <span style="color:blue">import</span> A <span style="color:blue">as</span> Interface <span style="color:blue">default</span> Implementation
+ ...
+}</code></pre>
+<p class="p noindent para-continued">Regardless of whether there is a default, the only things known about
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code> in this example is that it has a function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">addSome</code> that returns a
+strictly bigger result, so even with the default we still can&#39;t prove
+that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A.addSome(<span class="constant" style="color:purple">5</span>) == <span class="constant" style="color:purple">6</span></code>, only that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">6</span> &lt;= A.addSome(<span class="constant" style="color:purple">5</span>)</code>.
+</p>
+<p class="p indent">When you refine an abstract import into a concrete one, or giving a
+default, Dafny checkes that the concrete module is a
+refinement of the abstract one. This means that the methods must have
+compatible signatures, all the classes and datatypes with their
+constructors and fields in the abstract one must be present in the
+concrete one, the specifications must be compatible, etc.
+</p><h3 id="sec-module-ordering-and-dependencies" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">3.4</span>.&#8194;</span>Module Ordering and Dependencies</h3>
+<p class="p noindent">Dafny isn&#39;t particular about which order the modules appear in, but
+they must follow some rules to be well formed. As a rule of thumb,
+there should be a way to order the modules in a program such that each
+only refers to things defined <strong class="strong-star2">before</strong> it in the source text. That
+doesn&#39;t mean the modules have to be given in that order. Dafny will
+figure out that order for you, assuming you haven&#39;t made any circular
+references. For example, this is pretty clearly meaningless:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">import</span> A = B
+<span style="color:blue">import</span> B = A</code></pre>
+<p class="p noindent para-continued">You can have import statements at the toplevel, and you can import
+modules defined at the same level:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">import</span> A = B
+<span style="color:blue">method</span> m() {
+ A.whatever();
+}
+<span style="color:blue">module</span> B { ... }</code></pre>
+<p class="p noindent para-continued">In this case, everything is well defined because we can put <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code> first,
+followed by the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code> import, and then finally <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m()</code>. If there is no
+ordering, then Dafny will give an error, complaining about a cyclic
+dependency.
+</p>
+<p class="p indent">Note that when rearranging modules and imports, they have to be kept
+in the same containing module, which disallows some pathological
+module structures. Also, the imports and submodules are always
+considered to be first, even at the toplevel. This means that the
+following is not well formed:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">method</span> doIt() { }
+<span style="color:blue">module</span> M {
+ <span style="color:blue">method</span> m() {
+ doIt();
+ }
+}</code></pre>
+<p class="p noindent para-continued">because the module <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M</code> must come before any other kind of members, such
+as methods. To define global functions like this, you can put them in
+a module (called <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Globals</code>, say) and open it into any module that needs
+its functionality. Finally, if you import via a path, such as <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">import</span> A
+= B.C</code>, then this creates a dependency of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code> on <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code>, as we need to know
+what <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code> is (is it abstract or concrete, or a refinement?).
+</p><h3 id="sec-name-resolution" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">3.5</span>.&#8194;</span>Name Resolution</h3>
+<p class="p noindent">When Dafny sees something like <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A&lt;T&gt;.B&lt;U&gt;.C&lt;V&gt;</code>, how does it know what each part
+refers to? The process Dafny uses to determine what identifier
+sequences like this refer to is name resolution. Though the rules may
+seem complex, usually they do what you would expect. Dafny first looks
+up the initial identifier. Depending on what the first identifier
+refers to, the rest of the identifier is looked up in the appropriate
+context.
+</p>
+<p class="p indent">In terms of the grammar, sequences like the above are represented as
+a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_namesegment" class="ntref localref" style="color:maroon">NameSegment</a></span></code> followed by 0 or more <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span></code>es. A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span></code> is
+more general and the form shown above would be for when the
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span></code> is an <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_augmenteddotsuffix_" class="ntref localref" style="color:maroon">AugmentedDotSuffix_</a></span></code>.
+</p>
+<p class="p indent">The resolution is different depending on whether it is in
+an expression context or a type context.
+</p><h4 id="sec-expression-context-name-resolution" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">3.5.0</span>.&#8194;</span>Expression Context Name Resolution</h4>
+<p class="p noindent">The leading <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_namesegment" class="ntref localref" style="color:maroon">NameSegment</a></span></code> is resolved using the first following
+rule that succeeds.
+</p>
+<ol class="ol loose" start="0">
+<li class="li ol-li loose-li">
+<p>Local variables, parameters and bound variables. These are things like
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">y</code>, and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">i</code> in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">var</span> x;, ... <span style="color:blue">returns</span> (y: <span style="color:teal">int</span>)</code>, and
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span> i :: ....</code> The declaration chosen is the match from the
+innermost matching scope.
+</p></li>
+<li class="li ol-li loose-li">
+<p>If in a class, try to match a member of the class. If the member that
+is found is not static an implicit <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">this</span></code> is inserted. This works for
+fields, functions, and methods of the current class (if in a static
+context, then only static methods and functions are allowed). You can
+refer to fields of the current class either as <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">this</span>.f</code> or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f</code>,
+assuming of course that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f</code> hasn&#39;t be hidden by one of the above. You
+can always prefix this if needed, which cannot be hidden. (Note, a
+field whose name is a string of digits must always have some prefix.)
+</p></li>
+<li class="li ol-li loose-li">
+<p>If there is no <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span></code>, then look for a datatype constructor, if
+unambiguous. Any datatypes that don&#39;t need qualification (so the
+datatype name itself doesn&#39;t need a prefix), and also have a uniquely
+named constructor, can be referred to just by its name. So if
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">datatype</span> List = Cons(List) | Nil</code> is the only datatype that declares
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Cons</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Nil</code> constructors, then you can write <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Cons(Cons(Nil))</code>.
+If the constructor name is not unique, then you need to prefix it with
+the name of the datatype (for example <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">List.Cons(List.Nil)))</code>. This is
+done per constructor, not per datatype.
+</p></li>
+<li class="li ol-li loose-li">
+<p>Look for a member of the enclosing module.
+</p></li>
+<li class="li ol-li loose-li">
+<p>Module-level (static) functions and methods
+</p></li></ol>
+
+<p class="p noindent">TODO: Not sure about the following paragraph.
+Opened modules are treated at each level, after the declarations in the
+current module. Opened modules only affect steps 2, 3 and 5. If a
+ambiguous name is found, an error is generated, rather than continuing
+down the list. After the first identifier, the rules are basically the
+same, except in the new context. For example, if the first identifier is
+a module, then the next identifier looks into that module. Opened modules
+only apply within the module it is opened into. When looking up into
+another module, only things explicitly declared in that module are
+considered.
+</p>
+<p class="p indent">To resolve expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">E.id</code>:
+</p>
+<p class="p indent">First resolve expression E and any type arguments.
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">E</code> resolved to a module <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M</code>:
+
+<ol class="ol compact" start="0">
+<li class="li ol-li compact-li">If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">E.id&lt;T&gt;</code> is not followed by any further suffixes, look for
+unambiguous datatype constructor.
+</li>
+<li class="li ol-li compact-li">Member of module M: a sub-module (including submodules of imports),
+class, datatype, etc.
+</li>
+<li class="li ol-li compact-li">Static function or method.
+</li></ol></li>
+<li class="li ul-li list-star-li compact-li">If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">E</code> denotes a type:
+
+<ol class="ol compact" start="3">
+<li class="li ol-li compact-li">Look up id as a member of that type
+</li></ol></li>
+<li class="li ul-li list-star-li compact-li">If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">E</code> denotes an expression:
+
+<ol class="ol compact" start="4">
+<li class="li ol-li compact-li">Let T be the type of E. Look up id in T.
+</li></ol></li></ul>
+<h4 id="sec-type-context-name-resolution" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">3.5.1</span>.&#8194;</span>Type Context Name Resolution</h4>
+<p class="p noindent">In a type context the priority of <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_namesegment" class="ntref localref" style="color:maroon">NameSegment</a></span></code> resolution is:
+</p>
+<ol class="ol loose">
+<li class="li ol-li loose-li">
+<p>Type parameters.
+</p></li>
+<li class="li ol-li loose-li">
+<p>Member of enclosing module (type name or the name of a module).
+</p></li></ol>
+
+<p class="p noindent">To resolve expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">E.id</code>:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">E</code> resolved to a module <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M</code>:
+
+<ol class="ol compact" start="0">
+<li class="li ol-li compact-li">Member of module M: a sub-module (including submodules of imports),
+class, datatype, etc.
+</li></ol></li>
+<li class="li ul-li list-star-li compact-li">If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">E</code> denotes a type:
+
+<ol class="ol compact">
+<li class="li ol-li compact-li">If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">allowDanglingDotName</code>: Return the type of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">E</code> and the given <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">E.id</code>,
+letting the caller try to make sense of the final dot-name.
+TODO: I don&#39;t under this sentence. What is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">allowDanglingDotName</code>?
+</li></ol></li></ul>
+<h2 id="sec-specifications" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">4</span>.&#8194;</span>Specifications</h2>
+<p class="p noindent">Specifications describe logical properties of Dafny methods, functions,
+lambdas, iterators and loops. They specify preconditions, postconditions,
+invariants, what memory locations may be read or modified, and
+termination information by means of <em class="em-low1">specification clauses</em>.
+For each kind of specification zero or more specification
+clauses (of the type accepted for that type of specification)
+may be given, in any order.
+</p>
+<p class="p indent">We document specifications at these levels:
+</p>
+<ul class="ul list-dash compact">
+<li class="li ul-li list-dash-li compact-li">At the lowest level are the various kinds of specification clauses,
+e.g. a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_requiresclause_" class="ntref localref" style="color:maroon">RequiresClause_</a></span></code>.
+</li>
+<li class="li ul-li list-dash-li compact-li">Next are the specifications for entities that need them,
+e.g. a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_methodspec" class="ntref localref" style="color:maroon">MethodSpec</a></span></code>.
+</li>
+<li class="li ul-li list-dash-li compact-li">At the top level are the entity declarations that include
+the specifications, e.g. <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_methoddecl" class="ntref localref" style="color:maroon">MethodDecl</a></span></code>.
+</li></ul>
+
+<p class="p noindent">This section documents the first two of these in a bottom-up manner.
+We first document the clauses and then the specifications
+that use them.
+</p><h3 id="sec-specification-clauses" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">4.0</span>.&#8194;</span>Specification Clauses</h3><h4 id="sec-requires-clause" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">4.0.0</span>.&#8194;</span>Requires Clause</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_requiresclause_" class="ntdef" style="color:olive">RequiresClause_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"requires"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>)</code></pre>
+<p class="p noindent para-continued">The <strong class="strong-star2">requires</strong> clauses specify preconditions for methods,
+functions, lambda expressions and iterators. Dafny checks
+that the preconditions are met at all call sites. The
+callee may then assume the preconditions hold on entry.
+</p>
+<p class="p indent">If no <strong class="strong-star2">requires</strong> clause is specified it is taken to be <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code>.
+</p>
+<p class="p indent">If more than one <strong class="strong-star2">requires</strong> clause is given, then the
+precondition is the conjunction of all of the expressions
+from all of the <strong class="strong-star2">requires</strong> clauses.
+</p><h4 id="sec-ensures-clause" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">4.0.1</span>.&#8194;</span>Ensures Clause</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_ensuresclause_" class="ntdef" style="color:olive">EnsuresClause_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"ensures"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>)
+<span class="code-escaped"><span id="1_forallensuresclause_" class="ntdef" style="color:olive">ForAllEnsuresClause_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"ensures"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+<span class="code-escaped"><span id="1_functionensuresclause_" class="ntdef" style="color:olive">FunctionEnsuresClause_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"ensures"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>)</code></pre>
+<p class="p noindent para-continued">An <strong class="strong-star2">ensures</strong> clause specifies the post condition for a
+method, function or iterator.
+</p>
+<p class="p indent">If no <strong class="strong-star2">ensures</strong> clause is specified it is taken to be <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code>.
+</p>
+<p class="p indent">If more than one <strong class="strong-star2">ensures</strong> clause is given, then the
+postcondition is the conjunction of all of the expressions
+from all of the <strong class="strong-star2">ensures</strong> clauses.
+</p>
+<p class="p indent">TODO: In the present sources <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_functionensuresclause_" class="ntref localref" style="color:maroon">FunctionEnsuresClause_</a></span></code> differs from
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_ensuresclause_" class="ntref localref" style="color:maroon">EnsuresClause_</a></span></code> only in that it is not allowed to specify
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span></code>s. This seems like a bug and will likely
+be fixed in a future version.
+</p><h4 id="sec-decreases-clause" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">4.0.2</span>.&#8194;</span>Decreases Clause</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_decreasesclause_" class="ntdef" style="color:olive">DecreasesClause_</span></span>(allowWildcard, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"decreases"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span class="code-escaped"><a href="#1_decreaseslist" class="ntref localref" style="color:maroon">DecreasesList</a></span>(allowWildcard, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+<span class="code-escaped"><span id="1_functiondecreasesclause_" class="ntdef" style="color:olive">FunctionDecreasesClause_</span></span>(allowWildcard, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"decreases"</span></span> <span class="code-escaped"><a href="#1_decreaseslist" class="ntref localref" style="color:maroon">DecreasesList</a></span>(allowWildcard, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)</code></pre>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_decreaseslist" class="ntdef" style="color:olive">DecreasesList</span></span>(allowWildcard, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ <span class="code-escaped"><a href="#1_possiblywildexpression" class="ntref localref" style="color:maroon">PossiblyWildExpression</a></span>(allowLambda)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_possiblywildexpression" class="ntref localref" style="color:maroon">PossiblyWildExpression</a></span>(allowLambda) } </code></pre>
+<p class="p noindent para-continued">If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">allowWildcard</code> is false but one of the
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_possiblywildexpression" class="ntref localref" style="color:maroon">PossiblyWildExpression</a></span></code>s is a wild-card, an error is
+reported.
+</p>
+<p class="p indent">TODO: A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_functiondecreasesclause_" class="ntref localref" style="color:maroon">FunctionDecreasesClause_</a></span></code> is not allowed to specify
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span></code>s. this will be fixed in a future version.
+</p>
+<p class="p indent"><strong class="strong-star2">Decreases</strong> clauses are used to prove termination in the
+presence of recursion. if more than one <strong class="strong-star2">decreases</strong> clause is given
+it is as if a single <strong class="strong-star2">decreases</strong> clause had been given with the
+collected list of arguments. That is,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:purple">decreases</span> A, B
+<span style="color:purple">decreases</span> C, D</code></pre>
+<p class="p noindent para-continued">is equivalent to
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:purple">decreases</span> A, B, C, D</code></pre>
+<p class="p noindent para-continued">If any of the expressions in the <strong class="strong-star2">decreases</strong> clause are wild (i.e. &#8220;*&#8221;)
+then proof of termination will be skipped.
+</p>
+<p class="p indent">Termination metrics in Dafny, which are declared by <strong class="strong-star2">decreases</strong> clauses,
+are lexicographic tuples of expressions. At each recursive (or mutually
+recursive) call to a function or method, Dafny checks that the effective
+<strong class="strong-star2">decreases</strong> clause of the callee is strictly smaller than the effective
+<strong class="strong-star2">decreases</strong> clause of the caller.
+</p>
+<p class="p indent"> What does &#8220;strictly smaller&#8221; mean? Dafny provides a built-in
+ well-founded order for every type and, in some cases, between types. For
+ example, the Boolean &#8220;false&#8221; is strictly smaller than &#8220;true&#8221;, the
+ integer 78 is strictly smaller than 102, the set <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span class="constant" style="color:purple">2</span>,<span class="constant" style="color:purple">5</span>}</code> is strictly
+ smaller than the set <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span class="constant" style="color:purple">2</span>,<span class="constant" style="color:purple">3</span>,<span class="constant" style="color:purple">5</span>}</code>, and for &#8220;s&#8221; of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">seq</span>&lt;Color&gt;</code> where
+ <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Color</code> is some inductive datatype, the color <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[<span class="constant" style="color:purple">0</span>]</code> is strictly less than
+ <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code> (provided <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code> is nonempty).
+</p>
+<p class="p indent">What does &#8220;effective decreases clause&#8221; mean? Dafny always appends a
+&#8220;top&#8221; element to the lexicographic tuple given by the user. This top
+element cannot be syntactically denoted in a Dafny program and it never
+occurs as a run-time value either. Rather, it is a fictitious value,
+which here we will denote \top, such that each value that can ever occur
+in a Dafny program is strictly less than \top. Dafny sometimes also
+prepends expressions to the lexicographic tuple given by the user. The
+effective decreases clause is any such prefix, followed by the
+user-provided decreases clause, followed by \top. We said &#8220;user-provided
+decreases clause&#8221;, but if the user completely omits a &#8220;decreases&#8221; clause,
+then Dafny will usually make a guess at one, in which case the effective
+decreases clause is any prefix followed by the guess followed by \top.
+(If you&#39;re using the Dafny IDE in Visual Studio, you can hover the mouse
+over the name of a recursive function or method, or the &#8220;while&#8221; keyword
+for a loop, to see the &#8220;decreases&#8221; clause that Dafny guessed, if any.)
+</p>
+<p class="p indent">Here is a simple but interesting example: the Fibonacci function.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">function</span> Fib(n: <span style="color:teal">nat</span>) : <span style="color:teal">nat</span>
+{
+ <span style="color:blue">if</span> n &lt; <span class="constant" style="color:purple">2</span> <span style="color:blue">then</span> n <span style="color:blue">else</span> Fib(n-<span class="constant" style="color:purple">2</span>) + Fib(n-<span class="constant" style="color:purple">1</span>)
+}
+</code></pre>
+<p class="p noindent para-continued">In this example, if you hover your mouse over the function name
+you will see that Dafny has supplied a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">**<span style="color:purple">decreases</span>** n</code> clause.
+</p>
+<p class="p indent">Let&#39;s take a look at the kind of example where a mysterious-looking
+decreases clause like &#8220;Rank, 0&#8221; is useful.
+</p>
+<p class="p indent">Consider two mutually recursive methods, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code>:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">method</span> A(x: <span style="color:teal">nat</span>)
+{
+ B(x);
+}
+
+<span style="color:blue">method</span> B(x: <span style="color:teal">nat</span>)
+{
+ <span style="color:blue">if</span> x != <span class="constant" style="color:purple">0</span> { A(x-<span class="constant" style="color:purple">1</span>); }
+}</code></pre>
+<p class="p noindent para-continued">To prove termination of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code>, Dafny needs to have effective
+decreases clauses for A and B such that:
+</p>
+<ul class="ul list-star loose">
+<li class="li ul-li list-star-li loose-li">
+<p>the measure for the callee <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B(x)</code> is strictly smaller than the measure
+for the caller <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A(x)</code>, and
+</p></li>
+<li class="li ul-li list-star-li loose-li">
+<p>the measure for the callee <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A(x-<span class="constant" style="color:purple">1</span>)</code> is strictly smaller than the measure
+for the caller <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B(x)</code>.
+</p></li></ul>
+
+<p class="p noindent">Satisfying the second of these conditions is easy, but what about the
+first? Note, for example, that declaring both <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code> with &#8220;decreases x&#8221;
+does not work, because that won&#39;t prove a strict decrease for the call
+from <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A(x)</code> to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B(x)</code>.
+</p>
+<p class="p indent">Here&#39;s one possibility (for brevity, we will omit the method bodies):
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">method</span> A(x: <span style="color:teal">nat</span>)
+ <span style="color:purple">decreases</span> x, <span class="constant" style="color:purple">1</span>
+
+<span style="color:blue">method</span> B(x: <span style="color:teal">nat</span>)
+ <span style="color:purple">decreases</span> x, <span class="constant" style="color:purple">0</span></code></pre>
+<p class="p noindent para-continued">For the call from <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A(x)</code> to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B(x)</code>, the lexicographic tuple <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">x, 0</span><span style="color:maroon">&quot;</span></code> is
+strictly smaller than <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">x, 1</span><span style="color:maroon">&quot;</span></code>, and for the call from <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B(x)</code> to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A(x-<span class="constant" style="color:purple">1</span>)</code>, the
+lexicographic tuple <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">x-1, 1</span><span style="color:maroon">&quot;</span></code> is strictly smaller than <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">x, 0</span><span style="color:maroon">&quot;</span></code>.
+</p>
+<p class="p indent"> Two things to note: First, the choice of &#8220;0&#8221; and &#8220;1&#8221; as the second
+ components of these lexicographic tuples is rather arbitrary. It could
+ just as well have been &#8220;false&#8221; and &#8220;true&#8221;, respectively, or the sets
+ <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span class="constant" style="color:purple">2</span>,<span class="constant" style="color:purple">5</span>}</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span class="constant" style="color:purple">2</span>,<span class="constant" style="color:purple">3</span>,<span class="constant" style="color:purple">5</span>}</code>. Second, the keyword <strong class="strong-star2">decreases</strong> often gives rise to
+ an intuitive English reading of the declaration. For example, you might
+ say that the recursive calls in the definition of the familiar Fibonacci
+ function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Fib(n)</code> &#8220;decreases n&#8221;. But when the lexicographic tuple contains
+ constants, the English reading of the declaration becomes mysterious and
+ may give rise to questions like &#8220;how can you decrease the constant 0?&#8221;.
+ The keyword is just that&#8212;a keyword. It says &#8220;here comes a list of
+ expressions that make up the lexicographic tuple we want to use for the
+ termination measure&#8221;. What is important is that one effective decreases
+ clause is compared against another one, and it certainly makes sense to
+ compare something to a constant (and to compare one constant to
+ another).
+</p>
+<p class="p indent"> We can simplify things a little bit by remembering that Dafny appends
+ \top to the user-supplied decreases clause. For the A-and-B example,
+ this lets us drop the constant from the <strong class="strong-star2">decreases</strong> clause of A:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:blue">method</span> A(x: <span style="color:teal">nat</span>)
+ <span style="color:purple">decreases</span> x
+
+<span style="color:blue">method</span> B(x: <span style="color:teal">nat</span>)
+ <span style="color:purple">decreases</span> x, <span class="constant" style="color:purple">0</span></code></pre>
+<p class="p noindent para-continued">The effective decreases clause of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code> is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">x, </span><span style="color:gray">\t</span><span style="color:maroon">op</span><span style="color:maroon">&quot;</span></code> and the effective
+decreases clause of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code> is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">x, 0, </span><span style="color:gray">\t</span><span style="color:maroon">op</span><span style="color:maroon">&quot;</span></code>. These tuples still satisfy the two
+conditions <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(x, <span class="constant" style="color:purple">0</span>, \top) &lt; (x, \top)</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(x-<span class="constant" style="color:purple">1</span>, \top) &lt; (x, <span class="constant" style="color:purple">0</span>, \top)</code>. And
+as before, the constant &#8220;0&#8221; is arbitrary; anything less than \top (which
+is any Dafny expression) would work.
+</p>
+<p class="p indent">Let&#39;s take a look at one more example that better illustrates the utility
+of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">\top</code>. Consider again two mutually recursive methods, call them <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Outer</code>
+and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Inner</code>, representing the recursive counterparts of what iteratively
+might be two nested loops:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">method</span> Outer(x: <span style="color:teal">nat</span>)
+{
+ <span style="color:darkgreen">// set y to an arbitrary non-negative integer</span>
+ <span style="color:blue">var</span> y <span style="color:blue">:|</span> <span class="constant" style="color:purple">0</span> &lt;= y;
+ Inner(x, y);
+}
+
+<span style="color:blue">method</span> Inner(x: <span style="color:teal">nat</span>, y: <span style="color:teal">nat</span>)
+{
+ <span style="color:blue">if</span> y != <span class="constant" style="color:purple">0</span> {
+ Inner(x, y-<span class="constant" style="color:purple">1</span>);
+ } <span style="color:blue">else</span> <span style="color:blue">if</span> x != <span class="constant" style="color:purple">0</span> {
+ Outer(x-<span class="constant" style="color:purple">1</span>);
+ }
+}</code></pre>
+<p class="p noindent para-continued">The body of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Outer</code> uses an assign-such-that statement to represent some
+computation that takes place before <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Inner</code> is called. It sets &#8220;y&#8221; to some
+arbitrary non-negative value. In a more concrete example, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Inner</code> would do
+some work for each &#8220;y&#8221; and then continue as <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Outer</code> on the next smaller
+&#8220;x&#8221;.
+</p>
+<p class="p indent">Using a <strong class="strong-star2">decreases</strong> clause <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">x, y</span><span style="color:maroon">&quot;</span></code> for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Inner</code> seems natural, but if
+we don&#39;t have any bound on the size of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">y</span><span style="color:maroon">&quot;</span></code> computed by <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Outer</code>,
+there is no expression we can write in <strong class="strong-star2">decreases</strong> clause of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Outer</code>
+that is sure to lead to a strictly smaller value for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">y</span><span style="color:maroon">&quot;</span></code> when <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Inner</code>
+is called. <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">\top</code> to the rescue. If we arrange for the effective
+decreases clause of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Outer</code> to be <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">x, </span><span style="color:gray">\t</span><span style="color:maroon">op</span><span style="color:maroon">&quot;</span></code> and the effective decreases
+clause for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Inner</code> to be <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">x, y, </span><span style="color:gray">\t</span><span style="color:maroon">op</span><span style="color:maroon">&quot;</span></code>, then we can show the strict
+decreases as required. Since <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">\top</code> is implicitly appended, the two
+decreases clauses declared in the program text can be:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">method</span> Outer(x: <span style="color:teal">nat</span>)
+ <span style="color:purple">decreases</span> x
+
+<span style="color:blue">method</span> Inner(x: <span style="color:teal">nat</span>, y: <span style="color:teal">nat</span>)
+ <span style="color:purple">decreases</span> x, y</code></pre>
+<p class="p noindent para-continued">Moreover, remember that if a function or method has no user-declared
+<strong class="strong-star2">decreases</strong> clause, Dafny will make a guess. The guess is (usually)
+the list of arguments of the function/method, in the order given. This is
+exactly the decreases clauses needed here. Thus, Dafny successfully
+verifies the program without any explicit decreases clauses:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">method</span> Outer(x: <span style="color:teal">nat</span>)
+{
+ <span style="color:blue">var</span> y <span style="color:blue">:|</span> <span class="constant" style="color:purple">0</span> &lt;= y;
+ Inner(x, y);
+}
+
+<span style="color:blue">method</span> Inner(x: <span style="color:teal">nat</span>, y: <span style="color:teal">nat</span>)
+{
+ <span style="color:blue">if</span> y != <span class="constant" style="color:purple">0</span> {
+ Inner(x, y-<span class="constant" style="color:purple">1</span>);
+ } <span style="color:blue">else</span> <span style="color:blue">if</span> x != <span class="constant" style="color:purple">0</span> {
+ Outer(x-<span class="constant" style="color:purple">1</span>);
+ }
+}</code></pre>
+<p class="p noindent para-continued">The ingredients are simple, but the end result may seem like magic. For many users, however, there may be no magic at all &#8211; the end result may be so natural that the user never even has to bothered to think about that there was a need to prove termination in the first place.
+</p><h4 id="sec-framing" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">4.0.3</span>.&#8194;</span>Framing</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_frameexpression" class="ntdef" style="color:olive">FrameExpression</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ ( <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) [ <span class="code-escaped"><a href="#1_framefield" class="ntref localref" style="color:maroon">FrameField</a></span> ]
+ | <span class="code-escaped"><a href="#1_framefield" class="ntref localref" style="color:maroon">FrameField</a></span> )</code></pre>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_framefield" class="ntdef" style="color:olive">FrameField</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"`"</span></span> <span class="code-escaped"><a href="#1_ident" class="ntref localref" style="color:maroon">Ident</a></span> </code></pre>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_possiblywildframeexpression" class="ntdef" style="color:olive">PossiblyWildFrameExpression</span></span>(allowLemma) =
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"*"</span></span> | <span class="code-escaped"><a href="#1_frameexpression" class="ntref localref" style="color:maroon">FrameExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>) )</code></pre>
+<p class="p noindent para-continued">Frame expressions are used to denote the set of memory locations
+that a Dafny program element may read or write. A frame
+expression is a set expression. The form <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{}</code> (that is, the empty set)
+says that no memory locations may be modified,
+which is also the default if no <strong class="strong-star2">modifies</strong> clause is given explicitly.
+</p>
+<p class="p indent">Note that framing only applies to the heap, or memory accessed through
+references. Local variables are not stored on the heap, so they cannot be
+mentioned (well, they are not in scope in the declaration) in reads
+annotations. Note also that types like sets, sequences, and multisets are
+value types, and are treated like integers or local variables. Arrays and
+objects are reference types, and they are stored on the heap (though as
+always there is a subtle distinction between the reference itself and the
+value it points to.)
+</p>
+<p class="p indent">The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_framefield" class="ntref localref" style="color:maroon">FrameField</a></span></code> construct is used to specify a field of a
+class object. The identifier following the back-quote is the
+name of the field being referenced.
+If the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">FrameField</code> is preceded by an expression the expression
+must be a reference to an object having that field.
+If the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">FrameField</code> is not preceded by an expression then
+the frame expression is referring to that field of the current
+object. This form is only used from a method of a class.
+</p>
+<p class="p indent">The use of <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_framefield" class="ntref localref" style="color:maroon">FrameField</a></span></code> is discouraged as in practice it has not
+been shown to either be more concise or to perform better.
+Also, there&#39;s (unfortunately) no form of it for array
+elements&#8212;one could imagine
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:purple">modifies</span> a`[j]</code></pre>
+<p class="p noindent para-continued">Also, <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_framefield" class="ntref localref" style="color:maroon">FrameField</a></span></code> is not taken into consideration for
+lambda expressions.
+</p><h4 id="sec-reads-clause" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">4.0.4</span>.&#8194;</span>Reads Clause</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_functionreadsclause_" class="ntdef" style="color:olive">FunctionReadsClause_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"reads"</span></span>
+ <span class="code-escaped"><a href="#1_possiblywildframeexpression" class="ntref localref" style="color:maroon">PossiblyWildFrameExpression</a></span> (allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_possiblywildframeexpression" class="ntref localref" style="color:maroon">PossiblyWildFrameExpression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>) }
+<span class="code-escaped"><span id="1_lambdareadsclause_" class="ntdef" style="color:olive">LambdaReadsClause_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"reads"</span></span> <span class="code-escaped"><a href="#1_possiblywildframeexpression" class="ntref localref" style="color:maroon">PossiblyWildFrameExpression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+<span class="code-escaped"><span id="1_iteratorreadsclause_" class="ntdef" style="color:olive">IteratorReadsClause_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"reads"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ <span class="code-escaped"><a href="#1_frameexpression" class="ntref localref" style="color:maroon">FrameExpression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_frameexpression" class="ntref localref" style="color:maroon">FrameExpression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>) }
+<span class="code-escaped"><span id="1_possiblywildexpression" class="ntdef" style="color:olive">PossiblyWildExpression</span></span>(allowLambda) =
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"*"</span></span> | <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) ) </code></pre>
+<p class="p noindent para-continued">Functions are not allowed to have side effects but may be restricted in
+what they can read. The <em class="em-low1">reading frame</em> of a function (or predicate) is all
+the memory locations that the function is allowed to read. The reason we
+might limit what a function can read is so that when we write to memory,
+we can be sure that functions that did not read that part of memory have
+the same value they did before. For example, we might have two arrays,
+one of which we know is sorted. If we did not put a reads annotation on
+the sorted predicate, then when we modify the unsorted array, we cannot
+determine whether the other array stopped being sorted. While we might be
+able to give invariants to preserve it in this case, it gets even more
+complex when manipulating data structures. In this case, framing is
+essential to making the verification process feasible.
+</p>
+<p class="p indent">It is not just the body of a function that is subject to <strong class="strong-star2">reads</strong>
+checks, but also its precondition and the <strong class="strong-star2">reads</strong> clause itself.
+</p>
+<p class="p indent">A reads clause can list a wildcard (&#8220;*&#8221;), which allows the enclosing
+function to read anything. In many cases, and in particular in all cases
+where the function is defined recursively, this makes it next to
+impossible to make any use of the function. Nevertheless, as an
+experimental feature, the language allows it (and it is sound).
+Note that a &#8220;*&#8221; makes the rest of the frame expression irrelevant.
+</p>
+<p class="p indent">A <strong class="strong-star2">reads</strong> clause specifies the set of memory locations that a function,
+lambda, or iterator may read. If more than one <strong class="strong-star2">reads</strong> clause is given
+in a specification the effective read set is the union of the sets
+specified. If there are no <strong class="strong-star2">reads</strong> clauses the effective read set is
+empty. If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">*</span><span style="color:maroon">&quot;</span></code> is given in a <strong class="strong-star2">reads</strong> clause it means any memory may be
+read.
+</p>
+<p class="p indent">TODO: It would be nice if the different forms of read clauses could be
+combined. In a future version the single form of read clause will allow
+a list and attributes.
+</p><h4 id="sec-modifies-clause" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">4.0.5</span>.&#8194;</span>Modifies Clause</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_modifiesclause_" class="ntdef" style="color:olive">ModifiesClause_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"modifies"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ <span class="code-escaped"><a href="#1_frameexpression" class="ntref localref" style="color:maroon">FrameExpression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_frameexpression" class="ntref localref" style="color:maroon">FrameExpression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>) }</code></pre>
+<p class="p noindent para-continued">Frames also affect methods. As you might have guessed, methods are not
+required to list the things they read. Methods are allowed to read
+whatever memory they like, but they are required to list which parts of
+memory they modify, with a modifies annotation. They are almost identical
+to their reads cousins, except they say what can be changed, rather than
+what the value of the function depends on. In combination with reads,
+modification restrictions allow Dafny to prove properties of code that
+would otherwise be very difficult or impossible. Reads and modifies are
+one of the tools that allow Dafny to work on one method at a time,
+because they restrict what would otherwise be arbitrary modifications of
+memory to something that Dafny can reason about.
+</p>
+<p class="p indent">Note that fields of newly allocated objects can always be modified.
+</p>
+<p class="p indent">It is also possible to frame what can be modified by a block statement
+by means of the block form of the
+<a href="#sec-modify-statement" class="localref">modify statement</a> (Section&nbsp;<a href="#sec-modify-statement" title="21.16.&#8194;Modify Statement" class="localref" style="target-element:h2"><span class="heading-label">21.16</span></a>).
+</p>
+<p class="p indent">A <strong class="strong-star2">modifies</strong> clause specifies the set of memory locations that a
+method, iterator or loop body may modify. If more than one <strong class="strong-star2">modifies</strong>
+clause is given in a specification, the effective modifies set is the
+union of the sets specified. If no <strong class="strong-star2">modifies</strong> clause is given the
+effective modifies set is empty. A loop can also have a
+<strong class="strong-star2">modifies</strong> clause. If none is given, the loop gets to modify anything
+the enclosing context is allowed to modify.
+</p><h4 id="sec-invariant-clause" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">4.0.6</span>.&#8194;</span>Invariant Clause</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_invariantclause_" class="ntdef" style="color:olive">InvariantClause_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"invariant"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)</code></pre>
+<p class="p noindent para-continued">An <strong class="strong-star2">invariant</strong> clause is used to specify an invariant
+for a loop. If more than one <strong class="strong-star2">invariant</strong> clause is given for
+a loop the effective invariant is the conjunction of
+the conditions specified.
+</p>
+<p class="p indent">The invariant must hold on entry to the loop. And assuming it
+is valid on entry, Dafny must be able to prove that it then
+holds at the end of the loop.
+</p><h3 id="sec-method-specification" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">4.1</span>.&#8194;</span>Method Specification</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_methodspec" class="ntdef" style="color:olive">MethodSpec</span></span> =
+ { <span class="code-escaped"><a href="#1_modifiesclause_" class="ntref localref" style="color:maroon">ModifiesClause_</a></span>
+ | <span class="code-escaped"><a href="#1_requiresclause_" class="ntref localref" style="color:maroon">RequiresClause_</a></span>
+ | <span class="code-escaped"><a href="#1_ensuresclause_" class="ntref localref" style="color:maroon">EnsuresClause_</a></span>
+ | <span class="code-escaped"><a href="#1_decreasesclause_" class="ntref localref" style="color:maroon">DecreasesClause_</a></span>(allowWildcard: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>)
+ } </code></pre>
+<p class="p noindent para-continued">A method specification is zero or more <strong class="strong-star2">modifies</strong>, <strong class="strong-star2">requires</strong>,
+<strong class="strong-star2">ensures</strong> or <strong class="strong-star2">decreases</strong> clauses, in any order.
+A method does not have <strong class="strong-star2">reads</strong> clauses because methods are allowed to
+read any memory.
+</p><h3 id="sec-function-specification" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">4.2</span>.&#8194;</span>Function Specification</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_functionspec" class="ntdef" style="color:olive">FunctionSpec</span></span> =
+ { <span class="code-escaped"><a href="#1_requiresclause_" class="ntref localref" style="color:maroon">RequiresClause_</a></span>
+ | <span class="code-escaped"><a href="#1_functionreadsclause_" class="ntref localref" style="color:maroon">FunctionReadsClause_</a></span>
+ | <span class="code-escaped"><a href="#1_functionensuresclause_" class="ntref localref" style="color:maroon">FunctionEnsuresClause_</a></span>
+ | <span class="code-escaped"><a href="#1_functiondecreasesclause_" class="ntref localref" style="color:maroon">FunctionDecreasesClause_</a></span>(allowWildcard: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>)
+ } </code></pre>
+<p class="p noindent para-continued">A function specification is zero or more <strong class="strong-star2">reads</strong>, <strong class="strong-star2">requires</strong>,
+<strong class="strong-star2">ensures</strong> or <strong class="strong-star2">decreases</strong> clauses, in any order. A function
+specification does not have <strong class="strong-star2">modifies</strong> clauses because functions are not
+allowed to modify any memory.
+</p><h3 id="sec-lambda-specification" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">4.3</span>.&#8194;</span>Lambda Specification</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_lambdaspec_" class="ntdef" style="color:olive">LambdaSpec_</span></span> =
+ { <span class="code-escaped"><a href="#1_lambdareadsclause_" class="ntref localref" style="color:maroon">LambdaReadsClause_</a></span>
+ | <span class="code-escaped"><a href="#1_requiresclause_" class="ntref localref" style="color:maroon">RequiresClause_</a></span>
+ } </code></pre>
+<p class="p noindent para-continued">A lambda specification is zero or more <strong class="strong-star2">reads</strong> or <strong class="strong-star2">requires</strong> clauses.
+Lambda specifications do not have <strong class="strong-star2">ensures</strong> clauses because the body
+is never opaque.
+Lambda specifications do not have <strong class="strong-star2">decreases</strong>
+clauses because they do not have names and thus cannot be recursive. A
+lambda specification does not have <strong class="strong-star2">modifies</strong> clauses because lambdas
+are not allowed to modify any memory.
+</p><h3 id="sec-iterator-specification" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">4.4</span>.&#8194;</span>Iterator Specification</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_iteratorspec" class="ntdef" style="color:olive">IteratorSpec</span></span> =
+ { <span class="code-escaped"><a href="#1_iteratorreadsclause_" class="ntref localref" style="color:maroon">IteratorReadsClause_</a></span>
+ | <span class="code-escaped"><a href="#1_modifiesclause_" class="ntref localref" style="color:maroon">ModifiesClause_</a></span>
+ | [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"yield"</span></span> ] <span class="code-escaped"><a href="#1_requiresclause_" class="ntref localref" style="color:maroon">RequiresClause_</a></span>
+ | [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"yield"</span></span> ] <span class="code-escaped"><a href="#1_ensuresclause_" class="ntref localref" style="color:maroon">EnsuresClause_</a></span>
+ | <span class="code-escaped"><a href="#1_decreasesclause_" class="ntref localref" style="color:maroon">DecreasesClause_</a></span>(allowWildcard: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>)
+ } </code></pre>
+<p class="p noindent para-continued">An iterator specification applies both to the iterator&#39;s constructor
+method and to its <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MoveNext</code> method. The <strong class="strong-star2">reads</strong> and <strong class="strong-star2">modifies</strong>
+clauses apply to both of them. For the <strong class="strong-star2">requires</strong> and <strong class="strong-star2">ensures</strong>
+clauses, if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">yield</span></code> is not present they apply to the constructor,
+but if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">yield</span></code> is present they apply to the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MoveNext</code> method.
+</p>
+<p class="p indent">TODO: What is the meaning of a <strong class="strong-star2">decreases</strong> clause on an iterator?
+Does it apply to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MoveNext</code>? Make sure our description of
+iterators explains these.
+</p>
+<p class="p indent">TODO: What is the relationship between the post condition and
+the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Valid()</code> predicate?
+
+</p><h3 id="sec-loop-specification" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">4.5</span>.&#8194;</span>Loop Specification</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_loopspec" class="ntdef" style="color:olive">LoopSpec</span></span> =
+ { <span class="code-escaped"><a href="#1_invariantclause_" class="ntref localref" style="color:maroon">InvariantClause_</a></span>
+ | <span class="code-escaped"><a href="#1_decreasesclause_" class="ntref localref" style="color:maroon">DecreasesClause_</a></span>(allowWildcard: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ | <span class="code-escaped"><a href="#1_modifiesclause_" class="ntref localref" style="color:maroon">ModifiesClause_</a></span>
+ } </code></pre>
+<p class="p noindent para-continued">A loop specification provides the information Dafny needs to
+prove properties of a loop. The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_invariantclause_" class="ntref localref" style="color:maroon">InvariantClause_</a></span></code> clause
+is effectively a precondition and it along with the
+negation of the loop test condition provides the postcondition.
+The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_decreasesclause_" class="ntref localref" style="color:maroon">DecreasesClause_</a></span></code> clause is used to prove termination.
+</p><h2 id="sec-types" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">5</span>.&#8194;</span>Types</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_type" class="ntdef" style="color:olive">Type</span></span> = <span class="code-escaped"><a href="#1_domaintype" class="ntref localref" style="color:maroon">DomainType</a></span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"-&gt;"</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span> ] </code></pre>
+<p class="p noindent para-continued">A Dafny type is a domain type (i.e. a type that can be the domain of a
+function type) optionally followed by an arrow and a range type.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_domaintype" class="ntdef" style="color:olive">DomainType</span></span> =
+ ( <span class="code-escaped"><a href="#1_booltype_" class="ntref localref" style="color:maroon">BoolType_</a></span> | <span class="code-escaped"><a href="#1_chartype_" class="ntref localref" style="color:maroon">CharType_</a></span> | <span class="code-escaped"><a href="#1_nattype_" class="ntref localref" style="color:maroon">NatType_</a></span> | <span class="code-escaped"><a href="#1_inttype_" class="ntref localref" style="color:maroon">IntType_</a></span> | <span class="code-escaped"><a href="#1_realtype_" class="ntref localref" style="color:maroon">RealType_</a></span> | <span class="code-escaped"><a href="#1_objecttype_" class="ntref localref" style="color:maroon">ObjectType_</a></span>
+ | <span class="code-escaped"><a href="#1_finitesettype_" class="ntref localref" style="color:maroon">FiniteSetType_</a></span> | <span class="code-escaped"><a href="#1_infinitesettype_" class="ntref localref" style="color:maroon">InfiniteSetType_</a></span> | <span class="code-escaped"><a href="#1_multisettype_" class="ntref localref" style="color:maroon">MultisetType_</a></span>
+ | <span class="code-escaped"><a href="#1_sequencetype_" class="ntref localref" style="color:maroon">SequenceType_</a></span> | <span class="code-escaped"><a href="#1_stringtype_" class="ntref localref" style="color:maroon">StringType_</a></span>
+ | <span class="code-escaped"><a href="#1_finitemaptype_" class="ntref localref" style="color:maroon">FiniteMapType_</a></span> | <span class="code-escaped"><a href="#1_infinitemaptype_" class="ntref localref" style="color:maroon">InfiniteMapType_</a></span> | <span class="code-escaped"><a href="#1_arraytype_" class="ntref localref" style="color:maroon">ArrayType_</a></span>
+ | <span class="code-escaped"><a href="#1_tupletype_" class="ntref localref" style="color:maroon">TupleType_</a></span> | <span class="code-escaped"><a href="#1_namedtype_" class="ntref localref" style="color:maroon">NamedType_</a></span> ) </code></pre>
+<p class="p noindent para-continued">The domain types comprise the builtin scalar types, the builtin
+collection types, tuple types (including as a special case
+a parenthesized type) and reference types.
+</p>
+<p class="p indent">Dafny types may be categorized as either value types or reference types.
+</p><h3 id="sec-value-types" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">5.0</span>.&#8194;</span>Value Types</h3>
+<p class="p noindent">The value types are those whose values do not lie in the program heap.
+These are:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">The basic scalar types: <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">bool</span></code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">char</span></code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">nat</span></code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">real</span></code>
+</li>
+<li class="li ul-li list-star-li compact-li">The built-in collection types: <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">set</span></code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">multiset</span></code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">seq</span></code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">string</span></code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">map</span></code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">imap</span></code>
+</li>
+<li class="li ul-li list-star-li compact-li">Tuple Types
+</li>
+<li class="li ul-li list-star-li compact-li">Inductive and co-inductive types
+</li></ul>
+
+<p class="p noindent">Data items having value types are passed by value. Since they are not
+considered to occupy <em class="em-low1">memory</em>, framing expressions do not reference them.
+</p><h3 id="sec-reference-types" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">5.1</span>.&#8194;</span>Reference Types</h3>
+<p class="p noindent">Dafny offers a host of <em class="em-low1">reference types</em>. These represent
+<em class="em-low1">references</em> to objects allocated dynamically in the program heap. To
+access the members of an object, a reference to (that is, a <em class="em-low1">pointer</em>
+to or <em class="em-low1">object identity</em> of) the object is <em class="em-low1">dereferenced</em>.
+</p>
+<p class="p indent">The reference types are class types, traits and array types.
+</p>
+<p class="p indent">The special value <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">null</span></code> is part of every reference
+type.<sup id="back-fn-fn-nullable" ><a href="#fn-fn-nullable" title="0.This will change in a future version of Dafny that
+will support both nullable and (by default) non-null reference
+types.
+&#8617;" class="footnote-ref localref" ><span class="footnote-label">0</span></a></sup>
+</p><h3 id="sec-named-types" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">5.2</span>.&#8194;</span>Named Types</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_namedtype_" class="ntdef" style="color:olive">NamedType_</span></span> = <span class="code-escaped"><a href="#1_namesegmentfortypename" class="ntref localref" style="color:maroon">NameSegmentForTypeName</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"."</span></span> <span class="code-escaped"><a href="#1_namesegmentfortypename" class="ntref localref" style="color:maroon">NameSegmentForTypeName</a></span> }</code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_namedtype_" class="ntref localref" style="color:maroon">NamedType_</a></span></code> is used to specify a user-defined type by name
+(possibly module-qualified). Named types are introduced by
+class, trait, inductive, co-inductive, synonym and opaque
+type declarations. They are also used to refer to type variables.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_namesegmentfortypename" class="ntdef" style="color:olive">NameSegmentForTypeName</span></span> = <span class="code-escaped"><a href="#1_ident" class="ntref localref" style="color:maroon">Ident</a></span> [ <span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span> ] </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_namesegmentfortypename" class="ntref localref" style="color:maroon">NameSegmentForTypeName</a></span></code> is a type name optionally followed by a
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span></code> which supplies type parameters to a generic
+type, if needed. It is a special case of a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_namesegment" class="ntref localref" style="color:maroon">NameSegment</a></span></code>
+(See Section&nbsp;<a href="#sec-name-segment" title="22.35.&#8194;Name Segment" class="localref" style="target-element:h2"><span class="heading-label">22.35</span></a>)
+that does not allow a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_hashcall" class="ntref localref" style="color:maroon">HashCall</a></span></code>.
+</p>
+<p class="p indent">The following sections describe each of these kinds of types in more detail.
+</p><h2 id="sec-basic-types" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">6</span>.&#8194;</span>Basic types</h2>
+<p class="p noindent">Dafny offers these basic types: <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">bool</span></code> for booleans, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">char</span></code> for
+characters, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">nat</span></code> for integers, and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">real</span></code> for reals.
+</p><h3 id="sec-booleans" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">6.0</span>.&#8194;</span>Booleans</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_booltype_" class="ntdef" style="color:olive">BoolType_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"bool"</span></span> </code></pre>
+<p class="p noindent para-continued">There are two boolean values and each has a corresponding literal in
+the language: <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">false</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code>.
+</p>
+<p class="p indent">In addition to equality (<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">==</code>) and disequality (<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!=</code>), which are
+defined on all types, type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">bool</span></code> supports the following operations:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> operator </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;==&gt;</code> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> equivalence (if and only if) </td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left cell-line col-odd col-first" data-row="1" data-col="1"></td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right cell-line col-even col-last" data-row="1" data-col="2"></td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">==&gt;</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> implication (implies) </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;==</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> reverse implication (follows from) </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left cell-line col-odd col-first" data-row="3" data-col="1"></td><td class="tbody tr-odd col cell-border-left cell-border-right cell-line col-even col-last" data-row="3" data-col="2"></td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="4" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&amp;&amp;</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> conjunction (and) </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="5" data-col="1"> <span class="monospace">||</span> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="5" data-col="2"> disjunction (or) </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left cell-line col-odd col-first" data-row="5" data-col="1"></td><td class="tbody tr-odd col cell-border-left cell-border-right cell-line col-even col-last" data-row="5" data-col="2"></td></tr>
+<tr><td class="tbody tr-even tr-last col cell-border-left col-odd col-first" data-row="6" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!</code> </td><td class="tbody tr-even tr-last col cell-border-left cell-border-right col-even col-last" data-row="6" data-col="2"> negation (not) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="6" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="6" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">Negation is unary; the others are binary. The table shows the operators
+in groups of increasing binding power, with equality binding stronger
+than conjunction and disjunction, and weaker than negation. Within
+each group, different operators do not associate, so parentheses need
+to be used. For example,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>A &amp;&amp; B || C <span style="color:darkgreen">// error</span></code></pre>
+<p class="p noindent para-continued">would be ambiguous and instead has to be written as either
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>(A &amp;&amp; B) || C</code></pre>
+<p class="p noindent para-continued">or
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>A &amp;&amp; (B || C)</code></pre>
+<p class="p noindent para-continued">depending on the intended meaning.
+</p><h4 id="sec-equivalence-operator" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">6.0.0</span>.&#8194;</span>Equivalence Operator</h4>
+<p class="p noindent">The expressions <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A &lt;==&gt; B</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A == B</code> give the same value, but note
+that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;==&gt;</code> is <em class="em-low1">associative</em> whereas <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">==</code> is <em class="em-low1">chaining</em>. So,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>A &lt;==&gt; B &lt;==&gt; C</code></pre>
+<p class="p noindent para-continued">is the same as
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>A &lt;==&gt; (B &lt;==&gt; C)</code></pre>
+<p class="p noindent para-continued">and
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>(A &lt;==&gt; B) &lt;==&gt; C</code></pre>
+<p class="p noindent para-continued">whereas
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>A == B == C</code></pre>
+<p class="p noindent para-continued">is simply a shorthand for
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>A == B &amp;&amp; B == C</code></pre><h4 id="sec-conjunction-and-disjunction" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">6.0.1</span>.&#8194;</span>Conjunction and Disjunction</h4>
+<p class="p noindent">Conjunction is associative and so is disjunction. These operators are
+<em class="em-low1">short circuiting (from left to right)</em>, meaning that their second
+argument is evaluated only if the evaluation of the first operand does
+not determine the value of the expression. Logically speaking, the
+expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A &amp;&amp; B</code> is defined when <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code> is defined and either <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code>
+evaluates to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">false</span></code> or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code> is defined. When <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A &amp;&amp; B</code> is defined, its
+meaning is the same as the ordinary, symmetric mathematical
+conjunction &#8743;. The same holds for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">||</code> and &#8744;.
+</p><h4 id="sec-implication-and-reverse-implication" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">6.0.2</span>.&#8194;</span>Implication and Reverse Implication</h4>
+<p class="p noindent">Implication is <em class="em-low1">right associative</em> and is short-circuiting from left
+to right. Reverse implication <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B &lt;== A</code> is exactly the same as
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A ==&gt; B</code>, but gives the ability to write the operands in the opposite
+order. Consequently, reverse implication is <em class="em-low1">left associative</em> and is
+short-circuiting from <em class="em-low1">right to left</em>. To illustrate the
+associativity rules, each of the following four lines expresses the
+same property, for any <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code>, and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">bool</span></code>:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>A ==&gt; B ==&gt; C
+A ==&gt; (B ==&gt; C) <span style="color:darkgreen">// parentheses redundant, since ==&gt; is right associative</span>
+C &lt;== B &lt;== A
+(C &lt;== B) &lt;== A <span style="color:darkgreen">// parentheses redundant, since &lt;== is left associative</span></code></pre>
+<p class="p noindent para-continued">To illustrate the short-circuiting rules, note that the expression
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a.Length</code> is defined for an array <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a</code> only if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a</code> is not <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">null</span></code> (see
+Section&nbsp;<a href="#sec-reference-types" title="5.1.&#8194;Reference Types" class="localref" style="target-element:h2"><span class="heading-label">5.1</span></a>), which means the following two
+expressions are well-formed:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>a != <span style="color:blue">null</span> ==&gt; <span class="constant" style="color:purple">0</span> &lt;= a.Length
+<span class="constant" style="color:purple">0</span> &lt;= a.Length &lt;== a != <span style="color:blue">null</span></code></pre>
+<p class="p noindent para-continued">The contrapositive of these two expressions would be:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>a.Length &lt; <span class="constant" style="color:purple">0</span> ==&gt; a == <span style="color:blue">null</span> <span style="color:darkgreen">// not well-formed</span>
+a == <span style="color:blue">null</span> &lt;== a.Length &lt; <span class="constant" style="color:purple">0</span> <span style="color:darkgreen">// not well-formed</span></code></pre>
+<p class="p noindent para-continued">but these expressions are not well-formed, since well-formedness
+requires the left (and right, respectively) operand, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a.Length &lt; <span class="constant" style="color:purple">0</span></code>,
+to be well-formed by itself.
+</p>
+<p class="p indent">Implication <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A ==&gt; B</code> is equivalent to the disjunction <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!A || B</code>, but
+is sometimes (especially in specifications) clearer to read. Since,
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">||</code> is short-circuiting from left to right, note that
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>a == <span style="color:blue">null</span> || <span class="constant" style="color:purple">0</span> &lt;= a.Length</code></pre>
+<p class="p noindent para-continued">is well-formed, whereas
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="constant" style="color:purple">0</span> &lt;= a.Length || a == <span style="color:blue">null</span> <span style="color:darkgreen">// not well-formed</span></code></pre>
+<p class="p noindent para-continued">is not.
+</p>
+<p class="p indent">In addition, booleans support <em class="em-low1">logical quantifiers</em> (forall and
+exists), described in section&nbsp;<a href="#sec-quantifier-expression" title="22.30.&#8194;Quantifier Expression" class="localref" style="target-element:h2"><span class="heading-label">22.30</span></a>.
+</p><h3 id="sec-numeric-types" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">6.1</span>.&#8194;</span>Numeric types</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_inttype_" class="ntdef" style="color:olive">IntType_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"int"</span></span>
+<span class="code-escaped"><span id="1_realtype_" class="ntdef" style="color:olive">RealType_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"real"</span></span> </code></pre>
+<p class="p noindent para-continued">Dafny supports <em class="em-low1">numeric types</em> of two kinds, <em class="em-low1">integer-based</em>, which
+includes the basic type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code> of all integers, and <em class="em-low1">real-based</em>, which
+includes the basic type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">real</span></code> of all real numbers. User-defined
+numeric types based on <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">real</span></code>, called <em class="em-low1">newtypes</em>, are
+described in Section&nbsp;<a href="#sec-newtypes" title="19.&#8194;Newtypes" class="localref" style="target-element:h1"><span class="heading-label">19</span></a>. Also, the <em class="em-low1">subset type</em>
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">nat</span></code>, representing the non-negative subrange of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code>, is described
+in Section&nbsp;<a href="#sec-subset-types" title="20.&#8194;Subset types" class="localref" style="target-element:h1"><span class="heading-label">20</span></a>.
+</p>
+<p class="p indent">The language includes a literal for each non-negative integer, like
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0</span></code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">13</span></code>, and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">1985</span></code>. Integers can also be written in hexadecimal
+using the prefix &#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0</span>x</code>&#8221;, as in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0x0</span></code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0xD</span></code>, and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0x7c1</span></code> (always with
+a lower case <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code>, but the hexadecimal digits themselves are case
+insensitive). Leading zeros are allowed. To form negative integers,
+use the unary minus operator.
+</p>
+<p class="p indent">There are also literals for some of the non-negative reals. These are
+written as a decimal point with a nonempty sequence of decimal digits
+on both sides. For example, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">1.0</span></code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">1609.344</span></code>, and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0.5772156649</span></code>.
+</p>
+<p class="p indent">For integers (in both decimal and hexadecimal form) and reals,
+any two digits in a literal may be separated by an underscore in order
+to improve human readability of the literals. For example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="constant" style="color:purple">1_000_000</span> <span style="color:darkgreen">// easier to read than 1000000</span>
+<span class="constant" style="color:purple">0_12_345_6789</span> <span style="color:darkgreen">// strange but legal formatting of 123456789</span>
+<span class="constant" style="color:purple">0x8000_0000</span> <span style="color:darkgreen">// same as 0x80000000 -- hex digits are often placed in groups of 4</span>
+<span class="constant" style="color:purple">0.000_000_000_1</span> <span style="color:darkgreen">// same as 0.0000000001 -- 1 </span><span class="code-escaped"><span class="comment-color">&#197;ngstr&#246;m</span></span></code></pre>
+<p class="p noindent para-continued">In addition to equality and disequality, numeric types
+support the following relational operations:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> operator </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <span class="monospace">&lt;</span> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> less than </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;=</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> at most </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&gt;=</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> at least </td></tr>
+<tr><td class="tbody tr-even tr-last col cell-border-left col-odd col-first" data-row="4" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&gt;</code> </td><td class="tbody tr-even tr-last col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> greater than </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="4" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="4" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">Like equality and disequality, these operators are chaining, as long
+as they are chained in the &#8220;same direction&#8221;. That is,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>A &lt;= B &lt; C == D &lt;= E</code></pre>
+<p class="p noindent para-continued">is simply a shorthand for
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>A &lt;= B &amp;&amp; B &lt; C &amp;&amp; C == D &amp;&amp; D &lt;= E</code></pre>
+<p class="p noindent para-continued">whereas
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>A &lt; B &gt; C</code></pre>
+<p class="p noindent para-continued">is not allowed.
+</p>
+<p class="p indent">There are also operators on each numeric type:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> operator </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">+</code> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> addition (plus) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">-</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> subtraction (minus) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="2" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="2" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">*</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> multiplication (times) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="4" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">/</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> division (divided by) </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="5" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">%</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="5" data-col="2"> modulus (mod) </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left cell-line col-odd col-first" data-row="5" data-col="1"></td><td class="tbody tr-odd col cell-border-left cell-border-right cell-line col-even col-last" data-row="5" data-col="2"></td></tr>
+<tr><td class="tbody tr-even tr-last col cell-border-left col-odd col-first" data-row="6" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">-</code> </td><td class="tbody tr-even tr-last col cell-border-left cell-border-right col-even col-last" data-row="6" data-col="2"> negation (unary minus) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="6" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="6" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">The binary operators are left associative, and they associate with
+each other in the two groups. The groups are listed in order of
+increasing binding power, with equality binding more strongly than the
+multiplicative operators and weaker than the unary operator.
+Modulus is supported only for integer-based numeric types. Integer
+division and modulus are the <em class="em-low1">Euclidean division and modulus</em>. This
+means that modulus always returns a non-negative, regardless of the
+signs of the two operands. More precisely, for any integer <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a</code> and
+non-zero integer <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">b</code>,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>a == a / b * b + a % b
+<span class="constant" style="color:purple">0</span> &lt;= a % b &lt; B</code></pre>
+<p class="p noindent para-continued">where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code> denotes the absolute value of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">b</code>.
+</p>
+<p class="p indent">Real-based numeric types have a member <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Trunc</code> that returns the
+<em class="em-low1">floor</em> of the real value, that is, the largest integer not exceeding
+the real value. For example, the following properties hold, for any
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">r</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">r&#39;</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">real</span></code>:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="constant" style="color:purple">3.14</span>.Trunc == <span class="constant" style="color:purple">3</span>
+(-<span class="constant" style="color:purple">2.5</span>).Trunc == -<span class="constant" style="color:purple">3</span>
+-<span class="constant" style="color:purple">2.5</span>.Trunc == -<span class="constant" style="color:purple">2</span>
+<span style="color:teal">real</span>(r.Trunc) &lt;= r
+r &lt;= r&#39; ==&gt; r.Trunc &lt;= r&#39;.Trunc</code></pre>
+<p class="p noindent para-continued">Note in the third line that member access (like <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">.Trunc</code>) binds
+stronger than unary minus. The fourth line uses the conversion
+function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">real</span></code> from <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code> to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">real</span></code>, as described in Section
+<a href="#sec-numeric-conversion-operations" title="19.0.&#8194;Numeric conversion operations" class="localref" style="target-element:h2"><span class="heading-label">19.0</span></a>.
+</p><h3 id="sec-characters" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">6.2</span>.&#8194;</span>Characters</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_chartype_" class="ntdef" style="color:olive">CharType_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"char"</span></span> </code></pre>
+<p class="p noindent para-continued">Dafny supports a type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">char</span></code> of <em class="em-low1">characters</em>. Character literals are
+enclosed in single quotes, as in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&#39;D&#39;</span></code>. Their form is described
+by the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#2_chartoken" class="ntref localref" style="color:maroon">charToken</a></span></code> nonterminal in the grammar. To write a single quote as a
+character literal, it is necessary to use an <em class="em-low1">escape sequence</em>.
+Escape sequences can also be used to write other characters. The
+supported escape sequences are as follows:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> escape sequence </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> meaning </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">\&#39;</code> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> the character <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&#39;</code> </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <span class="monospace">\"</span> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> the character <span class="monospace">"</span> </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">\\</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> the character <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">\</code> </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="4" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">\<span class="constant" style="color:purple">0</span></code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> the null character, same as <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">\u0000</code> </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="5" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">\n</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="5" data-col="2"> line feed </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="6" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">\r</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="6" data-col="2"> carriage return </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="7" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">\t</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="7" data-col="2"> horizontal tab </td></tr>
+<tr><td class="tbody tr-even tr-last col cell-border-left col-odd col-first" data-row="8" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">\u<span class="code-escaped"><em class="em-low1">xxxx</em></span></code> </td><td class="tbody tr-even tr-last col cell-border-left cell-border-right col-even col-last" data-row="8" data-col="2"> universal character whose hexadecimal code is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="code-escaped"><em class="em-low1">xxxx</em></span></code> </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="8" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="8" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">The escape sequence for a double quote is redundant, because
+<span class="monospace">'"'</span> and <span class="monospace">'\"'</span> denote the same
+character&#8212;both forms are provided in order to support the same
+escape sequences as for string literals (Section&nbsp;<a href="#sec-strings" title="9.2.4.&#8194;Strings" class="localref" style="target-element:h3"><span class="heading-label">9.2.4</span></a>).
+In the form <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">\u<span class="code-escaped"><em class="em-low1">xxxx</em></span></code>, the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">u</code> is always lower case, but the four
+hexadecimal digits are case insensitive.
+</p>
+<p class="p indent">Character values are ordered and can be compared using the standard
+relational operators:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> operator </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <span class="monospace">&lt;</span> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> less than </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;=</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> at most </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&gt;=</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> at least </td></tr>
+<tr><td class="tbody tr-even tr-last col cell-border-left col-odd col-first" data-row="4" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&gt;</code> </td><td class="tbody tr-even tr-last col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> greater than </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="4" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="4" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">Sequences of characters represent <em class="em-low1">strings</em>, as described in Section
+<a href="#sec-strings" title="9.2.4.&#8194;Strings" class="localref" style="target-element:h3"><span class="heading-label">9.2.4</span></a>.
+</p>
+<p class="p indent">The only other operations on characters are obtaining a character
+by indexing into a string, and the implicit conversion to string
+when used as a parameter of a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">print</span></code> statement.
+</p>
+<p class="p indent">TODO: Are there any conversions between <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">char</span></code> values and numeric values?
+</p><h2 id="sec-type-parameters" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">7</span>.&#8194;</span>Type parameters</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_genericparameters" class="ntdef" style="color:olive">GenericParameters</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&lt;"</span></span> <span class="code-escaped"><a href="#1_typevariablename" class="ntref localref" style="color:maroon">TypeVariableName</a></span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"=="</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> ]
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_typevariablename" class="ntref localref" style="color:maroon">TypeVariableName</a></span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"=="</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> ] } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&gt;"</span></span> </code></pre>
+<p class="p noindent para-continued">Many of the types (as well as functions and methods) in Dafny can be
+parameterized by types. These <em class="em-low1">type parameters</em> are typically
+declared inside angle brackets and can stand for any type.
+</p>
+<p class="p indent">It is sometimes necessary to restrict these type parameters so that
+they can only be instantiated by certain families of types. As such,
+Dafny distinguishes types that support the equality operation
+not only in ghost contexts but also in compiled contexts. To indicate
+that a type parameter is restricted to such <em class="em-low1">equality supporting</em>
+types, the name of the type parameter takes the suffix
+&#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(==)</code>&#8221;.<sup id="back-fn-fn-type-mode" ><a href="#fn-fn-type-mode" title="1.Being equality-supporting is just one of many
+modes that one can imagine types in a rich type system to have.
+For example, other modes could include having a total order,
+being zero-initializable, and possibly being uninhabited. If
+Dafny were to support more modes in the future, the &#8220;(&#160;)&#8221;-suffix
+syntax may be extended. For now, the suffix can only indicate the
+equality-supporting mode.
+&#8617;" class="footnote-ref localref" ><span class="footnote-label">1</span></a></sup> For example,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">method</span> Compare&lt;T(==)&gt;(a: T, b: T) <span style="color:blue">returns</span> (eq: <span style="color:teal">bool</span>)
+{
+ <span style="color:blue">if</span> a == b { eq <span style="color:blue">:=</span> <span style="color:blue">true</span>; } <span style="color:blue">else</span> { eq <span style="color:blue">:=</span> <span style="color:blue">false</span>; }
+}</code></pre>
+<p class="p noindent para-continued">is a method whose type parameter is restricted to equality-supporting
+types. Again, note that <em class="em-low1">all</em> types support equality in <em class="em-low1">ghost</em>
+contexts; the difference is only for non-ghost (that is, compiled)
+code. Co-inductive datatypes, function types, as well as inductive
+datatypes with ghost parameters are examples of types that are not
+equality supporting.
+</p>
+<p class="p indent">Dafny has some inference support that makes certain signatures less
+cluttered (described in a different part of the Dafny language
+reference). In some cases, this support will
+infer that a type parameter must be restricted to equality-supporting
+types, in which case Dafny adds the &#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(==)</code>&#8221; automatically.
+</p>
+<p class="p indent">TODO: Need to describe type inference somewhere.
+</p><h2 id="sec-generic-instantiation" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">8</span>.&#8194;</span>Generic Instantiation</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_genericinstantiation" class="ntdef" style="color:olive">GenericInstantiation</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&lt;"</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span> } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&gt;"</span></span> </code></pre>
+<p class="p noindent para-continued">When a generic entity is used, actual types must be specified for each
+generic parameter. This is done using a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span></code>.
+If the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">GenericInstantiation</code> is omitted, type inference will try
+to fill these in.
+</p><h2 id="sec-collection-types" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">9</span>.&#8194;</span>Collection types</h2>
+<p class="p noindent">Dafny offers several built-in collection types.
+</p><h3 id="sec-sets" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">9.0</span>.&#8194;</span>Sets</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_finitesettype_" class="ntdef" style="color:olive">FiniteSetType_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"set"</span></span> [ <span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span> ]
+<span class="code-escaped"><span id="1_infinitesettype_" class="ntdef" style="color:olive">InfiniteSetType_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"iset"</span></span> [ <span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span> ]</code></pre>
+<p class="p noindent para-continued">For any type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>, each value of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">set</span>&lt;T&gt;</code> is a finite set of
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> values.
+</p>
+<p class="p indent">TODO:
+Set membership is determined by equality in the type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>,
+so <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">set</span>&lt;T&gt;</code> can be used in a non-ghost context only if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> is equality
+supporting.
+</p>
+<p class="p indent">For any type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>, each value of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">iset</span>&lt;T&gt;</code> is a potentially infinite
+set of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> values.
+</p>
+<p class="p indent">A set can be formed using a <em class="em-low1">set display</em> expression, which is a
+possibly empty, unordered, duplicate-insensitive list of expressions
+enclosed in curly braces. To illustrate,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>{} {<span class="constant" style="color:purple">2</span>, <span class="constant" style="color:purple">7</span>, <span class="constant" style="color:purple">5</span>, <span class="constant" style="color:purple">3</span>} {<span class="constant" style="color:purple">4</span>+<span class="constant" style="color:purple">2</span>, <span class="constant" style="color:purple">1</span>+<span class="constant" style="color:purple">5</span>, a*b}</code></pre>
+<p class="p noindent para-continued">are three examples of set displays. There is also a <em class="em-low1">set comprehension</em>
+expression (with a binder, like in logical quantifications), described in
+section&nbsp;<a href="#sec-set-comprehension-expressions" title="22.31.&#8194;Set Comprehension Expressions" class="localref" style="target-element:h2"><span class="heading-label">22.31</span></a>.
+</p>
+<p class="p indent">In addition to equality and disequality, set types
+support the following relational operations:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> operator </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <span class="monospace">&lt;</span> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> proper subset </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;=</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> subset </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&gt;=</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> superset </td></tr>
+<tr><td class="tbody tr-even tr-last col cell-border-left col-odd col-first" data-row="4" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&gt;</code> </td><td class="tbody tr-even tr-last col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> proper superset </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="4" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="4" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">Like the arithmetic relational operators, these operators are
+chaining.
+</p>
+<p class="p indent">Sets support the following binary operators, listed in order of
+increasing binding power:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> operator </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!!</code> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> disjointness </td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left cell-line col-odd col-first" data-row="1" data-col="1"></td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right cell-line col-even col-last" data-row="1" data-col="2"></td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">+</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> set union </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">-</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> set difference </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left cell-line col-odd col-first" data-row="3" data-col="1"></td><td class="tbody tr-odd col cell-border-left cell-border-right cell-line col-even col-last" data-row="3" data-col="2"></td></tr>
+<tr><td class="tbody tr-even tr-last col cell-border-left col-odd col-first" data-row="4" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">*</code> </td><td class="tbody tr-even tr-last col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> set intersection </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="4" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="4" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">The associativity rules of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">+</code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">-</code>, and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">*</code> are like those of the
+arithmetic operators with the same names. The expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A !! B</code>,
+whose binding power is the same as equality (but which neither
+associates nor chains with equality), says that sets <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code> have
+no elements in common, that is, it is equivalent to
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>A * B == {}</code></pre>
+<p class="p noindent para-continued">However, the disjointness operator is chaining, so <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A !! B !! C !! D</code>
+means:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>A * B == {} &amp;&amp; (A + B) * C == {} &amp;&amp; (A + B + C) * D == {}</code></pre>
+<p class="p noindent para-continued">In addition, for any set <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">set</span>&lt;T&gt;</code> or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">iset</span>&lt;T&gt;</code> and any
+expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>, sets support the following operations:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> expression </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <span class="monospace">|s|</span> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> set cardinality </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e <span style="color:blue">in</span> s</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> set membership </td></tr>
+<tr><td class="tbody tr-odd tr-last col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e !<span style="color:blue">in</span> s</code> </td><td class="tbody tr-odd tr-last col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> set non-membership </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left cell-line col-odd col-first" data-row="3" data-col="1"></td><td class="tbody tr-odd col cell-border-left cell-border-right cell-line col-even col-last" data-row="3" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">The expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e !<span style="color:blue">in</span> s</code> is a syntactic shorthand for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!(e <span style="color:blue">in</span> s)</code>.
+</p><h3 id="sec-multisets" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">9.1</span>.&#8194;</span>Multisets</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_multisettype_" class="ntdef" style="color:olive">MultisetType_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"multiset"</span></span> [ <span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span> ] </code></pre>
+<p class="p noindent para-continued">A <em class="em-low1">multiset</em> is similar to a set, but keeps track of the multiplicity
+of each element, not just its presence or absence. For any type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>,
+each value of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">multiset</span>&lt;T&gt;</code> is a map from <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> values to natural
+numbers denoting each element&#39;s multiplicity. Multisets in Dafny
+are finite, that is, they contain a finite number of each of a finite
+set of elements. Stated differently, a multiset maps only a finite
+number of elements to non-zero (finite) multiplicities.
+</p>
+<p class="p indent">Like sets, multiset membership is determined by equality in the type
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>, so <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">multiset</span>&lt;T&gt;</code> can be used in a non-ghost context only if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>
+is equality supporting.
+</p>
+<p class="p indent">A multiset can be formed using a <em class="em-low1">multiset display</em> expression, which
+is a possibly empty, unordered list of expressions enclosed in curly
+braces after the keyword <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">multiset</span></code>. To illustrate,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:teal">multiset</span>{} <span style="color:teal">multiset</span>{<span class="constant" style="color:purple">0</span>, <span class="constant" style="color:purple">1</span>, <span class="constant" style="color:purple">1</span>, <span class="constant" style="color:purple">2</span>, <span class="constant" style="color:purple">3</span>, <span class="constant" style="color:purple">5</span>} <span style="color:teal">multiset</span>{<span class="constant" style="color:purple">4</span>+<span class="constant" style="color:purple">2</span>, <span class="constant" style="color:purple">1</span>+<span class="constant" style="color:purple">5</span>, a*b}</code></pre>
+<p class="p noindent para-continued">are three examples of multiset displays. There is no multiset
+comprehension expression.
+</p>
+<p class="p indent">In addition to equality and disequality, multiset types
+support the following relational operations:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> operator </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <span class="monospace">&lt;</span> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> proper multiset subset </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;=</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> multiset subset </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&gt;=</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> multiset superset </td></tr>
+<tr><td class="tbody tr-even tr-last col cell-border-left col-odd col-first" data-row="4" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&gt;</code> </td><td class="tbody tr-even tr-last col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> proper multiset superset </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="4" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="4" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">Like the arithmetic relational operators, these operators are
+chaining.
+</p>
+<p class="p indent">Multisets support the following binary operators, listed in order of
+increasing binding power:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> operator </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!!</code> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> multiset disjointness </td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left cell-line col-odd col-first" data-row="1" data-col="1"></td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right cell-line col-even col-last" data-row="1" data-col="2"></td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">+</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> multiset union </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">-</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> multiset difference </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left cell-line col-odd col-first" data-row="3" data-col="1"></td><td class="tbody tr-odd col cell-border-left cell-border-right cell-line col-even col-last" data-row="3" data-col="2"></td></tr>
+<tr><td class="tbody tr-even tr-last col cell-border-left col-odd col-first" data-row="4" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">*</code> </td><td class="tbody tr-even tr-last col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> multiset intersection </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="4" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="4" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">The associativity rules of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">+</code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">-</code>, and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">*</code> are like those of the
+arithmetic operators with the same names. The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">+</code> operator
+adds the multiplicity of corresponding elements, the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">-</code> operator
+subtracts them (but 0 is the minimum multiplicity),
+and the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">*</code> has multiplicity that is the minimum of the
+multiplicity of the operands.
+</p>
+<p class="p indent">The expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A !! B</code>
+says that multisets <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">B</code> have no elements in common, that is,
+it is equivalent to
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>A * B == <span style="color:teal">multiset</span>{}</code></pre>
+<p class="p noindent para-continued">Like the analogous set operator, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!!</code> is chaining.
+</p>
+<p class="p indent">In addition, for any multiset <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">multiset</span>&lt;T&gt;</code>,
+expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>, and non-negative integer-based numeric
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n</code>, multisets support the following operations:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> expression </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <span class="monospace">|s|</span> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> multiset cardinality </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e <span style="color:blue">in</span> s</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> multiset membership </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e !<span style="color:blue">in</span> s</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> multiset non-membership </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="4" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[e]</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> multiplicity of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e</code> in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code> </td></tr>
+<tr><td class="tbody tr-odd tr-last col cell-border-left col-odd col-first" data-row="5" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[e <span style="color:blue">:=</span> n]</code> </td><td class="tbody tr-odd tr-last col cell-border-left cell-border-right col-even col-last" data-row="5" data-col="2"> multiset update (change of multiplicity) </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left cell-line col-odd col-first" data-row="5" data-col="1"></td><td class="tbody tr-odd col cell-border-left cell-border-right cell-line col-even col-last" data-row="5" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">The expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e <span style="color:blue">in</span> s</code> returns <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code> if and only if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[e] != <span class="constant" style="color:purple">0</span></code>.
+The expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e !<span style="color:blue">in</span> s</code> is a syntactic shorthand for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!(e <span style="color:blue">in</span> s)</code>.
+The expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[e <span style="color:blue">:=</span> n]</code> denotes a multiset like
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code>, but where the multiplicity of element <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e</code> is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n</code>. Note that
+the multiset update <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[e <span style="color:blue">:=</span> <span class="constant" style="color:purple">0</span>]</code> results in a multiset like <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code> but
+without any occurrences of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e</code> (whether or not <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code> has occurrences of
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e</code> in the first place). As another example, note that
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s - <span style="color:teal">multiset</span>{e}</code> is equivalent to:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">if</span> e <span style="color:blue">in</span> s <span style="color:blue">then</span> s[e <span style="color:blue">:=</span> s[e] - <span class="constant" style="color:purple">1</span>] <span style="color:blue">else</span> s</code></pre><h3 id="sec-sequences" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">9.2</span>.&#8194;</span>Sequences</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_sequencetype_" class="ntdef" style="color:olive">SequenceType_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"seq"</span></span> [ <span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span> ] </code></pre>
+<p class="p noindent para-continued">For any type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>, a value of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">seq</span>&lt;T&gt;</code> denotes a <em class="em-low1">sequence</em> of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>
+elements, that is, a mapping from a finite downward-closed set of natural
+numbers (called <em class="em-low1">indices</em>) to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> values. (Thinking of it as a map,
+a sequence is therefore something of a dual of a multiset.)
+</p><h4 id="sec-sequence-displays" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">9.2.0</span>.&#8194;</span>Sequence Displays</h4>
+<p class="p noindent">A sequence can be formed using a <em class="em-low1">sequence display</em> expression, which
+is a possibly empty, ordered list of expressions enclosed in square
+brackets. To illustrate,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>[] [<span class="constant" style="color:purple">3</span>, <span class="constant" style="color:purple">1</span>, <span class="constant" style="color:purple">4</span>, <span class="constant" style="color:purple">1</span>, <span class="constant" style="color:purple">5</span>, <span class="constant" style="color:purple">9</span>, <span class="constant" style="color:purple">3</span>] [<span class="constant" style="color:purple">4</span>+<span class="constant" style="color:purple">2</span>, <span class="constant" style="color:purple">1</span>+<span class="constant" style="color:purple">5</span>, a*b]</code></pre>
+<p class="p noindent para-continued">are three examples of sequence displays. There is no sequence
+comprehension expression.
+</p><h4 id="sec-sequence-relational-operators" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">9.2.1</span>.&#8194;</span>Sequence Relational Operators</h4>
+<p class="p noindent">In addition to equality and disequality, sequence types
+support the following relational operations:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> operator </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <span class="monospace">&lt;</span> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> proper prefix </td></tr>
+<tr><td class="tbody tr-even tr-last col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;=</code> </td><td class="tbody tr-even tr-last col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> prefix </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="2" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="2" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">Like the arithmetic relational operators, these operators are
+chaining. Note the absence of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&gt;</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&gt;=</code>.
+</p><h4 id="sec-sequence-concatenation" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">9.2.2</span>.&#8194;</span>Sequence Concatenation</h4>
+<p class="p noindent">Sequences support the following binary operator:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> operator </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-last tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">+</code> </td><td class="tbody tr-odd tr-last tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> concatenation </td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left cell-line col-odd col-first" data-row="1" data-col="1"></td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right cell-line col-even col-last" data-row="1" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">Operator <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">+</code> is associative, like the arithmetic operator with the
+same name.
+</p><h4 id="sec-other-sequence-expressions" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">9.2.3</span>.&#8194;</span>Other Sequence Expressions</h4>
+<p class="p noindent">In addition, for any sequence <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">seq</span>&lt;T&gt;</code>, expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e</code>
+of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>, integer-based numeric <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">i</code> satisfying <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0</span> &lt;= i &lt; |s|</code>, and
+integer-based numerics <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">lo</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi</code> satisfying
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0</span> &lt;= lo &lt;= hi &lt;= |s|</code>, sequences support the following operations:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> expression </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <span class="monospace">|s|</span> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> sequence length </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[i]</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> sequence selection </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[i <span style="color:blue">:=</span> e]</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> sequence update </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="4" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e <span style="color:blue">in</span> s</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> sequence membership </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="5" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e !<span style="color:blue">in</span> s</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="5" data-col="2"> sequence non-membership </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="6" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[lo..hi]</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="6" data-col="2"> subsequence </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="7" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[lo..]</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="7" data-col="2"> drop </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="8" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[..hi]</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="8" data-col="2"> take </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="9" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[<span class="code-escaped"><em class="em-low1">slices</em></span>]</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="9" data-col="2"> slice </td></tr>
+<tr><td class="tbody tr-even tr-last col cell-border-left col-odd col-first" data-row="10" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">multiset</span>(s)</code> </td><td class="tbody tr-even tr-last col cell-border-left cell-border-right col-even col-last" data-row="10" data-col="2"> sequence conversion to a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">multiset</span>&lt;T&gt;</code> </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="10" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="10" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">Expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[i <span style="color:blue">:=</span> e]</code> returns a sequence like <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code>, except that the
+element at index <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">i</code> is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e</code>. The expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e <span style="color:blue">in</span> s</code> says there
+exists an index <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">i</code> such that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[i] == e</code>. It is allowed in non-ghost
+contexts only if the element type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> is equality supporting.
+The expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e !<span style="color:blue">in</span> s</code> is a syntactic shorthand for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!(e <span style="color:blue">in</span> s)</code>.
+</p>
+<p class="p indent">Expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[lo..hi]</code> yields a sequence formed by taking the first
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi</code> elements and then dropping the first <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">lo</code> elements. The
+resulting sequence thus has length <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi - lo</code>. Note that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[<span class="constant" style="color:purple">0</span>..|s|]</code>
+equals <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code>. If the upper bound is omitted, it
+defaults to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">|s|</code>, so <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[lo..]</code> yields the sequence formed by dropping
+the first <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">lo</code> elements of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code>. If the lower bound is omitted, it
+defaults to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0</span></code>, so <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[..hi]</code> yields the sequence formed by taking the
+first <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi</code> elements of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code>.
+</p>
+<p class="p indent">In the sequence slice operation, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="code-escaped"><em class="em-low1">slices</em></span></code> is a nonempty list of
+length designators separated and optionally terminated by a colon, and
+there is at least one colon. Each length designator is a non-negative
+integer-based numeric, whose sum is no greater than <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">|s|</code>. If there
+are <em class="em-low1">k</em> colons, the operation produces <em class="em-low1">k + 1</em> consecutive subsequences
+from <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code>, each of the length indicated by the corresponding length
+designator, and returns these as a sequence of
+sequences.<sup id="back-fn-fn-slice-into-tuple" ><a href="#fn-fn-slice-into-tuple" title="2.Now that Dafny supports built-in tuples, the
+plan is to change the sequence slice operation to return not a
+sequence of subsequences, but a tuple of subsequences.
+&#8617;" class="footnote-ref localref" ><span class="footnote-label">2</span></a></sup> If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="code-escaped"><em class="em-low1">slices</em></span></code> is terminated by a
+colon, then the length of the last slice extends until the end of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code>,
+that is, its length is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">|s|</code> minus the sum of the given length
+designators. For example, the following equalities hold, for any
+sequence <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code> of length at least <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">10</span></code>:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> t <span style="color:blue">:=</span> [<span class="constant" style="color:purple">3.14</span>, <span class="constant" style="color:purple">2.7</span>, <span class="constant" style="color:purple">1.41</span>, <span class="constant" style="color:purple">1985.44</span>, <span class="constant" style="color:purple">100.0</span>, <span class="constant" style="color:purple">37.2</span>][<span class="constant" style="color:purple">1</span>:<span class="constant" style="color:purple">0</span>:<span class="constant" style="color:purple">3</span>];
+<span style="color:blue">assert</span> |t| == <span class="constant" style="color:purple">3</span> &amp;&amp; t[<span class="constant" style="color:purple">0</span>] == [<span class="constant" style="color:purple">3.14</span>] &amp;&amp; t[<span class="constant" style="color:purple">1</span>] == [];
+<span style="color:blue">assert</span> t[<span class="constant" style="color:purple">2</span>] == [<span class="constant" style="color:purple">2.7</span>, <span class="constant" style="color:purple">1.41</span>, <span class="constant" style="color:purple">1985.44</span>];
+<span style="color:blue">var</span> u <span style="color:blue">:=</span> [<span style="color:blue">true</span>, <span style="color:blue">false</span>, <span style="color:blue">false</span>, <span style="color:blue">true</span>][<span class="constant" style="color:purple">1</span>:<span class="constant" style="color:purple">1</span>:];
+<span style="color:blue">assert</span> |u| == <span class="constant" style="color:purple">3</span> &amp;&amp; u[<span class="constant" style="color:purple">0</span>][<span class="constant" style="color:purple">0</span>] &amp;&amp; !u[<span class="constant" style="color:purple">1</span>][<span class="constant" style="color:purple">0</span>] &amp;&amp; u[<span class="constant" style="color:purple">2</span>] == [<span style="color:blue">false</span>, <span style="color:blue">true</span>];
+<span style="color:blue">assert</span> s[<span class="constant" style="color:purple">10</span>:][<span class="constant" style="color:purple">0</span>] == s[..<span class="constant" style="color:purple">10</span>];
+<span style="color:blue">assert</span> s[<span class="constant" style="color:purple">10</span>:][<span class="constant" style="color:purple">1</span>] == s[<span class="constant" style="color:purple">10</span>..];</code></pre>
+<p class="p noindent para-continued">The operation <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">multiset</span>(s)</code> yields the multiset of elements of
+sequence <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code>. It is allowed in non-ghost contexts only if the element
+type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> is equality supporting.
+</p><h4 id="sec-strings" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">9.2.4</span>.&#8194;</span>Strings</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_stringtype_" class="ntdef" style="color:olive">StringType_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"string"</span></span> </code></pre>
+<p class="p noindent para-continued">A special case of a sequence type is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">seq</span>&lt;<span style="color:teal">char</span>&gt;</code>, for which Dafny
+provides a synonym: <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">string</span></code>. Strings are like other sequences, but
+provide additional syntax for sequence display expressions, namely
+<em class="em-low1">string literals</em>. There are two forms of the syntax for string
+literals: the <em class="em-low1">standard form</em> and the <em class="em-low1">verbatim form</em>.
+</p>
+<p class="p indent">String literals of the standard form are enclosed in double quotes, as
+in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">Dafny</span><span style="color:maroon">&quot;</span></code>. To include a double quote in such a string literal,
+it is necessary to use an escape sequence. Escape sequences can also
+be used to include other characters. The supported escape sequences
+are the same as those for character literals, see Section&nbsp;<a href="#sec-characters" title="6.2.&#8194;Characters" class="localref" style="target-element:h2"><span class="heading-label">6.2</span></a>.
+For example, the Dafny expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">say </span><span style="color:gray">\&quot;</span><span style="color:maroon">yes</span><span style="color:gray">\&quot;</span><span style="color:maroon">&quot;</span></code> represents the
+string <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&#39;say <span style="color:maroon">&quot;</span><span style="color:maroon">yes</span><span style="color:maroon">&quot;</span>&#39;</code>.
+The escape sequence for a single quote is redundant, because
+<span class="monospace">"'"</span> and <span class="monospace">"\'"</span> denote the same
+string&#8212;both forms are provided in order to support the same
+escape sequences as for character literals.
+</p>
+<p class="p indent">String literals of the verbatim form are bracketed by
+<span class="monospace">@"</span> and <span class="monospace">"</span>, as in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">@&quot;</span><span style="color:maroon">Dafny</span><span style="color:maroon">&quot;</span></code>. To include
+a double quote in such a string literal, it is necessary to use the
+escape sequence <span class="monospace">""</span>, that is, to write the character
+twice. In the verbatim form, there are no other escape sequences.
+Even characters like newline can be written inside the string literal
+(hence spanning more than one line in the program text).
+</p>
+<p class="p indent">For example, the following three expressions denote the same string:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:maroon">&quot;</span><span style="color:maroon">C:</span><span style="color:gray">\\</span><span style="color:maroon">tmp.txt</span><span style="color:maroon">&quot;</span>
+<span style="color:maroon">@&quot;</span><span style="color:maroon">C:\tmp.txt</span><span style="color:maroon">&quot;</span>
+[<span style="color:maroon">&#39;C&#39;</span>, <span style="color:maroon">&#39;:&#39;</span>, <span style="color:maroon">&#39;</span><span style="color:gray">\\</span><span style="color:maroon">&#39;</span>, <span style="color:maroon">&#39;t&#39;</span>, <span style="color:maroon">&#39;m&#39;</span>, <span style="color:maroon">&#39;p&#39;</span>, <span style="color:maroon">&#39;.&#39;</span>, <span style="color:maroon">&#39;t&#39;</span>, <span style="color:maroon">&#39;x&#39;</span>, <span style="color:maroon">&#39;t&#39;</span>]</code></pre>
+<p class="p noindent para-continued">Since strings are sequences, the relational operators <span class="monospace">&lt;</span>
+and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;=</code> are defined on them. Note, however, that these operators
+still denote proper prefix and prefix, respectively, not some kind of
+alphabetic comparison as might be desirable, for example, when
+sorting strings.
+</p><h3 id="sec-finite-and-infinite-maps" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">9.3</span>.&#8194;</span>Finite and Infinite Maps</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_finitemaptype_" class="ntdef" style="color:olive">FiniteMapType_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"map"</span></span> [ <span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span> ]
+<span class="code-escaped"><span id="1_infinitemaptype_" class="ntdef" style="color:olive">InfiniteMapType_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"imap"</span></span> [ <span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span> ] </code></pre>
+<p class="p noindent para-continued">For any types <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">U</code>, a value of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">map</span>&lt;T,U&gt;</code> denotes a
+<em class="em-low1">(finite) map</em>
+from <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">U</code>. In other words, it is a look-up table indexed by
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>. The <em class="em-low1">domain</em> of the map is a finite set of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> values that have
+associated <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">U</code> values. Since the keys in the domain are compared
+using equality in the type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>, type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">map</span>&lt;T,U&gt;</code> can be used in a
+non-ghost context only if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> is equality supporting.
+</p>
+<p class="p indent">Similarly, for any types <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">U</code>, a value of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">imap</span>&lt;T,U&gt;</code>
+denotes a <em class="em-low1">(possibly) infinite map</em>. In most regards, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">imap</span>&lt;T,U&gt;</code> is
+like <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">map</span>&lt;T,U&gt;</code>, but a map of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">imap</span>&lt;T,U&gt;</code> is allowed to have an
+infinite domain.
+</p>
+<p class="p indent">A map can be formed using a <em class="em-low1">map display</em> expression (see <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_mapdisplayexpr" class="ntref localref" style="color:maroon">MapDisplayExpr</a></span></code>),
+which is a possibly empty, ordered list of <em class="em-low1">maplets</em>, each maplet having the
+form <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t <span style="color:blue">:=</span> u</code> where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t</code> is an expression of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">u</code> is an
+expression of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">U</code>, enclosed in square brackets after the keyword
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">map</span></code>. To illustrate,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:teal">map</span>[] <span style="color:teal">map</span>[<span class="constant" style="color:purple">20</span> <span style="color:blue">:=</span> <span style="color:blue">true</span>, <span class="constant" style="color:purple">3</span> <span style="color:blue">:=</span> <span style="color:blue">false</span>, <span class="constant" style="color:purple">20</span> <span style="color:blue">:=</span> <span style="color:blue">false</span>] <span style="color:teal">map</span>[a+b <span style="color:blue">:=</span> c+d]</code></pre>
+<p class="p noindent para-continued">are three examples of map displays. By using the keyword <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">imap</span></code>
+instead of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">map</span></code>, the map produced will be of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">imap</span>&lt;T,U&gt;</code>
+instead of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">map</span>&lt;T,U&gt;</code>. Note that an infinite map (<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">imap</span></code>) is allowed
+to have a finite domain, whereas a finite map (<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">map</span></code>) is not allowed
+to have an infinite domain.
+If the same key occurs more than
+once, only the last occurrence appears in the resulting
+map.<sup id="back-fn-fn-map-display" ><a href="#fn-fn-map-display" title="3.This is likely to change in the future to disallow
+multiple occurrences of the same key.
+&#8617;" class="footnote-ref localref" ><span class="footnote-label">3</span></a></sup> There is also a <em class="em-low1">map comprehension expression</em>,
+explained in section&nbsp;<a href="#sec-map-comprehension-expression" title="22.34.&#8194;Map Comprehension Expression" class="localref" style="target-element:h2"><span class="heading-label">22.34</span></a>.
+</p>
+<p class="p indent">For any map <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">fm</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">map</span>&lt;T,U&gt;</code>,
+any map <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">map</span>&lt;T,U&gt;</code> or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">imap</span>&lt;T,U&gt;</code>,
+any expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>,
+any expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">u</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">U</code>, and any <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">d</code> in the domain of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m</code> (that
+is, satisfying <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">d <span style="color:blue">in</span> m</code>), maps support the following operations:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> expression </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <span class="monospace">|fm|</span> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> map cardinality </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m[d]</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> map selection </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m[t <span style="color:blue">:=</span> u]</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> map update </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="4" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t <span style="color:blue">in</span> m</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> map domain membership </td></tr>
+<tr><td class="tbody tr-odd tr-last col cell-border-left col-odd col-first" data-row="5" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t !<span style="color:blue">in</span> m</code> </td><td class="tbody tr-odd tr-last col cell-border-left cell-border-right col-even col-last" data-row="5" data-col="2"> map domain non-membership </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left cell-line col-odd col-first" data-row="5" data-col="1"></td><td class="tbody tr-odd col cell-border-left cell-border-right cell-line col-even col-last" data-row="5" data-col="2"></td></tr></tbody></table>
+<p class="p noindent"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">|fm|</code> denotes the number of mappings in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">fm</code>, that is, the
+cardinality of the domain of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">fm</code>. Note that the cardinality operator
+is not supported for infinite maps.
+Expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m[d]</code> returns the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">U</code> value that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m</code> associates with <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">d</code>.
+Expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m[t <span style="color:blue">:=</span> u]</code> is a map like <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m</code>, except that the
+element at key <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t</code> is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">u</code>. The expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t <span style="color:blue">in</span> m</code> says <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t</code> is in the
+domain of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t !<span style="color:blue">in</span> m</code> is a syntactic shorthand for
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!(t <span style="color:blue">in</span> m)</code>.<sup id="back-fn-fn-map-membership" ><a href="#fn-fn-map-membership" title="4.This is likely to change in the future as
+follows: The in and !in operations will no longer be
+supported on maps. Instead, for any map m, m.Domain will
+return its domain as a set and m.Range will return, also as a
+set, the image of m under its domain.
+&#8617;" class="footnote-ref localref" ><span class="footnote-label">4</span></a></sup>
+</p>
+<p class="p indent">Here is a small example, where a map <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">cache</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">map</span>&lt;<span style="color:teal">int</span>,<span style="color:teal">real</span>&gt;</code>
+is used to cache computed values of Joule-Thomson coefficients for
+some fixed gas at a given temperature:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">if</span> K <span style="color:blue">in</span> cache { <span style="color:darkgreen">// check if temperature is in domain of cache</span>
+ coeff <span style="color:blue">:=</span> cache[K]; <span style="color:darkgreen">// read result in cache</span>
+} <span style="color:blue">else</span> {
+ coeff <span style="color:blue">:=</span> ComputeJouleThomsonCoefficient(K); <span style="color:darkgreen">// do expensive computation</span>
+ cache <span style="color:blue">:=</span> cache[K <span style="color:blue">:=</span> coeff]; <span style="color:darkgreen">// update the cache</span>
+}</code></pre><h2 id="sec-types-that-stand-for-other-types" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">10</span>.&#8194;</span>Types that stand for other types</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_synonymtypedecl" class="ntdef" style="color:olive">SynonymTypeDecl</span></span> =
+ ( <span class="code-escaped"><a href="#1_synonymtypedefinition_" class="ntref localref" style="color:maroon">SynonymTypeDefinition_</a></span> | <span class="code-escaped"><a href="#1_opaquetypedefinition_" class="ntref localref" style="color:maroon">OpaqueTypeDefinition_</a></span> ) [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span> ] </code></pre>
+<p class="p noindent para-continued">It is sometimes useful to know a type by several names or to treat a
+type abstractly. Synonym and opaque types serve this purpose.
+</p><h3 id="sec-type-synonyms" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">10.0</span>.&#8194;</span>Type synonyms</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_synonymtypedefinition_" class="ntdef" style="color:olive">SynonymTypeDefinition_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"type"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span class="code-escaped"><a href="#1_synonymtypename" class="ntref localref" style="color:maroon">SynonymTypeName</a></span> [ <span class="code-escaped"><a href="#1_genericparameters" class="ntref localref" style="color:maroon">GenericParameters</a></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"="</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span></code></pre>
+<p class="p noindent para-continued">A <em class="em-low1">type synonym</em> declaration:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">type</span> Y&lt;T&gt; = G</code></pre>
+<p class="p noindent para-continued">declares <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Y&lt;T&gt;</code> to be a synonym for the type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">G</code>. Here, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> is a
+nonempty list of type parameters (each of which is optionally
+designated with the suffix &#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(==)</code>&#8221;), which can be used as free type
+variables in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">G</code>. If the synonym has no type parameters, the &#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;T&gt;</code>&#8221;
+is dropped. In all cases, a type synonym is just a synonym. That is,
+there is never a difference, other than possibly in error messages
+produced, between <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Y&lt;T&gt;</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">G</code>.
+</p>
+<p class="p indent">For example, the names of the following type synonyms may improve the
+readability of a program:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">type</span> Replacements&lt;T&gt; = <span style="color:teal">map</span>&lt;T,T&gt;
+<span style="color:blue">type</span> Vertex = <span style="color:teal">int</span></code></pre>
+<p class="p noindent para-continued">As already described in Section&nbsp;<a href="#sec-strings" title="9.2.4.&#8194;Strings" class="localref" style="target-element:h3"><span class="heading-label">9.2.4</span></a>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">string</span></code> is a built-in
+type synonym for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">seq</span>&lt;<span style="color:teal">char</span>&gt;</code>, as if it would have been declared as
+follows:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">type</span> <span style="color:teal">string</span> = <span style="color:teal">seq</span>&lt;<span style="color:teal">char</span>&gt;</code></pre><h3 id="sec-opaque-types" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">10.1</span>.&#8194;</span>Opaque types</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_opaquetypedefinition_" class="ntdef" style="color:olive">OpaqueTypeDefinition_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"type"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span class="code-escaped"><a href="#1_synonymtypename" class="ntref localref" style="color:maroon">SynonymTypeName</a></span>
+ [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"=="</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> ] [ <span class="code-escaped"><a href="#1_genericparameters" class="ntref localref" style="color:maroon">GenericParameters</a></span> ] </code></pre>
+<p class="p noindent para-continued">A special case of a type synonym is one that is underspecified. Such
+a type is declared simply by:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">type</span> Y&lt;T&gt;</code></pre>
+<p class="p noindent para-continued">It is known as an <em class="em-low1">opaque type</em>. Its definition can be revealed in a
+refining module. To indicate that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Y</code> designates an
+equality-supporting type, &#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(==)</code>&#8221; can be written immediately
+following the name &#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Y</code>&#8221;.
+</p>
+<p class="p indent">For example, the declarations
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">type</span> T
+<span style="color:blue">function</span> F(t: T): T</code></pre>
+<p class="p noindent para-continued">can be used to model an uninterpreted function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">F</code> on some
+arbitrary type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>. As another example,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">type</span> Monad&lt;T&gt;</code></pre>
+<p class="p noindent para-continued">can be used abstractly to represent an arbitrary parameterized monad.
+</p><h2 id="sec-well-founded-functions-and-extreme-predicates" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">11</span>.&#8194;</span>Well-founded Functions and Extreme Predicates</h2>
+<p class="p noindent">This section is a tutorial on well-founded functions and extreme predicates.
+We place it here in preparation for Section&nbsp;<a href="#sec-class-types" title="12.&#8194;Class Types" class="localref" style="target-element:h1"><span class="heading-label">12</span></a>
+where function and predicate definitions are described.
+</p>
+<p class="p indent">Recursive functions are a core part of computer science and mathematics.
+Roughly speaking, when the definition of such a function spells out a
+terminating computation from given arguments, we may refer to
+it as a <em class="em-low1">well-founded function</em>. For example, the common factorial and
+Fibonacci functions are well-founded functions.
+</p>
+<p class="p indent">There are also other ways to define functions. An important case
+regards the definition of a boolean function as an extreme solution
+(that is, a least or greatest solution) to some equation. For
+computer scientists with interests in logic or programming languages,
+these <em class="em-low1">extreme predicates</em> are important because they describe the
+judgments that can be justified by a given set of inference rules
+(see, e.g.,&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#camillerimelham:inductiverelations" title="Juanito Camilleri and Tom Melham.
+Reasoning with inductively defined relations in the HOL theorem prover.
+Technical Report 265, University of Cambridge Computer Laboratory, 1992." class="bibref localref" style="target-element:bibitem"><span class="cite-number">3</span></a>, <a href="#leroygrall:coinductivebigstep" title="Xavier Leroy and Herv&#233; Grall.
+Coinductive big-step operational semantics." class="bibref localref" style="target-element:bibitem"><span class="cite-number">24</span></a>, <a href="#nipkowklein:concretesemantics" title="Tobias Nipkow and Gerwin Klein.
+Concrete Semantics with Isabelle/HOL." class="bibref localref" style="target-element:bibitem"><span class="cite-number">28</span></a>, <a href="#pierce:softwarefoundations" title="Benjamin&#160;C. Pierce, Chris Casinghino, Marco Gaboardi, Michael Greenberg, Catalin Hri&#355;cu, Vilhelm Sj&#246;berg, and Brent Yorgey.
+Software Foundations.
+http://www.cis.upenn.edu/~bcpierce/sf, version 3.2 edition, January 2015." class="bibref localref" style="target-element:bibitem"><span class="cite-number">31</span></a>, <a href="#winskel:formalsemantics" title="Glynn Winskel.
+The Formal Semantics of Programming Languages: An Introduction.
+MIT Press, 1993." class="bibref localref" style="target-element:bibitem"><span class="cite-number">36</span></a>]</span>).
+</p>
+<p class="p indent">To benefit from machine-assisted reasoning, it is necessary not just
+to understand extreme predicates but also to have techniques for
+proving theorems about them. A foundation for this reasoning was
+developed by Paulin-Mohring&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#paulinmohring:inductivecoq" title="Christine Paulin-Mohring.
+Inductive definitions in the system Coq &#8212; rules and properties." class="bibref localref" style="target-element:bibitem"><span class="cite-number">29</span></a>]</span> and is the
+basis of the constructive logic supported by Coq&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#coq:book" title="Yves Bertot and Pierre Cast&#233;ran.
+Interactive Theorem Proving and Program Development &#8212; Coq&#39;Art: The Calculus of Inductive Constructions.
+Texts in Theoretical Computer Science. Springer, 2004." class="bibref localref" style="target-element:bibitem"><span class="cite-number">1</span></a>]</span> as well
+as other proof assistants&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#bovedybjernorell:briefagda" title="Ana Bove, Peter Dybjer, and Ulf Norell.
+A brief overview of Agda &#8212; a functional language with dependent types.
+In Stefan Berghofer, Tobias Nipkow, Christian Urban, and Makarius Wenzel, editors, Theorem Proving in Higher Order Logics, 22nd International Conference, TPHOLs 2009, volume 5674 of Lecture Notes in Computer Science, pages 73&#8211;78. Springer, August 2009." class="bibref localref" style="target-element:bibitem"><span class="cite-number">2</span></a>, <a href="#swamyetal:fstar2011" title="Nikhil Swamy, Juan Chen, C&#233;dric Fournet, Pierre-Yves Strub, Karthikeyan Bhargavan, and Jean Yang.
+Secure distributed programming with value-dependent types.
+In ICFP 2011, pages 266&#8211;278. ACM, September 2011." class="bibref localref" style="target-element:bibitem"><span class="cite-number">34</span></a>]</span>. Essentially, the idea is to represent the
+knowledge that an extreme predicate holds by the proof term by which
+this knowledge was derived. For a predicate defined as the least
+solution, such proof terms are values of an inductive datatype (that
+is, finite proof trees), and for the greatest solution, a coinductive
+datatype (that is, possibly infinite proof trees). This means that
+one can use induction and coinduction when reasoning about these proof
+trees. Therefore, these extreme predicates are known as,
+respectively, <em class="em-low1">inductive predicates</em> and <em class="em-low1">coinductive predicates</em> (or,
+<em class="em-low1">co-predicates</em> for short). Support for extreme predicates is also
+available in the proof assistants Isabelle&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#paulson:cade1994" title="Lawrence&#160;C. Paulson.
+A fixedpoint approach to implementing (co)inductive definitions." class="bibref localref" style="target-element:bibitem"><span class="cite-number">30</span></a>]</span> and HOL
+<span class="citations" style="target-element:bibitem">[<a href="#harrison:inductivedefs" title="John Harrison.
+Inductive definitions: Automation and application." class="bibref localref" style="target-element:bibitem"><span class="cite-number">6</span></a>]</span>.
+</p>
+<p class="p indent">Dafny supports both well-founded functions and extreme predicates.
+This section is a tutorial that describes the difference in general
+terms, and then describes novel syntactic support in Dafny for
+defining and proving lemmas with extreme predicates. Although Dafny&#39;s
+verifier has at its core a first-order SMT solver, Dafny&#39;s logical
+encoding makes it possible to reason about fixpoints in an automated
+way.
+</p>
+<p class="p indent">The encoding for coinductive predicates in Dafny was described previously
+<span class="citations" style="target-element:bibitem">[<a href="#leinomoskal:coinduction" title="K.&#160;Rustan&#160;M. Leino and Micha&#322; Moskal.
+Co-induction simply &#8212; automatic co-inductive proofs in a program verifier.
+In FM 2014, volume 8442 of LNCS, pages 382&#8211;398. Springer, May 2014b." class="bibref localref" style="target-element:bibitem"><span class="cite-number">21</span></a>]</span> and is here described in Section
+<a href="#sec-co-inductive-datatypes" title="18.2.&#8194;Co-inductive datatypes" class="localref" style="target-element:h2"><span class="heading-label">18.2</span></a>.
+</p><h3 id="sec-function-definitions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">11.0</span>.&#8194;</span>Function Definitions</h3>
+<p class="p noindent para-continue">To define a function <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:4.8277em" viewBox='-0.2 25.78 45.978 9.466' ><desc>$f \colon X \to Y$</desc><g id='page1'><use x='0' xlink:href='#g3-102' y='33.004'/><use x='7.039' xlink:href='#g5-58' y='33.004'/><use x='13.127' xlink:href='#g3-88' y='33.004'/><use x='24.899' xlink:href='#g1-33' y='33.004'/><use x='37.592' xlink:href='#g3-89' y='33.004'/></g></svg> in terms of itself, one can
+write an equation like
+</p>
+<div id="eq-general" class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(0)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:1.0881em;width:5.9833em" viewBox='195.957 27.324 56.984 10.363' ><desc>\[ f \Equal \F(f)
+\]</desc><g id='page2'><use x='196.157' xlink:href='#g3-102' y='34.996'/><use x='213.158' xlink:href='#g5-61' y='34.996'/><use x='231.947' xlink:href='#g1-70' y='34.996'/><use x='240.07' xlink:href='#g5-40' y='34.996'/><use x='243.93' xlink:href='#g3-102' y='34.996'/><use x='249.862' xlink:href='#g5-41' y='34.996'/></g></svg></div></div>
+<p class="p noindent para-continued">where <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-0.2 25.919 8.669 7.603' ><desc>$\mathcal{F}$</desc><g id='page5'><use x='0' xlink:href='#g1-70' y='33.004'/></g></svg> is a non-recursive function of type
+<svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:9.2896em" viewBox='-18.2 25.332 88.472 10.363' ><desc>$(X \to Y) \to X \to Y$</desc><g id='page4'><use x='-18' xlink:href='#g5-40' y='33.004'/><use x='-14.14' xlink:href='#g3-88' y='33.004'/><use x='-2.368' xlink:href='#g1-33' y='33.004'/><use x='10.325' xlink:href='#g3-89' y='33.004'/><use x='18.301' xlink:href='#g5-41' y='33.004'/><use x='24.928' xlink:href='#g1-33' y='33.004'/><use x='37.621' xlink:href='#g3-88' y='33.004'/><use x='49.393' xlink:href='#g1-33' y='33.004'/><use x='62.086' xlink:href='#g3-89' y='33.004'/></g></svg>. Because it takes a function as an argument,
+<svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-0.2 25.919 8.669 7.603' ><desc>$\mathcal{F}$</desc><g id='page5'><use x='0' xlink:href='#g1-70' y='33.004'/></g></svg> is referred to as a <em class="em-low1">functor</em> (or <em class="em-low1">functional</em>, but not to be
+confused by the category-theory notion of a functor).
+Throughout, I will assume that <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:2.3294em" viewBox='-18.2 25.332 22.185 10.363' ><desc>$\F(f)$</desc><g id='page8'><use x='-18' xlink:href='#g1-70' y='33.004'/><use x='-9.877' xlink:href='#g5-40' y='33.004'/><use x='-6.017' xlink:href='#g3-102' y='33.004'/><use x='-0.085' xlink:href='#g5-41' y='33.004'/></g></svg> by itself is well defined,
+for example that it does not divide by zero. I will also assume that <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg> occurs
+only in fully applied calls in <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:2.3294em" viewBox='-18.2 25.332 22.185 10.363' ><desc>$\F(f)$</desc><g id='page8'><use x='-18' xlink:href='#g1-70' y='33.004'/><use x='-9.877' xlink:href='#g5-40' y='33.004'/><use x='-6.017' xlink:href='#g3-102' y='33.004'/><use x='-0.085' xlink:href='#g5-41' y='33.004'/></g></svg>; eta expansion can be applied to
+ensure this. If <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg> is a boolean function, that is, if <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7564em;width:0.8805em" viewBox='-18.2 25.999 8.386 7.204' ><desc>$Y$</desc><g id='page210'><use x='-18' xlink:href='#g3-89' y='33.004'/></g></svg> is
+the type of booleans, then I call <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg> a <em class="em-low1">predicate</em>.
+</p>
+<p class="p indent para-continue">For example, the common Fibonacci function over the
+natural numbers can be defined by the equation
+</p>
+<div class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(1)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:1.0881em;width:27.1042em" viewBox='95.112 27.324 258.135 10.363' ><desc>\[ \fib \Equal
+ \lambda n \bullet\; \ite{n &lt; 2}{n}{\fib(n-2) + \fib(n-1)}
+\]</desc><g id='page12'><use x='95.561' xlink:href='#g0-12' y='34.996'/><use x='101.142' xlink:href='#g0-98' y='34.996'/><use x='117.444' xlink:href='#g5-61' y='34.996'/><use x='136.233' xlink:href='#g3-21' y='34.996'/><use x='142.022' xlink:href='#g3-110' y='34.996'/><use x='150.194' xlink:href='#g1-15' y='34.996'/><use x='160.138' xlink:href='#g5-105' y='34.996'/><use x='162.895' xlink:href='#g5-102' y='34.996'/><use x='171.484' xlink:href='#g3-110' y='34.996'/><use x='180.209' xlink:href='#g3-60' y='34.996'/><use x='190.696' xlink:href='#g5-50' y='34.996'/><use x='201.194' xlink:href='#g5-116' y='34.996'/><use x='205.053' xlink:href='#g5-104' y='34.996'/><use x='210.568' xlink:href='#g5-101' y='34.996'/><use x='214.979' xlink:href='#g5-110' y='34.996'/><use x='226.1' xlink:href='#g3-110' y='34.996'/><use x='237.592' xlink:href='#g5-101' y='34.996'/><use x='242.004' xlink:href='#g5-108' y='34.996'/><use x='244.761' xlink:href='#g5-115' y='34.996'/><use x='248.676' xlink:href='#g5-101' y='34.996'/><use x='258.68' xlink:href='#g0-12' y='34.996'/><use x='264.26' xlink:href='#g0-98' y='34.996'/><use x='269.493' xlink:href='#g5-40' y='34.996'/><use x='273.352' xlink:href='#g3-110' y='34.996'/><use x='281.524' xlink:href='#g1-0' y='34.996'/><use x='291.458' xlink:href='#g5-50' y='34.996'/><use x='296.42' xlink:href='#g5-41' y='34.996'/><use x='302.494' xlink:href='#g5-43' y='34.996'/><use x='312.428' xlink:href='#g0-12' y='34.996'/><use x='318.008' xlink:href='#g0-98' y='34.996'/><use x='323.241' xlink:href='#g5-40' y='34.996'/><use x='327.1' xlink:href='#g3-110' y='34.996'/><use x='335.272' xlink:href='#g1-0' y='34.996'/><use x='345.206' xlink:href='#g5-49' y='34.996'/><use x='350.168' xlink:href='#g5-41' y='34.996'/></g></svg></div></div>
+<p class="p noindent para-continued para-continue">With the understanding that the argument <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.5159em;width:0.6686em" viewBox='-18.2 28.4 6.368 4.913' ><desc>$n$</desc><g id='page56'><use x='-18' xlink:href='#g3-110' y='33.004'/></g></svg> is universally
+quantified, we can write this equation equivalently as
+</p>
+<div id="eq-fib" class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(2)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:1.0881em;width:26.0302em" viewBox='100.232 27.324 247.907 10.363' ><desc>\[ \fib(n) \Equal
+ \ite{n &lt; 2}{n}{\fib(n-2) + \fib(n-1)}
+\]</desc><g id='page14'><use x='100.681' xlink:href='#g0-12' y='34.996'/><use x='106.261' xlink:href='#g0-98' y='34.996'/><use x='111.494' xlink:href='#g5-40' y='34.996'/><use x='115.354' xlink:href='#g3-110' y='34.996'/><use x='121.311' xlink:href='#g5-41' y='34.996'/><use x='136.241' xlink:href='#g5-61' y='34.996'/><use x='155.03' xlink:href='#g5-105' y='34.996'/><use x='157.787' xlink:href='#g5-102' y='34.996'/><use x='166.376' xlink:href='#g3-110' y='34.996'/><use x='175.101' xlink:href='#g3-60' y='34.996'/><use x='185.588' xlink:href='#g5-50' y='34.996'/><use x='196.085' xlink:href='#g5-116' y='34.996'/><use x='199.945' xlink:href='#g5-104' y='34.996'/><use x='205.459' xlink:href='#g5-101' y='34.996'/><use x='209.871' xlink:href='#g5-110' y='34.996'/><use x='220.992' xlink:href='#g3-110' y='34.996'/><use x='232.484' xlink:href='#g5-101' y='34.996'/><use x='236.895' xlink:href='#g5-108' y='34.996'/><use x='239.652' xlink:href='#g5-115' y='34.996'/><use x='243.567' xlink:href='#g5-101' y='34.996'/><use x='253.572' xlink:href='#g0-12' y='34.996'/><use x='259.152' xlink:href='#g0-98' y='34.996'/><use x='264.384' xlink:href='#g5-40' y='34.996'/><use x='268.244' xlink:href='#g3-110' y='34.996'/><use x='276.416' xlink:href='#g1-0' y='34.996'/><use x='286.349' xlink:href='#g5-50' y='34.996'/><use x='291.312' xlink:href='#g5-41' y='34.996'/><use x='297.386' xlink:href='#g5-43' y='34.996'/><use x='307.32' xlink:href='#g0-12' y='34.996'/><use x='312.9' xlink:href='#g0-98' y='34.996'/><use x='318.132' xlink:href='#g5-40' y='34.996'/><use x='321.992' xlink:href='#g3-110' y='34.996'/><use x='330.164' xlink:href='#g1-0' y='34.996'/><use x='340.097' xlink:href='#g5-49' y='34.996'/><use x='345.06' xlink:href='#g5-41' y='34.996'/></g></svg></div></div>
+<p class="p noindent para-continued">The fact that the function being defined occurs on both sides of the equation
+causes concern that we might not be defining the function properly, leading to a
+logical inconsistency. In general, there
+could be many solutions to an equation like&nbsp;<a href="#eq-general" class="localref" style="target-element:equation"><span class="equation-label">(0)</span></a> or there could be none.
+Let&#39;s consider two ways to make sure we&#39;re defining the function uniquely.
+</p><h4 id="sec-well-founded-functions" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">11.0.0</span>.&#8194;</span>Well-founded Functions</h4>
+<p class="p noindent para-continue">A standard way to ensure that equation&nbsp;<a href="#eq-general" class="localref" style="target-element:equation"><span class="equation-label">(0)</span></a> has a unique solution in <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg> is
+to make sure the recursion is well-founded, which roughly means that the
+recursion terminates. This is done by introducing any well-founded
+relation <svg class="math-inline math-render-svg math" style="vertical-align:-0.0685em;height:0.7031em;width:1.0852em" viewBox='-18.2 27.165 10.335 6.696' ><desc>$\Less$</desc><g id='page32'><use x='-18' xlink:href='#g1-28' y='33.004'/></g></svg> on the domain of <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg> and making sure that the argument to each recursive
+call goes down in this ordering. More precisely, if we formulate&nbsp;<a href="#eq-general" class="localref" style="target-element:equation"><span class="equation-label">(0)</span></a> as
+</p>
+<div class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(3)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:1.1448em;width:7.6831em" viewBox='187.838 26.784 73.172 10.903' ><desc>\[ f(x) \Equal \F&#39;(f)
+\]</desc><g id='page18'><use x='188.038' xlink:href='#g3-102' y='34.996'/><use x='193.97' xlink:href='#g5-40' y='34.996'/><use x='197.83' xlink:href='#g3-120' y='34.996'/><use x='203.502' xlink:href='#g5-41' y='34.996'/><use x='218.432' xlink:href='#g5-61' y='34.996'/><use x='237.221' xlink:href='#g1-70' y='34.996'/><use x='245.344' xlink:href='#g2-48' y='30.883'/><use x='248.139' xlink:href='#g5-40' y='34.996'/><use x='251.999' xlink:href='#g3-102' y='34.996'/><use x='257.931' xlink:href='#g5-41' y='34.996'/></g></svg></div></div>
+<p class="p noindent para-continued">then we want to check <svg class="math-inline math-render-svg math" style="vertical-align:-0.0655em;height:0.8224em;width:3.0916em" viewBox='-0.2 26.029 29.444 7.832' ><desc>$E \Less x$</desc><g id='page19'><use x='0' xlink:href='#g3-69' y='33.004'/><use x='10.668' xlink:href='#g1-28' y='33.004'/><use x='23.361' xlink:href='#g3-120' y='33.004'/></g></svg> for each call <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:2.3061em" viewBox='-18.2 25.332 21.963 10.363' ><desc>$f(E)$</desc><g id='page20'><use x='-18' xlink:href='#g3-102' y='33.004'/><use x='-12.068' xlink:href='#g5-40' y='33.004'/><use x='-8.208' xlink:href='#g3-69' y='33.004'/><use x='-0.307' xlink:href='#g5-41' y='33.004'/></g></svg> in <svg class="math-inline math-render-svg math" style="vertical-align:-0.2936em;height:1.0924em;width:2.6229em" viewBox='-0.2 25.29 24.98 10.404' ><desc>$\F&#39;(f)$</desc><g id='page209'><use x='0' xlink:href='#g1-70' y='33.004'/><use x='8.123' xlink:href='#g2-48' y='29.388'/><use x='10.918' xlink:href='#g5-40' y='33.004'/><use x='14.778' xlink:href='#g3-102' y='33.004'/><use x='20.71' xlink:href='#g5-41' y='33.004'/></g></svg>. When a function
+definition satisfies this <em class="em-low1">decrement condition</em>, then the function is said to be
+<em class="em-low1">well-founded</em>.
+</p>
+<p class="p indent para-continue">For example, to check the decrement condition for <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:1.2046em" viewBox='-0.449 25.78 11.472 9.466' ><desc>$\fib$</desc><g id='page41'><use x='0' xlink:href='#g0-12' y='33.004'/><use x='5.58' xlink:href='#g0-98' y='33.004'/></g></svg> in&nbsp;<a href="#eq-fib" class="localref" style="target-element:equation"><span class="equation-label">(2)</span></a>, we can pick
+<svg class="math-inline math-render-svg math" style="vertical-align:-0.0685em;height:0.7031em;width:1.0852em" viewBox='-18.2 27.165 10.335 6.696' ><desc>$\Less$</desc><g id='page32'><use x='-18' xlink:href='#g1-28' y='33.004'/></g></svg> to be the arithmetic less-than relation on natural numbers and check the
+following, for any <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.5159em;width:0.6686em" viewBox='-18.2 28.4 6.368 4.913' ><desc>$n$</desc><g id='page56'><use x='-18' xlink:href='#g3-110' y='33.004'/></g></svg>:
+</p>
+<div class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(4)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:0.8820em;width:18.2190em" viewBox='155.898 28.161 173.514 8.4' ><desc>\[ 2 \leq n \;\;\Imp\;\; n-2 \Less n \;\And\; n-1 \Less n
+\]</desc><g id='page25'><use x='156.098' xlink:href='#g5-50' y='34.996'/><use x='163.828' xlink:href='#g1-20' y='34.996'/><use x='174.316' xlink:href='#g3-110' y='34.996'/><use x='191.343' xlink:href='#g5-61' y='34.996'/><use x='197.402' xlink:href='#g1-41' y='34.996'/><use x='218.397' xlink:href='#g3-110' y='34.996'/><use x='226.568' xlink:href='#g1-0' y='34.996'/><use x='236.502' xlink:href='#g5-50' y='34.996'/><use x='244.232' xlink:href='#g1-28' y='34.996'/><use x='256.925' xlink:href='#g3-110' y='34.996'/><use x='270.631' xlink:href='#g1-94' y='34.996'/><use x='284.996' xlink:href='#g3-110' y='34.996'/><use x='293.168' xlink:href='#g1-0' y='34.996'/><use x='303.101' xlink:href='#g5-49' y='34.996'/><use x='310.831' xlink:href='#g1-28' y='34.996'/><use x='323.524' xlink:href='#g3-110' y='34.996'/></g></svg></div></div>
+<p class="p noindent para-continued">Note that we are entitled to using the antecedent <svg class="math-inline math-render-svg math" style="vertical-align:-0.1747em;height:0.8820em;width:2.5814em" viewBox='-18.2 26.168 24.585 8.4' ><desc>$2 \leq n$</desc><g id='page26'><use x='-18' xlink:href='#g5-50' y='33.004'/><use x='-10.27' xlink:href='#g1-20' y='33.004'/><use x='0.217' xlink:href='#g3-110' y='33.004'/></g></svg>, because that is the
+condition under which the else branch in&nbsp;<a href="#eq-fib" class="localref" style="target-element:equation"><span class="equation-label">(2)</span></a> is evaluated.
+</p>
+<p class="p indent para-continue">A well-founded function is often thought of as &#8220;terminating&#8221; in the sense
+that the recursive <em class="em-low1">depth</em> in evaluating <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg>
+on any given argument is finite. That is, there are no infinite descending chains
+of recursive calls. However, the evaluation of <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg> on a given argument
+may fail to terminate, because its <em class="em-low1">width</em> may be infinite. For example, let <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7564em;width:0.8574em" viewBox='-0.2 25.999 8.166 7.204' ><desc>$P$</desc><g id='page29'><use x='0' xlink:href='#g3-80' y='33.004'/></g></svg>
+be some predicate defined on the ordinals and let <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7795em;width:5.6203em" viewBox='-18.2 25.89 53.527 7.424' ><desc>$\PDownward$</desc><g id='page30'><use x='-18' xlink:href='#g0-80' y='33.004'/><use x='-11.267' xlink:href='#g0-68' y='33.004'/><use x='-3.774' xlink:href='#g0-111' y='33.004'/><use x='1.299' xlink:href='#g0-119' y='33.004'/><use x='7.894' xlink:href='#g0-110' y='33.004'/><use x='13.474' xlink:href='#g0-119' y='33.004'/><use x='20.069' xlink:href='#g0-97' y='33.004'/><use x='25.142' xlink:href='#g0-114' y='33.004'/><use x='28.818' xlink:href='#g0-100' y='33.004'/></g></svg> be a predicate on the
+ordinals defined by the following equation:
+</p>
+<div class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(5)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:1.0881em;width:28.6237em" viewBox='105.997 27.324 272.607 10.363' ><desc>\[ \PDownward(o) \Equal
+ P(o) \And \forall p \bullet\; p \Less o \Imp \PDownward(p)
+\]</desc><g id='page31'><use x='106.197' xlink:href='#g0-80' y='34.996'/><use x='112.93' xlink:href='#g0-68' y='34.996'/><use x='120.424' xlink:href='#g0-111' y='34.996'/><use x='125.497' xlink:href='#g0-119' y='34.996'/><use x='132.091' xlink:href='#g0-110' y='34.996'/><use x='137.672' xlink:href='#g0-119' y='34.996'/><use x='144.267' xlink:href='#g0-97' y='34.996'/><use x='149.339' xlink:href='#g0-114' y='34.996'/><use x='153.015' xlink:href='#g0-100' y='34.996'/><use x='159.314' xlink:href='#g5-40' y='34.996'/><use x='163.174' xlink:href='#g3-111' y='34.996'/><use x='167.985' xlink:href='#g5-41' y='34.996'/><use x='182.915' xlink:href='#g5-61' y='34.996'/><use x='201.704' xlink:href='#g3-80' y='34.996'/><use x='209.46' xlink:href='#g5-40' y='34.996'/><use x='213.32' xlink:href='#g3-111' y='34.996'/><use x='218.131' xlink:href='#g5-41' y='34.996'/><use x='226.972' xlink:href='#g1-94' y='34.996'/><use x='238.57' xlink:href='#g1-56' y='34.996'/><use x='244.084' xlink:href='#g3-112' y='34.996'/><use x='251.292' xlink:href='#g1-15' y='34.996'/><use x='261.236' xlink:href='#g3-112' y='34.996'/><use x='268.997' xlink:href='#g1-28' y='34.996'/><use x='281.689' xlink:href='#g3-111' y='34.996'/><use x='292.035' xlink:href='#g5-61' y='34.996'/><use x='298.095' xlink:href='#g1-41' y='34.996'/><use x='313.555' xlink:href='#g0-80' y='34.996'/><use x='320.287' xlink:href='#g0-68' y='34.996'/><use x='327.781' xlink:href='#g0-111' y='34.996'/><use x='332.854' xlink:href='#g0-119' y='34.996'/><use x='339.449' xlink:href='#g0-110' y='34.996'/><use x='345.029' xlink:href='#g0-119' y='34.996'/><use x='351.624' xlink:href='#g0-97' y='34.996'/><use x='356.697' xlink:href='#g0-114' y='34.996'/><use x='360.373' xlink:href='#g0-100' y='34.996'/><use x='366.672' xlink:href='#g5-40' y='34.996'/><use x='370.532' xlink:href='#g3-112' y='34.996'/><use x='375.525' xlink:href='#g5-41' y='34.996'/></g></svg></div></div>
+<p class="p noindent para-continued">With <svg class="math-inline math-render-svg math" style="vertical-align:-0.0685em;height:0.7031em;width:1.0852em" viewBox='-18.2 27.165 10.335 6.696' ><desc>$\Less$</desc><g id='page32'><use x='-18' xlink:href='#g1-28' y='33.004'/></g></svg> as the usual ordering on ordinals, this equation satisfies the decrement
+condition, but evaluating <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:7.1171em" viewBox='-0.2 25.332 67.782 10.363' ><desc>$\PDownward(\omega)$</desc><g id='page33'><use x='0' xlink:href='#g0-80' y='33.004'/><use x='6.733' xlink:href='#g0-68' y='33.004'/><use x='14.226' xlink:href='#g0-111' y='33.004'/><use x='19.299' xlink:href='#g0-119' y='33.004'/><use x='25.894' xlink:href='#g0-110' y='33.004'/><use x='31.474' xlink:href='#g0-119' y='33.004'/><use x='38.069' xlink:href='#g0-97' y='33.004'/><use x='43.142' xlink:href='#g0-114' y='33.004'/><use x='46.818' xlink:href='#g0-100' y='33.004'/><use x='53.117' xlink:href='#g5-40' y='33.004'/><use x='56.977' xlink:href='#g3-33' y='33.004'/><use x='63.513' xlink:href='#g5-41' y='33.004'/></g></svg> would require evaluating
+<svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:7.0565em" viewBox='-18.2 25.332 67.205 10.363' ><desc>$\PDownward(n)$</desc><g id='page34'><use x='-18' xlink:href='#g0-80' y='33.004'/><use x='-11.267' xlink:href='#g0-68' y='33.004'/><use x='-3.774' xlink:href='#g0-111' y='33.004'/><use x='1.299' xlink:href='#g0-119' y='33.004'/><use x='7.894' xlink:href='#g0-110' y='33.004'/><use x='13.474' xlink:href='#g0-119' y='33.004'/><use x='20.069' xlink:href='#g0-97' y='33.004'/><use x='25.142' xlink:href='#g0-114' y='33.004'/><use x='28.818' xlink:href='#g0-100' y='33.004'/><use x='35.117' xlink:href='#g5-40' y='33.004'/><use x='38.977' xlink:href='#g3-110' y='33.004'/><use x='44.935' xlink:href='#g5-41' y='33.004'/></g></svg> for every natural number <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.5159em;width:0.6686em" viewBox='-18.2 28.4 6.368 4.913' ><desc>$n$</desc><g id='page56'><use x='-18' xlink:href='#g3-110' y='33.004'/></g></svg>. However, what we are concerned
+about here is to avoid mathematical inconsistencies, and that is
+indeed a consequence of the decrement condition.
+</p><h5 id="sec-fib-example" class="h4" data-heading-depth="4" style="display:block"><span class="heading-before"><span class="heading-label">11.0.0.0</span>.&#8194;</span>Example with Well-founded Functions</h5>
+<p class="p noindent">So that we can later see how inductive proofs are done in Dafny, let&#39;s prove that
+for any <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.5159em;width:0.6686em" viewBox='-18.2 28.4 6.368 4.913' ><desc>$n$</desc><g id='page56'><use x='-18' xlink:href='#g3-110' y='33.004'/></g></svg>, <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:2.6406em" viewBox='-0.449 25.332 25.149 10.363' ><desc>$\fib(n)$</desc><g id='page37'><use x='0' xlink:href='#g0-12' y='33.004'/><use x='5.58' xlink:href='#g0-98' y='33.004'/><use x='10.813' xlink:href='#g5-40' y='33.004'/><use x='14.673' xlink:href='#g3-110' y='33.004'/><use x='20.63' xlink:href='#g5-41' y='33.004'/></g></svg> is even iff <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.5159em;width:0.6686em" viewBox='-18.2 28.4 6.368 4.913' ><desc>$n$</desc><g id='page56'><use x='-18' xlink:href='#g3-110' y='33.004'/></g></svg> is a multiple of <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7617em;width:0.5642em" viewBox='-0.2 26.168 5.373 7.254' ><desc>$3$</desc><g id='page39'><use x='0' xlink:href='#g5-51' y='33.004'/></g></svg>.
+We split our task into
+two cases. If <svg class="math-inline math-render-svg math" style="vertical-align:-0.0656em;height:0.7806em;width:2.5814em" viewBox='-18.2 26.168 24.585 7.434' ><desc>$n &lt; 2$</desc><g id='page40'><use x='-18' xlink:href='#g3-110' y='33.004'/><use x='-9.275' xlink:href='#g3-60' y='33.004'/><use x='1.212' xlink:href='#g5-50' y='33.004'/></g></svg>, then the property follows directly from the definition
+of <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:1.2046em" viewBox='-0.449 25.78 11.472 9.466' ><desc>$\fib$</desc><g id='page41'><use x='0' xlink:href='#g0-12' y='33.004'/><use x='5.58' xlink:href='#g0-98' y='33.004'/></g></svg>. Otherwise, note that exactly one of the three numbers <svg class="math-inline math-render-svg math" style="vertical-align:-0.1069em;height:0.7502em;width:2.4652em" viewBox='-18.2 26.168 23.478 7.145' ><desc>$n-2$</desc><g id='page54'><use x='-18' xlink:href='#g3-110' y='33.004'/><use x='-9.829' xlink:href='#g1-0' y='33.004'/><use x='0.105' xlink:href='#g5-50' y='33.004'/></g></svg>, <svg class="math-inline math-render-svg math" style="vertical-align:-0.1069em;height:0.7502em;width:2.4652em" viewBox='-0.2 26.168 23.478 7.145' ><desc>$n-1$</desc><g id='page55'><use x='0' xlink:href='#g3-110' y='33.004'/><use x='8.171' xlink:href='#g1-0' y='33.004'/><use x='18.105' xlink:href='#g5-49' y='33.004'/></g></svg>, and <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.5159em;width:0.6686em" viewBox='-18.2 28.4 6.368 4.913' ><desc>$n$</desc><g id='page56'><use x='-18' xlink:href='#g3-110' y='33.004'/></g></svg>
+is a multiple of 3. If <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.5159em;width:0.6686em" viewBox='-18.2 28.4 6.368 4.913' ><desc>$n$</desc><g id='page56'><use x='-18' xlink:href='#g3-110' y='33.004'/></g></svg> is the multiple of 3, then by invoking the
+induction hypothesis on <svg class="math-inline math-render-svg math" style="vertical-align:-0.1069em;height:0.7502em;width:2.4652em" viewBox='-18.2 26.168 23.478 7.145' ><desc>$n-2$</desc><g id='page54'><use x='-18' xlink:href='#g3-110' y='33.004'/><use x='-9.829' xlink:href='#g1-0' y='33.004'/><use x='0.105' xlink:href='#g5-50' y='33.004'/></g></svg>
+and <svg class="math-inline math-render-svg math" style="vertical-align:-0.1069em;height:0.7502em;width:2.4652em" viewBox='-0.2 26.168 23.478 7.145' ><desc>$n-1$</desc><g id='page55'><use x='0' xlink:href='#g3-110' y='33.004'/><use x='8.171' xlink:href='#g1-0' y='33.004'/><use x='18.105' xlink:href='#g5-49' y='33.004'/></g></svg>, we obtain that <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:10.0808em" viewBox='-0.449 25.332 96.008 10.363' ><desc>$\fib(n-2) + \fib(n-1)$</desc><g id='page53'><use x='0' xlink:href='#g0-12' y='33.004'/><use x='5.58' xlink:href='#g0-98' y='33.004'/><use x='10.813' xlink:href='#g5-40' y='33.004'/><use x='14.673' xlink:href='#g3-110' y='33.004'/><use x='22.844' xlink:href='#g1-0' y='33.004'/><use x='32.778' xlink:href='#g5-50' y='33.004'/><use x='37.741' xlink:href='#g5-41' y='33.004'/><use x='43.814' xlink:href='#g5-43' y='33.004'/><use x='53.748' xlink:href='#g0-12' y='33.004'/><use x='59.328' xlink:href='#g0-98' y='33.004'/><use x='64.561' xlink:href='#g5-40' y='33.004'/><use x='68.421' xlink:href='#g3-110' y='33.004'/><use x='76.592' xlink:href='#g1-0' y='33.004'/><use x='86.526' xlink:href='#g5-49' y='33.004'/><use x='91.489' xlink:href='#g5-41' y='33.004'/></g></svg> is the sum of two odd numbers,
+which is even. If <svg class="math-inline math-render-svg math" style="vertical-align:-0.1069em;height:0.7502em;width:2.4652em" viewBox='-18.2 26.168 23.478 7.145' ><desc>$n-2$</desc><g id='page54'><use x='-18' xlink:href='#g3-110' y='33.004'/><use x='-9.829' xlink:href='#g1-0' y='33.004'/><use x='0.105' xlink:href='#g5-50' y='33.004'/></g></svg> or <svg class="math-inline math-render-svg math" style="vertical-align:-0.1069em;height:0.7502em;width:2.4652em" viewBox='-0.2 26.168 23.478 7.145' ><desc>$n-1$</desc><g id='page55'><use x='0' xlink:href='#g3-110' y='33.004'/><use x='8.171' xlink:href='#g1-0' y='33.004'/><use x='18.105' xlink:href='#g5-49' y='33.004'/></g></svg> is a multiple of 3, then by invoking the induction
+hypothesis on <svg class="math-inline math-render-svg math" style="vertical-align:-0.1069em;height:0.7502em;width:2.4652em" viewBox='-18.2 26.168 23.478 7.145' ><desc>$n-2$</desc><g id='page54'><use x='-18' xlink:href='#g3-110' y='33.004'/><use x='-9.829' xlink:href='#g1-0' y='33.004'/><use x='0.105' xlink:href='#g5-50' y='33.004'/></g></svg> and <svg class="math-inline math-render-svg math" style="vertical-align:-0.1069em;height:0.7502em;width:2.4652em" viewBox='-0.2 26.168 23.478 7.145' ><desc>$n-1$</desc><g id='page55'><use x='0' xlink:href='#g3-110' y='33.004'/><use x='8.171' xlink:href='#g1-0' y='33.004'/><use x='18.105' xlink:href='#g5-49' y='33.004'/></g></svg>, we obtain that <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:10.0808em" viewBox='-0.449 25.332 96.008 10.363' ><desc>$\fib(n-2) + \fib(n-1)$</desc><g id='page53'><use x='0' xlink:href='#g0-12' y='33.004'/><use x='5.58' xlink:href='#g0-98' y='33.004'/><use x='10.813' xlink:href='#g5-40' y='33.004'/><use x='14.673' xlink:href='#g3-110' y='33.004'/><use x='22.844' xlink:href='#g1-0' y='33.004'/><use x='32.778' xlink:href='#g5-50' y='33.004'/><use x='37.741' xlink:href='#g5-41' y='33.004'/><use x='43.814' xlink:href='#g5-43' y='33.004'/><use x='53.748' xlink:href='#g0-12' y='33.004'/><use x='59.328' xlink:href='#g0-98' y='33.004'/><use x='64.561' xlink:href='#g5-40' y='33.004'/><use x='68.421' xlink:href='#g3-110' y='33.004'/><use x='76.592' xlink:href='#g1-0' y='33.004'/><use x='86.526' xlink:href='#g5-49' y='33.004'/><use x='91.489' xlink:href='#g5-41' y='33.004'/></g></svg> is the sum of an
+even number and an odd number, which is odd. In this proof, we invoked the induction
+hypothesis on <svg class="math-inline math-render-svg math" style="vertical-align:-0.1069em;height:0.7502em;width:2.4652em" viewBox='-18.2 26.168 23.478 7.145' ><desc>$n-2$</desc><g id='page54'><use x='-18' xlink:href='#g3-110' y='33.004'/><use x='-9.829' xlink:href='#g1-0' y='33.004'/><use x='0.105' xlink:href='#g5-50' y='33.004'/></g></svg> and on <svg class="math-inline math-render-svg math" style="vertical-align:-0.1069em;height:0.7502em;width:2.4652em" viewBox='-0.2 26.168 23.478 7.145' ><desc>$n-1$</desc><g id='page55'><use x='0' xlink:href='#g3-110' y='33.004'/><use x='8.171' xlink:href='#g1-0' y='33.004'/><use x='18.105' xlink:href='#g5-49' y='33.004'/></g></svg>. This is allowed, because both are smaller than
+<svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.5159em;width:0.6686em" viewBox='-18.2 28.4 6.368 4.913' ><desc>$n$</desc><g id='page56'><use x='-18' xlink:href='#g3-110' y='33.004'/></g></svg>, and hence the invocations go down in the well-founded ordering on natural numbers.
+</p><h4 id="sec-extreme-solutions" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">11.0.1</span>.&#8194;</span>Extreme Solutions</h4>
+<p class="p noindent">We don&#39;t need to exclude the possibility of equation&nbsp;<a href="#eq-general" class="localref" style="target-element:equation"><span class="equation-label">(0)</span></a> having multiple
+solutions&#8212;instead, we can just be clear about which one of them we want.
+Let&#39;s explore this, after a smidgen of lattice theory.
+</p>
+<p class="p indent para-continue">For any complete lattice <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:2.7912em" viewBox='-0.2 25.332 26.583 10.363' ><desc>$(Y,\leq)$</desc><g id='page57'><use x='0' xlink:href='#g5-40' y='33.004'/><use x='3.86' xlink:href='#g3-89' y='33.004'/><use x='10.176' xlink:href='#g3-59' y='33.004'/><use x='14.593' xlink:href='#g1-20' y='33.004'/><use x='22.313' xlink:href='#g5-41' y='33.004'/></g></svg> and any set <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7564em;width:0.9886em" viewBox='-18.2 25.999 9.415 7.204' ><desc>$X$</desc><g id='page214'><use x='-18' xlink:href='#g3-88' y='33.004'/></g></svg>, we can by <em class="em-low1">pointwise extension</em> define
+a complete lattice <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:6.7578em" viewBox='-0.2 25.332 64.36 10.363' ><desc>$(X \to Y, \FBelow)$</desc><g id='page59'><use x='0' xlink:href='#g5-40' y='33.004'/><use x='3.86' xlink:href='#g3-88' y='33.004'/><use x='15.632' xlink:href='#g1-33' y='33.004'/><use x='28.325' xlink:href='#g3-89' y='33.004'/><use x='34.64' xlink:href='#g3-59' y='33.004'/><use x='48.19' xlink:href='#g5-95' y='33.004'/><use x='44.593' xlink:href='#g1-41' y='33.004'/><use x='60.09' xlink:href='#g5-41' y='33.004'/></g></svg>, where for any <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:5.7680em" viewBox='-18.2 25.78 54.933 9.466' ><desc>$f,g \colon X \to Y$</desc><g id='page60'><use x='-18' xlink:href='#g3-102' y='33.004'/><use x='-12.622' xlink:href='#g3-59' y='33.004'/><use x='-8.204' xlink:href='#g3-103' y='33.004'/><use x='-2.006' xlink:href='#g5-58' y='33.004'/><use x='4.082' xlink:href='#g3-88' y='33.004'/><use x='15.854' xlink:href='#g1-33' y='33.004'/><use x='28.547' xlink:href='#g3-89' y='33.004'/></g></svg>,
+</p>
+<div class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(6)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:1.0881em;width:13.6397em" viewBox='177.414 27.324 129.902 10.363' ><desc>\[ f \FBelow q \Equiv
+ \forall x \bullet\; f(x) \leq g(x)
+\]</desc><g id='page61'><use x='177.614' xlink:href='#g3-102' y='34.996'/><use x='192.678' xlink:href='#g5-95' y='34.996'/><use x='189.08' xlink:href='#g1-41' y='34.996'/><use x='204.577' xlink:href='#g3-113' y='34.996'/><use x='217.668' xlink:href='#g1-17' y='34.996'/><use x='233.689' xlink:href='#g1-56' y='34.996'/><use x='239.204' xlink:href='#g3-120' y='34.996'/><use x='247.09' xlink:href='#g1-15' y='34.996'/><use x='257.034' xlink:href='#g3-102' y='34.996'/><use x='262.966' xlink:href='#g5-40' y='34.996'/><use x='266.826' xlink:href='#g3-120' y='34.996'/><use x='272.498' xlink:href='#g5-41' y='34.996'/><use x='279.126' xlink:href='#g1-20' y='34.996'/><use x='289.613' xlink:href='#g3-103' y='34.996'/><use x='294.704' xlink:href='#g5-40' y='34.996'/><use x='298.564' xlink:href='#g3-120' y='34.996'/><use x='304.237' xlink:href='#g5-41' y='34.996'/></g></svg></div></div>
+<p class="p noindent para-continued">In particular, if <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7564em;width:0.8805em" viewBox='-18.2 25.999 8.386 7.204' ><desc>$Y$</desc><g id='page210'><use x='-18' xlink:href='#g3-89' y='33.004'/></g></svg> is the set of booleans ordered by implication (<svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:5.4810em" viewBox='-0.449 25.78 52.2 9.466' ><desc>$\false \leq \true$</desc><g id='page63'><use x='0' xlink:href='#g0-102' y='33.004'/><use x='3.044' xlink:href='#g0-97' y='33.004'/><use x='8.117' xlink:href='#g0-108' y='33.004'/><use x='10.653' xlink:href='#g0-115' y='33.004'/><use x='14.712' xlink:href='#g0-101' y='33.004'/><use x='22.865' xlink:href='#g1-20' y='33.004'/><use x='33.353' xlink:href='#g0-116' y='33.004'/><use x='36.65' xlink:href='#g0-114' y='33.004'/><use x='40.835' xlink:href='#g0-117' y='33.004'/><use x='46.162' xlink:href='#g0-101' y='33.004'/></g></svg>),
+then the set of predicates over any domain <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7564em;width:0.9886em" viewBox='-18.2 25.999 9.415 7.204' ><desc>$X$</desc><g id='page214'><use x='-18' xlink:href='#g3-88' y='33.004'/></g></svg> forms a complete lattice.
+Tarski&#39;s Theorem&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#tarski:theorem" title="Alfred Tarski.
+A lattice-theoretical fixpoint theorem and its applications." class="bibref localref" style="target-element:bibitem"><span class="cite-number">35</span></a>]</span> tells us that any monotonic function over a
+complete lattice has a least and a greatest fixpoint. In particular, this means that
+<svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-18.2 25.919 8.669 7.603' ><desc>$\F$</desc><g id='page144'><use x='-18' xlink:href='#g1-70' y='33.004'/></g></svg> has a least fixpoint and a greatest fixpoint, provided <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-18.2 25.919 8.669 7.603' ><desc>$\F$</desc><g id='page144'><use x='-18' xlink:href='#g1-70' y='33.004'/></g></svg> is monotonic.
+</p>
+<p class="p indent">Speaking about the <em class="em-low1">set of solutions</em> in <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg> to&nbsp;<a href="#eq-general" class="localref" style="target-element:equation"><span class="equation-label">(0)</span></a> is the same as speaking
+about the <em class="em-low1">set of fixpoints</em> of functor <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-18.2 25.919 8.669 7.603' ><desc>$\F$</desc><g id='page144'><use x='-18' xlink:href='#g1-70' y='33.004'/></g></svg>. In particular, the least and greatest
+solutions to&nbsp;<a href="#eq-general" class="localref" style="target-element:equation"><span class="equation-label">(0)</span></a> are the same as the least and greatest fixpoints of <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-18.2 25.919 8.669 7.603' ><desc>$\F$</desc><g id='page144'><use x='-18' xlink:href='#g1-70' y='33.004'/></g></svg>.
+In casual speak, it happens that we say &#8220;fixpoint of&nbsp;<a href="#eq-general" class="localref" style="target-element:equation"><span class="equation-label">(0)</span></a>&#8221;, or more
+grotesquely, &#8220;fixpoint of <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg>&#8221; when we really mean &#8220;fixpoint of <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-18.2 25.919 8.669 7.603' ><desc>$\F$</desc><g id='page144'><use x='-18' xlink:href='#g1-70' y='33.004'/></g></svg>&#8221;.
+</p>
+<p class="p indent">In conclusion of our little excursion into lattice theory, we have that, under the
+proviso of <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-18.2 25.919 8.669 7.603' ><desc>$\F$</desc><g id='page144'><use x='-18' xlink:href='#g1-70' y='33.004'/></g></svg> being monotonic, the set of solutions in <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg> to&nbsp;<a href="#eq-general" class="localref" style="target-element:equation"><span class="equation-label">(0)</span></a> is nonempty,
+and among these solutions, there is in the <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7680em;width:2.2514em" viewBox='-18.2 26.139 21.442 7.314' ><desc>$\FBelow$</desc><g id='page74'><use x='-8.868' xlink:href='#g5-95' y='33.004'/><use x='-12.465' xlink:href='#g1-41' y='33.004'/></g></svg> ordering a least solution (that is,
+a function that returns <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:2.1795em" viewBox='-0.449 25.78 20.757 9.466' ><desc>$\false$</desc><g id='page221'><use x='0' xlink:href='#g0-102' y='33.004'/><use x='3.044' xlink:href='#g0-97' y='33.004'/><use x='8.117' xlink:href='#g0-108' y='33.004'/><use x='10.653' xlink:href='#g0-115' y='33.004'/><use x='14.712' xlink:href='#g0-101' y='33.004'/></g></svg> more often than any other) and a greatest solution (that
+is, a function that returns <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7083em;width:1.9529em" viewBox='-0.2 26.567 18.599 6.746' ><desc>$\true$</desc><g id='page203'><use x='0' xlink:href='#g0-116' y='33.004'/><use x='3.297' xlink:href='#g0-114' y='33.004'/><use x='7.483' xlink:href='#g0-117' y='33.004'/><use x='12.809' xlink:href='#g0-101' y='33.004'/></g></svg> more often than any other).
+</p>
+<p class="p indent">When discussing extreme solutions, I will now restrict my attention to boolean functions
+(that is, with <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7564em;width:0.8805em" viewBox='-18.2 25.999 8.386 7.204' ><desc>$Y$</desc><g id='page210'><use x='-18' xlink:href='#g3-89' y='33.004'/></g></svg> being the type of booleans). Functor <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-18.2 25.919 8.669 7.603' ><desc>$\F$</desc><g id='page144'><use x='-18' xlink:href='#g1-70' y='33.004'/></g></svg> is monotonic
+if the calls to <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg> in <svg class="math-inline math-render-svg math" style="vertical-align:-0.2936em;height:1.0924em;width:2.6229em" viewBox='-0.2 25.29 24.98 10.404' ><desc>$\F&#39;(f)$</desc><g id='page209'><use x='0' xlink:href='#g1-70' y='33.004'/><use x='8.123' xlink:href='#g2-48' y='29.388'/><use x='10.918' xlink:href='#g5-40' y='33.004'/><use x='14.778' xlink:href='#g3-102' y='33.004'/><use x='20.71' xlink:href='#g5-41' y='33.004'/></g></svg> are in <em class="em-low1">positive positions</em> (that is, under an even number
+of negations). Indeed, from now on, I will restrict my attention to such monotonic
+functors <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-18.2 25.919 8.669 7.603' ><desc>$\F$</desc><g id='page144'><use x='-18' xlink:href='#g1-70' y='33.004'/></g></svg>.
+</p>
+<p class="p indent para-continue">Let me introduce a running example. Consider the following equation,
+where <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.5159em;width:0.6387em" viewBox='-0.2 28.4 6.083 4.913' ><desc>$x$</desc><g id='page151'><use x='0' xlink:href='#g3-120' y='33.004'/></g></svg> ranges over the integers:
+</p>
+<div id="eq-evennat" class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(7)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:1.0881em;width:13.8122em" viewBox='176.57 27.324 131.545 10.363' ><desc>\[ g(x) \Equal (x = 0 \Or g(x-2))
+\]</desc><g id='page83'><use x='176.77' xlink:href='#g3-103' y='34.996'/><use x='181.861' xlink:href='#g5-40' y='34.996'/><use x='185.721' xlink:href='#g3-120' y='34.996'/><use x='191.394' xlink:href='#g5-41' y='34.996'/><use x='206.323' xlink:href='#g5-61' y='34.996'/><use x='225.112' xlink:href='#g5-40' y='34.996'/><use x='228.972' xlink:href='#g3-120' y='34.996'/><use x='237.412' xlink:href='#g5-61' y='34.996'/><use x='247.899' xlink:href='#g5-48' y='34.996'/><use x='257.843' xlink:href='#g1-95' y='34.996'/><use x='269.441' xlink:href='#g3-103' y='34.996'/><use x='274.533' xlink:href='#g5-40' y='34.996'/><use x='278.393' xlink:href='#g3-120' y='34.996'/><use x='286.279' xlink:href='#g1-0' y='34.996'/><use x='296.213' xlink:href='#g5-50' y='34.996'/><use x='301.176' xlink:href='#g5-41' y='34.996'/><use x='305.035' xlink:href='#g5-41' y='34.996'/></g></svg></div></div>
+<p class="p noindent para-continued para-continue">This equation has four solutions in <svg class="math-inline math-render-svg math" style="vertical-align:-0.2446em;height:0.7188em;width:0.5777em" viewBox='-18.2 28.4 5.502 6.846' ><desc>$g$</desc><g id='page224'><use x='-18' xlink:href='#g3-103' y='33.004'/></g></svg>. With <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.5159em;width:0.8173em" viewBox='-0.2 28.4 7.784 4.913' ><desc>$w$</desc><g id='page85'><use x='0' xlink:href='#g3-119' y='33.004'/></g></svg> ranging over the integers, they are:
+</p>
+<div class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(8)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:4.8539em;width:22.0048em" viewBox='119.698 24.734 209.57 46.228' ><desc>\[ \begin{array}{r@{}l}
+ g(x) \Equiv{}&amp; x \in \{w \;|\; 0 \leq w \And w\textrm{ even}\} \\
+ g(x) \Equiv{}&amp; x \in \{w \;|\; w\textrm{ even}\} \\
+ g(x) \Equiv{}&amp; x \in \{w \;|\; (0 \leq w \And w\textrm{ even}) \Or w\textrm{ odd}\} \\
+ g(x) \Equiv{}&amp; x \in \{w \;|\; \true\}
+ \end{array}
+\]</desc><g id='page86'><use x='119.898' xlink:href='#g3-103' y='32.406'/><use x='124.989' xlink:href='#g5-40' y='32.406'/><use x='128.849' xlink:href='#g3-120' y='32.406'/><use x='134.522' xlink:href='#g5-41' y='32.406'/><use x='146.684' xlink:href='#g1-17' y='32.406'/><use x='162.802' xlink:href='#g3-120' y='32.406'/><use x='171.242' xlink:href='#g1-50' y='32.406'/><use x='180.627' xlink:href='#g1-102' y='32.406'/><use x='185.589' xlink:href='#g3-119' y='32.406'/><use x='195.73' xlink:href='#g1-106' y='32.406'/><use x='201.255' xlink:href='#g5-48' y='32.406'/><use x='208.985' xlink:href='#g1-20' y='32.406'/><use x='219.472' xlink:href='#g3-119' y='32.406'/><use x='231.827' xlink:href='#g1-94' y='32.406'/><use x='243.425' xlink:href='#g3-119' y='32.406'/><use x='254.12' xlink:href='#g5-101' y='32.406'/><use x='258.531' xlink:href='#g5-118' y='32.406'/><use x='263.493' xlink:href='#g5-101' y='32.406'/><use x='267.904' xlink:href='#g5-110' y='32.406'/><use x='273.492' xlink:href='#g1-103' y='32.406'/><use x='119.898' xlink:href='#g3-103' y='44.361'/><use x='124.989' xlink:href='#g5-40' y='44.361'/><use x='128.849' xlink:href='#g3-120' y='44.361'/><use x='134.522' xlink:href='#g5-41' y='44.361'/><use x='146.684' xlink:href='#g1-17' y='44.361'/><use x='162.802' xlink:href='#g3-120' y='44.361'/><use x='171.242' xlink:href='#g1-50' y='44.361'/><use x='180.627' xlink:href='#g1-102' y='44.361'/><use x='185.589' xlink:href='#g3-119' y='44.361'/><use x='195.73' xlink:href='#g1-106' y='44.361'/><use x='201.255' xlink:href='#g3-119' y='44.361'/><use x='211.95' xlink:href='#g5-101' y='44.361'/><use x='216.361' xlink:href='#g5-118' y='44.361'/><use x='221.323' xlink:href='#g5-101' y='44.361'/><use x='225.734' xlink:href='#g5-110' y='44.361'/><use x='231.321' xlink:href='#g1-103' y='44.361'/><use x='119.898' xlink:href='#g3-103' y='56.316'/><use x='124.989' xlink:href='#g5-40' y='56.316'/><use x='128.849' xlink:href='#g3-120' y='56.316'/><use x='134.522' xlink:href='#g5-41' y='56.316'/><use x='146.684' xlink:href='#g1-17' y='56.316'/><use x='162.802' xlink:href='#g3-120' y='56.316'/><use x='171.242' xlink:href='#g1-50' y='56.316'/><use x='180.627' xlink:href='#g1-102' y='56.316'/><use x='185.589' xlink:href='#g3-119' y='56.316'/><use x='195.73' xlink:href='#g1-106' y='56.316'/><use x='201.255' xlink:href='#g5-40' y='56.316'/><use x='205.115' xlink:href='#g5-48' y='56.316'/><use x='212.845' xlink:href='#g1-20' y='56.316'/><use x='223.332' xlink:href='#g3-119' y='56.316'/><use x='235.687' xlink:href='#g1-94' y='56.316'/><use x='247.285' xlink:href='#g3-119' y='56.316'/><use x='257.98' xlink:href='#g5-101' y='56.316'/><use x='262.391' xlink:href='#g5-118' y='56.316'/><use x='267.353' xlink:href='#g5-101' y='56.316'/><use x='271.764' xlink:href='#g5-110' y='56.316'/><use x='277.352' xlink:href='#g5-41' y='56.316'/><use x='286.193' xlink:href='#g1-95' y='56.316'/><use x='297.791' xlink:href='#g3-119' y='56.316'/><use x='308.486' xlink:href='#g5-111' y='56.316'/><use x='313.725' xlink:href='#g5-100' y='56.316'/><use x='319.239' xlink:href='#g5-100' y='56.316'/><use x='324.813' xlink:href='#g1-103' y='56.316'/><use x='119.898' xlink:href='#g3-103' y='68.271'/><use x='124.989' xlink:href='#g5-40' y='68.271'/><use x='128.849' xlink:href='#g3-120' y='68.271'/><use x='134.522' xlink:href='#g5-41' y='68.271'/><use x='146.684' xlink:href='#g1-17' y='68.271'/><use x='162.802' xlink:href='#g3-120' y='68.271'/><use x='171.242' xlink:href='#g1-50' y='68.271'/><use x='180.627' xlink:href='#g1-102' y='68.271'/><use x='185.589' xlink:href='#g3-119' y='68.271'/><use x='195.73' xlink:href='#g1-106' y='68.271'/><use x='201.255' xlink:href='#g0-116' y='68.271'/><use x='204.552' xlink:href='#g0-114' y='68.271'/><use x='208.737' xlink:href='#g0-117' y='68.271'/><use x='214.064' xlink:href='#g0-101' y='68.271'/><use x='219.443' xlink:href='#g1-103' y='68.271'/></g></svg></div></div>
+<p class="p noindent para-continued">The first of these is the least solution and the last is the greatest solution.
+</p>
+<p class="p indent para-continue">In the literature, the definition of an extreme predicate is often given as a set of
+<em class="em-low1">inference rules</em>. To designate the least solution, a single line separating the
+antecedent (on top) from conclusion (on bottom) is used:
+</p>
+<div id="g-ind-rule" class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(9)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:2.5133em;width:10.1001em" viewBox='194.905 23.837 96.191 23.936' ><desc>\[ \frac{}{g(0)}
+ \qquad\qquad
+ \frac{g(x-2)}{g(x)}
+\]</desc><g id='page87'><rect height='0.398' width='17.839' x='195.105' y='35.559'/><use x='195.105' xlink:href='#g3-103' y='45.083'/><use x='200.196' xlink:href='#g5-40' y='45.083'/><use x='204.056' xlink:href='#g5-48' y='45.083'/><use x='209.019' xlink:href='#g5-41' y='45.083'/><use x='255.186' xlink:href='#g3-103' y='31.509'/><use x='260.277' xlink:href='#g5-40' y='31.509'/><use x='264.137' xlink:href='#g3-120' y='31.509'/><use x='272.024' xlink:href='#g1-0' y='31.509'/><use x='281.957' xlink:href='#g5-50' y='31.509'/><use x='286.92' xlink:href='#g5-41' y='31.509'/><rect height='0.398' width='35.71' x='255.186' y='35.559'/><use x='263.765' xlink:href='#g3-103' y='45.083'/><use x='268.856' xlink:href='#g5-40' y='45.083'/><use x='272.716' xlink:href='#g3-120' y='45.083'/><use x='278.389' xlink:href='#g5-41' y='45.083'/></g></svg></div></div>
+<p class="p noindent para-continued">Through repeated applications of such rules, one can show that the predicate holds for
+a particular value. For example, the <em class="em-low1">derivation</em>, or <em class="em-low1">proof tree</em>,
+to the left in Figure&nbsp;<a href="#fig-proof-trees" title="Left: a finite proof tree that uses the rules of&nbsp;(9) to establish $g(6)$. Right: an infinite proof tree that uses the rules of&nbsp;(10) to establish $g(1)$." class="localref" style="target-element:figure"><span class="figure-label">0</span></a> shows that <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:1.9093em" viewBox='-18.2 25.332 18.184 10.363' ><desc>$g(6)$</desc><g id='page154'><use x='-18' xlink:href='#g3-103' y='33.004'/><use x='-12.908' xlink:href='#g5-40' y='33.004'/><use x='-9.049' xlink:href='#g5-54' y='33.004'/><use x='-4.086' xlink:href='#g5-41' y='33.004'/></g></svg> holds.
+(In this simple example, the derivation is a rather degenerate proof &#8220;tree&#8221;.)
+The use of these inference rules gives rise to a least solution, because proof trees are
+accepted only if they are <em class="em-low1">finite</em>.
+</p>
+<figure id="fig-proof-trees" class="figure floating align-center float" style="text-align:center;float-env:figure;float-name:Figure"><table class="columns block">
+<tbody><tr><td class="column" style="vertical-align:bottom">
+<div class="math para-block">
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:6.1406em;width:2.6683em" viewBox='230.294 25.07 25.412 58.482' ><desc>\[\dfrac{
+ \dfrac{
+ \dfrac{
+ \dfrac{}{g(0)\xstrut}
+ }{g(2)\xstrut}
+ }{g(4)\xstrut}
+ }{g(6)\xupstrut}
+\]</desc><g id='page91'><rect height='0.398' width='17.839' x='234.08' y='25.27'/><use x='234.08' xlink:href='#g3-103' y='35.432'/><use x='239.172' xlink:href='#g5-40' y='35.432'/><use x='243.032' xlink:href='#g5-48' y='35.432'/><use x='247.994' xlink:href='#g5-41' y='35.432'/><rect height='0.398' width='20.23' x='232.885' y='40.413'/><use x='234.08' xlink:href='#g3-103' y='50.575'/><use x='239.172' xlink:href='#g5-40' y='50.575'/><use x='243.032' xlink:href='#g5-50' y='50.575'/><use x='247.994' xlink:href='#g5-41' y='50.575'/><rect height='0.398' width='22.621' x='231.689' y='55.556'/><use x='234.08' xlink:href='#g3-103' y='65.718'/><use x='239.172' xlink:href='#g5-40' y='65.718'/><use x='243.032' xlink:href='#g5-52' y='65.718'/><use x='247.994' xlink:href='#g5-41' y='65.718'/><rect height='0.398' width='25.012' x='230.494' y='70.7'/><use x='234.08' xlink:href='#g3-103' y='80.861'/><use x='239.172' xlink:href='#g5-40' y='80.861'/><use x='243.032' xlink:href='#g5-54' y='80.861'/><use x='247.994' xlink:href='#g5-41' y='80.861'/></g></svg></div></div></td><td class="column" style="width:5em"></td><td class="column" style="vertical-align:bottom">
+<div class="math para-block">
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:7.2767em;width:3.4819em" viewBox='208.419 -23.645 33.161 69.302' ><desc>\[\Dfrac{
+ \Dfrac{
+ \Dfrac{
+ \Dfrac{
+ {}_{\vdots }
+ }{{g(-5)}}
+ }{{g(-3)}}
+ }{{g(-1)}}
+ }{g(1)}
+\]</desc><g id='page92'><use x='223.367' xlink:href='#g5-46' y='-22.389'/><use x='223.367' xlink:href='#g5-46' y='-18.404'/><use x='223.367' xlink:href='#g5-46' y='-14.419'/><rect height='1.395' width='25.588' x='212.206' y='-13.024'/><use x='212.206' xlink:href='#g3-103' y='-1.866'/><use x='217.298' xlink:href='#g5-40' y='-1.866'/><use x='221.157' xlink:href='#g1-0' y='-1.866'/><use x='228.877' xlink:href='#g5-53' y='-1.866'/><use x='233.84' xlink:href='#g5-41' y='-1.866'/><rect fill='#ffffff' height='0.598' width='25.588' x='212.206' y='-12.625'/><rect height='1.395' width='27.979' x='211.01' y='1.92'/><use x='212.206' xlink:href='#g3-103' y='13.078'/><use x='217.298' xlink:href='#g5-40' y='13.078'/><use x='221.157' xlink:href='#g1-0' y='13.078'/><use x='228.877' xlink:href='#g5-51' y='13.078'/><use x='233.84' xlink:href='#g5-41' y='13.078'/><rect fill='#ffffff' height='0.598' width='27.979' x='211.01' y='2.319'/><rect height='1.395' width='30.37' x='209.815' y='16.864'/><use x='212.206' xlink:href='#g3-103' y='28.022'/><use x='217.298' xlink:href='#g5-40' y='28.022'/><use x='221.157' xlink:href='#g1-0' y='28.022'/><use x='228.877' xlink:href='#g5-49' y='28.022'/><use x='233.84' xlink:href='#g5-41' y='28.022'/><rect fill='#ffffff' height='0.598' width='30.37' x='209.815' y='17.263'/><rect height='1.395' width='32.761' x='208.619' y='31.808'/><use x='216.08' xlink:href='#g3-103' y='42.966'/><use x='221.172' xlink:href='#g5-40' y='42.966'/><use x='225.032' xlink:href='#g5-49' y='42.966'/><use x='229.995' xlink:href='#g5-41' y='42.966'/><rect fill='#ffffff' height='0.598' width='32.761' x='208.619' y='32.207'/></g></svg></div></div></td></tr></tbody></table>
+<hr class="figureline madoko" style="display:block">
+
+<div class="p noindent"><fig-caption class="figure-caption"><span class="caption-before"><strong class="strong-star2">Figure&#160;<span class="figure-label">0</span>.</strong> </span><span class="caption-text">Left: a finite proof tree that uses the rules of&nbsp;<a href="#g-ind-rule" class="localref" style="target-element:equation"><span class="equation-label">(9)</span></a> to establish <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:1.9093em" viewBox='-18.2 25.332 18.184 10.363' ><desc>$g(6)$</desc><g id='page154'><use x='-18' xlink:href='#g3-103' y='33.004'/><use x='-12.908' xlink:href='#g5-40' y='33.004'/><use x='-9.049' xlink:href='#g5-54' y='33.004'/><use x='-4.086' xlink:href='#g5-41' y='33.004'/></g></svg>. Right: an infinite proof tree that uses the rules of&nbsp;<a href="#g-coind-rule" class="localref" style="target-element:equation"><span class="equation-label">(10)</span></a> to establish <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:1.9093em" viewBox='-0.2 25.332 18.184 10.363' ><desc>$g(1)$</desc><g id='page155'><use x='0' xlink:href='#g3-103' y='33.004'/><use x='5.092' xlink:href='#g5-40' y='33.004'/><use x='8.951' xlink:href='#g5-49' y='33.004'/><use x='13.914' xlink:href='#g5-41' y='33.004'/></g></svg>.</span></fig-caption></div></figure>
+<p class="p indent para-continue">When inference rules are to designate the greatest solution, a double
+line is used:
+</p>
+<div id="g-coind-rule" class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(10)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:2.7827em;width:10.1001em" viewBox='194.905 19.155 96.191 26.502' ><desc>\[ \Dfrac{}{g(0)}
+ \qquad\qquad
+ \Dfrac{g(x-2)}{g(x)}
+\]</desc><g id='page95'><rect height='1.395' width='17.839' x='195.105' y='31.808'/><use x='195.105' xlink:href='#g3-103' y='42.966'/><use x='200.196' xlink:href='#g5-40' y='42.966'/><use x='204.056' xlink:href='#g5-48' y='42.966'/><use x='209.019' xlink:href='#g5-41' y='42.966'/><rect fill='#ffffff' height='0.598' width='17.839' x='195.105' y='32.207'/><use x='255.186' xlink:href='#g3-103' y='26.827'/><use x='260.277' xlink:href='#g5-40' y='26.827'/><use x='264.137' xlink:href='#g3-120' y='26.827'/><use x='272.024' xlink:href='#g1-0' y='26.827'/><use x='281.957' xlink:href='#g5-50' y='26.827'/><use x='286.92' xlink:href='#g5-41' y='26.827'/><rect height='1.395' width='35.71' x='255.186' y='31.808'/><use x='263.765' xlink:href='#g3-103' y='42.966'/><use x='268.856' xlink:href='#g5-40' y='42.966'/><use x='272.716' xlink:href='#g3-120' y='42.966'/><use x='278.389' xlink:href='#g5-41' y='42.966'/><rect fill='#ffffff' height='0.598' width='35.71' x='255.186' y='32.207'/></g></svg></div></div>
+<p class="p noindent para-continued">In this case, proof trees are allowed to be infinite. For example, the (partial depiction
+of the) infinite proof tree on the right in Figure&nbsp;<a href="#fig-proof-trees" title="Left: a finite proof tree that uses the rules of&nbsp;(9) to establish $g(6)$. Right: an infinite proof tree that uses the rules of&nbsp;(10) to establish $g(1)$." class="localref" style="target-element:figure"><span class="figure-label">0</span></a> shows that <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:1.9093em" viewBox='-0.2 25.332 18.184 10.363' ><desc>$g(1)$</desc><g id='page155'><use x='0' xlink:href='#g3-103' y='33.004'/><use x='5.092' xlink:href='#g5-40' y='33.004'/><use x='8.951' xlink:href='#g5-49' y='33.004'/><use x='13.914' xlink:href='#g5-41' y='33.004'/></g></svg> holds.
+</p>
+<p class="p indent">Note that derivations may not be unique. For example, in the case of the greatest
+solution for <svg class="math-inline math-render-svg math" style="vertical-align:-0.2446em;height:0.7188em;width:0.5777em" viewBox='-18.2 28.4 5.502 6.846' ><desc>$g$</desc><g id='page224'><use x='-18' xlink:href='#g3-103' y='33.004'/></g></svg>, there are two proof trees that establish <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:1.9093em" viewBox='-18.2 25.332 18.184 10.363' ><desc>$g(0)$</desc><g id='page100'><use x='-18' xlink:href='#g3-103' y='33.004'/><use x='-12.908' xlink:href='#g5-40' y='33.004'/><use x='-9.049' xlink:href='#g5-48' y='33.004'/><use x='-4.086' xlink:href='#g5-41' y='33.004'/></g></svg>: one is the finite
+proof tree that uses the left-hand rule of&nbsp;<a href="#g-coind-rule" class="localref" style="target-element:equation"><span class="equation-label">(10)</span></a> once, the other is the infinite
+proof tree that keeps on using the right-hand rule of&nbsp;<a href="#g-coind-rule" class="localref" style="target-element:equation"><span class="equation-label">(10)</span></a>.
+</p><h4 id="sec-working-with-extreme-predicates" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">11.0.2</span>.&#8194;</span>Working with Extreme Predicates</h4>
+<p class="p noindent">In general, one cannot evaluate whether or not an extreme predicate holds for some
+input, because doing so may take an infinite number of steps. For example, following
+the recursive calls in the definition&nbsp;<a href="#eq-evennat" class="localref" style="target-element:equation"><span class="equation-label">(7)</span></a> to try to evaluate <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:1.9093em" viewBox='-0.2 25.332 18.184 10.363' ><desc>$g(7)$</desc><g id='page101'><use x='0' xlink:href='#g3-103' y='33.004'/><use x='5.092' xlink:href='#g5-40' y='33.004'/><use x='8.951' xlink:href='#g5-55' y='33.004'/><use x='13.914' xlink:href='#g5-41' y='33.004'/></g></svg> would never
+terminate. However, there are useful ways to establish that an extreme predicate holds
+and there are ways to make use of one once it has been established.
+</p>
+<p class="p indent para-continue">For any <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-18.2 25.919 8.669 7.603' ><desc>$\F$</desc><g id='page144'><use x='-18' xlink:href='#g1-70' y='33.004'/></g></svg> as in&nbsp;<a href="#eq-general" class="localref" style="target-element:equation"><span class="equation-label">(0)</span></a>, I define two infinite series of well-founded
+functions, <svg class="math-inline math-render-svg math" style="vertical-align:-0.2874em;height:1.2339em;width:1.3989em" viewBox='-0.2 23.958 13.323 11.751' ><desc>$\iter{f}_k$</desc><g id='page129'><use x='0' xlink:href='#g4-91' y='29.388'/><use x='2.062' xlink:href='#g3-102' y='33.004'/><use x='8.012' xlink:href='#g4-107' y='35.439'/></g></svg> and <svg class="math-inline math-render-svg math" style="vertical-align:-0.2913em;height:1.2090em;width:1.3989em" viewBox='-0.2 24.195 13.323 11.514' ><desc>$\Iter{f}_k$</desc><g id='page131'><use x='0' xlink:href='#g4-93' y='29.388'/><use x='2.062' xlink:href='#g3-102' y='33.004'/><use x='8.012' xlink:href='#g4-107' y='35.439'/></g></svg>
+where <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7795em;width:0.6186em" viewBox='-18.2 25.89 5.891 7.424' ><desc>$k$</desc><g id='page200'><use x='-18' xlink:href='#g3-107' y='33.004'/></g></svg> ranges over the natural numbers:
+</p>
+<div id="eq-least-approx" class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(11)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:2.5515em;width:17.2078em" viewBox='139.707 24.076 163.884 24.3' ><desc>\[ \iter{f}_k(x) \Equal \left\{
+ \begin{array}{ll}
+ \false &amp; \textrm{if } k = 0 \\
+ \F(\iter{f}_{k-1})(x) &amp; \textrm{if } k &gt; 0
+ \end{array}
+ \right.
+\]</desc><g id='page106'><use x='139.907' xlink:href='#g4-91' y='34.608'/><use x='141.969' xlink:href='#g3-102' y='38.722'/><use x='147.918' xlink:href='#g4-107' y='41.157'/><use x='152.82' xlink:href='#g5-40' y='38.722'/><use x='156.68' xlink:href='#g3-120' y='38.722'/><use x='162.353' xlink:href='#g5-41' y='38.722'/><use x='177.282' xlink:href='#g5-61' y='38.722'/><use x='196.071' xlink:href='#g7-26' y='24.674'/><use x='208.524' xlink:href='#g0-102' y='32.406'/><use x='211.568' xlink:href='#g0-97' y='32.406'/><use x='216.641' xlink:href='#g0-108' y='32.406'/><use x='219.178' xlink:href='#g0-115' y='32.406'/><use x='223.236' xlink:href='#g0-101' y='32.406'/><use x='270.94' xlink:href='#g5-105' y='32.406'/><use x='273.697' xlink:href='#g5-102' y='32.406'/><use x='280.072' xlink:href='#g3-107' y='32.406'/><use x='288.32' xlink:href='#g5-61' y='32.406'/><use x='298.808' xlink:href='#g5-48' y='32.406'/><use x='208.524' xlink:href='#g1-70' y='44.838'/><use x='216.648' xlink:href='#g5-40' y='44.838'/><use x='220.508' xlink:href='#g4-91' y='41.223'/><use x='222.569' xlink:href='#g3-102' y='44.838'/><use x='228.519' xlink:href='#g4-107' y='47.273'/><use x='232.907' xlink:href='#g2-0' y='47.273'/><use x='239.111' xlink:href='#g6-49' y='47.273'/><use x='243.619' xlink:href='#g5-41' y='44.838'/><use x='247.479' xlink:href='#g5-40' y='44.838'/><use x='251.339' xlink:href='#g3-120' y='44.838'/><use x='257.011' xlink:href='#g5-41' y='44.838'/><use x='270.94' xlink:href='#g5-105' y='44.838'/><use x='273.697' xlink:href='#g5-102' y='44.838'/><use x='280.072' xlink:href='#g3-107' y='44.838'/><use x='288.32' xlink:href='#g3-62' y='44.838'/><use x='298.808' xlink:href='#g5-48' y='44.838'/></g></svg></div></div>
+<div id="eq-greatest-approx" class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(12)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:2.5515em;width:17.2078em" viewBox='157.707 23.882 163.884 24.3' ><desc>\[ \Iter{f}_k(x) \Equal \left\{
+ \begin{array}{ll}
+ \true &amp; \textrm{if } k = 0 \\
+ \F(\Iter{f}_{k-1})(x) &amp; \textrm{if } k &gt; 0
+ \end{array}
+ \right.
+\]</desc><g id='page107'><use x='157.907' xlink:href='#g4-93' y='34.414'/><use x='159.969' xlink:href='#g3-102' y='38.528'/><use x='165.918' xlink:href='#g4-107' y='40.963'/><use x='170.82' xlink:href='#g5-40' y='38.528'/><use x='174.68' xlink:href='#g3-120' y='38.528'/><use x='180.353' xlink:href='#g5-41' y='38.528'/><use x='195.282' xlink:href='#g5-61' y='38.528'/><use x='214.071' xlink:href='#g7-26' y='24.48'/><use x='226.524' xlink:href='#g0-116' y='32.406'/><use x='229.822' xlink:href='#g0-114' y='32.406'/><use x='234.007' xlink:href='#g0-117' y='32.406'/><use x='239.334' xlink:href='#g0-101' y='32.406'/><use x='288.94' xlink:href='#g5-105' y='32.406'/><use x='291.697' xlink:href='#g5-102' y='32.406'/><use x='298.072' xlink:href='#g3-107' y='32.406'/><use x='306.32' xlink:href='#g5-61' y='32.406'/><use x='316.808' xlink:href='#g5-48' y='32.406'/><use x='226.524' xlink:href='#g1-70' y='44.451'/><use x='234.648' xlink:href='#g5-40' y='44.451'/><use x='238.508' xlink:href='#g4-93' y='40.835'/><use x='240.569' xlink:href='#g3-102' y='44.451'/><use x='246.519' xlink:href='#g4-107' y='46.886'/><use x='250.907' xlink:href='#g2-0' y='46.886'/><use x='257.111' xlink:href='#g6-49' y='46.886'/><use x='261.619' xlink:href='#g5-41' y='44.451'/><use x='265.479' xlink:href='#g5-40' y='44.451'/><use x='269.339' xlink:href='#g3-120' y='44.451'/><use x='275.011' xlink:href='#g5-41' y='44.451'/><use x='288.94' xlink:href='#g5-105' y='44.451'/><use x='291.697' xlink:href='#g5-102' y='44.451'/><use x='298.072' xlink:href='#g3-107' y='44.451'/><use x='306.32' xlink:href='#g3-62' y='44.451'/><use x='316.808' xlink:href='#g5-48' y='44.451'/></g></svg></div></div>
+<p class="p noindent para-continued">These functions are called the <em class="em-low1">iterates</em> of <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg>, and I will also refer to them
+as the <em class="em-low1">prefix predicates</em> of <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg> (or the <em class="em-low1">prefix predicate</em> of <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg>, if we think
+of <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7795em;width:0.6186em" viewBox='-18.2 25.89 5.891 7.424' ><desc>$k$</desc><g id='page200'><use x='-18' xlink:href='#g3-107' y='33.004'/></g></svg> as being a parameter).
+Alternatively, we can define <svg class="math-inline math-render-svg math" style="vertical-align:-0.2874em;height:1.2339em;width:1.3989em" viewBox='-0.2 23.958 13.323 11.751' ><desc>$\iter{f}_k$</desc><g id='page129'><use x='0' xlink:href='#g4-91' y='29.388'/><use x='2.062' xlink:href='#g3-102' y='33.004'/><use x='8.012' xlink:href='#g4-107' y='35.439'/></g></svg> and <svg class="math-inline math-render-svg math" style="vertical-align:-0.2913em;height:1.2090em;width:1.3989em" viewBox='-0.2 24.195 13.323 11.514' ><desc>$\Iter{f}_k$</desc><g id='page131'><use x='0' xlink:href='#g4-93' y='29.388'/><use x='2.062' xlink:href='#g3-102' y='33.004'/><use x='8.012' xlink:href='#g4-107' y='35.439'/></g></svg> without mentioning <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.5159em;width:0.6387em" viewBox='-0.2 28.4 6.083 4.913' ><desc>$x$</desc><g id='page151'><use x='0' xlink:href='#g3-120' y='33.004'/></g></svg>:
+Let <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7408em;width:0.8537em" viewBox='-0.2 26.149 8.13 7.055' ><desc>$\bot$</desc><g id='page115'><use x='0' xlink:href='#g1-63' y='33.004'/></g></svg> denote the function that always returns <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:2.1795em" viewBox='-0.449 25.78 20.757 9.466' ><desc>$\false$</desc><g id='page221'><use x='0' xlink:href='#g0-102' y='33.004'/><use x='3.044' xlink:href='#g0-97' y='33.004'/><use x='8.117' xlink:href='#g0-108' y='33.004'/><use x='10.653' xlink:href='#g0-115' y='33.004'/><use x='14.712' xlink:href='#g0-101' y='33.004'/></g></svg>, let <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7408em;width:0.8537em" viewBox='-0.2 26.149 8.13 7.055' ><desc>$\top$</desc><g id='page117'><use x='0' xlink:href='#g1-62' y='33.004'/></g></svg>
+denote the function that always returns <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7083em;width:1.9529em" viewBox='-0.2 26.567 18.599 6.746' ><desc>$\true$</desc><g id='page203'><use x='0' xlink:href='#g0-116' y='33.004'/><use x='3.297' xlink:href='#g0-114' y='33.004'/><use x='7.483' xlink:href='#g0-117' y='33.004'/><use x='12.809' xlink:href='#g0-101' y='33.004'/></g></svg>, and let a superscript on <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-18.2 25.919 8.669 7.603' ><desc>$\F$</desc><g id='page144'><use x='-18' xlink:href='#g1-70' y='33.004'/></g></svg> denote
+exponentiation (for example, <svg class="math-inline math-render-svg math" style="vertical-align:-0.2958em;height:1.1694em;width:4.8132em" viewBox='-18.2 24.558 45.84 11.137' ><desc>$\F^0(f) = f$</desc><g id='page120'><use x='-18' xlink:href='#g1-70' y='33.004'/><use x='-9.877' xlink:href='#g6-48' y='29.388'/><use x='-5.407' xlink:href='#g5-40' y='33.004'/><use x='-1.547' xlink:href='#g3-102' y='33.004'/><use x='4.384' xlink:href='#g5-41' y='33.004'/><use x='11.011' xlink:href='#g5-61' y='33.004'/><use x='21.499' xlink:href='#g3-102' y='33.004'/></g></svg> and <svg class="math-inline math-render-svg math" style="vertical-align:-0.2958em;height:1.1694em;width:8.1402em" viewBox='-0.2 24.558 77.526 11.137' ><desc>$\F^2(f) = \F(\F(f))$</desc><g id='page121'><use x='0' xlink:href='#g1-70' y='33.004'/><use x='8.123' xlink:href='#g6-50' y='29.388'/><use x='12.593' xlink:href='#g5-40' y='33.004'/><use x='16.452' xlink:href='#g3-102' y='33.004'/><use x='22.384' xlink:href='#g5-41' y='33.004'/><use x='29.011' xlink:href='#g5-61' y='33.004'/><use x='39.499' xlink:href='#g1-70' y='33.004'/><use x='47.622' xlink:href='#g5-40' y='33.004'/><use x='51.482' xlink:href='#g1-70' y='33.004'/><use x='59.605' xlink:href='#g5-40' y='33.004'/><use x='63.465' xlink:href='#g3-102' y='33.004'/><use x='69.397' xlink:href='#g5-41' y='33.004'/><use x='73.256' xlink:href='#g5-41' y='33.004'/></g></svg>).
+Then,&nbsp;<a href="#eq-least-approx" class="localref" style="target-element:equation"><span class="equation-label">(11)</span></a> and&nbsp;<a href="#eq-greatest-approx" class="localref" style="target-element:equation"><span class="equation-label">(12)</span></a> can be stated equivalently as
+<svg class="math-inline math-render-svg math" style="vertical-align:-0.2921em;height:1.2339em;width:5.7795em" viewBox='-18.2 23.958 55.043 11.751' ><desc>$\iter{f}_k = \F^k(\bot)$</desc><g id='page122'><use x='-18' xlink:href='#g4-91' y='29.388'/><use x='-15.938' xlink:href='#g3-102' y='33.004'/><use x='-9.988' xlink:href='#g4-107' y='35.439'/><use x='-2.319' xlink:href='#g5-61' y='33.004'/><use x='8.168' xlink:href='#g1-70' y='33.004'/><use x='16.291' xlink:href='#g4-107' y='29.388'/><use x='21.193' xlink:href='#g5-40' y='33.004'/><use x='25.053' xlink:href='#g1-63' y='33.004'/><use x='32.773' xlink:href='#g5-41' y='33.004'/></g></svg> and <svg class="math-inline math-render-svg math" style="vertical-align:-0.2960em;height:1.2090em;width:5.7795em" viewBox='-0.2 24.195 55.043 11.514' ><desc>$\Iter{f}_k = \F^k(\top)$</desc><g id='page123'><use x='0' xlink:href='#g4-93' y='29.388'/><use x='2.062' xlink:href='#g3-102' y='33.004'/><use x='8.012' xlink:href='#g4-107' y='35.439'/><use x='15.681' xlink:href='#g5-61' y='33.004'/><use x='26.168' xlink:href='#g1-70' y='33.004'/><use x='34.291' xlink:href='#g4-107' y='29.388'/><use x='39.193' xlink:href='#g5-40' y='33.004'/><use x='43.053' xlink:href='#g1-62' y='33.004'/><use x='50.773' xlink:href='#g5-41' y='33.004'/></g></svg>.
+</p>
+<p class="p indent para-continue">For any solution <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg> to equation&nbsp;<a href="#eq-general" class="localref" style="target-element:equation"><span class="equation-label">(0)</span></a>, we have, for any <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7795em;width:0.6186em" viewBox='-18.2 25.89 5.891 7.424' ><desc>$k$</desc><g id='page200'><use x='-18' xlink:href='#g3-107' y='33.004'/></g></svg> and <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7920em;width:0.4773em" viewBox='-18.2 25.78 4.546 7.543' ><desc>$\ell$</desc><g id='page126'><use x='-18' xlink:href='#g3-96' y='33.004'/></g></svg>
+such that <svg class="math-inline math-render-svg math" style="vertical-align:-0.1721em;height:0.9228em;width:2.4445em" viewBox='-0.2 25.78 23.281 8.789' ><desc>$k \leq \ell$</desc><g id='page127'><use x='0' xlink:href='#g3-107' y='33.004'/><use x='8.248' xlink:href='#g1-20' y='33.004'/><use x='18.735' xlink:href='#g3-96' y='33.004'/></g></svg>:
+</p>
+<div id="eq-prefix-postfix" class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(13)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:1.2876em;width:22.9665em" viewBox='115.152 25.452 218.729 12.263' ><desc>\[ \iter{f}_k \quad\FBelow\quad
+ \iter{f}_\ell \quad\FBelow\quad
+ f \quad\FBelow\quad
+ \Iter{f}_\ell \quad\FBelow\quad
+ \Iter{f}_k
+\]</desc><g id='page128'><use x='115.352' xlink:href='#g4-91' y='30.883'/><use x='117.414' xlink:href='#g3-102' y='34.996'/><use x='123.364' xlink:href='#g4-107' y='37.431'/><use x='147.361' xlink:href='#g5-95' y='34.996'/><use x='143.763' xlink:href='#g1-41' y='34.996'/><use x='169.223' xlink:href='#g4-91' y='30.883'/><use x='171.285' xlink:href='#g3-102' y='34.996'/><use x='177.235' xlink:href='#g4-96' y='37.431'/><use x='200.163' xlink:href='#g5-95' y='34.996'/><use x='196.565' xlink:href='#g1-41' y='34.996'/><use x='222.025' xlink:href='#g3-102' y='34.996'/><use x='247.052' xlink:href='#g5-95' y='34.996'/><use x='243.454' xlink:href='#g1-41' y='34.996'/><use x='268.914' xlink:href='#g4-93' y='30.883'/><use x='270.976' xlink:href='#g3-102' y='34.996'/><use x='276.926' xlink:href='#g4-96' y='37.431'/><use x='299.854' xlink:href='#g5-95' y='34.996'/><use x='296.256' xlink:href='#g1-41' y='34.996'/><use x='321.716' xlink:href='#g4-93' y='30.883'/><use x='323.778' xlink:href='#g3-102' y='34.996'/><use x='329.728' xlink:href='#g4-107' y='37.431'/></g></svg></div></div>
+<p class="p noindent para-continued para-continue">In other words, every <svg class="math-inline math-render-svg math" style="vertical-align:-0.2874em;height:1.2339em;width:1.3989em" viewBox='-0.2 23.958 13.323 11.751' ><desc>$\iter{f}_k$</desc><g id='page129'><use x='0' xlink:href='#g4-91' y='29.388'/><use x='2.062' xlink:href='#g3-102' y='33.004'/><use x='8.012' xlink:href='#g4-107' y='35.439'/></g></svg> is a <em class="em-low1">pre-fixpoint</em> of <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg> and every <svg class="math-inline math-render-svg math" style="vertical-align:-0.2913em;height:1.2090em;width:1.3989em" viewBox='-0.2 24.195 13.323 11.514' ><desc>$\Iter{f}_k$</desc><g id='page131'><use x='0' xlink:href='#g4-93' y='29.388'/><use x='2.062' xlink:href='#g3-102' y='33.004'/><use x='8.012' xlink:href='#g4-107' y='35.439'/></g></svg> is a <em class="em-low1">post-fixpoint</em>
+of <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg>. Next, I define two functions, <svg class="math-inline math-render-svg math" style="vertical-align:-0.2342em;height:1.1443em;width:1.1468em" viewBox='-18.2 24.348 10.922 10.898' ><desc>$f\least$</desc><g id='page142'><use x='-18' xlink:href='#g3-102' y='33.004'/><use x='-12.068' xlink:href='#g2-35' y='29.388'/></g></svg> and <svg class="math-inline math-render-svg math" style="vertical-align:-0.2342em;height:1.1443em;width:1.1468em" viewBox='-0.2 24.348 10.922 10.898' ><desc>$f\greatest$</desc><g id='page143'><use x='0' xlink:href='#g3-102' y='33.004'/><use x='5.932' xlink:href='#g2-34' y='29.388'/></g></svg>, in
+terms of the prefix predicates:
+</p>
+<div id="eq-least-is-exists" class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(14)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:1.2861em;width:10.7773em" viewBox='191.086 25.452 102.641 12.249' ><desc>\[ f\least(x) \Equal \exists k \bullet\; \iter{f}_k(x)
+\]</desc><g id='page135'><use x='191.286' xlink:href='#g3-102' y='34.996'/><use x='197.218' xlink:href='#g2-35' y='30.883'/><use x='201.798' xlink:href='#g5-40' y='34.996'/><use x='205.658' xlink:href='#g3-120' y='34.996'/><use x='211.331' xlink:href='#g5-41' y='34.996'/><use x='226.26' xlink:href='#g5-61' y='34.996'/><use x='245.049' xlink:href='#g1-57' y='34.996'/><use x='250.563' xlink:href='#g3-107' y='34.996'/><use x='258.258' xlink:href='#g1-15' y='34.996'/><use x='268.202' xlink:href='#g4-91' y='30.883'/><use x='270.263' xlink:href='#g3-102' y='34.996'/><use x='276.213' xlink:href='#g4-107' y='37.431'/><use x='281.115' xlink:href='#g5-40' y='34.996'/><use x='284.975' xlink:href='#g3-120' y='34.996'/><use x='290.648' xlink:href='#g5-41' y='34.996'/></g></svg></div></div>
+<div id="eq-greatest-is-forall" class="equation para-block" style="line-adjust:0"><span class="equation-before"><span class="equation-label">(15)</span></span>
+
+<div class="mathdisplay para-block input-math" data-math-needpdf="" style="line-adjust:0"><svg class="mathdisplay math-display math-render-svg math" data-math-needpdf="" style="vertical-align:-0.0210em;height:1.2613em;width:10.7773em" viewBox='173.086 25.689 102.641 12.012' ><desc>\[ f\greatest(x) \Equal \forall k \bullet\; \Iter{f}_k(x)
+\]</desc><g id='page136'><use x='173.286' xlink:href='#g3-102' y='34.996'/><use x='179.218' xlink:href='#g2-34' y='30.883'/><use x='183.798' xlink:href='#g5-40' y='34.996'/><use x='187.658' xlink:href='#g3-120' y='34.996'/><use x='193.331' xlink:href='#g5-41' y='34.996'/><use x='208.26' xlink:href='#g5-61' y='34.996'/><use x='227.049' xlink:href='#g1-56' y='34.996'/><use x='232.563' xlink:href='#g3-107' y='34.996'/><use x='240.258' xlink:href='#g1-15' y='34.996'/><use x='250.202' xlink:href='#g4-93' y='30.883'/><use x='252.263' xlink:href='#g3-102' y='34.996'/><use x='258.213' xlink:href='#g4-107' y='37.431'/><use x='263.115' xlink:href='#g5-40' y='34.996'/><use x='266.975' xlink:href='#g3-120' y='34.996'/><use x='272.648' xlink:href='#g5-41' y='34.996'/></g></svg></div></div>
+<p class="p noindent para-continued">By&nbsp;<a href="#eq-prefix-postfix" class="localref" style="target-element:equation"><span class="equation-label">(13)</span></a>, we also have that <svg class="math-inline math-render-svg math" style="vertical-align:-0.2342em;height:1.1443em;width:1.1468em" viewBox='-18.2 24.348 10.922 10.898' ><desc>$f\least$</desc><g id='page142'><use x='-18' xlink:href='#g3-102' y='33.004'/><use x='-12.068' xlink:href='#g2-35' y='29.388'/></g></svg> is a pre-fixpoint of <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-18.2 25.919 8.669 7.603' ><desc>$\F$</desc><g id='page144'><use x='-18' xlink:href='#g1-70' y='33.004'/></g></svg> and <svg class="math-inline math-render-svg math" style="vertical-align:-0.2342em;height:1.1443em;width:1.1468em" viewBox='-0.2 24.348 10.922 10.898' ><desc>$f\greatest$</desc><g id='page143'><use x='0' xlink:href='#g3-102' y='33.004'/><use x='5.932' xlink:href='#g2-34' y='29.388'/></g></svg>
+is a post-fixpoint of <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-18.2 25.919 8.669 7.603' ><desc>$\F$</desc><g id='page144'><use x='-18' xlink:href='#g1-70' y='33.004'/></g></svg>. The marvelous thing is that, if <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-18.2 25.919 8.669 7.603' ><desc>$\F$</desc><g id='page144'><use x='-18' xlink:href='#g1-70' y='33.004'/></g></svg> is <em class="em-low1">continuous</em>, then
+<svg class="math-inline math-render-svg math" style="vertical-align:-0.2342em;height:1.1443em;width:1.1468em" viewBox='-18.2 24.348 10.922 10.898' ><desc>$f\least$</desc><g id='page142'><use x='-18' xlink:href='#g3-102' y='33.004'/><use x='-12.068' xlink:href='#g2-35' y='29.388'/></g></svg> and <svg class="math-inline math-render-svg math" style="vertical-align:-0.2342em;height:1.1443em;width:1.1468em" viewBox='-0.2 24.348 10.922 10.898' ><desc>$f\greatest$</desc><g id='page143'><use x='0' xlink:href='#g3-102' y='33.004'/><use x='5.932' xlink:href='#g2-34' y='29.388'/></g></svg> are the least and greatest fixpoints of <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7983em;width:0.9102em" viewBox='-18.2 25.919 8.669 7.603' ><desc>$\F$</desc><g id='page144'><use x='-18' xlink:href='#g1-70' y='33.004'/></g></svg>.
+These equations let us do proofs by induction when dealing with extreme predicates.
+I will explain in Section&nbsp;<a href="#sec-friendliness" title="11.1.2.&#8194;Extreme Predicates in Dafny" class="localref" style="target-element:h3"><span class="heading-label">11.1.2</span></a> how to check for continuity.
+</p>
+<p class="p indent">Let&#39;s consider two examples, both involving function <svg class="math-inline math-render-svg math" style="vertical-align:-0.2446em;height:0.7188em;width:0.5777em" viewBox='-18.2 28.4 5.502 6.846' ><desc>$g$</desc><g id='page224'><use x='-18' xlink:href='#g3-103' y='33.004'/></g></svg> in
+<a href="#eq-evennat" class="localref" style="target-element:equation"><span class="equation-label">(7)</span></a>. As it turns out, <svg class="math-inline math-render-svg math" style="vertical-align:-0.2446em;height:0.7188em;width:0.5777em" viewBox='-18.2 28.4 5.502 6.846' ><desc>$g$</desc><g id='page224'><use x='-18' xlink:href='#g3-103' y='33.004'/></g></svg>&#39;s defining functor is continuous,
+and therefore I will write <svg class="math-inline math-render-svg math" style="vertical-align:-0.2342em;height:1.1443em;width:1.0586em" viewBox='-0.2 24.348 10.082 10.898' ><desc>$g\least$</desc><g id='page161'><use x='0' xlink:href='#g3-103' y='33.004'/><use x='5.092' xlink:href='#g2-35' y='29.388'/></g></svg> and <svg class="math-inline math-render-svg math" style="vertical-align:-0.2342em;height:1.1443em;width:1.0586em" viewBox='-18.2 24.348 10.082 10.898' ><desc>$g\greatest$</desc><g id='page186'><use x='-18' xlink:href='#g3-103' y='33.004'/><use x='-12.908' xlink:href='#g2-34' y='29.388'/></g></svg> to denote the
+least and greatest solutions for <svg class="math-inline math-render-svg math" style="vertical-align:-0.2446em;height:0.7188em;width:0.5777em" viewBox='-18.2 28.4 5.502 6.846' ><desc>$g$</desc><g id='page224'><use x='-18' xlink:href='#g3-103' y='33.004'/></g></svg> in&nbsp;<a href="#eq-evennat" class="localref" style="target-element:equation"><span class="equation-label">(7)</span></a>.
+</p><h5 id="sec-example-least-solution" class="h4" data-heading-depth="4" style="display:block"><span class="heading-before"><span class="heading-label">11.0.2.0</span>.&#8194;</span>Example with Least Solution</h5>
+<p class="p noindent">The main technique for establishing that <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:2.4648em" viewBox='-0.2 24.348 23.474 11.346' ><desc>$g\least(x)$</desc><g id='page163'><use x='0' xlink:href='#g3-103' y='33.004'/><use x='5.092' xlink:href='#g2-35' y='29.388'/><use x='9.672' xlink:href='#g5-40' y='33.004'/><use x='13.532' xlink:href='#g3-120' y='33.004'/><use x='19.204' xlink:href='#g5-41' y='33.004'/></g></svg> holds for some
+<svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.5159em;width:0.6387em" viewBox='-0.2 28.4 6.083 4.913' ><desc>$x$</desc><g id='page151'><use x='0' xlink:href='#g3-120' y='33.004'/></g></svg>, that is, proving something of the form <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:6.1294em" viewBox='-18.2 24.348 58.375 11.346' ><desc>$Q \Imp g\least(x)$</desc><g id='page152'><use x='-18' xlink:href='#g3-81' y='33.004'/><use x='-4.619' xlink:href='#g5-61' y='33.004'/><use x='1.441' xlink:href='#g1-41' y='33.004'/><use x='16.901' xlink:href='#g3-103' y='33.004'/><use x='21.992' xlink:href='#g2-35' y='29.388'/><use x='26.572' xlink:href='#g5-40' y='33.004'/><use x='30.432' xlink:href='#g3-120' y='33.004'/><use x='36.105' xlink:href='#g5-41' y='33.004'/></g></svg>, is to
+construct a proof tree like the one for <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:1.9093em" viewBox='-18.2 25.332 18.184 10.363' ><desc>$g(6)$</desc><g id='page154'><use x='-18' xlink:href='#g3-103' y='33.004'/><use x='-12.908' xlink:href='#g5-40' y='33.004'/><use x='-9.049' xlink:href='#g5-54' y='33.004'/><use x='-4.086' xlink:href='#g5-41' y='33.004'/></g></svg> in Figure
+<a href="#fig-proof-trees" title="Left: a finite proof tree that uses the rules of&nbsp;(9) to establish $g(6)$. Right: an infinite proof tree that uses the rules of&nbsp;(10) to establish $g(1)$." class="localref" style="target-element:figure"><span class="figure-label">0</span></a>. For a proof in this direction, since we&#39;re just
+applying the defining equation, the fact that
+we&#39;re using a least solution for <svg class="math-inline math-render-svg math" style="vertical-align:-0.2446em;height:0.7188em;width:0.5777em" viewBox='-18.2 28.4 5.502 6.846' ><desc>$g$</desc><g id='page224'><use x='-18' xlink:href='#g3-103' y='33.004'/></g></svg> never plays a role (as long as we
+limit ourselves to finite derivations).
+</p>
+<p class="p indent">The technique for going in the other direction, proving something <em class="em-low1">from</em> an established
+<svg class="math-inline math-render-svg math" style="vertical-align:-0.2342em;height:1.1443em;width:1.0586em" viewBox='-0.2 24.348 10.082 10.898' ><desc>$g\least$</desc><g id='page161'><use x='0' xlink:href='#g3-103' y='33.004'/><use x='5.092' xlink:href='#g2-35' y='29.388'/></g></svg> property, that is, showing something of the form <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:6.1048em" viewBox='-18.2 24.348 58.141 11.346' ><desc>$g\least(x) \Imp R$</desc><g id='page158'><use x='-18' xlink:href='#g3-103' y='33.004'/><use x='-12.908' xlink:href='#g2-35' y='29.388'/><use x='-8.328' xlink:href='#g5-40' y='33.004'/><use x='-4.468' xlink:href='#g3-120' y='33.004'/><use x='1.204' xlink:href='#g5-41' y='33.004'/><use x='10.599' xlink:href='#g5-61' y='33.004'/><use x='16.658' xlink:href='#g1-41' y='33.004'/><use x='32.118' xlink:href='#g3-82' y='33.004'/></g></svg>, typically
+uses induction on the structure of the proof tree. When the antecedent of our proof
+obligation includes a predicate term <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:2.4648em" viewBox='-0.2 24.348 23.474 11.346' ><desc>$g\least(x)$</desc><g id='page163'><use x='0' xlink:href='#g3-103' y='33.004'/><use x='5.092' xlink:href='#g2-35' y='29.388'/><use x='9.672' xlink:href='#g5-40' y='33.004'/><use x='13.532' xlink:href='#g3-120' y='33.004'/><use x='19.204' xlink:href='#g5-41' y='33.004'/></g></svg>, it is sound to
+imagine that we have been given a proof tree for <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:2.4648em" viewBox='-0.2 24.348 23.474 11.346' ><desc>$g\least(x)$</desc><g id='page163'><use x='0' xlink:href='#g3-103' y='33.004'/><use x='5.092' xlink:href='#g2-35' y='29.388'/><use x='9.672' xlink:href='#g5-40' y='33.004'/><use x='13.532' xlink:href='#g3-120' y='33.004'/><use x='19.204' xlink:href='#g5-41' y='33.004'/></g></svg>. Such a proof tree
+would be a data structure&#8212;to be more precise, a term in an
+<em class="em-low1">inductive datatype</em>.
+For this reason, least solutions like <svg class="math-inline math-render-svg math" style="vertical-align:-0.2342em;height:1.1443em;width:1.0586em" viewBox='-0.2 24.348 10.082 10.898' ><desc>$g\least$</desc><g id='page161'><use x='0' xlink:href='#g3-103' y='33.004'/><use x='5.092' xlink:href='#g2-35' y='29.388'/></g></svg> have been given the
+name <em class="em-low1">inductive predicate</em>.
+</p>
+<p class="p indent">Let&#39;s prove <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:12.5331em" viewBox='-18.2 24.348 119.363 11.346' ><desc>$g\least(x) \Imp 0 \leq x \And x \textrm{ even}$</desc><g id='page162'><use x='-18' xlink:href='#g3-103' y='33.004'/><use x='-12.908' xlink:href='#g2-35' y='29.388'/><use x='-8.328' xlink:href='#g5-40' y='33.004'/><use x='-4.468' xlink:href='#g3-120' y='33.004'/><use x='1.204' xlink:href='#g5-41' y='33.004'/><use x='10.599' xlink:href='#g5-61' y='33.004'/><use x='16.658' xlink:href='#g1-41' y='33.004'/><use x='32.118' xlink:href='#g5-48' y='33.004'/><use x='39.848' xlink:href='#g1-20' y='33.004'/><use x='50.335' xlink:href='#g3-120' y='33.004'/><use x='60.989' xlink:href='#g1-94' y='33.004'/><use x='72.587' xlink:href='#g3-120' y='33.004'/><use x='81.581' xlink:href='#g5-101' y='33.004'/><use x='85.992' xlink:href='#g5-118' y='33.004'/><use x='90.954' xlink:href='#g5-101' y='33.004'/><use x='95.365' xlink:href='#g5-110' y='33.004'/></g></svg>.
+We split our task into two cases, corresponding to which of the two
+proof rules in&nbsp;<a href="#g-ind-rule" class="localref" style="target-element:equation"><span class="equation-label">(9)</span></a> was the
+last one applied to establish <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:2.4648em" viewBox='-0.2 24.348 23.474 11.346' ><desc>$g\least(x)$</desc><g id='page163'><use x='0' xlink:href='#g3-103' y='33.004'/><use x='5.092' xlink:href='#g2-35' y='29.388'/><use x='9.672' xlink:href='#g5-40' y='33.004'/><use x='13.532' xlink:href='#g3-120' y='33.004'/><use x='19.204' xlink:href='#g5-41' y='33.004'/></g></svg>. If it was the left-hand rule, then <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7617em;width:2.5515em" viewBox='-0.2 26.168 24.3 7.254' ><desc>$x=0$</desc><g id='page181'><use x='0' xlink:href='#g3-120' y='33.004'/><use x='8.44' xlink:href='#g5-61' y='33.004'/><use x='18.927' xlink:href='#g5-48' y='33.004'/></g></svg>,
+which makes it easy to establish the conclusion of our proof goal. If it was the
+right-hand rule, then we unfold the proof tree one level and obtain <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:4.2613em" viewBox='-18.2 24.348 40.584 11.346' ><desc>$g\least(x-2)$</desc><g id='page166'><use x='-18' xlink:href='#g3-103' y='33.004'/><use x='-12.908' xlink:href='#g2-35' y='29.388'/><use x='-8.328' xlink:href='#g5-40' y='33.004'/><use x='-4.468' xlink:href='#g3-120' y='33.004'/><use x='3.418' xlink:href='#g1-0' y='33.004'/><use x='13.352' xlink:href='#g5-50' y='33.004'/><use x='18.314' xlink:href='#g5-41' y='33.004'/></g></svg>.
+Since the proof tree for <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:4.2613em" viewBox='-18.2 24.348 40.584 11.346' ><desc>$g\least(x-2)$</desc><g id='page166'><use x='-18' xlink:href='#g3-103' y='33.004'/><use x='-12.908' xlink:href='#g2-35' y='29.388'/><use x='-8.328' xlink:href='#g5-40' y='33.004'/><use x='-4.468' xlink:href='#g3-120' y='33.004'/><use x='3.418' xlink:href='#g1-0' y='33.004'/><use x='13.352' xlink:href='#g5-50' y='33.004'/><use x='18.314' xlink:href='#g5-41' y='33.004'/></g></svg> is smaller than where we started, we invoke
+the <em class="em-low1">induction hypothesis</em> and obtain <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:12.4850em" viewBox='-18.2 25.332 118.905 10.363' ><desc>$0 \leq (x-2) \And (x-2) \textrm{ even}$</desc><g id='page184'><use x='-18' xlink:href='#g5-48' y='33.004'/><use x='-10.27' xlink:href='#g1-20' y='33.004'/><use x='0.217' xlink:href='#g5-40' y='33.004'/><use x='4.077' xlink:href='#g3-120' y='33.004'/><use x='11.964' xlink:href='#g1-0' y='33.004'/><use x='21.897' xlink:href='#g5-50' y='33.004'/><use x='26.86' xlink:href='#g5-41' y='33.004'/><use x='35.701' xlink:href='#g1-94' y='33.004'/><use x='47.299' xlink:href='#g5-40' y='33.004'/><use x='51.159' xlink:href='#g3-120' y='33.004'/><use x='59.046' xlink:href='#g1-0' y='33.004'/><use x='68.979' xlink:href='#g5-50' y='33.004'/><use x='73.942' xlink:href='#g5-41' y='33.004'/><use x='81.123' xlink:href='#g5-101' y='33.004'/><use x='85.534' xlink:href='#g5-118' y='33.004'/><use x='90.496' xlink:href='#g5-101' y='33.004'/><use x='94.907' xlink:href='#g5-110' y='33.004'/></g></svg>, from which
+it is easy to establish the conclusion of our proof goal.
+</p>
+<p class="p indent">Here&#39;s how we do the proof formally using&nbsp;<a href="#eq-least-is-exists" class="localref" style="target-element:equation"><span class="equation-label">(14)</span></a>. We massage the
+general form of our proof goal:
+</p><table class="madoko block">
+<tbody><tr><td class="tbody tr-odd tr-first col col-odd col-first" data-row="1" data-col="1"> </td><td class="tbody tr-odd tr-first col col-even col-last" data-row="1" data-col="2"> <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:6.1931em" viewBox='-18.2 24.348 58.982 11.346' ><desc>$f\greatest(x) \Imp R$</desc><g id='page168'><use x='-18' xlink:href='#g3-102' y='33.004'/><use x='-12.068' xlink:href='#g2-34' y='29.388'/><use x='-7.488' xlink:href='#g5-40' y='33.004'/><use x='-3.628' xlink:href='#g3-120' y='33.004'/><use x='2.044' xlink:href='#g5-41' y='33.004'/><use x='11.439' xlink:href='#g5-61' y='33.004'/><use x='17.498' xlink:href='#g1-41' y='33.004'/><use x='32.958' xlink:href='#g3-82' y='33.004'/></g></svg> </td></tr>
+<tr><td class="tbody tr-even col col-odd col-first" data-row="2" data-col="1"> = </td><td class="tbody tr-even col col-even col-last" data-row="2" data-col="2"> &#160;&#160;&#160;&#160; {&nbsp;<a href="#eq-least-is-exists" class="localref" style="target-element:equation"><span class="equation-label">(14)</span></a> } </td></tr>
+<tr><td class="tbody tr-odd col col-odd col-first" data-row="3" data-col="1"> </td><td class="tbody tr-odd col col-even col-last" data-row="3" data-col="2"> <svg class="math-inline math-render-svg math" style="vertical-align:-0.2921em;height:1.2339em;width:9.6869em" viewBox='-0.2 23.958 92.256 11.751' ><desc>$(\exists k \bullet\; \iter{f}_k(x)) \Imp R$</desc><g id='page169'><use x='0' xlink:href='#g5-40' y='33.004'/><use x='3.86' xlink:href='#g1-57' y='33.004'/><use x='9.374' xlink:href='#g3-107' y='33.004'/><use x='17.069' xlink:href='#g1-15' y='33.004'/><use x='27.013' xlink:href='#g4-91' y='29.388'/><use x='29.074' xlink:href='#g3-102' y='33.004'/><use x='35.024' xlink:href='#g4-107' y='35.439'/><use x='39.926' xlink:href='#g5-40' y='33.004'/><use x='43.786' xlink:href='#g3-120' y='33.004'/><use x='49.459' xlink:href='#g5-41' y='33.004'/><use x='53.319' xlink:href='#g5-41' y='33.004'/><use x='62.713' xlink:href='#g5-61' y='33.004'/><use x='68.773' xlink:href='#g1-41' y='33.004'/><use x='84.233' xlink:href='#g3-82' y='33.004'/></g></svg> </td></tr>
+<tr><td class="tbody tr-even col col-odd col-first" data-row="4" data-col="1"> = </td><td class="tbody tr-even col col-even col-last" data-row="4" data-col="2"> &#160;&#160;&#160;&#160; { distribute <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.6173em;width:2.3025em" viewBox='-0.2 27.573 21.929 5.879' ><desc>$\Imp$</desc><g id='page195'><use x='2.767' xlink:href='#g5-61' y='33.004'/><use x='8.827' xlink:href='#g1-41' y='33.004'/></g></svg> over <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7680em;width:0.6220em" viewBox='-0.2 25.89 5.924 7.314' ><desc>$\exists$</desc><g id='page171'><use x='0' xlink:href='#g1-57' y='33.004'/></g></svg> to the left } </td></tr>
+<tr><td class="tbody tr-odd tr-last col col-odd col-first" data-row="5" data-col="1"> </td><td class="tbody tr-odd tr-last col col-even col-last" data-row="5" data-col="2"> <svg class="math-inline math-render-svg math" style="vertical-align:-0.2921em;height:1.2339em;width:9.6869em" viewBox='-18.2 23.958 92.256 11.751' ><desc>$\forall k \bullet\; (\iter{f}_k(x) \Imp R)$</desc><g id='page172'><use x='-18' xlink:href='#g1-56' y='33.004'/><use x='-12.486' xlink:href='#g3-107' y='33.004'/><use x='-4.791' xlink:href='#g1-15' y='33.004'/><use x='5.153' xlink:href='#g5-40' y='33.004'/><use x='9.013' xlink:href='#g4-91' y='29.388'/><use x='11.074' xlink:href='#g3-102' y='33.004'/><use x='17.024' xlink:href='#g4-107' y='35.439'/><use x='21.926' xlink:href='#g5-40' y='33.004'/><use x='25.786' xlink:href='#g3-120' y='33.004'/><use x='31.459' xlink:href='#g5-41' y='33.004'/><use x='40.853' xlink:href='#g5-61' y='33.004'/><use x='46.913' xlink:href='#g1-41' y='33.004'/><use x='62.373' xlink:href='#g3-82' y='33.004'/><use x='69.986' xlink:href='#g5-41' y='33.004'/></g></svg> </td></tr></tbody></table>
+<p class="p noindent">The last line can be proved by induction over <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7795em;width:0.6186em" viewBox='-18.2 25.89 5.891 7.424' ><desc>$k$</desc><g id='page200'><use x='-18' xlink:href='#g3-107' y='33.004'/></g></svg>. So, in our case, we prove
+<svg class="math-inline math-render-svg math" style="vertical-align:-0.2921em;height:1.2339em;width:12.7852em" viewBox='-18.2 23.958 121.764 11.751' ><desc>$\iter{g}_k(x) \Imp 0 \leq x \And x \textrm{ even}$</desc><g id='page174'><use x='-18' xlink:href='#g4-91' y='29.388'/><use x='-15.938' xlink:href='#g3-103' y='33.004'/><use x='-10.829' xlink:href='#g4-107' y='35.439'/><use x='-5.927' xlink:href='#g5-40' y='33.004'/><use x='-2.067' xlink:href='#g3-120' y='33.004'/><use x='3.605' xlink:href='#g5-41' y='33.004'/><use x='13' xlink:href='#g5-61' y='33.004'/><use x='19.059' xlink:href='#g1-41' y='33.004'/><use x='34.519' xlink:href='#g5-48' y='33.004'/><use x='42.25' xlink:href='#g1-20' y='33.004'/><use x='52.737' xlink:href='#g3-120' y='33.004'/><use x='63.391' xlink:href='#g1-94' y='33.004'/><use x='74.989' xlink:href='#g3-120' y='33.004'/><use x='83.982' xlink:href='#g5-101' y='33.004'/><use x='88.394' xlink:href='#g5-118' y='33.004'/><use x='93.355' xlink:href='#g5-101' y='33.004'/><use x='97.767' xlink:href='#g5-110' y='33.004'/></g></svg> for every <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7795em;width:0.6186em" viewBox='-18.2 25.89 5.891 7.424' ><desc>$k$</desc><g id='page200'><use x='-18' xlink:href='#g3-107' y='33.004'/></g></svg>.
+If <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7910em;width:2.5313em" viewBox='-0.2 25.89 24.108 7.533' ><desc>$k=0$</desc><g id='page201'><use x='0' xlink:href='#g3-107' y='33.004'/><use x='8.248' xlink:href='#g5-61' y='33.004'/><use x='18.735' xlink:href='#g5-48' y='33.004'/></g></svg>, then <svg class="math-inline math-render-svg math" style="vertical-align:-0.2921em;height:1.2339em;width:2.7169em" viewBox='-0.2 23.958 25.875 11.751' ><desc>$\iter{g}_k(x)$</desc><g id='page177'><use x='0' xlink:href='#g4-91' y='29.388'/><use x='2.062' xlink:href='#g3-103' y='33.004'/><use x='7.171' xlink:href='#g4-107' y='35.439'/><use x='12.073' xlink:href='#g5-40' y='33.004'/><use x='15.933' xlink:href='#g3-120' y='33.004'/><use x='21.605' xlink:href='#g5-41' y='33.004'/></g></svg> is <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:2.1795em" viewBox='-0.449 25.78 20.757 9.466' ><desc>$\false$</desc><g id='page221'><use x='0' xlink:href='#g0-102' y='33.004'/><use x='3.044' xlink:href='#g0-97' y='33.004'/><use x='8.117' xlink:href='#g0-108' y='33.004'/><use x='10.653' xlink:href='#g0-115' y='33.004'/><use x='14.712' xlink:href='#g0-101' y='33.004'/></g></svg>, so our goal holds trivially.
+If <svg class="math-inline math-render-svg math" style="vertical-align:-0.0642em;height:0.8099em;width:2.5313em" viewBox='-18.2 25.89 24.108 7.713' ><desc>$k &gt; 0$</desc><g id='page204'><use x='-18' xlink:href='#g3-107' y='33.004'/><use x='-9.752' xlink:href='#g3-62' y='33.004'/><use x='0.735' xlink:href='#g5-48' y='33.004'/></g></svg>, then <svg class="math-inline math-render-svg math" style="vertical-align:-0.3537em;height:1.2339em;width:14.7097em" viewBox='-18.2 23.958 140.092 11.751' ><desc>$\iter{g}_k(x) = (x = 0 \Or \iter{g}_{k-1}(x-2))$</desc><g id='page180'><use x='-18' xlink:href='#g4-91' y='29.388'/><use x='-15.938' xlink:href='#g3-103' y='33.004'/><use x='-10.829' xlink:href='#g4-107' y='35.439'/><use x='-5.927' xlink:href='#g5-40' y='33.004'/><use x='-2.067' xlink:href='#g3-120' y='33.004'/><use x='3.605' xlink:href='#g5-41' y='33.004'/><use x='10.233' xlink:href='#g5-61' y='33.004'/><use x='20.72' xlink:href='#g5-40' y='33.004'/><use x='24.58' xlink:href='#g3-120' y='33.004'/><use x='33.02' xlink:href='#g5-61' y='33.004'/><use x='43.507' xlink:href='#g5-48' y='33.004'/><use x='53.451' xlink:href='#g1-95' y='33.004'/><use x='65.049' xlink:href='#g4-91' y='29.388'/><use x='67.111' xlink:href='#g3-103' y='33.004'/><use x='72.22' xlink:href='#g4-107' y='35.439'/><use x='76.608' xlink:href='#g2-0' y='35.439'/><use x='82.811' xlink:href='#g6-49' y='35.439'/><use x='87.32' xlink:href='#g5-40' y='33.004'/><use x='91.18' xlink:href='#g3-120' y='33.004'/><use x='99.066' xlink:href='#g1-0' y='33.004'/><use x='109' xlink:href='#g5-50' y='33.004'/><use x='113.962' xlink:href='#g5-41' y='33.004'/><use x='117.822' xlink:href='#g5-41' y='33.004'/></g></svg>. Our goal holds easily
+for the first disjunct (<svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7617em;width:2.5515em" viewBox='-0.2 26.168 24.3 7.254' ><desc>$x=0$</desc><g id='page181'><use x='0' xlink:href='#g3-120' y='33.004'/><use x='8.44' xlink:href='#g5-61' y='33.004'/><use x='18.927' xlink:href='#g5-48' y='33.004'/></g></svg>). For the other disjunct,
+we apply the induction hypothesis (on the smaller <svg class="math-inline math-render-svg math" style="vertical-align:-0.1045em;height:0.7795em;width:2.4151em" viewBox='-18.2 25.89 23.001 7.424' ><desc>$k-1$</desc><g id='page206'><use x='-18' xlink:href='#g3-107' y='33.004'/><use x='-10.305' xlink:href='#g1-0' y='33.004'/><use x='-0.372' xlink:href='#g5-49' y='33.004'/></g></svg> and with <svg class="math-inline math-render-svg math" style="vertical-align:-0.1069em;height:0.7502em;width:2.4353em" viewBox='-0.2 26.168 23.193 7.145' ><desc>$x-2$</desc><g id='page207'><use x='0' xlink:href='#g3-120' y='33.004'/><use x='7.887' xlink:href='#g1-0' y='33.004'/><use x='17.82' xlink:href='#g5-50' y='33.004'/></g></svg>) and
+obtain <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:12.4850em" viewBox='-18.2 25.332 118.905 10.363' ><desc>$0 \leq (x-2) \And (x-2) \textrm{ even}$</desc><g id='page184'><use x='-18' xlink:href='#g5-48' y='33.004'/><use x='-10.27' xlink:href='#g1-20' y='33.004'/><use x='0.217' xlink:href='#g5-40' y='33.004'/><use x='4.077' xlink:href='#g3-120' y='33.004'/><use x='11.964' xlink:href='#g1-0' y='33.004'/><use x='21.897' xlink:href='#g5-50' y='33.004'/><use x='26.86' xlink:href='#g5-41' y='33.004'/><use x='35.701' xlink:href='#g1-94' y='33.004'/><use x='47.299' xlink:href='#g5-40' y='33.004'/><use x='51.159' xlink:href='#g3-120' y='33.004'/><use x='59.046' xlink:href='#g1-0' y='33.004'/><use x='68.979' xlink:href='#g5-50' y='33.004'/><use x='73.942' xlink:href='#g5-41' y='33.004'/><use x='81.123' xlink:href='#g5-101' y='33.004'/><use x='85.534' xlink:href='#g5-118' y='33.004'/><use x='90.496' xlink:href='#g5-101' y='33.004'/><use x='94.907' xlink:href='#g5-110' y='33.004'/></g></svg>, from which our proof goal
+follows.
+</p><h5 id="sec-example-greatest-solution" class="h4" data-heading-depth="4" style="display:block"><span class="heading-before"><span class="heading-label">11.0.2.1</span>.&#8194;</span>Example with Greatest Solution</h5>
+<p class="p noindent">We can think of a given predicate <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:2.4648em" viewBox='-0.2 24.348 23.474 11.346' ><desc>$g\greatest(x)$</desc><g id='page191'><use x='0' xlink:href='#g3-103' y='33.004'/><use x='5.092' xlink:href='#g2-34' y='29.388'/><use x='9.672' xlink:href='#g5-40' y='33.004'/><use x='13.532' xlink:href='#g3-120' y='33.004'/><use x='19.204' xlink:href='#g5-41' y='33.004'/></g></svg> as being represented
+by a proof tree&#8212;in this case a term in a <em class="em-low1">coinductive datatype</em>,
+since the proof may be infinite.
+For this reason, greatest solutions like <svg class="math-inline math-render-svg math" style="vertical-align:-0.2342em;height:1.1443em;width:1.0586em" viewBox='-18.2 24.348 10.082 10.898' ><desc>$g\greatest$</desc><g id='page186'><use x='-18' xlink:href='#g3-103' y='33.004'/><use x='-12.908' xlink:href='#g2-34' y='29.388'/></g></svg> have
+been given the name <em class="em-low1">coinductive predicate</em>, or <em class="em-low1">co-predicate</em> for short.
+The main technique for proving something from a given proof tree, that
+is, to prove something of the form <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:6.1048em" viewBox='-0.2 24.348 58.141 11.346' ><desc>$g\greatest(x) \Imp R$</desc><g id='page187'><use x='0' xlink:href='#g3-103' y='33.004'/><use x='5.092' xlink:href='#g2-34' y='29.388'/><use x='9.672' xlink:href='#g5-40' y='33.004'/><use x='13.532' xlink:href='#g3-120' y='33.004'/><use x='19.204' xlink:href='#g5-41' y='33.004'/><use x='28.599' xlink:href='#g5-61' y='33.004'/><use x='34.658' xlink:href='#g1-41' y='33.004'/><use x='50.118' xlink:href='#g3-82' y='33.004'/></g></svg>, is to
+destruct the proof. Since this is just unfolding the defining
+equation, the fact that we&#39;re using a greatest solution for <svg class="math-inline math-render-svg math" style="vertical-align:-0.2446em;height:0.7188em;width:0.5777em" viewBox='-18.2 28.4 5.502 6.846' ><desc>$g$</desc><g id='page224'><use x='-18' xlink:href='#g3-103' y='33.004'/></g></svg> never
+plays a role (as long as we limit ourselves to a finite number of
+unfoldings).
+</p>
+<p class="p indent">To go in the other direction, to establish a predicate defined as a greatest solution,
+like <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:6.1294em" viewBox='-0.2 24.348 58.375 11.346' ><desc>$Q \Imp g\greatest(x)$</desc><g id='page189'><use x='0' xlink:href='#g3-81' y='33.004'/><use x='13.381' xlink:href='#g5-61' y='33.004'/><use x='19.441' xlink:href='#g1-41' y='33.004'/><use x='34.901' xlink:href='#g3-103' y='33.004'/><use x='39.992' xlink:href='#g2-34' y='29.388'/><use x='44.572' xlink:href='#g5-40' y='33.004'/><use x='48.432' xlink:href='#g3-120' y='33.004'/><use x='54.105' xlink:href='#g5-41' y='33.004'/></g></svg>, we may need an infinite number of steps. For this purpose,
+we can use induction&#39;s dual, <em class="em-low1">coinduction</em>. Were it not for one little detail, coinduction
+is as simple as continuations in programming: the next part of the proof obligation
+is delegated to the <em class="em-low1">coinduction hypothesis</em>. The little detail is making sure that
+it is the &#8220;next&#8221; part we&#39;re passing on for the continuation, not the same part. This
+detail is called <em class="em-low1">productivity</em> and corresponds to the requirement in
+induction of making sure we&#39;re going down a well-founded relation when
+applying the induction hypothesis. There are
+many sources with more information, see for example the classic account by
+Jacobs and Rutten&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#jacobsrutten:introductioncoalgebra" title="Bart Jacobs and Jan Rutten.
+An introduction to (co)algebra and (co)induction." class="bibref localref" style="target-element:bibitem"><span class="cite-number">8</span></a>]</span>
+or a new attempt by Kozen and Silva
+that aims to emphasize the simplicity, not the mystery, of
+coinduction&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#kozensilva:coinduction" title="Dexter Kozen and Alexandra Silva.
+Practical coinduction." class="bibref localref" style="target-element:bibitem"><span class="cite-number">11</span></a>]</span>.
+</p>
+<p class="p indent">Let&#39;s prove <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:7.2153em" viewBox='-18.2 24.348 68.717 11.346' ><desc>$\true \Imp g\greatest(x)$</desc><g id='page190'><use x='-18' xlink:href='#g0-116' y='33.004'/><use x='-14.703' xlink:href='#g0-114' y='33.004'/><use x='-10.517' xlink:href='#g0-117' y='33.004'/><use x='-5.191' xlink:href='#g0-101' y='33.004'/><use x='5.723' xlink:href='#g5-61' y='33.004'/><use x='11.783' xlink:href='#g1-41' y='33.004'/><use x='27.243' xlink:href='#g3-103' y='33.004'/><use x='32.334' xlink:href='#g2-34' y='29.388'/><use x='36.914' xlink:href='#g5-40' y='33.004'/><use x='40.774' xlink:href='#g3-120' y='33.004'/><use x='46.447' xlink:href='#g5-41' y='33.004'/></g></svg>. The intuitive coinductive proof goes like this:
+According to the right-hand rule of&nbsp;<a href="#g-coind-rule" class="localref" style="target-element:equation"><span class="equation-label">(10)</span></a>, <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:2.4648em" viewBox='-0.2 24.348 23.474 11.346' ><desc>$g\greatest(x)$</desc><g id='page191'><use x='0' xlink:href='#g3-103' y='33.004'/><use x='5.092' xlink:href='#g2-34' y='29.388'/><use x='9.672' xlink:href='#g5-40' y='33.004'/><use x='13.532' xlink:href='#g3-120' y='33.004'/><use x='19.204' xlink:href='#g5-41' y='33.004'/></g></svg> follows if we
+establish <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:4.2613em" viewBox='-18.2 24.348 40.584 11.346' ><desc>$g\greatest(x-2)$</desc><g id='page192'><use x='-18' xlink:href='#g3-103' y='33.004'/><use x='-12.908' xlink:href='#g2-34' y='29.388'/><use x='-8.328' xlink:href='#g5-40' y='33.004'/><use x='-4.468' xlink:href='#g3-120' y='33.004'/><use x='3.418' xlink:href='#g1-0' y='33.004'/><use x='13.352' xlink:href='#g5-50' y='33.004'/><use x='18.314' xlink:href='#g5-41' y='33.004'/></g></svg>, and that&#39;s easy to do by invoking the coinduction hypothesis.
+The &#8220;little detail&#8221;, productivity, is satisfied in this proof because we applied
+a rule in&nbsp;<a href="#g-coind-rule" class="localref" style="target-element:equation"><span class="equation-label">(10)</span></a> before invoking the coinduction hypothesis.
+</p>
+<p class="p indent">For anyone who may have felt that the intuitive proof felt too easy, here is a formal
+proof using&nbsp;<a href="#eq-greatest-is-forall" class="localref" style="target-element:equation"><span class="equation-label">(15)</span></a>, which relies only on induction. We massage the
+general form of our proof goal:
+</p><table class="madoko block">
+<tbody><tr><td class="tbody tr-odd tr-first col col-odd col-first" data-row="1" data-col="1"> </td><td class="tbody tr-odd tr-first col col-even col-last" data-row="1" data-col="2"> <svg class="math-inline math-render-svg math" style="vertical-align:-0.2920em;height:1.1913em;width:6.2176em" viewBox='-0.2 24.348 59.215 11.346' ><desc>$Q \Imp f\greatest(x)$</desc><g id='page193'><use x='0' xlink:href='#g3-81' y='33.004'/><use x='13.381' xlink:href='#g5-61' y='33.004'/><use x='19.441' xlink:href='#g1-41' y='33.004'/><use x='34.901' xlink:href='#g3-102' y='33.004'/><use x='40.832' xlink:href='#g2-34' y='29.388'/><use x='45.413' xlink:href='#g5-40' y='33.004'/><use x='49.272' xlink:href='#g3-120' y='33.004'/><use x='54.945' xlink:href='#g5-41' y='33.004'/></g></svg> </td></tr>
+<tr><td class="tbody tr-even col col-odd col-first" data-row="2" data-col="1"> = </td><td class="tbody tr-even col col-even col-last" data-row="2" data-col="2"> &#160;&#160;&#160;&#160; {&nbsp;<a href="#eq-greatest-is-forall" class="localref" style="target-element:equation"><span class="equation-label">(15)</span></a> } </td></tr>
+<tr><td class="tbody tr-odd col col-odd col-first" data-row="3" data-col="1"> </td><td class="tbody tr-odd col col-even col-last" data-row="3" data-col="2"> <svg class="math-inline math-render-svg math" style="vertical-align:-0.2960em;height:1.2090em;width:8.9007em" viewBox='-18.2 24.195 84.769 11.514' ><desc>$Q \Imp \forall k \bullet\; \Iter{f}_k(x)$</desc><g id='page194'><use x='-18' xlink:href='#g3-81' y='33.004'/><use x='-4.619' xlink:href='#g5-61' y='33.004'/><use x='1.441' xlink:href='#g1-41' y='33.004'/><use x='16.901' xlink:href='#g1-56' y='33.004'/><use x='22.415' xlink:href='#g3-107' y='33.004'/><use x='30.11' xlink:href='#g1-15' y='33.004'/><use x='40.054' xlink:href='#g4-93' y='29.388'/><use x='42.115' xlink:href='#g3-102' y='33.004'/><use x='48.065' xlink:href='#g4-107' y='35.439'/><use x='52.967' xlink:href='#g5-40' y='33.004'/><use x='56.827' xlink:href='#g3-120' y='33.004'/><use x='62.5' xlink:href='#g5-41' y='33.004'/></g></svg> </td></tr>
+<tr><td class="tbody tr-even col col-odd col-first" data-row="4" data-col="1"> = </td><td class="tbody tr-even col col-even col-last" data-row="4" data-col="2"> &#160;&#160;&#160;&#160; { distribute <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.6173em;width:2.3025em" viewBox='-0.2 27.573 21.929 5.879' ><desc>$\Imp$</desc><g id='page195'><use x='2.767' xlink:href='#g5-61' y='33.004'/><use x='8.827' xlink:href='#g1-41' y='33.004'/></g></svg> over <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7910em;width:0.6236em" viewBox='-18.2 25.89 5.939 7.533' ><desc>$\forall$</desc><g id='page196'><use x='-18' xlink:href='#g1-56' y='33.004'/></g></svg> to the right } </td></tr>
+<tr><td class="tbody tr-odd tr-last col col-odd col-first" data-row="5" data-col="1"> </td><td class="tbody tr-odd tr-last col col-even col-last" data-row="5" data-col="2"> <svg class="math-inline math-render-svg math" style="vertical-align:-0.2960em;height:1.2090em;width:8.9007em" viewBox='-0.2 24.195 84.769 11.514' ><desc>$\forall k \bullet\; Q \Imp \Iter{f}_k(x)$</desc><g id='page197'><use x='0' xlink:href='#g1-56' y='33.004'/><use x='5.514' xlink:href='#g3-107' y='33.004'/><use x='13.209' xlink:href='#g1-15' y='33.004'/><use x='23.153' xlink:href='#g3-81' y='33.004'/><use x='36.534' xlink:href='#g5-61' y='33.004'/><use x='42.593' xlink:href='#g1-41' y='33.004'/><use x='58.054' xlink:href='#g4-93' y='29.388'/><use x='60.115' xlink:href='#g3-102' y='33.004'/><use x='66.065' xlink:href='#g4-107' y='35.439'/><use x='70.967' xlink:href='#g5-40' y='33.004'/><use x='74.827' xlink:href='#g3-120' y='33.004'/><use x='80.5' xlink:href='#g5-41' y='33.004'/></g></svg> </td></tr></tbody></table>
+<p class="p noindent">The last line can be proved by induction over <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7795em;width:0.6186em" viewBox='-18.2 25.89 5.891 7.424' ><desc>$k$</desc><g id='page200'><use x='-18' xlink:href='#g3-107' y='33.004'/></g></svg>. So, in our case, we prove
+<svg class="math-inline math-render-svg math" style="vertical-align:-0.2960em;height:1.2090em;width:7.4674em" viewBox='-0.2 24.195 71.118 11.514' ><desc>$\true \Imp \Iter{g}_k(x)$</desc><g id='page199'><use x='0' xlink:href='#g0-116' y='33.004'/><use x='3.297' xlink:href='#g0-114' y='33.004'/><use x='7.483' xlink:href='#g0-117' y='33.004'/><use x='12.809' xlink:href='#g0-101' y='33.004'/><use x='23.723' xlink:href='#g5-61' y='33.004'/><use x='29.783' xlink:href='#g1-41' y='33.004'/><use x='45.243' xlink:href='#g4-93' y='29.388'/><use x='47.305' xlink:href='#g3-103' y='33.004'/><use x='52.414' xlink:href='#g4-107' y='35.439'/><use x='57.316' xlink:href='#g5-40' y='33.004'/><use x='61.176' xlink:href='#g3-120' y='33.004'/><use x='66.848' xlink:href='#g5-41' y='33.004'/></g></svg> for every <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7795em;width:0.6186em" viewBox='-18.2 25.89 5.891 7.424' ><desc>$k$</desc><g id='page200'><use x='-18' xlink:href='#g3-107' y='33.004'/></g></svg>.
+If <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7910em;width:2.5313em" viewBox='-0.2 25.89 24.108 7.533' ><desc>$k=0$</desc><g id='page201'><use x='0' xlink:href='#g3-107' y='33.004'/><use x='8.248' xlink:href='#g5-61' y='33.004'/><use x='18.735' xlink:href='#g5-48' y='33.004'/></g></svg>, then <svg class="math-inline math-render-svg math" style="vertical-align:-0.2960em;height:1.2090em;width:2.7169em" viewBox='-18.2 24.195 25.875 11.514' ><desc>$\Iter{g}_k(x)$</desc><g id='page202'><use x='-18' xlink:href='#g4-93' y='29.388'/><use x='-15.938' xlink:href='#g3-103' y='33.004'/><use x='-10.829' xlink:href='#g4-107' y='35.439'/><use x='-5.927' xlink:href='#g5-40' y='33.004'/><use x='-2.067' xlink:href='#g3-120' y='33.004'/><use x='3.605' xlink:href='#g5-41' y='33.004'/></g></svg> is <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7083em;width:1.9529em" viewBox='-0.2 26.567 18.599 6.746' ><desc>$\true$</desc><g id='page203'><use x='0' xlink:href='#g0-116' y='33.004'/><use x='3.297' xlink:href='#g0-114' y='33.004'/><use x='7.483' xlink:href='#g0-117' y='33.004'/><use x='12.809' xlink:href='#g0-101' y='33.004'/></g></svg>, so our goal holds trivially.
+If <svg class="math-inline math-render-svg math" style="vertical-align:-0.0642em;height:0.8099em;width:2.5313em" viewBox='-18.2 25.89 24.108 7.713' ><desc>$k &gt; 0$</desc><g id='page204'><use x='-18' xlink:href='#g3-107' y='33.004'/><use x='-9.752' xlink:href='#g3-62' y='33.004'/><use x='0.735' xlink:href='#g5-48' y='33.004'/></g></svg>, then <svg class="math-inline math-render-svg math" style="vertical-align:-0.3577em;height:1.2090em;width:14.7097em" viewBox='-0.2 24.195 140.092 11.514' ><desc>$\Iter{g}_k(x) = (x = 0 \Or \Iter{g}_{k-1}(x-2))$</desc><g id='page205'><use x='0' xlink:href='#g4-93' y='29.388'/><use x='2.062' xlink:href='#g3-103' y='33.004'/><use x='7.171' xlink:href='#g4-107' y='35.439'/><use x='12.073' xlink:href='#g5-40' y='33.004'/><use x='15.933' xlink:href='#g3-120' y='33.004'/><use x='21.605' xlink:href='#g5-41' y='33.004'/><use x='28.233' xlink:href='#g5-61' y='33.004'/><use x='38.72' xlink:href='#g5-40' y='33.004'/><use x='42.58' xlink:href='#g3-120' y='33.004'/><use x='51.02' xlink:href='#g5-61' y='33.004'/><use x='61.507' xlink:href='#g5-48' y='33.004'/><use x='71.451' xlink:href='#g1-95' y='33.004'/><use x='83.049' xlink:href='#g4-93' y='29.388'/><use x='85.111' xlink:href='#g3-103' y='33.004'/><use x='90.22' xlink:href='#g4-107' y='35.439'/><use x='94.608' xlink:href='#g2-0' y='35.439'/><use x='100.811' xlink:href='#g6-49' y='35.439'/><use x='105.32' xlink:href='#g5-40' y='33.004'/><use x='109.18' xlink:href='#g3-120' y='33.004'/><use x='117.066' xlink:href='#g1-0' y='33.004'/><use x='127' xlink:href='#g5-50' y='33.004'/><use x='131.962' xlink:href='#g5-41' y='33.004'/><use x='135.822' xlink:href='#g5-41' y='33.004'/></g></svg>. We establish the second
+disjunct by applying the induction hypothesis (on the smaller <svg class="math-inline math-render-svg math" style="vertical-align:-0.1045em;height:0.7795em;width:2.4151em" viewBox='-18.2 25.89 23.001 7.424' ><desc>$k-1$</desc><g id='page206'><use x='-18' xlink:href='#g3-107' y='33.004'/><use x='-10.305' xlink:href='#g1-0' y='33.004'/><use x='-0.372' xlink:href='#g5-49' y='33.004'/></g></svg> and with <svg class="math-inline math-render-svg math" style="vertical-align:-0.1069em;height:0.7502em;width:2.4353em" viewBox='-0.2 26.168 23.193 7.145' ><desc>$x-2$</desc><g id='page207'><use x='0' xlink:href='#g3-120' y='33.004'/><use x='7.887' xlink:href='#g1-0' y='33.004'/><use x='17.82' xlink:href='#g5-50' y='33.004'/></g></svg>).
+</p><h4 id="sec-other-techniques" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">11.0.3</span>.&#8194;</span>Other Techniques</h4>
+<p class="p noindent">Although in this paper I consider only well-founded functions and extreme
+predicates, it is worth mentioning that there are additional ways of making sure that
+the set of solutions to&nbsp;<a href="#eq-general" class="localref" style="target-element:equation"><span class="equation-label">(0)</span></a> is nonempty. For example, if all calls to <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg> in
+<svg class="math-inline math-render-svg math" style="vertical-align:-0.2936em;height:1.0924em;width:2.6229em" viewBox='-0.2 25.29 24.98 10.404' ><desc>$\F&#39;(f)$</desc><g id='page209'><use x='0' xlink:href='#g1-70' y='33.004'/><use x='8.123' xlink:href='#g2-48' y='29.388'/><use x='10.918' xlink:href='#g5-40' y='33.004'/><use x='14.778' xlink:href='#g3-102' y='33.004'/><use x='20.71' xlink:href='#g5-41' y='33.004'/></g></svg> are <em class="em-low1">tail-recursive calls</em>, then (under the assumption that <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7564em;width:0.8805em" viewBox='-18.2 25.999 8.386 7.204' ><desc>$Y$</desc><g id='page210'><use x='-18' xlink:href='#g3-89' y='33.004'/></g></svg> is nonempty) the set of
+solutions is nonempty. To see this, consider an attempted evaluation of <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:2.0721em" viewBox='-0.2 25.332 19.734 10.363' ><desc>$f(x)$</desc><g id='page211'><use x='0' xlink:href='#g3-102' y='33.004'/><use x='5.932' xlink:href='#g5-40' y='33.004'/><use x='9.792' xlink:href='#g3-120' y='33.004'/><use x='15.464' xlink:href='#g5-41' y='33.004'/></g></svg> that fails
+to determine a definite result value because of an infinite chain of calls that applies <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg>
+to each value of some subset <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.8310em;width:1.2821em" viewBox='-18.2 25.29 12.21 7.914' ><desc>$X&#39;$</desc><g id='page216'><use x='-18' xlink:href='#g3-88' y='33.004'/><use x='-8.995' xlink:href='#g2-48' y='29.388'/></g></svg> of <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.7564em;width:0.9886em" viewBox='-18.2 25.999 9.415 7.204' ><desc>$X$</desc><g id='page214'><use x='-18' xlink:href='#g3-88' y='33.004'/></g></svg>. Then, apparently, the value of <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:0.6659em" viewBox='-0.2 25.78 6.342 9.466' ><desc>$f$</desc><g id='page215'><use x='0' xlink:href='#g3-102' y='33.004'/></g></svg> for any one
+of the values in <svg class="math-inline math-render-svg math" style="vertical-align:-0.0210em;height:0.8310em;width:1.2821em" viewBox='-18.2 25.29 12.21 7.914' ><desc>$X&#39;$</desc><g id='page216'><use x='-18' xlink:href='#g3-88' y='33.004'/><use x='-8.995' xlink:href='#g2-48' y='29.388'/></g></svg> is not determined by the equation, but picking any particular result
+values for these makes for a consistent definition.
+This was pointed out by Manolios and Moore&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#manoliosmoore:partialfunctions" title="Panagiotis Manolios and J&#160;Strother Moore.
+Partial functions in ACL2." class="bibref localref" style="target-element:bibitem"><span class="cite-number">25</span></a>]</span>.
+Functions can be underspecified in this way in the proof assistants ACL2&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#acl2:book" title="Matt Kaufmann, Panagiotis Manolios, and J&#160;Strother Moore.
+Computer-Aided Reasoning: An Approach." class="bibref localref" style="target-element:bibitem"><span class="cite-number">10</span></a>]</span>
+and HOL&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#krauss:phd" title="Alexander Krauss.
+Automating Recursive Definitions and Termination Proofs in Higher-Order Logic.
+PhD thesis, Technische Universit&#228;t M&#252;nchen, 2009." class="bibref localref" style="target-element:bibitem"><span class="cite-number">12</span></a>]</span>.
+</p><h3 id="sec-functions-in-dafny" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">11.1</span>.&#8194;</span>Functions in Dafny</h3>
+<p class="p noindent">In this section, I explain with examples the support in
+Dafny<sup id="back-fn-fn-on-da-web" ><a href="#fn-fn-on-da-web" title="5.Dafny is open source at&nbsp;dafny.codeplex.com and can also be used online at&nbsp;rise4fun.com/dafny.
+&#8617;" class="footnote-ref localref" ><span class="footnote-label">5</span></a></sup> for well-founded functions, extreme predicates,
+and proofs regarding these.
+</p><h4 id="sec-well-founded-functions-in-dafny" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">11.1.0</span>.&#8194;</span>Well-founded Functions in Dafny</h4>
+<p class="p noindent">Declarations of well-founded functions are unsurprising. For example, the Fibonacci
+function is declared as follows:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">function</span> fib(n: <span style="color:teal">nat</span>): <span style="color:teal">nat</span>
+{
+ <span style="color:blue">if</span> n &lt; <span class="constant" style="color:purple">2</span> <span style="color:blue">then</span> n <span style="color:blue">else</span> fib(n-<span class="constant" style="color:purple">2</span>) + fib(n-<span class="constant" style="color:purple">1</span>)
+}</code></pre>
+<p class="p noindent para-continued">Dafny verifies that the body (given as an expression in curly braces) is well defined.
+This includes decrement checks for recursive (and mutually recursive) calls. Dafny
+predefines a well-founded relation on each type and extends it to lexicographic tuples
+of any (fixed) length. For example, the well-founded relation <svg class="math-inline math-render-svg math" style="vertical-align:-0.2459em;height:0.8485em;width:2.8104em" viewBox='-0.2 27.165 26.766 8.081' ><desc>$x \Less y$</desc><g id='page217'><use x='0' xlink:href='#g3-120' y='33.004'/><use x='8.44' xlink:href='#g1-28' y='33.004'/><use x='21.133' xlink:href='#g3-121' y='33.004'/></g></svg> for integers
+is <svg class="math-inline math-render-svg math" style="vertical-align:-0.2419em;height:0.9531em;width:6.7810em" viewBox='-18.2 26.168 64.581 9.077' ><desc>$x &lt; y \And 0 \leq y$</desc><g id='page218'><use x='-18' xlink:href='#g3-120' y='33.004'/><use x='-9.56' xlink:href='#g3-60' y='33.004'/><use x='0.927' xlink:href='#g3-121' y='33.004'/><use x='11.132' xlink:href='#g1-94' y='33.004'/><use x='22.73' xlink:href='#g5-48' y='33.004'/><use x='30.46' xlink:href='#g1-20' y='33.004'/><use x='40.947' xlink:href='#g3-121' y='33.004'/></g></svg>, the one for reals is <svg class="math-inline math-render-svg math" style="vertical-align:-0.2419em;height:0.9531em;width:10.1988em" viewBox='-0.2 26.168 97.131 9.077' ><desc>$x \leq y - 1.0 \And 0.0 \leq y$</desc><g id='page219'><use x='0' xlink:href='#g3-120' y='33.004'/><use x='8.44' xlink:href='#g1-20' y='33.004'/><use x='18.927' xlink:href='#g3-121' y='33.004'/><use x='26.365' xlink:href='#g1-0' y='33.004'/><use x='36.298' xlink:href='#g5-49' y='33.004'/><use x='41.261' xlink:href='#g3-58' y='33.004'/><use x='44.018' xlink:href='#g5-48' y='33.004'/><use x='53.962' xlink:href='#g1-94' y='33.004'/><use x='65.56' xlink:href='#g5-48' y='33.004'/><use x='70.523' xlink:href='#g3-58' y='33.004'/><use x='73.28' xlink:href='#g5-48' y='33.004'/><use x='81.01' xlink:href='#g1-20' y='33.004'/><use x='91.497' xlink:href='#g3-121' y='33.004'/></g></svg>
+(this is the same ordering as for integers, if you read the integer
+relation as <svg class="math-inline math-render-svg math" style="vertical-align:-0.2419em;height:0.9531em;width:8.5776em" viewBox='-18.2 26.168 81.691 9.077' ><desc>$x \leq y - 1 \And 0 \leq y$</desc><g id='page220'><use x='-18' xlink:href='#g3-120' y='33.004'/><use x='-9.56' xlink:href='#g1-20' y='33.004'/><use x='0.927' xlink:href='#g3-121' y='33.004'/><use x='8.365' xlink:href='#g1-0' y='33.004'/><use x='18.298' xlink:href='#g5-49' y='33.004'/><use x='28.242' xlink:href='#g1-94' y='33.004'/><use x='39.84' xlink:href='#g5-48' y='33.004'/><use x='47.571' xlink:href='#g1-20' y='33.004'/><use x='58.058' xlink:href='#g3-121' y='33.004'/></g></svg>), the one for inductive
+datatypes is structural inclusion,
+and the one for coinductive datatypes is <svg class="math-inline math-render-svg math" style="vertical-align:-0.2384em;height:0.9939em;width:2.1795em" viewBox='-0.449 25.78 20.757 9.466' ><desc>$\false$</desc><g id='page221'><use x='0' xlink:href='#g0-102' y='33.004'/><use x='3.044' xlink:href='#g0-97' y='33.004'/><use x='8.117' xlink:href='#g0-108' y='33.004'/><use x='10.653' xlink:href='#g0-115' y='33.004'/><use x='14.712' xlink:href='#g0-101' y='33.004'/></g></svg>.
+</p>
+<p class="p indent">Using a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">decreases</span></code> clause, the programmer can specify the term in this predefined
+order. When a function definition omits a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">decreases</span></code> clause, Dafny makes a simple
+guess. This guess (which can be inspected by hovering over the function name in the
+Dafny IDE) is very often correct, so users are rarely bothered to provide explicit
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">decreases</span></code> clauses.
+</p>
+<p class="p indent">If a function returns <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">bool</span></code>, one can drop the result type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">: <span style="color:teal">bool</span></code> and change the
+keyword <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">function</span></code> to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">predicate</span></code>.
+</p><h4 id="sec-proofs-in-dafny" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">11.1.1</span>.&#8194;</span>Proofs in Dafny</h4>
+<p class="p noindent">Dafny has <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">lemma</span></code> declarations. These are really just special cases of methods:
+they can have pre- and postcondition specifications and their body is a code block.
+Here is the lemma we stated and proved in Section&nbsp;<a href="#sec-fib-example" title="11.0.0.0.&#8194;Example with Well-founded Functions" class="localref" style="target-element:h4"><span class="heading-label">11.0.0.0</span></a>:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">lemma</span> FibProperty(n: <span style="color:teal">nat</span>)
+ <span style="color:purple">ensures</span> fib(n) % <span class="constant" style="color:purple">2</span> == <span class="constant" style="color:purple">0</span> &lt;==&gt; n % <span class="constant" style="color:purple">3</span> == <span class="constant" style="color:purple">0</span>
+{
+ <span style="color:blue">if</span> n &lt; <span class="constant" style="color:purple">2</span> {
+ } <span style="color:blue">else</span> {
+ FibProperty(n-<span class="constant" style="color:purple">2</span>); FibProperty(n-<span class="constant" style="color:purple">1</span>);
+ }
+}</code></pre>
+<p class="p noindent para-continued">The postcondition of this lemma (keyword <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">ensures</span></code>) gives the proof
+goal. As in any program-correctness logic (e.g.,
+<span class="citations" style="target-element:bibitem">[<a href="#hoare:axiomaticbasis" title="C.&#160;A.&#160;R. Hoare.
+An axiomatic basis for computer programming." class="bibref localref" style="target-element:bibitem"><span class="cite-number">7</span></a>]</span>), the postcondition must
+be established on every control path through the lemma&#39;s body. For
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">FibProperty</code>, I give the proof by
+an <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">if</span></code> statement, hence introducing a case split. The then branch is empty, because
+Dafny can prove the postcondition automatically in this case. The else branch
+performs two recursive calls to the lemma. These are the invocations of the induction
+hypothesis and they follow the usual program-correctness rules,
+namely: the precondition must hold at the call site, the call must terminate, and then
+the caller gets to assume the postcondition upon return. The &#8220;proof glue&#8221; needed
+to complete the proof is done automatically by Dafny.
+</p>
+<p class="p indent">Dafny features an aggregate statement using which it is possible to make (possibly
+infinitely) many calls at once. For example, the induction hypothesis can be called
+at once on all values <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n&#39;</code> smaller than <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n</code>:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">forall</span> n&#39; | <span class="constant" style="color:purple">0</span> &lt;= n&#39; &lt; n {
+ FibProperty(n&#39;);
+}</code></pre>
+<p class="p noindent para-continued">For our purposes, this corresponds to <em class="em-low1">strong induction</em>. More
+generally, the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> statement has the form
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">forall</span> k | P(k)
+ <span style="color:purple">ensures</span> Q(k)
+{ Statements; }</code></pre>
+<p class="p noindent para-continued">Logically, this statement corresponds to <em class="em-low1">universal introduction</em>: the body proves that
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q(k)</code> holds for an arbitrary <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">k</code> such that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P(k)</code>, and the conclusion of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> statement
+is then <svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:9.7252em" viewBox='-18.2 25.332 92.621 10.363' ><desc>$\forall k \bullet\; P(k) \Imp Q(k)$</desc><g id='page222'><use x='-18' xlink:href='#g1-56' y='33.004'/><use x='-12.486' xlink:href='#g3-107' y='33.004'/><use x='-4.791' xlink:href='#g1-15' y='33.004'/><use x='5.153' xlink:href='#g3-80' y='33.004'/><use x='12.909' xlink:href='#g5-40' y='33.004'/><use x='16.769' xlink:href='#g3-107' y='33.004'/><use x='22.249' xlink:href='#g5-41' y='33.004'/><use x='31.644' xlink:href='#g5-61' y='33.004'/><use x='37.703' xlink:href='#g1-41' y='33.004'/><use x='53.164' xlink:href='#g3-81' y='33.004'/><use x='61.01' xlink:href='#g5-40' y='33.004'/><use x='64.87' xlink:href='#g3-107' y='33.004'/><use x='70.351' xlink:href='#g5-41' y='33.004'/></g></svg>. When the body of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> statement is
+a single call (or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">calc</span></code> statement), the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">ensures</span></code> clause is inferred and can be omitted,
+like in our <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">FibProperty</code> example.
+</p>
+<p class="p indent">Lemma <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">FibProperty</code> is simple enough that its whole body can be replaced by the one
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> statement above. In fact, Dafny goes one step further: it automatically
+inserts such a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> statement at the beginning of every lemma&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#leino:induction" title="K.&#160;Rustan&#160;M. Leino.
+Automating induction with an SMT solver." class="bibref localref" style="target-element:bibitem"><span class="cite-number">19</span></a>]</span>.
+Thus, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">FibProperty</code> can be declared and proved simply by:
+</p>
+<pre class="para-block para-end pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">lemma</span> FibProperty(n: <span style="color:teal">nat</span>)
+ <span style="color:purple">ensures</span> fib(n) % <span class="constant" style="color:purple">2</span> == <span class="constant" style="color:purple">0</span> &lt;==&gt; n % <span class="constant" style="color:purple">3</span> == <span class="constant" style="color:purple">0</span>
+{ }</code></pre>
+<p class="p indent">Going in the other direction from universal introduction is existential elimination,
+also known as Skolemization. Dafny has a statement for this, too:
+for any variable <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> and boolean expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q</code>, the
+<em class="em-low1">assign such that</em> statement <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x <span style="color:blue">:|</span> Q;</code> says to assign to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> a value such that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q</code>
+will hold. A proof obligation when using this statement is to show that there
+exists an <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> such that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q</code> holds. For example, if the fact
+<svg class="math-inline math-render-svg math" style="vertical-align:-0.2930em;height:1.0881em;width:10.9055em" viewBox='-0.2 25.332 103.862 10.363' ><desc>$\exists k \bullet\; 100 \leq \fib(k) &lt; 200$</desc><g id='page223'><use x='0' xlink:href='#g1-57' y='33.004'/><use x='5.514' xlink:href='#g3-107' y='33.004'/><use x='13.209' xlink:href='#g1-15' y='33.004'/><use x='23.153' xlink:href='#g5-49' y='33.004'/><use x='28.116' xlink:href='#g5-48' y='33.004'/><use x='33.078' xlink:href='#g5-48' y='33.004'/><use x='40.808' xlink:href='#g1-20' y='33.004'/><use x='51.295' xlink:href='#g0-12' y='33.004'/><use x='56.876' xlink:href='#g0-98' y='33.004'/><use x='62.108' xlink:href='#g5-40' y='33.004'/><use x='65.968' xlink:href='#g3-107' y='33.004'/><use x='71.449' xlink:href='#g5-41' y='33.004'/><use x='78.076' xlink:href='#g3-60' y='33.004'/><use x='88.563' xlink:href='#g5-50' y='33.004'/><use x='93.526' xlink:href='#g5-48' y='33.004'/><use x='98.489' xlink:href='#g5-48' y='33.004'/></g></svg> is known, then the statement
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">k <span style="color:blue">:|</span> <span class="constant" style="color:purple">100</span> &lt;= fib(k) &lt; <span class="constant" style="color:purple">200</span>;</code> will assign to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">k</code> some value (chosen arbitrarily)
+for which <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">fib(k)</code> falls in the given range.
+</p><h4 id="sec-friendliness" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">11.1.2</span>.&#8194;</span>Extreme Predicates in Dafny</h4>
+<p class="p noindent">In this previous subsection, I explained that a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">predicate</span></code> declaration introduces a
+well-founded predicate. The declarations for introducing extreme predicates are
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">inductive</span> <span style="color:blue">predicate</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">copredicate</span></code>. Here is the definition of the least and
+greatest solutions of <svg class="math-inline math-render-svg math" style="vertical-align:-0.2446em;height:0.7188em;width:0.5777em" viewBox='-18.2 28.4 5.502 6.846' ><desc>$g$</desc><g id='page224'><use x='-18' xlink:href='#g3-103' y='33.004'/></g></svg> from above, let&#39;s call them <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">g</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">G</code>:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">inductive</span> <span style="color:blue">predicate</span> g(x: <span style="color:teal">int</span>) { x == <span class="constant" style="color:purple">0</span> || g(x-<span class="constant" style="color:purple">2</span>) }
+<span style="color:blue">copredicate</span> G(x: <span style="color:teal">int</span>) { x == <span class="constant" style="color:purple">0</span> || G(x-<span class="constant" style="color:purple">2</span>) }</code></pre>
+<p class="p noindent para-continued">When Dafny receives either of these definitions, it automatically declares the corresponding
+prefix predicates. Instead of the names <svg class="math-inline math-render-svg math" style="vertical-align:-0.2874em;height:1.2339em;width:1.3107em" viewBox='-0.2 23.958 12.483 11.751' ><desc>$\iter{g}_k$</desc><g id='page225'><use x='0' xlink:href='#g4-91' y='29.388'/><use x='2.062' xlink:href='#g3-103' y='33.004'/><use x='7.171' xlink:href='#g4-107' y='35.439'/></g></svg> and <svg class="math-inline math-render-svg math" style="vertical-align:-0.2913em;height:1.2090em;width:1.3107em" viewBox='-18.2 24.195 12.483 11.514' ><desc>$\Iter{g}_k$</desc><g id='page226'><use x='-18' xlink:href='#g4-93' y='29.388'/><use x='-15.938' xlink:href='#g3-103' y='33.004'/><use x='-10.829' xlink:href='#g4-107' y='35.439'/></g></svg> that I used above, Dafny
+names the prefix predicates <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">g#[k]</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">G#[k]</code>, respectively, that is, the name of
+the extreme predicate appended with <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">#</code>, and the subscript is given as an argument in
+square brackets. The definition of the prefix predicate derives from the body of
+the extreme predicate and follows the form in&nbsp;<a href="#eq-least-approx" class="localref" style="target-element:equation"><span class="equation-label">(11)</span></a> and&nbsp;<a href="#eq-greatest-approx" class="localref" style="target-element:equation"><span class="equation-label">(12)</span></a>.
+Using a faux-syntax for illustrative purposes, here are the prefix
+predicates that Dafny defines automatically from the extreme
+predicates <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">g</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">G</code>:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">predicate</span> g#[_k: <span style="color:teal">nat</span>](x: <span style="color:teal">int</span>) { _k != <span class="constant" style="color:purple">0</span> &amp;&amp; (x == <span class="constant" style="color:purple">0</span> || g#[_k-<span class="constant" style="color:purple">1</span>](x-<span class="constant" style="color:purple">2</span>)) }
+<span style="color:blue">predicate</span> G#[_k: <span style="color:teal">nat</span>](x: <span style="color:teal">int</span>) { _k != <span class="constant" style="color:purple">0</span> ==&gt; (x == <span class="constant" style="color:purple">0</span> || G#[_k-<span class="constant" style="color:purple">1</span>](x-<span class="constant" style="color:purple">2</span>)) }</code></pre>
+<p class="p noindent para-continued">The Dafny verifier is aware of the connection between extreme predicates and their
+prefix predicates,&nbsp;<a href="#eq-least-is-exists" class="localref" style="target-element:equation"><span class="equation-label">(14)</span></a> and&nbsp;<a href="#eq-greatest-is-forall" class="localref" style="target-element:equation"><span class="equation-label">(15)</span></a>.
+</p>
+<p class="p indent">Remember that to be well defined, the defining functor of an extreme predicate
+must be monotonic, and for&nbsp;<a href="#eq-least-is-exists" class="localref" style="target-element:equation"><span class="equation-label">(14)</span></a> and&nbsp;<a href="#eq-greatest-is-forall" class="localref" style="target-element:equation"><span class="equation-label">(15)</span></a> to hold,
+the functor must be continuous. Dafny enforces the former of these by checking that
+recursive calls of extreme predicates are in positive positions. The continuity
+requirement comes down to checking that they are also in <em class="em-low1">continuous positions</em>:
+that recursive calls to inductive predicates are
+not inside unbounded universal quantifiers and that recursive calls to co-predicates
+are not inside unbounded existential quantifiers&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#leinomoskal:coinduction" title="K.&#160;Rustan&#160;M. Leino and Micha&#322; Moskal.
+Co-induction simply &#8212; automatic co-inductive proofs in a program verifier.
+In FM 2014, volume 8442 of LNCS, pages 382&#8211;398. Springer, May 2014b." class="bibref localref" style="target-element:bibitem"><span class="cite-number">21</span></a>, <a href="#milner:ccs" title="Robin Milner.
+A Calculus of Communicating Systems." class="bibref localref" style="target-element:bibitem"><span class="cite-number">26</span></a>]</span>.
+</p><h4 id="sec-proofs-about-extreme-predicates" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">11.1.3</span>.&#8194;</span>Proofs about Extreme Predicates</h4>
+<p class="p noindent">From what I have presented so far, we can do the formal proofs from Sections
+<a href="#sec-example-least-solution" title="11.0.2.0.&#8194;Example with Least Solution" class="localref" style="target-element:h4"><span class="heading-label">11.0.2.0</span></a> and&nbsp;<a href="#sec-example-greatest-solution" title="11.0.2.1.&#8194;Example with Greatest Solution" class="localref" style="target-element:h4"><span class="heading-label">11.0.2.1</span></a>. Here is the
+former:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">lemma</span> EvenNat(x: <span style="color:teal">int</span>)
+ <span style="color:purple">requires</span> g(x)
+ <span style="color:purple">ensures</span> <span class="constant" style="color:purple">0</span> &lt;= x &amp;&amp; x % <span class="constant" style="color:purple">2</span> == <span class="constant" style="color:purple">0</span>
+{
+ <span style="color:blue">var</span> k: <span style="color:teal">nat</span> <span style="color:blue">:|</span> g#[k](x);
+ EvenNatAux(k, x);
+}
+<span style="color:blue">lemma</span> EvenNatAux(k: <span style="color:teal">nat</span>, x: <span style="color:teal">int</span>)
+ <span style="color:purple">requires</span> g#[k](x)
+ <span style="color:purple">ensures</span> <span class="constant" style="color:purple">0</span> &lt;= x &amp;&amp; x % <span class="constant" style="color:purple">2</span> == <span class="constant" style="color:purple">0</span>
+{
+ <span style="color:blue">if</span> x == <span class="constant" style="color:purple">0</span> { } <span style="color:blue">else</span> { EvenNatAux(k-<span class="constant" style="color:purple">1</span>, x-<span class="constant" style="color:purple">2</span>); }
+}</code></pre>
+<p class="p noindent para-continued">Lemma <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">EvenNat</code> states the property we wish to prove. From its
+precondition (keyword <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">requires</span></code>) and
+<a href="#eq-least-is-exists" class="localref" style="target-element:equation"><span class="equation-label">(14)</span></a>, we know there is some <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">k</code> that will make the condition in the
+assign-such-that statement true. Such a value is then assigned to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">k</code> and passed to
+the auxiliary lemma, which promises to establish the proof goal. Given the condition
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">g#[k](x)</code>, the definition of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">g#</code> lets us conclude <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">k != <span class="constant" style="color:purple">0</span></code> as well as the disjunction
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x == <span class="constant" style="color:purple">0</span> || g#[k-<span class="constant" style="color:purple">1</span>](x-<span class="constant" style="color:purple">2</span>)</code>. The then branch considers the case of the first disjunct,
+from which the proof goal follows automatically. The else branch can then assume
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">g#[k-<span class="constant" style="color:purple">1</span>](x-<span class="constant" style="color:purple">2</span>)</code> and calls the induction hypothesis with those parameters. The proof
+glue that shows the proof goal for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> to follow from the proof goal with <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x-<span class="constant" style="color:purple">2</span></code> is
+done automatically.
+</p>
+<p class="p indent">Because Dafny automatically inserts the statement
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">forall</span> k&#39;, x&#39; | <span class="constant" style="color:purple">0</span> &lt;= k&#39; &lt; k &amp;&amp; g#[k&#39;](x&#39;) {
+ EvenNatAux(k&#39;, x&#39;);
+}</code></pre>
+<p class="p noindent para-continued">at the beginning of the body of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">EvenNatAux</code>, the body can be left empty and Dafny
+completes the proof automatically.
+</p>
+<p class="p indent">Here is the Dafny program that gives the proof from Section&nbsp;<a href="#sec-example-greatest-solution" title="11.0.2.1.&#8194;Example with Greatest Solution" class="localref" style="target-element:h4"><span class="heading-label">11.0.2.1</span></a>:
+</p>
+<pre class="para-block para-end pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">lemma</span> Always(x: <span style="color:teal">int</span>)
+ <span style="color:purple">ensures</span> G(x)
+{ <span style="color:blue">forall</span> k: <span style="color:teal">nat</span> { AlwaysAux(k, x); } }
+<span style="color:blue">lemma</span> AlwaysAux(k: <span style="color:teal">nat</span>, x: <span style="color:teal">int</span>)
+ <span style="color:purple">ensures</span> G#[k](x)
+{ }</code></pre>
+<p class="p indent">While each of these proofs involves only basic proof rules, the setup feels a bit clumsy,
+even with the empty body of the auxiliary lemmas. Moreover,
+the proofs do not reflect the intuitive proofs I described in
+Section&nbsp;<a href="#sec-example-least-solution" title="11.0.2.0.&#8194;Example with Least Solution" class="localref" style="target-element:h4"><span class="heading-label">11.0.2.0</span></a> and&nbsp;<a href="#sec-example-greatest-solution" title="11.0.2.1.&#8194;Example with Greatest Solution" class="localref" style="target-element:h4"><span class="heading-label">11.0.2.1</span></a>.
+These shortcoming are addressed in the next subsection.
+</p><h4 id="sec-nicer-proofs-of-extreme-predicates" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">11.1.4</span>.&#8194;</span>Nicer Proofs of Extreme Predicates</h4>
+<p class="p noindent">The proofs we just saw follow standard forms:
+use Skolemization to convert the inductive predicate into a prefix predicate for some <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">k</code>
+and then do the proof inductively over <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">k</code>; respectively,
+by induction over <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">k</code>, prove the prefix predicate for every <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">k</code>, then use
+universal introduction to convert to the coinductive predicate.
+With the declarations <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">inductive</span> <span style="color:blue">lemma</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">colemma</span></code>, Dafny offers to
+set up the proofs
+in these standard forms. What is gained is not just fewer characters in the program
+text, but also a possible intuitive reading of the proofs. (Okay, to be fair, the
+reading is intuitive for simpler proofs; complicated proofs may or may not be intuitive.)
+</p>
+<p class="p indent">Somewhat analogous to the creation of prefix predicates from extreme predicates, Dafny
+automatically creates a <em class="em-low1">prefix lemma</em> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">L#</code> from each &#8220;extreme lemma&#8221; <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">L</code>. The pre-
+and postconditions of a prefix lemma are copied from those of the extreme lemma,
+except for the following replacements:
+For an inductive lemma, Dafny looks in the precondition to find calls (in positive, continuous
+positions) to inductive predicates <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P(x)</code> and replaces these with <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P#[_k](x)</code>.
+For a
+co-lemma, Dafny looks in the postcondition to find calls (in positive, continuous positions)
+to co-predicates <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P</code> (including equality among coinductive datatypes, which is a built-in
+co-predicate) and replaces these with <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P#[_k](x)</code>.
+In each case, these predicates <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P</code> are the lemma&#39;s <em class="em-low1">focal predicates</em>.
+</p>
+<p class="p indent">The body of the extreme lemma is moved to the prefix lemma, but with
+replacing each recursive
+call <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">L(x)</code> with <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">L#[_k-<span class="constant" style="color:purple">1</span>](x)</code> and replacing each occurrence of a call
+to a focal predicate
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P(x)</code> with <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P#[_k-<span class="constant" style="color:purple">1</span>](x)</code>. The bodies of the extreme lemmas are then replaced as shown
+in the previous subsection. By construction, this new body correctly leads to the
+extreme lemma&#39;s postcondition.
+</p>
+<p class="p indent">Let us see what effect these rewrites have on how one can write proofs. Here are the proofs
+of our running example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">inductive</span> <span style="color:blue">lemma</span> EvenNat(x: <span style="color:teal">int</span>)
+ <span style="color:purple">requires</span> g(x)
+ <span style="color:purple">ensures</span> <span class="constant" style="color:purple">0</span> &lt;= x &amp;&amp; x % <span class="constant" style="color:purple">2</span> == <span class="constant" style="color:purple">0</span>
+{ <span style="color:blue">if</span> x == <span class="constant" style="color:purple">0</span> { } <span style="color:blue">else</span> { EvenNat(x-<span class="constant" style="color:purple">2</span>); } }
+<span style="color:blue">colemma</span> Always(x: <span style="color:teal">int</span>)
+ <span style="color:purple">ensures</span> G(x)
+{ Always(x-<span class="constant" style="color:purple">2</span>); }</code></pre>
+<p class="p noindent para-continued">Both of these proofs follow the intuitive proofs given in Sections
+<a href="#sec-example-least-solution" title="11.0.2.0.&#8194;Example with Least Solution" class="localref" style="target-element:h4"><span class="heading-label">11.0.2.0</span></a> and&nbsp;<a href="#sec-example-greatest-solution" title="11.0.2.1.&#8194;Example with Greatest Solution" class="localref" style="target-element:h4"><span class="heading-label">11.0.2.1</span></a>. Note that in these
+simple examples, the user is never bothered with either prefix predicates nor
+prefix lemmas&#8212;the proofs just look like &#8220;what you&#39;d expect&#8221;.
+</p>
+<p class="p indent">Since Dafny automatically inserts calls to the induction hypothesis at the beginning of
+each lemma, the bodies of the given extreme lemmas <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">EvenNat</code> and
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Always</code> can be empty and Dafny still completes the proofs.
+Folks, it doesn&#39;t get any simpler than that!
+</p><h2 id="sec-class-types" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">12</span>.&#8194;</span>Class Types</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_classdecl" class="ntdef" style="color:olive">ClassDecl</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"class"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span class="code-escaped"><a href="#1_classname" class="ntref localref" style="color:maroon">ClassName</a></span> [ <span class="code-escaped"><a href="#1_genericparameters" class="ntref localref" style="color:maroon">GenericParameters</a></span> ]
+ [<span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"extends"</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span> {<span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span>} ]
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"{"</span></span> { { <span class="code-escaped"><a href="#1_declmodifier" class="ntref localref" style="color:maroon">DeclModifier</a></span> } <span class="code-escaped"><a href="#1_classmemberdecl" class="ntref localref" style="color:maroon">ClassMemberDecl</a></span>(moduleLevelDecl: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>) } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"}"</span></span> </code></pre>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_classmemberdecl" class="ntdef" style="color:olive">ClassMemberDecl</span></span>(moduleLevelDecl) =
+ ( <span class="code-escaped"><a href="#1_fielddecl" class="ntref localref" style="color:maroon">FieldDecl</a></span> | <span class="code-escaped"><a href="#1_functiondecl" class="ntref localref" style="color:maroon">FunctionDecl</a></span> |
+ <span class="code-escaped"><a href="#1_methoddecl" class="ntref localref" style="color:maroon">MethodDecl</a></span>(isGhost: (<span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"ghost"</span></span> <span class="code-escaped"><a href="#2_was" class="ntref localref" style="color:maroon">was</a></span> <span class="code-escaped"><a href="#2_present" class="ntref localref" style="color:maroon">present</a></span>),
+ <span class="code-escaped"><a href="#2_allowconstructor" class="ntref localref" style="color:maroon">allowConstructor</a></span>: !<span class="code-escaped"><a href="#2_moduleleveldecl" class="ntref localref" style="color:maroon">moduleLevelDecl</a></span>)
+ ) </code></pre>
+<p class="p noindent para-continued">The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_classmemberdecl" class="ntref localref" style="color:maroon">ClassMemberDecl</a></span></code> parameter <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">moduleLevelDecl</code> will be true if
+the member declaration is at the top level or directly within a
+module declaration. It will be false for <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_classmemberdecl" class="ntref localref" style="color:maroon">ClassMemberDecl</a></span></code>s
+that are part of a class or trait declaration. If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">moduleLevelDecl</code> is
+false <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_fielddecl" class="ntref localref" style="color:maroon">FieldDecl</a></span></code>s are not allowed.
+</p>
+<p class="p indent">A <em class="em-low1">class</em> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code> is a reference type declared as follows:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">class</span> C&lt;T&gt; <span style="color:blue">extends</span> J1, ..., Jn
+{
+ <span class="code-escaped"><em class="em-low1">members</em></span>
+}</code></pre>
+<p class="p noindent para-continued">where the list of type parameters <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> is optional and so is
+&#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">extends</span> J1, ..., Jn</code>&#8221;, which says that the class extends traits <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">J1</code> &#8230; <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Jn</code>.
+The members of a class are <em class="em-low1">fields</em>, <em class="em-low1">functions</em>, and
+<em class="em-low1">methods</em>. These are accessed or invoked by dereferencing a reference
+to a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code> instance.
+</p>
+<p class="p indent">A function or method is invoked on an <em class="em-low1">instance</em>
+of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code>, unless the function or method is declared <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">static</span></code>.
+A function or method that is not <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">static</span></code> is called an
+<em class="em-low1">instance</em> function or method.
+</p>
+<p class="p indent">An instance function or method takes an implicit <em class="em-low1">receiver</em>
+parameter, namely, the instance used to access the member. In the
+specification and body of an instance function or method, the receiver
+parameter can be referred to explicitly by the keyword <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">this</span></code>.
+However, in such places, members of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">this</span></code> can also be mentioned
+without any qualification. To illustrate, the qualified <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">this</span>.f</code> and
+the unqualified <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f</code> refer to the same field of the same object in the
+following example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">class</span> C {
+ <span style="color:blue">var</span> f: <span style="color:teal">int</span>
+ <span style="color:blue">method</span> Example() <span style="color:blue">returns</span> (b: <span style="color:teal">bool</span>)
+ {
+ b <span style="color:blue">:=</span> f == <span style="color:blue">this</span>.f;
+ }
+}</code></pre>
+<p class="p noindent para-continued">so the method body always assigns <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code> to the out-parameter <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">b</code>.
+There is no semantic difference between qualified and
+unqualified accesses to the same receiver and member.
+</p>
+<p class="p indent">A <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code> instance is created using <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">new</span></code>, for example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>c <span style="color:blue">:=</span> <span style="color:blue">new</span> C;</code></pre>
+<p class="p noindent para-continued">Note that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">new</span></code> simply allocates a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code> object and returns a reference
+to it; the initial values of its fields are arbitrary values of their
+respective types. Therefore, it is common to invoke a method, known
+as an <em class="em-low1">initialization method</em>, immediately after creation, for
+example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>c <span style="color:blue">:=</span> <span style="color:blue">new</span> C;
+c.InitFromList(xs, <span class="constant" style="color:purple">3</span>);</code></pre>
+<p class="p noindent para-continued">When an initialization method has no out-parameters and modifies no
+more than <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">this</span></code>, then the two statements above can be combined into
+one:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>c <span style="color:blue">:=</span> <span style="color:blue">new</span> C.InitFromList(xs, <span class="constant" style="color:purple">3</span>);</code></pre>
+<p class="p noindent para-continued">Note that a class can contain several initialization methods, that
+these methods can be invoked at any time, not just as part of a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">new</span></code>,
+and that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">new</span></code> does not require that an initialization method be
+invoked at creation.
+</p>
+<p class="p indent">A clas can declare special initializing methods called <em class="em-low1">constructor methods</em>.
+See Section&nbsp;<a href="#sec-method-declarations" title="12.1.&#8194;Method Declarations" class="localref" style="target-element:h2"><span class="heading-label">12.1</span></a>.
+</p><h3 id="sec-field-declarations" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">12.0</span>.&#8194;</span>Field Declarations</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_fielddecl" class="ntdef" style="color:olive">FieldDecl</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"var"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span class="code-escaped"><a href="#1_fidenttype" class="ntref localref" style="color:maroon">FIdentType</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_fidenttype" class="ntref localref" style="color:maroon">FIdentType</a></span> }</code></pre>
+<p class="p noindent para-continued">An <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_fidenttype" class="ntref localref" style="color:maroon">FIdentType</a></span></code> is used to declare a field. The field name is either an
+identifier (that is not allowed to start with a leading underscore) or
+some digits. Digits are used if you want to number your fields, e.g. &#8220;0&#8221;,
+&#8220;1&#8221;, etc.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_fidenttype" class="ntdef" style="color:olive">FIdentType</span></span> = ( <span class="code-escaped"><a href="#1_fieldident" class="ntref localref" style="color:maroon">FieldIdent</a></span> | <span class="code-escaped"><a href="#2_digits" class="ntref localref" style="color:maroon">digits</a></span> ) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":"</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span> </code></pre>
+<p class="p noindent para-continued">A field x of some type T is declared as:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> x: T</code></pre>
+<p class="p noindent para-continued">A field declaration declares one or more fields of the enclosing class.
+Each field is a named part of the state of an object of that class. A
+field declaration is similar to but distinct from a variable declaration
+statement. Unlike for local variables and bound variables, the type is
+required and will not be inferred.
+</p>
+<p class="p indent">Unlike method and function declarations, a field declaration
+cannot be given at the top level. Fields can be declared in either a
+class or a trait. A class that inherits from multiple traits will
+have all the fields declared in any of its parent traits.
+</p>
+<p class="p indent">Fields that are declared as <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">ghost</span></code> can only be used in specifications,
+not in code that will be compiled into executable code.
+</p>
+<p class="p indent">Fields may not be declared static.
+</p>
+<p class="p indent"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">protected</span></code> is not allowed for fields.
+</p><h3 id="sec-method-declarations" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">12.1</span>.&#8194;</span>Method Declarations</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_methoddecl" class="ntdef" style="color:olive">MethodDecl</span></span>(isGhost, <span class="code-escaped"><a href="#2_allowconstructor" class="ntref localref" style="color:maroon">allowConstructor</a></span>) =
+ <span class="code-escaped"><a href="#1_methodkeyword" class="ntref localref" style="color:maroon">MethodKeyword</a></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } [ <span class="code-escaped"><a href="#1_methodname" class="ntref localref" style="color:maroon">MethodName</a></span> ]
+ ( <span class="code-escaped"><a href="#1_methodsignature" class="ntref localref" style="color:maroon">MethodSignature</a></span>(isGhost) | <span class="code-escaped"><a href="#1_signatureellipsis_" class="ntref localref" style="color:maroon">SignatureEllipsis_</a></span> )
+ <span class="code-escaped"><a href="#1_methodspec" class="ntref localref" style="color:maroon">MethodSpec</a></span> [ <span class="code-escaped"><a href="#1_blockstmt" class="ntref localref" style="color:maroon">BlockStmt</a></span> ]</code></pre>
+<p class="p noindent para-continued">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">isGhost</code> parameter is true iff the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">ghost</span></code> keyword
+preceded the method declaration.
+</p>
+<p class="p indent">If the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">allowConstructor</code> parameter is false then
+the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_methoddecl" class="ntref localref" style="color:maroon">MethodDecl</a></span></code> must not be a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">constructor</span></code>
+declaration.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_methodkeyword" class="ntdef" style="color:olive">MethodKeyword</span></span> = (<span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"method"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"lemma"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"colemma"</span></span>
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"inductive"</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"lemma"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"constructor"</span></span> )</code></pre>
+<p class="p noindent para-continued">The method keyword is used to specify special kinds of methods
+as explained below.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_methodsignature" class="ntdef" style="color:olive">MethodSignature</span></span>(isGhost) =
+ [ <span class="code-escaped"><a href="#1_genericparameters" class="ntref localref" style="color:maroon">GenericParameters</a></span> ]
+ <span class="code-escaped"><a href="#1_formals" class="ntref localref" style="color:maroon">Formals</a></span>(allowGhost: !<span class="code-escaped"><a href="#2_isghost" class="ntref localref" style="color:maroon">isGhost</a></span>)
+ [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"returns"</span></span> <span class="code-escaped"><a href="#1_formals" class="ntref localref" style="color:maroon">Formals</a></span>(allowGhost: !<span class="code-escaped"><a href="#2_isghost" class="ntref localref" style="color:maroon">isGhost</a></span>) ]</code></pre>
+<p class="p noindent para-continued">A method signature specifies the method generic parameters,
+input parameters and return parameters.
+The formal parameters are not allowed to have <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">ghost</span></code> specified
+if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">ghost</span></code> was already specified for the method.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_signatureellipsis_" class="ntdef" style="color:olive">SignatureEllipsis_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&#8230;"</span></span></code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_signatureellipsis_" class="ntref localref" style="color:maroon">SignatureEllipsis_</a></span></code> is used when a method or function is being redeclared
+in module that refines another module. In that case the signature is
+copied from the module that is being refined. This works because
+Dafny does not support method or function overloading, so the
+name of the class method uniquely identifies it without the
+signature.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_formals" class="ntdef" style="color:olive">Formals</span></span>(allowGhostKeyword) =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> [ <span class="code-escaped"><a href="#1_gidenttype" class="ntref localref" style="color:maroon">GIdentType</a></span>(allowGhostKeyword)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_gidenttype" class="ntref localref" style="color:maroon">GIdentType</a></span>(allowGhostKeyword) } ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> </code></pre>
+<p class="p noindent para-continued">The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_formals" class="ntref localref" style="color:maroon">Formals</a></span></code> specifies the names and types of the method input or
+output parameters.
+</p>
+<p class="p indent">See section&nbsp;<a href="#sec-method-specification" title="4.1.&#8194;Method Specification" class="localref" style="target-element:h2"><span class="heading-label">4.1</span></a> for a description of <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_methodspec" class="ntref localref" style="color:maroon">MethodSpec</a></span></code>.
+</p>
+<p class="p indent">A method declaration adheres to the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_methoddecl" class="ntref localref" style="color:maroon">MethodDecl</a></span></code> grammar above.
+Here is an example of a method declaration.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">method</span> {<span style="color:purple">:att1</span>}{<span style="color:purple">:att2</span>} M&lt;T1, T2&gt;(a: A, b: B, c: C) <span style="color:blue">returns</span> (x: X, y: Y, z: Z)
+ <span style="color:purple">requires</span> Pre
+ <span style="color:purple">modifies</span> Frame
+ <span style="color:purple">ensures</span> Post
+ <span style="color:purple">decreases</span> Rank
+{
+ Body
+}</code></pre>
+<p class="p noindent para-continued">where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">:att1</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">:att2</code> are attributes of the method,
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T1</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T2</code> are type parameters of the method (if generic),
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a, b, c</code> are the method’s in-parameters, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x, y, z</code> are the
+method’s out-parameters, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pre</code> is a boolean expression denoting the
+method’s precondition, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Frame</code> denotes a set of objects whose fields may
+be updated by the method, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Post</code> is a boolean expression denoting the
+method’s postcondition, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Rank</code> is the method’s variant function, and
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Body</code> is a statement that implements the method. <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Frame</code> can be a list
+of expressions, each of which is a set of objects or a single object, the
+latter standing for the singleton set consisting of that one object. The
+method’s frame is the union of these sets, plus the set of objects
+allocated by the method body. For example, if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">c</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">d</code> are parameters
+of a class type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code>, then
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:purple">modifies</span> {c, d}
+
+<span style="color:purple">modifies</span> {c} + {d}
+
+<span style="color:purple">modifies</span> c, {d}
+
+<span style="color:purple">modifies</span> c, d</code></pre>
+<p class="p noindent para-continued">all mean the same thing.
+</p>
+<p class="p indent">A method can be declared as ghost by preceding the declaration with the
+keyword ghost. By default, a method has an implicit receiver parameter,
+this. This parameter can be removed by preceding the method declaration
+with the keyword static. A static method M in a class C can be invoked by
+C.M(…).
+</p>
+<p class="p indent">In a class, a method can be declared to be a constructor method by
+replacing the keyword <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">method</span></code> with the keyword <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">constructor</span></code>. A constructor
+can only be called at the time an object is allocated (see
+object-creation examples below), and for a class that contains one or
+more constructors, object creation must be done in conjunction with a
+call to a constructor.
+</p>
+<p class="p indent">An ordinary method is declared with the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">method</span></code> keyword.
+Section&nbsp;<a href="#sec-constructors" title="12.1.0.&#8194;Constructors" class="localref" style="target-element:h3"><span class="heading-label">12.1.0</span></a> explains methods that instead use the
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">constructor</span></code> keyword. Section&nbsp;<a href="#sec-lemmas" title="12.1.1.&#8194;Lemmas" class="localref" style="target-element:h3"><span class="heading-label">12.1.1</span></a> discusses methods that are
+declared with the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">lemma</span></code> keyword. Methods declared with the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">inductive</span></code>
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">lemma</span></code> keywords are discussed later in the context of inductive
+predicates (see&nbsp;<a href="#sec-inductive-datatypes" title="18.0.&#8194;Inductive datatypes" class="localref" style="target-element:h2"><span class="heading-label">18.0</span></a>). Methods declared with the
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">colemma</span></code> keyword are discussed later in the context of co-inductive
+types, in section&nbsp;<a href="#sec-colemmas" title="18.2.4.1.&#8194;Colemmas" class="localref" style="target-element:h4"><span class="heading-label">18.2.4.1</span></a>.
+</p>
+<p class="p indent">A method without is body is <em class="em-low1">abstract</em>. A method is allowed to be
+abstract under the following circumstances:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">It contains an <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:axiom</span>}</code> attribute
+</li>
+<li class="li ul-li list-star-li compact-li">It contains an <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:imported</span>}</code> attribute
+</li>
+<li class="li ul-li list-star-li compact-li">It contains a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:decl</span>}</code> attribute
+</li>
+<li class="li ul-li list-star-li compact-li">It is a declaration in an abstract module.
+Note that when there is no body, Dafny assumes that the <em class="em-star1">ensures</em>
+clauses are true without proof.
+</li></ul>
+<h4 id="sec-constructors" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">12.1.0</span>.&#8194;</span>Constructors</h4>
+<p class="p noindent">To write structured object-oriented programs, one often relies on that
+objects are constructed only in certain ways. For this purpose, Dafny
+provides <em class="em-low1">constructor (method)s</em>, which are a restricted form of
+initialization methods. A constructor is declared with the keyword
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">constructor</span></code> instead of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">method</span></code>. When a class contains a
+constructor, every call to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">new</span></code> for that class must be accompanied
+with a call to one of the constructors. Moreover, a constructor
+cannot be called at other times, only during object creation. Other
+than these restrictions, there is no semantic difference between using
+ordinary initialization methods and using constructors.
+</p>
+<p class="p indent">The Dafny design allows the constructors to be named, which promotes
+using names like <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">InitFromList</code> above. Still, many classes have just
+one constructor or have a typical constructor. Therefore, Dafny
+allows one <em class="em-low1">anonymous constructor</em>, that is, a constructor whose name
+is essentially &#8220;&#8221;. For example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">class</span> Item {
+ <span style="color:blue">constructor</span> (x: <span style="color:teal">int</span>, y: <span style="color:teal">int</span>)
+ <span style="color:darkgreen">// ...</span>
+}</code></pre>
+<p class="p noindent para-continued">When invoking this constructor, the &#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">.</code>&#8221; is dropped, as in:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>m <span style="color:blue">:=</span> <span style="color:blue">new</span> Item(<span class="constant" style="color:purple">45</span>, <span class="constant" style="color:purple">29</span>);</code></pre>
+<p class="p noindent para-continued">Note that an anonymous constructor is just one way to name a
+constructor; there can be other constructors as well.
+</p><h4 id="sec-lemmas" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">12.1.1</span>.&#8194;</span>Lemmas</h4>
+<p class="p noindent">Sometimes there are steps of logic required to prove a program correct,
+but they are too complex for Dafny to discover and use on its own. When
+this happens, we can often give Dafny assistance by providing a lemma.
+This is done by declaring a method with the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">lemma</span></code> keyword.
+Lemmas are implicitly ghost methods and the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">ghost</span></code> keyword cannot
+be applied to them.
+</p>
+<p class="p indent">For an example, see the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">FibProperty</code> lemma in
+Section&nbsp;<a href="#sec-proofs-in-dafny" title="11.1.1.&#8194;Proofs in Dafny" class="localref" style="target-element:h3"><span class="heading-label">11.1.1</span></a>.
+</p>
+<p class="p indent">See&nbsp;<a href="http://rise4fun.com/Dafny/tutorial/Lemmas">the Dafny Lemmas tutorial</a>
+for more examples and hints for using lemmas.
+</p><h3 id="sec-function-declarations" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">12.2</span>.&#8194;</span>Function Declarations</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_functiondecl" class="ntdef" style="color:olive">FunctionDecl</span></span> =
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"function"</span></span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"method"</span></span> ] { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ <span class="code-escaped"><a href="#1_functionname" class="ntref localref" style="color:maroon">FunctionName</a></span>
+ <span class="code-escaped"><a href="#1_functionsignatureorellipsis_" class="ntref localref" style="color:maroon">FunctionSignatureOrEllipsis_</a></span>(allowGhostKeyword: (<span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"method"</span></span> <span class="code-escaped"><a href="#2_present" class="ntref localref" style="color:maroon">present</a></span>))
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"predicate"</span></span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"method"</span></span> ] { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ <span class="code-escaped"><a href="#1_predicatename" class="ntref localref" style="color:maroon">PredicateName</a></span>
+ <span class="code-escaped"><a href="#1_predicatesignatureorellipsis_" class="ntref localref" style="color:maroon">PredicateSignatureOrEllipsis_</a></span>(allowGhostKeyword: (<span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"method"</span></span> <span class="code-escaped"><a href="#2_present" class="ntref localref" style="color:maroon">present</a></span>))
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"inductive"</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"predicate"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ <span class="code-escaped"><a href="#1_predicatename" class="ntref localref" style="color:maroon">PredicateName</a></span>
+ <span class="code-escaped"><a href="#1_predicatesignatureorellipsis_" class="ntref localref" style="color:maroon">PredicateSignatureOrEllipsis_</a></span>(allowGhostKeyword: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>)
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"copredicate"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ <span class="code-escaped"><a href="#1_copredicatename" class="ntref localref" style="color:maroon">CopredicateName</a></span>
+ <span class="code-escaped"><a href="#1_predicatesignatureorellipsis_" class="ntref localref" style="color:maroon">PredicateSignatureOrEllipsis_</a></span>(allowGhostKeyword: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>)
+ )
+ <span class="code-escaped"><a href="#1_functionspec" class="ntref localref" style="color:maroon">FunctionSpec</a></span> [ <span class="code-escaped"><a href="#1_functionbody" class="ntref localref" style="color:maroon">FunctionBody</a></span> ]
+
+<span class="code-escaped"><span id="1_functionsignatureorellipsis_" class="ntdef" style="color:olive">FunctionSignatureOrEllipsis_</span></span>(allowGhostKeyword) =
+ <span class="code-escaped"><a href="#1_functionsignature_" class="ntref localref" style="color:maroon">FunctionSignature_</a></span> | <span class="code-escaped"><a href="#1_signatureellipsis_" class="ntref localref" style="color:maroon">SignatureEllipsis_</a></span>
+<span class="code-escaped"><span id="1_functionsignature_" class="ntdef" style="color:olive">FunctionSignature_</span></span>(allowGhostKeyword) =
+ [ <span class="code-escaped"><a href="#1_genericparameters" class="ntref localref" style="color:maroon">GenericParameters</a></span> ] <span class="code-escaped"><a href="#1_formals" class="ntref localref" style="color:maroon">Formals</a></span>(allowGhostKeyword) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":"</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span>
+
+<span class="code-escaped"><span id="1_predicatesignatureorellipsis_" class="ntdef" style="color:olive">PredicateSignatureOrEllipsis_</span></span>(allowGhostKeyword) =
+ <span class="code-escaped"><a href="#1_predicatesignature_" class="ntref localref" style="color:maroon">PredicateSignature_</a></span>(allowGhostKeyword) | <span class="code-escaped"><a href="#1_signatureellipsis_" class="ntref localref" style="color:maroon">SignatureEllipsis_</a></span>
+<span class="code-escaped"><span id="1_predicatesignature_" class="ntdef" style="color:olive">PredicateSignature_</span></span>(allowGhostKeyword) =
+ [ <span class="code-escaped"><a href="#1_genericparameters" class="ntref localref" style="color:maroon">GenericParameters</a></span> ] <span class="code-escaped"><a href="#1_formals" class="ntref localref" style="color:maroon">Formals</a></span>(allowGhostKeyword)
+
+<span class="code-escaped"><span id="1_functionbody" class="ntdef" style="color:olive">FunctionBody</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"{"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"}"</span></span> </code></pre>
+<p class="p noindent para-continued">In the above productions, allowGhostKeyword is true if the optional
+&#8220;method&#8221; keyword was specified. This allows some of the
+formal parameters of a function method to be specified as ghost.
+</p>
+<p class="p indent">See section&nbsp;<a href="#sec-function-specification" title="4.2.&#8194;Function Specification" class="localref" style="target-element:h2"><span class="heading-label">4.2</span></a> for a description of <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_functionspec" class="ntref localref" style="color:maroon">FunctionSpec</a></span></code>.
+</p>
+<p class="p indent">A Dafny function is a pure mathematical function. It is allowed to
+read memory that was specified in its <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">reads</span></code> expression but is not
+allowed to have any side effects.
+</p>
+<p class="p indent">Here is an example function declaration:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">function</span> {<span style="color:purple">:att1</span>}{<span style="color:purple">:att2</span>} F&lt;T1, T2&gt;(a: A, b: B, c: C): T
+ <span style="color:purple">requires</span> Pre
+ <span style="color:purple">reads</span> Frame
+ <span style="color:purple">ensures</span> Post
+ <span style="color:purple">decreases</span> Rank
+{
+ Body
+}</code></pre>
+<p class="p noindent para-continued">where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">:att1</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">:att2</code> are attributes of the function, if any, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T1</code>
+and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T2</code> are type parameters of the function (if generic), <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a, b, c</code> are
+the functions’s parameters, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> is the type of the function’s result,
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pre</code> is a boolean expression denoting the function’s precondition,
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Frame</code> denotes a set of objects whose fields the function body may
+depend on, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Post</code> is a boolean expression denoting the function’s
+postcondition, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Rank</code> is the function’s variant function, and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Body</code> is
+an expression that defines the function return value. The precondition
+allows a function to be partial, that is, the precondition says when the
+function is defined (and Dafny will verify that every use of the function
+meets the precondition). The postcondition is usually not needed, since
+the body of the function gives the full definition. However, the
+postcondition can be a convenient place to declare properties of the
+function that may require an inductive proof to establish. For example:
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="2_function" class="ntdef" style="color:olive">function</span></span> <span class="code-escaped"><a href="#1_factorial" class="ntref localref" style="color:maroon">Factorial</a></span>(n: <span class="code-escaped"><a href="#2_int" class="ntref localref" style="color:maroon">int</a></span>): <span class="code-escaped"><a href="#2_int" class="ntref localref" style="color:maroon">int</a></span>
+ <span class="code-escaped"><a href="#2_requires" class="ntref localref" style="color:maroon">requires</a></span> <span class="constant" style="color:purple">0</span> &lt;= n
+ <span class="code-escaped"><a href="#2_ensures" class="ntref localref" style="color:maroon">ensures</a></span> <span class="constant" style="color:purple">1</span> &lt;= <span class="code-escaped"><a href="#1_factorial" class="ntref localref" style="color:maroon">Factorial</a></span>(n)
+{
+ <span class="code-escaped"><a href="#2_if" class="ntref localref" style="color:maroon">if</a></span> n == <span class="constant" style="color:purple">0</span> <span class="code-escaped"><a href="#2_then" class="ntref localref" style="color:maroon">then</a></span> <span class="constant" style="color:purple">1</span> <span class="code-escaped"><a href="#2_else" class="ntref localref" style="color:maroon">else</a></span> <span class="code-escaped"><a href="#1_factorial" class="ntref localref" style="color:maroon">Factorial</a></span>(n-<span class="constant" style="color:purple">1</span>) * n
+}</code></pre>
+<p class="p noindent para-continued">says that the result of Factorial is always positive, which Dafny
+verifies inductively from the function body. To refer to the function’s
+result in the postcondition, use the function itself, as shown in the
+example.
+</p>
+<p class="p indent">By default, a function is <em class="em-star1">ghost</em>, and cannot be called from non-ghost
+code. To make it non-ghost, replace the keyword function with the two
+keywords &#8220;function method&#8221;.
+</p>
+<p class="p indent">By default, a function has an implicit receiver parameter, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">this</span></code>. This
+parameter can be removed by preceding the function declaration with the
+keyword <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">static</span></code>. A static function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">F</code> in a class <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code> can be invoked
+by <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C.F(…)</code>. This can give a convenient way to declare a number of helper
+functions in a separate class.
+</p>
+<p class="p indent">As for methods, a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_signatureellipsis_" class="ntref localref" style="color:maroon">SignatureEllipsis_</a></span></code> is used when declaring
+a function in a module refinement. For example, if module <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M0</code> declares
+function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">F</code>, a module <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M1</code> can be declared to refine <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M0</code> and
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M1</code> can then refine <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">F</code>. The refinement function, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M1.F</code> can have
+a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_signatureellipsis_" class="ntref localref" style="color:maroon">SignatureEllipsis_</a></span></code> which means to copy the signature form
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M0.F</code>. A refinement function can furnish a body for a function
+(if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M0.F</code> does not provide one). It can also add <strong class="strong-star2">ensures</strong>
+clauses. And if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">F</code> is a predicate, it can add conjuncts to
+a previously given body.
+</p><h4 id="sec-function-transparency" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">12.2.0</span>.&#8194;</span>Function Transparency</h4>
+<p class="p noindent">A function is said to be <em class="em-low1">transparent</em> in a location if the
+contents of the body of the function is visible at that point.
+A function is said to be <em class="em-low1">opaque</em> at a location if it is not
+transparent. However the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_functionspec" class="ntref localref" style="color:maroon">FunctionSpec</a></span></code> of a function
+is always available.
+</p>
+<p class="p indent">A function is usually transparent up to some unrolling level (up to
+1, or maybe 2 or 3). If its arguments are all literals it is
+transparent all the way.
+</p>
+<p class="p indent">But the transparency of a function is affected by the following:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">whether the function was declared to be protected, and
+</li>
+<li class="li ul-li list-star-li compact-li">whether the function was given the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:opaque</span>}</code> attribute (as explained
+in Section&nbsp;<a href="#sec-opaque" title="24.1.12.&#8194;opaque" class="localref" style="target-element:h3"><span class="heading-label">24.1.12</span></a>).
+</li></ul>
+
+<p class="p noindent">The following table summarizes where the function is transparent.
+The module referenced in the table is the module in which the
+function is defined.
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1" style="text-align:center"></th><th class="thead tr-even col cell-border-left cell-line col-even" data-row="0" data-col="2" style="text-align:center"></th><th class="thead tr-even col cell-border-left cell-line col-odd" data-row="0" data-col="3" style="text-align:center"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="4" style="text-align:center"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold;text-align:center"> Protected? </th><th class="col cell-border-left thead tr-odd tr-first col-even" data-row="1" data-col="2" style="font-weight:bold;text-align:center"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:opaque</span>}</code>? </th><th class="col cell-border-left thead tr-odd tr-first col-odd" data-row="1" data-col="3" style="font-weight:bold;text-align:center"> Transparent </th><th class="col cell-border-left cell-border-right thead tr-odd tr-first col-even col-last" data-row="1" data-col="4" style="font-weight:bold;text-align:center"> Transparent </th></tr>
+<tr><th class="col cell-border-left thead tr-even col-odd col-first" data-row="2" data-col="1" style="font-weight:bold;text-align:center"> </th><th class="col cell-border-left thead tr-even col-even" data-row="2" data-col="2" style="font-weight:bold;text-align:center"> </th><th class="col cell-border-left thead tr-even col-odd" data-row="2" data-col="3" style="font-weight:bold;text-align:center"> Inside </th><th class="col cell-border-left cell-border-right thead tr-even col-even col-last" data-row="2" data-col="4" style="font-weight:bold;text-align:center"> Outside </th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last col-odd col-first" data-row="3" data-col="1" style="font-weight:bold;text-align:center"> </th><th class="col cell-border-left thead tr-odd tr-last col-even" data-row="3" data-col="2" style="font-weight:bold;text-align:center"> </th><th class="col cell-border-left thead tr-odd tr-last col-odd" data-row="3" data-col="3" style="font-weight:bold;text-align:center"> Module </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last col-even col-last" data-row="3" data-col="4" style="font-weight:bold;text-align:center"> Module </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1" style="text-align:center"></td><td class="tbody tr-even col cell-border-left cell-line col-even" data-row="0" data-col="2" style="text-align:center"></td><td class="tbody tr-even col cell-border-left cell-line col-odd" data-row="0" data-col="3" style="text-align:center"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="4" style="text-align:center"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1" style="text-align:center"> N </td><td class="tbody tr-odd tr-first col cell-border-left col-even" data-row="1" data-col="2" style="text-align:center"> N </td><td class="tbody tr-odd tr-first col cell-border-left col-odd" data-row="1" data-col="3" style="text-align:center"> Y </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="4" style="text-align:center"> Y </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1" style="text-align:center"> Y </td><td class="tbody tr-even col cell-border-left col-even" data-row="2" data-col="2" style="text-align:center"> N </td><td class="tbody tr-even col cell-border-left col-odd" data-row="2" data-col="3" style="text-align:center"> Y </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="4" style="text-align:center"> N </td></tr>
+<tr><td class="tbody tr-odd tr-last col cell-border-left col-odd col-first" data-row="3" data-col="1" style="text-align:center"> N </td><td class="tbody tr-odd tr-last col cell-border-left col-even" data-row="3" data-col="2" style="text-align:center"> Y </td><td class="tbody tr-odd tr-last col cell-border-left col-odd" data-row="3" data-col="3" style="text-align:center"> N </td><td class="tbody tr-odd tr-last col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="4" style="text-align:center"> N </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left cell-line col-odd col-first" data-row="3" data-col="1" style="text-align:center"></td><td class="tbody tr-odd col cell-border-left cell-line col-even" data-row="3" data-col="2" style="text-align:center"></td><td class="tbody tr-odd col cell-border-left cell-line col-odd" data-row="3" data-col="3" style="text-align:center"></td><td class="tbody tr-odd col cell-border-left cell-border-right cell-line col-even col-last" data-row="3" data-col="4" style="text-align:center"></td></tr></tbody></table>
+<p class="p noindent">When <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:opaque</span>}</code> is specified for function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">g</code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">g</code> is opaque,
+however the lemma <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">reveal_g</code> is available to give the semantics
+of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">g</code> whether in the defining module or outside.
+</p>
+<p class="p indent">It currently is not allowed to have both <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">protected</span></code> and
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:opaque</span>}</code> specified for a function.
+</p><h4 id="sec-predicates" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">12.2.1</span>.&#8194;</span>Predicates</h4>
+<p class="p noindent">A function that returns a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">bool</span></code> results is called a <em class="em-low1">predicate</em>. As an
+alternative syntax, a predicate can be declared by replacing the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">function</span></code>
+keyword with the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">predicate</span></code> keyword and omitting a declaration of the
+return type.
+</p><h4 id="sec-inductive-predicates-and-lemmas" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">12.2.2</span>.&#8194;</span>Inductive Predicates and Lemmas</h4>
+<p class="p noindent">See section&nbsp;<a href="#sec-friendliness" title="11.1.2.&#8194;Extreme Predicates in Dafny" class="localref" style="target-element:h3"><span class="heading-label">11.1.2</span></a> for descriptions
+of inductive predicates and lemmas.
+</p><h2 id="sec-trait-types" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">13</span>.&#8194;</span>Trait Types</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_traitdecl" class="ntdef" style="color:olive">TraitDecl</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"trait"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span class="code-escaped"><a href="#1_traitname" class="ntref localref" style="color:maroon">TraitName</a></span> [ <span class="code-escaped"><a href="#1_genericparameters" class="ntref localref" style="color:maroon">GenericParameters</a></span> ]
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"{"</span></span> { { <span class="code-escaped"><a href="#1_declmodifier" class="ntref localref" style="color:maroon">DeclModifier</a></span> } <span class="code-escaped"><a href="#1_classmemberdecl" class="ntref localref" style="color:maroon">ClassMemberDecl</a></span>(moduleLevelDecl: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>) } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"}"</span></span> </code></pre>
+<p class="p noindent para-continued">A <em class="em-low1">trait</em> is an &#8220;abstract superclass&#8221;, or call it an &#8220;interface&#8221; or
+&#8220;mixin&#8221;. Traits are new to Dafny and are likely to evolve for a
+while.
+</p>
+<p class="p indent">The declaration of a trait is much like that of a class:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">trait</span> J
+{
+ <span class="code-escaped"><em class="em-low1">members</em></span>
+}</code></pre>
+<p class="p noindent para-continued">where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="code-escaped"><em class="em-low1">members</em></span></code> can include fields, functions, and methods, but
+no constructor methods. The functions and methods are allowed to be
+declared <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">static</span></code>.
+</p>
+<p class="p indent">A reference type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code> that extends a trait <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">J</code> is assignable to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">J</code>, but
+not the other way around. The members of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">J</code> are available as members
+of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code>. A member in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">J</code> is not allowed to be redeclared in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code>,
+except if the member is a non-<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">static</span></code> function or method without a
+body in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">J</code>. By doing so, type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code> can supply a stronger
+specification and a body for the member.
+</p>
+<p class="p indent"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">new</span></code> is not allowed to be used with traits. Therefore, there is no
+object whose allocated type is a trait. But there can of course be
+objects of a class <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code> that implements a trait <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">J</code>, and a reference to
+such a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code> object can be used as a value of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">J</code>.
+</p>
+<p class="p indent">As an example, the following trait represents movable geometric shapes:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">trait</span> Shape
+{
+ <span style="color:blue">function</span> <span style="color:blue">method</span> Width(): <span style="color:teal">real</span>
+ <span style="color:purple">reads</span> <span style="color:blue">this</span>
+ <span style="color:blue">method</span> Move(dx: <span style="color:teal">real</span>, dy: <span style="color:teal">real</span>)
+ <span style="color:purple">modifies</span> <span style="color:blue">this</span>
+ <span style="color:blue">method</span> MoveH(dx: <span style="color:teal">real</span>)
+ <span style="color:purple">modifies</span> <span style="color:blue">this</span>
+ {
+ Move(dx, <span class="constant" style="color:purple">0.0</span>);
+ }
+}</code></pre>
+<p class="p noindent para-continued">Members <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Width</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Move</code> are <em class="em-low1">abstract</em> (that is, body less) and can
+be implemented differently by different classes that extend the trait.
+The implementation of method <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MoveH</code> is given in the trait and thus
+gets used by all classes that extend <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Shape</code>. Here are two classes
+that each extends <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Shape</code>:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">class</span> UnitSquare <span style="color:blue">extends</span> Shape
+{
+ <span style="color:blue">var</span> x: <span style="color:teal">real</span>, y: <span style="color:teal">real</span>
+ <span style="color:blue">function</span> <span style="color:blue">method</span> Width(): <span style="color:teal">real</span> { <span style="color:darkgreen">// note the empty reads clause</span>
+ <span class="constant" style="color:purple">1.0</span>
+ }
+ <span style="color:blue">method</span> Move(dx: <span style="color:teal">real</span>, dy: <span style="color:teal">real</span>)
+ <span style="color:purple">modifies</span> <span style="color:blue">this</span>
+ {
+ x, y <span style="color:blue">:=</span> x + dx, y + dy;
+ }
+}
+<span style="color:blue">class</span> LowerRightTriangle <span style="color:blue">extends</span> Shape
+{
+ <span style="color:blue">var</span> xNW: <span style="color:teal">real</span>, yNW: <span style="color:teal">real</span>, xSE: <span style="color:teal">real</span>, ySE: <span style="color:teal">real</span>
+ <span style="color:blue">function</span> <span style="color:blue">method</span> Width(): <span style="color:teal">real</span>
+ <span style="color:purple">reads</span> <span style="color:blue">this</span>
+ {
+ xSE - xNW
+ }
+ <span style="color:blue">method</span> Move(dx: <span style="color:teal">real</span>, dy: <span style="color:teal">real</span>)
+ <span style="color:purple">modifies</span> <span style="color:blue">this</span>
+ {
+ xNW, yNW, xSE, ySE <span style="color:blue">:=</span> xNW + dx, yNW + dy, xSE + dx, ySE + dy;
+ }
+}</code></pre>
+<p class="p noindent para-continued">Note that the classes can declare additional members, that they supply
+implementations for the abstract members of the trait,
+that they repeat the member signatures, and that they are responsible
+for providing their own member specifications that both strengthen the
+corresponding specification in the trait and are satisfied by the
+provided body.
+Finally, here is some code that creates two class instances and uses
+them together as shapes:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> myShapes: <span style="color:teal">seq</span>&lt;Shape&gt;;
+<span style="color:blue">var</span> A <span style="color:blue">:=</span> <span style="color:blue">new</span> UnitSquare;
+myShapes <span style="color:blue">:=</span> [A];
+<span style="color:blue">var</span> tri <span style="color:blue">:=</span> <span style="color:blue">new</span> LowerRightTriangle;
+<span style="color:darkgreen">// myShapes contains two Shape values, of different classes</span>
+myShapes <span style="color:blue">:=</span> myShapes + [tri];
+<span style="color:darkgreen">// move shape 1 to the right by the width of shape 0</span>
+myShapes[<span class="constant" style="color:purple">1</span>].MoveH(myShapes[<span class="constant" style="color:purple">0</span>].Width());</code></pre><h2 id="sec-array-types" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">14</span>.&#8194;</span>Array Types</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_arraytype_" class="ntdef" style="color:olive">ArrayType_</span></span> = <span class="code-escaped"><a href="#2_arraytoken" class="ntref localref" style="color:maroon">arrayToken</a></span> [ <span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span> ] </code></pre>
+<p class="p noindent para-continued">Dafny supports mutable fixed-length <em class="em-low1">array types</em> of any positive
+dimension. Array types are reference types.
+</p><h3 id="sec-one-dimensional-arrays" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">14.0</span>.&#8194;</span>One-dimensional arrays</h3>
+<p class="p noindent">A one-dimensional array of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n</code> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> elements is created as follows:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>a <span style="color:blue">:=</span> <span style="color:blue">new</span> T[n];</code></pre>
+<p class="p noindent para-continued">The initial values of the array elements are arbitrary values of type
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>.
+The length of an array is retrieved using the immutable <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Length</code>
+member. For example, the array allocated above satisfies:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>a.Length == n</code></pre>
+<p class="p noindent para-continued">For any integer-based numeric <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">i</code> in the range <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0</span> &lt;= i &lt; a.Length</code>,
+the <em class="em-low1">array selection</em> expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a[i]</code> retrieves element <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">i</code> (that
+is, the element preceded by <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">i</code> elements in the array). The
+element stored at <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">i</code> can be changed to a value <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t</code> using the array
+update statement:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>a[i] <span style="color:blue">:=</span> t;</code></pre>
+<p class="p noindent para-continued">Caveat: The type of the array created by <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">new</span> T[n]</code> is
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array</span>&lt;T&gt;</code>. A mistake that is simple to make and that can lead to
+befuddlement is to write <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array</span>&lt;T&gt;</code> instead of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> after <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">new</span></code>.
+For example, consider the following:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> a <span style="color:blue">:=</span> <span style="color:blue">new</span> <span style="color:teal">array</span>&lt;T&gt;;
+<span style="color:blue">var</span> b <span style="color:blue">:=</span> <span style="color:blue">new</span> <span style="color:teal">array</span>&lt;T&gt;[n];
+<span style="color:blue">var</span> c <span style="color:blue">:=</span> <span style="color:blue">new</span> <span style="color:teal">array</span>&lt;T&gt;(n); <span style="color:darkgreen">// resolution error</span>
+<span style="color:blue">var</span> d <span style="color:blue">:=</span> <span style="color:blue">new</span> <span style="color:teal">array</span>(n); <span style="color:darkgreen">// resolution error</span></code></pre>
+<p class="p noindent para-continued">The first statement allocates an array of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array</span>&lt;T&gt;</code>, but of
+unknown length. The second allocates an array of type
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array</span>&lt;<span style="color:teal">array</span>&lt;T&gt;&gt;</code> of length <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n</code>, that is, an array that holds <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n</code>
+values of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array</span>&lt;T&gt;</code>. The third statement allocates an
+array of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array</span>&lt;T&gt;</code> and then attempts to invoke an anonymous
+constructor on this array, passing argument <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n</code>. Since <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array</span></code> has no
+constructors, let alone an anonymous constructor, this statement
+gives rise to an error. If the type-parameter list is omitted for a
+type that expects type parameters, Dafny will attempt to fill these
+in, so as long as the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array</span></code> type parameter can be inferred, it is
+okay to leave off the &#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;T&gt;</code>&#8221; in the fourth statement above. However,
+as with the third statement, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array</span></code> has no anonymous constructor, so
+an error message is generated.
+</p>
+<p class="p indent">One-dimensional arrays support operations that convert a stretch of
+consecutive elements into a sequence. For any array <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a</code> of type
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array</span>&lt;T&gt;</code>, integer-based numerics <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">lo</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi</code> satisfying
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0</span> &lt;= lo &lt;= hi &lt;= a.Length</code>, the following operations each yields a
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">seq</span>&lt;T&gt;</code>:
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> expression </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a[lo..hi]</code> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> subarray conversion to sequence </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a[lo..]</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> drop </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a[..hi]</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> take </td></tr>
+<tr><td class="tbody tr-even tr-last col cell-border-left col-odd col-first" data-row="4" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a[..]</code> </td><td class="tbody tr-even tr-last col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> array conversion to sequence </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="4" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="4" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">The expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a[lo..hi]</code> takes the first <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi</code> elements of the array,
+then drops the first <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">lo</code> elements thereof and returns what remains as
+a sequence. The resulting sequence thus has length <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi - lo</code>.
+The other operations are special instances of the first. If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">lo</code> is
+omitted, it defaults to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0</span></code> and if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi</code> is omitted, it defaults to
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a.Length</code>.
+In the last operation, both <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">lo</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi</code> have been omitted, thus
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a[..]</code> returns the sequence consisting of all the array elements of
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a</code>.
+</p>
+<p class="p indent">The subarray operations are especially useful in specifications. For
+example, the loop invariant of a binary search algorithm that uses
+variables <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">lo</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi</code> to delimit the subarray where the search <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">key</code>
+may be still found can be expressed as follows:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>key !<span style="color:blue">in</span> a[..lo] &amp;&amp; key !<span style="color:blue">in</span> a[hi..]</code></pre>
+<p class="p noindent para-continued">Another use is to say that a certain range of array elements have not
+been changed since the beginning of a method:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>a[lo..hi] == <span style="color:blue">old</span>(a[lo..hi])</code></pre>
+<p class="p noindent para-continued">or since the beginning of a loop:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">ghost</span> <span style="color:blue">var</span> prevElements <span style="color:blue">:=</span> a[..];
+<span style="color:blue">while</span> <span style="color:darkgreen">// ...</span>
+ <span style="color:purple">invariant</span> a[lo..hi] == prevElements[lo..hi]
+{
+ <span style="color:darkgreen">// ...</span>
+}</code></pre>
+<p class="p noindent para-continued">Note that the type of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">prevElements</code> in this example is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">seq</span>&lt;T&gt;</code>, if
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a</code> has type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array</span>&lt;T&gt;</code>.
+</p>
+<p class="p indent">A final example of the subarray operation lies in expressing that an
+array&#39;s elements are a permutation of the array&#39;s elements at the
+beginning of a method, as would be done in most sorting algorithms.
+Here, the subarray operation is combined with the sequence-to-multiset
+conversion:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:teal">multiset</span>(a[..]) == <span style="color:teal">multiset</span>(<span style="color:blue">old</span>(a[..]))</code></pre><h3 id="sec-multi-dimensional-arrays" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">14.1</span>.&#8194;</span>Multi-dimensional arrays</h3>
+<p class="p noindent">An array of 2 or more dimensions is mostly like a one-dimensional
+array, except that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">new</span></code> takes more length arguments (one for each
+dimension), and the array selection expression and the array update
+statement take more indices. For example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>matrix <span style="color:blue">:=</span> <span style="color:blue">new</span> T[m, n];
+matrix[i, j], matrix[x, y] <span style="color:blue">:=</span> matrix[x, y], matrix[i, j];</code></pre>
+<p class="p noindent para-continued">create a 2-dimensional array whose dimensions have lengths <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m</code> and
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n</code>, respectively, and then swaps the elements at <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">i,j</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x,y</code>.
+The type of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">matrix</code> is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array2</span>&lt;T&gt;</code>, and similarly for
+higher-dimensional arrays (<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array3</span>&lt;T&gt;</code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array4</span>&lt;T&gt;</code>, etc.). Note,
+however, that there is no type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">array0&lt;T&gt;</code>, and what could have been
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">array1&lt;T&gt;</code> is actually named just <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">array</span>&lt;T&gt;</code>.
+</p>
+<p class="p indent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">new</span></code> operation above requires <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n</code> to be non-negative
+integer-based numerics. These lengths can be retrieved using the
+immutable fields <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Length0</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Length1</code>. For example, the following
+holds of the array created above:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>matrix.Length0 == m &amp;&amp; matrix.Length1 == n</code></pre>
+<p class="p noindent para-continued">Higher-dimensional arrays are similar (<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Length0</code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Length1</code>,
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Length2</code>, &#8230;). The array selection expression and array update
+statement require that the indices are in bounds. For example, the
+swap statement above is well-formed only if:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="constant" style="color:purple">0</span> &lt;= i &lt; matrix.Length0 &amp;&amp; <span class="constant" style="color:purple">0</span> &lt;= j &lt; matrix.Length1 &amp;&amp;
+<span class="constant" style="color:purple">0</span> &lt;= x &lt; matrix.Length0 &amp;&amp; <span class="constant" style="color:purple">0</span> &lt;= y &lt; matrix.Length1</code></pre>
+<p class="p noindent para-continued">In contrast to one-dimensional arrays, there is no operation to
+convert stretches of elements from a multi-dimensional array to a
+sequence.
+</p><h2 id="sec-type-object" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">15</span>.&#8194;</span>Type object</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_objecttype_" class="ntdef" style="color:olive">ObjectType_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"object"</span></span></code></pre>
+<p class="p noindent para-continued">There is a built-in trait <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">object</span></code> that is like a supertype of all
+reference types.<sup id="back-fn-fn-object-trait" ><a href="#fn-fn-object-trait" title="6.The current compiler restriction that object cannot
+be used as a type parameter needs to be removed.
+&#8617;" class="footnote-ref localref" ><span class="footnote-label">6</span></a></sup> Every class automatically extends
+object and so does every user-defined trait. The purpose of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">object</span></code>
+is to enable a uniform treatment of <em class="em-low1">dynamic frames</em>. In particular, it
+is useful to keep a ghost field (typically named <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Repr</code> for
+&#8220;representation&#8221;) of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">set</span>&lt;<span style="color:teal">object</span>&gt;</code>.
+</p><h2 id="sec-iterator-types" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">16</span>.&#8194;</span>Iterator types</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_iteratordecl" class="ntdef" style="color:olive">IteratorDecl</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"iterator"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span class="code-escaped"><a href="#1_iteratorname" class="ntref localref" style="color:maroon">IteratorName</a></span>
+ ( [ <span class="code-escaped"><a href="#1_genericparameters" class="ntref localref" style="color:maroon">GenericParameters</a></span> ]
+ <span class="code-escaped"><a href="#1_formals" class="ntref localref" style="color:maroon">Formals</a></span>(allowGhostKeyword: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"yields"</span></span> <span class="code-escaped"><a href="#1_formals" class="ntref localref" style="color:maroon">Formals</a></span>(allowGhostKeyword: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) ]
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&#8230;"</span></span>
+ )
+ <span class="code-escaped"><a href="#1_iteratorspec" class="ntref localref" style="color:maroon">IteratorSpec</a></span> [ <span class="code-escaped"><a href="#1_blockstmt" class="ntref localref" style="color:maroon">BlockStmt</a></span> ] </code></pre>
+<p class="p noindent para-continued">See section&nbsp;<a href="#sec-iterator-specification" title="4.4.&#8194;Iterator Specification" class="localref" style="target-element:h2"><span class="heading-label">4.4</span></a> for a description of <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_iteratorspec" class="ntref localref" style="color:maroon">IteratorSpec</a></span></code>.
+</p>
+<p class="p indent">An <em class="em-low1">iterator</em> provides a programming abstraction for writing code that
+iteratively returns elements. These CLU-style iterators are
+<em class="em-low1">co-routines</em> in the sense that they keep track of their own program
+counter and control can be transferred into and out of the iterator
+body.
+</p>
+<p class="p indent">An iterator is declared as follows:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">iterator</span> Iter&lt;T&gt;(<span class="code-escaped"><em class="em-low1">in-params</em></span>) <span style="color:blue">yields</span> (<span class="code-escaped"><em class="em-low1">yield-params</em></span>)
+ <span class="code-escaped"><em class="em-low1">specification</em></span>
+{
+ <span class="code-escaped"><em class="em-low1">body</em></span>
+}</code></pre>
+<p class="p noindent para-continued">where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> is a list of type parameters (as usual, if there are no type
+parameters, &#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;T&gt;</code>&#8221; is omitted). This declaration gives rise to a
+reference type with the same name, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Iter&lt;T&gt;</code>. In the signature,
+in-parameters and yield-parameters are the iterator&#39;s analog of a
+method&#39;s in-parameters and out-parameters. The difference is that the
+out-parameters of a method are returned to a caller just once, whereas
+the yield-parameters of an iterator are returned each time the iterator
+body performs a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">yield</span></code>. The body consists of statements, like in a
+method body, but with the availability also of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">yield</span></code> statements.
+</p>
+<p class="p indent">From the perspective of an iterator client, the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">iterator</span></code> declaration
+can be understood as generating a class <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Iter&lt;T&gt;</code> with various
+members, a simplified version of which is described next.
+</p>
+<p class="p indent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Iter&lt;T&gt;</code> class contains an anonymous constructor whose parameters
+are the iterator&#39;s in-parameters:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">predicate</span> Valid()
+<span style="color:blue">constructor</span> (<span class="code-escaped"><em class="em-low1">in-params</em></span>)
+ <span style="color:purple">modifies</span> <span style="color:blue">this</span>
+ <span style="color:purple">ensures</span> Valid()</code></pre>
+<p class="p noindent para-continued">An iterator is created using <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">new</span></code> and this anonymous constructor.
+For example, an iterator willing to return ten consecutive integers
+from <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">start</code> can be declared as follows:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">iterator</span> Gen(start: <span style="color:teal">int</span>) <span style="color:blue">yields</span> (x: <span style="color:teal">int</span>)
+{
+ <span style="color:blue">var</span> i <span style="color:blue">:=</span> <span class="constant" style="color:purple">0</span>;
+ <span style="color:blue">while</span> i &lt; <span class="constant" style="color:purple">10</span> {
+ x <span style="color:blue">:=</span> start + i;
+ <span style="color:blue">yield</span>;
+ i <span style="color:blue">:=</span> i + <span class="constant" style="color:purple">1</span>;
+ }
+}</code></pre>
+<p class="p noindent para-continued">An instance of this iterator is created using:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>iter <span style="color:blue">:=</span> <span style="color:blue">new</span> Gen(<span class="constant" style="color:purple">30</span>);</code></pre>
+<p class="p noindent para-continued">The predicate <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Valid()</code> says when the iterator is in a state where one
+can attempt to compute more elements. It is a postcondition of the
+constructor and occurs in the specification of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MoveNext</code> member:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">method</span> MoveNext() <span style="color:blue">returns</span> (more: <span style="color:teal">bool</span>)
+ <span style="color:purple">requires</span> Valid()
+ <span style="color:purple">modifies</span> <span style="color:blue">this</span>
+ <span style="color:purple">ensures</span> more ==&gt; Valid()</code></pre>
+<p class="p noindent para-continued">Note that the iterator remains valid as long as <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MoveNext</code> returns
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code>. Once <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MoveNext</code> returns <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">false</span></code>, the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MoveNext</code> method can no
+longer be called. Note, the client is under no obligation to keep
+calling <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MoveNext</code> until it returns <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">false</span></code>, and the body of the
+iterator is allowed to keep returning elements forever.
+</p>
+<p class="p indent">The in-parameters of the iterator are stored in immutable fields of
+the iterator class. To illustrate in terms of the example above, the
+iterator class <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Gen</code> contains the following field:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> start: <span style="color:teal">int</span></code></pre>
+<p class="p noindent para-continued">The yield-parameters also result in members of the iterator class:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> x: <span style="color:teal">int</span></code></pre>
+<p class="p noindent para-continued">These fields are set by the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MoveNext</code> method. If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MoveNext</code> returns
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code>, the latest yield values are available in these fields and the
+client can read them from there.
+</p>
+<p class="p indent">To aid in writing specifications, the iterator class also contains
+ghost members that keep the history of values returned by
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MoveNext</code>. The names of these ghost fields follow the names of the
+yield-parameters with an &#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code>&#8221; appended to the name (to suggest
+plural). Name checking rules make sure these names do not give rise
+to ambiguities. The iterator class for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Gen</code> above thus contains:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">ghost</span> <span style="color:blue">var</span> xs: <span style="color:teal">seq</span>&lt;<span style="color:teal">int</span>&gt;</code></pre>
+<p class="p noindent para-continued">These history fields are changed automatically by <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">MoveNext</code>, but are
+not assignable by user code.
+</p>
+<p class="p indent">Finally, the iterator class contains some special fields for use in
+specifications. In particular, the iterator specification gets
+recorded in the following immutable fields:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">ghost</span> <span style="color:blue">var</span> _reads: <span style="color:teal">set</span>&lt;<span style="color:teal">object</span>&gt;
+<span style="color:blue">ghost</span> <span style="color:blue">var</span> _modifies: <span style="color:teal">set</span>&lt;<span style="color:teal">object</span>&gt;
+<span style="color:blue">ghost</span> <span style="color:blue">var</span> _decreases0: T0
+<span style="color:blue">ghost</span> <span style="color:blue">var</span> _decreases1: T1
+<span style="color:darkgreen">// ...</span></code></pre>
+<p class="p noindent para-continued">where there is a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_decreases<span class="code-escaped"><em class="em-low1">i</em></span>: T<span class="code-escaped"><em class="em-low1">i</em></span></code> field for each
+component of the iterator&#39;s <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">decreases</span></code>
+clause.<sup id="back-fn-fn-iterator-field-names" ><a href="#fn-fn-iterator-field-names" title="7.It would make sense to rename the special
+fields _reads and _modifies to have the same names as the
+corresponding keywords, reads and modifies, as is done for
+function values. Also, the various _decreasesi fields can
+combined into one field named decreases whose type is a
+n-tuple. Thse changes may be incorporated into a future version
+of Dafny.
+
+&#8230;" class="footnote-ref localref" ><span class="footnote-label">7</span></a></sup>
+In addition, there is a field:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">ghost</span> <span style="color:blue">var</span> _new: <span style="color:teal">set</span>&lt;<span style="color:teal">object</span>&gt;;</code></pre>
+<p class="p noindent para-continued">to which any objects allocated on behalf of the iterator body get
+added. The iterator body is allowed to remove elements from the
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_new</code> set, but cannot by assignment to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_new</code> add any elements.
+</p>
+<p class="p indent">Note, in the precondition of the iterator, which is to hold upon
+construction of the iterator, the in-parameters are indeed
+in-parameters, not fields of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">this</span></code>.
+</p>
+<p class="p indent">It&#39;s regrettably tricky to use iterators. The language really
+ought to have a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">foreach</code> statement to make this easier.
+Here is an example showing definition and use of an iterator.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">iterator</span> Iter&lt;T&gt;(s: <span style="color:teal">set</span>&lt;T&gt;) <span style="color:blue">yields</span> (x: T)
+ <span style="color:blue">yield</span> <span style="color:purple">ensures</span> x <span style="color:blue">in</span> s &amp;&amp; x !<span style="color:blue">in</span> xs[..|xs|-<span class="constant" style="color:purple">1</span>];
+ <span style="color:purple">ensures</span> s == <span style="color:teal">set</span> z | z <span style="color:blue">in</span> xs;
+{
+ <span style="color:blue">var</span> r <span style="color:blue">:=</span> s;
+ <span style="color:blue">while</span> (r != {})
+ <span style="color:purple">invariant</span> <span style="color:blue">forall</span> z :: z <span style="color:blue">in</span> xs ==&gt; x !<span style="color:blue">in</span> r; <span style="color:darkgreen">// r and xs are disjoint</span>
+ <span style="color:purple">invariant</span> s == r + <span style="color:teal">set</span> z | z <span style="color:blue">in</span> xs;
+ {
+ <span style="color:blue">var</span> y <span style="color:blue">:|</span> y <span style="color:blue">in</span> r;
+ r, x <span style="color:blue">:=</span> r - {y}, y;
+ <span style="color:blue">yield</span>;
+ <span style="color:blue">assert</span> y == xs[|xs|-<span class="constant" style="color:purple">1</span>]; <span style="color:darkgreen">// needed as a lemma to prove loop invariant</span>
+ }
+}
+
+<span style="color:blue">method</span> UseIterToCopy&lt;T&gt;(s: <span style="color:teal">set</span>&lt;T&gt;) <span style="color:blue">returns</span> (t: <span style="color:teal">set</span>&lt;T&gt;)
+ <span style="color:purple">ensures</span> s == t;
+{
+ t <span style="color:blue">:=</span> {};
+ <span style="color:blue">var</span> m <span style="color:blue">:=</span> <span style="color:blue">new</span> Iter(s);
+ <span style="color:blue">while</span> (<span style="color:blue">true</span>)
+ <span style="color:purple">invariant</span> m.Valid() &amp;&amp; <span style="color:blue">fresh</span>(m._new);
+ <span style="color:purple">invariant</span> t == <span style="color:teal">set</span> z | z <span style="color:blue">in</span> m.xs;
+ <span style="color:purple">decreases</span> s - t;
+ {
+ <span style="color:blue">var</span> more <span style="color:blue">:=</span> m.MoveNext();
+ <span style="color:blue">if</span> (!more) { <span style="color:blue">break</span>; }
+ t <span style="color:blue">:=</span> t + {m.x};
+ }
+}</code></pre><!--
+# Async-task types
+
+Another experimental feature in Dafny that is likely to undergo some
+evolution is _asynchronous methods_. When an asynchronous method is
+called, it does not return values for the out-parameters, but instead
+returns an instance of an _async-task type_. An asynchronous method
+declared in a class `C` with the following signature:
+```
+async method AM<T>(\(_in-params_\)) returns (\(_out-params_\))
+```
+also gives rise to an async-task type `AM<T>` (outside the enclosing
+class, the name of the type needs the qualification `C.AM<T>`). The
+async-task type is a reference type and can be understood as a class
+with various members, a simplified version of which is described next.
+
+Each in-parameter `x` of type `X` of the asynchronous method gives
+rise to a immutable ghost field of the async-task type:
+```
+ghost var x: X;
+```
+Each out-parameter `y` of type `Y` gives rise to a field
+```
+var y: Y;
+```
+These fields are changed automatically by the time the asynchronous
+method is successfully awaited, but are not assignable by user code.
+
+The async-task type also gets a number of special fields that are used
+to keep track of dependencies, outstanding tasks, newly allocated
+objects, etc. These fields will be described in more detail as the
+design of asynchronous methods evolves.
+
+-->
+
+
+<h2 id="sec-function-types" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">17</span>.&#8194;</span>Function types</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_type" class="ntdef" style="color:olive">Type</span></span> = <span class="code-escaped"><a href="#1_domaintype" class="ntref localref" style="color:maroon">DomainType</a></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"-&gt;"</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span> </code></pre>
+<p class="p noindent para-continued">Functions are first-class values in Dafny. Function types have the form
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(T) -&gt; U</code> where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> is a comma-delimited list of types and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">U</code> is a
+type. <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> is called the function&#39;s <em class="em-low1">domain type(s)</em> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">U</code> is its
+<em class="em-low1">range type</em>. For example, the type of a function
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">function</span> F(x: <span style="color:teal">int</span>, b: <span style="color:teal">bool</span>): <span style="color:teal">real</span></code></pre>
+<p class="p noindent para-continued">is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(<span style="color:teal">int</span>, <span style="color:teal">bool</span>) -&gt; <span style="color:teal">real</span></code>. Parameters are not allowed to be ghost.
+</p>
+<p class="p indent">To simplify the appearance of the basic case where a function&#39;s
+domain consist of a list of exactly one type, the parentheses around
+the domain type can be dropped in this case, as in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T -&gt; U</code>.
+This innocent simplification requires additional explanation in the
+case where that one type is a tuple type, since tuple types are also
+written with enclosing parentheses.
+If the function takes a single argument that is a tuple, an additional
+set of parentheses is needed. For example, the function
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">function</span> G(pair: (<span style="color:teal">int</span>, <span style="color:teal">bool</span>)): <span style="color:teal">real</span></code></pre>
+<p class="p noindent para-continued">has type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">((<span style="color:teal">int</span>, <span style="color:teal">bool</span>)) -&gt; <span style="color:teal">real</span></code>. Note the necessary double
+parentheses. Similarly, a function that takes no arguments is
+different from one that takes a 0-tuple as an argument. For instance,
+the functions
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">function</span> NoArgs(): <span style="color:teal">real</span>
+<span style="color:blue">function</span> Z(unit: ()): <span style="color:teal">real</span></code></pre>
+<p class="p noindent para-continued">have types <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">() -&gt; <span style="color:teal">real</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(()) -&gt; <span style="color:teal">real</span></code>, respectively.
+</p>
+<p class="p indent">The function arrow, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">-&gt;</code>, is right associative, so <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A -&gt; B -&gt; C</code> means
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">A -&gt; (B -&gt; C)</code>. The other association requires explicit parentheses:
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(A -&gt; B) -&gt; C</code>.
+</p>
+<p class="p indent">Note that the receiver parameter of a named function is not part of
+the type. Rather, it is used when looking up the function and can
+then be thought of as being captured into the function definition.
+For example, suppose function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">F</code> above is declared in a class <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code> and
+that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">c</code> references an object of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code>; then, the following is type
+correct:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> f: (<span style="color:teal">int</span>, <span style="color:teal">bool</span>) -&gt; <span style="color:teal">real</span> <span style="color:blue">:=</span> c.F;</code></pre>
+<p class="p noindent para-continued">whereas it would have been incorrect to have written something like:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> f&#39;: (C, <span style="color:teal">int</span>, <span style="color:teal">bool</span>) -&gt; <span style="color:teal">real</span> <span style="color:blue">:=</span> F; <span style="color:darkgreen">// not correct</span></code></pre>
+<p class="p noindent para-continued">Outside its type signature, each function value has three properties,
+described next.
+</p>
+<p class="p indent">Every function implicitly takes the heap as an argument. No function
+ever depends on the <em class="em-low1">entire</em> heap, however. A property of the
+function is its declared upper bound on the set of heap locations it
+depends on for a given input. This lets the verifier figure out that
+certain heap modifications have no effect on the value returned by a
+certain function. For a function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f: T -&gt; U</code> and a value <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t</code> of type
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>, the dependency set is denoted <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f.<span style="color:purple">reads</span>(t)</code> and has type
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">set</span>&lt;<span style="color:teal">object</span>&gt;</code>.
+</p>
+<p class="p indent">The second property of functions stems from the fact that every function
+is potentially <em class="em-low1">partial</em>. In other words, a property of a function is its
+<em class="em-low1">precondition</em>. For a function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f: T -&gt; U</code>, the precondition of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f</code> for a
+parameter value <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> is denoted <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f.<span style="color:purple">requires</span>(t)</code> and has type
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">bool</span></code>.
+</p>
+<p class="p indent">The third property of a function is more obvious&#8212;the function&#39;s
+body. For a function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f: T -&gt; U</code>, the value that the function yields
+for an input <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code> is denoted <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f(t)</code> and has type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">U</code>.
+</p>
+<p class="p indent">Note that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f.<span style="color:purple">reads</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f.<span style="color:purple">requires</span></code> are themselves functions.
+Suppose <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f</code> has type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T -&gt; U</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t</code> has type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>. Then, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f.<span style="color:purple">reads</span></code>
+is a function of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T -&gt; <span style="color:teal">set</span>&lt;<span style="color:teal">object</span>&gt;</code> whose <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">reads</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">requires</span></code>
+properties are:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>f.<span style="color:purple">reads</span>.<span style="color:purple">reads</span>(t) == f.<span style="color:purple">reads</span>(t)
+f.<span style="color:purple">reads</span>.<span style="color:purple">requires</span>(t) == <span style="color:blue">true</span></code></pre>
+<p class="p noindent para-continued"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f.<span style="color:purple">requires</span></code> is a function of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T -&gt; <span style="color:teal">bool</span></code> whose <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">reads</span></code> and
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">requires</span></code> properties are:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>f.<span style="color:purple">requires</span>.<span style="color:purple">reads</span>(t) == f.<span style="color:purple">reads</span>(t)
+f.<span style="color:purple">requires</span>.<span style="color:purple">requires</span>(t) == <span style="color:blue">true</span></code></pre>
+<p class="p noindent para-continued">Dafny also support anonymous functions by means of
+<em class="em-low1">lambda expressions</em>. See section&nbsp;<a href="#sec-lambda-expressions" title="22.9.&#8194;Lambda expressions" class="localref" style="target-element:h2"><span class="heading-label">22.9</span></a>.
+</p><h2 id="sec-algebraic-datatypes" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">18</span>.&#8194;</span>Algebraic Datatypes</h2>
+<p class="p noindent">Dafny offers two kinds of algebraic datatypes, those defined
+inductively and those defined co-inductively. The salient property of
+every datatype is that each value of the type uniquely identifies one
+of the datatype&#39;s constructors and each constructor is injective in
+its parameters.
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_datatypedecl" class="ntdef" style="color:olive">DatatypeDecl</span></span> = ( <span class="code-escaped"><a href="#1_inductivedatatypedecl" class="ntref localref" style="color:maroon">InductiveDatatypeDecl</a></span> | <span class="code-escaped"><a href="#1_coinductivedatatypedecl" class="ntref localref" style="color:maroon">CoinductiveDatatypeDecl</a></span> ) </code></pre><h3 id="sec-inductive-datatypes" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">18.0</span>.&#8194;</span>Inductive datatypes</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_inductivedatatypedecl_" class="ntdef" style="color:olive">InductiveDatatypeDecl_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"datatype"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span class="code-escaped"><a href="#1_datatypename" class="ntref localref" style="color:maroon">DatatypeName</a></span> [ <span class="code-escaped"><a href="#1_genericparameters" class="ntref localref" style="color:maroon">GenericParameters</a></span> ]
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"="</span></span> <span class="code-escaped"><a href="#1_datatypememberdecl" class="ntref localref" style="color:maroon">DatatypeMemberDecl</a></span> { <span style="color:maroon">&quot;</span><span style="color:maroon">|</span><span style="color:maroon">&quot;</span> <span class="code-escaped"><a href="#1_datatypememberdecl" class="ntref localref" style="color:maroon">DatatypeMemberDecl</a></span> } [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span> ]
+<span class="code-escaped"><span id="1_datatypememberdecl" class="ntdef" style="color:olive">DatatypeMemberDecl</span></span> = { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span class="code-escaped"><a href="#1_datatypemembername" class="ntref localref" style="color:maroon">DatatypeMemberName</a></span> [ <span class="code-escaped"><a href="#1_formalsoptionalids" class="ntref localref" style="color:maroon">FormalsOptionalIds</a></span> ] </code></pre>
+<p class="p noindent para-continued">The values of inductive datatypes can be seen as finite trees where
+the leaves are values of basic types, numeric types, reference types,
+co-inductive datatypes, or function types. Indeed, values of
+inductive datatypes can be compared using Dafny&#39;s well-founded
+<span class="monospace">&lt;</span> ordering.
+</p>
+<p class="p indent">An inductive datatype is declared as follows:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">datatype</span> D&lt;T&gt; = <span class="code-escaped"><em class="em-low1">Ctors</em></span></code></pre>
+<p class="p noindent para-continued">where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="code-escaped"><em class="em-low1">Ctors</em></span></code> is a nonempty <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">|</code>-separated list of
+<em class="em-low1">(datatype) constructors</em> for the datatype. Each constructor has the
+form:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>C(<span class="code-escaped"><em class="em-low1">params</em></span>)</code></pre>
+<p class="p noindent para-continued">where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="code-escaped"><em class="em-low1">params</em></span></code> is a comma-delimited list of types, optionally
+preceded by a name for the parameter and a colon, and optionally
+preceded by the keyword <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">ghost</span></code>. If a constructor has no parameters,
+the parentheses after the constructor name can be omitted. If no
+constructor takes a parameter, the type is usually called an
+<em class="em-low1">enumeration</em>; for example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">datatype</span> Friends = Agnes | Agatha | Jermaine | Jack</code></pre>
+<p class="p noindent para-continued">For every constructor <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code>, Dafny defines a <em class="em-low1">discriminator</em> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C?</code>, which
+is a member that returns <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code> if and only if the datatype value has
+been constructed using <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code>. For every named parameter <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">p</code> of a
+constructor <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code>, Dafny defines a <em class="em-low1">destructor</em> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">p</code>, which is a member
+that returns the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">p</code> parameter from the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code> call used to construct the
+datatype value; its use requires that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C?</code> holds. For example, for
+the standard <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">List</code> type
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">datatype</span> List&lt;T&gt; = Nil | Cons(head: T, tail: List&lt;T&gt;)</code></pre>
+<p class="p noindent para-continued">the following holds:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>Cons(<span class="constant" style="color:purple">5</span>, Nil).Cons? &amp;&amp; Cons(<span class="constant" style="color:purple">5</span>, Nil).head == <span class="constant" style="color:purple">5</span></code></pre>
+<p class="p noindent para-continued">Note that the expression
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>Cons(<span class="constant" style="color:purple">5</span>, Nil).tail.head</code></pre>
+<p class="p noindent para-continued">is not well-formed, since <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Cons(<span class="constant" style="color:purple">5</span>, Nil).tail</code> does not satisfy
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Cons?</code>.
+</p>
+<p class="p indent">The names of the destructors must be unique across all the
+constructors of the datatype. A constructor can have the same name as
+the enclosing datatype; this is especially useful for
+single-constructor datatypes, which are often called
+<em class="em-low1">record types</em>. For example, a record type for black-and-white pixels
+might be represented as follows:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">datatype</span> Pixel = Pixel(x: <span style="color:teal">int</span>, y: <span style="color:teal">int</span>, on: <span style="color:teal">bool</span>)</code></pre>
+<p class="p noindent para-continued">To call a constructor, it is usually necessary only to mention the
+name of the constructor, but if this is ambiguous, it is always
+possible to qualify the name of constructor by the name of the
+datatype. For example, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Cons(<span class="constant" style="color:purple">5</span>, Nil)</code> above can be written
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>List.Cons(<span class="constant" style="color:purple">5</span>, List.Nil)</code></pre>
+<p class="p noindent para-continued">As an alternative to calling a datatype constructor explicitly, a
+datatype value can be constructed as a change in one parameter from a
+given datatype value using the <em class="em-low1">datatype update</em> expression. For any
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">d</code> whose type is a datatype that includes a constructor <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code> that has
+a parameter (destructor) named <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>, and any expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t</code>
+of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>d[f <span style="color:blue">:=</span> t]</code></pre>
+<p class="p noindent para-continued">constructs a value like <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">d</code> but whose <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f</code> parameter is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t</code>. The
+operation requires that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">d</code> satisfies <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C?</code>. For example, the
+following equality holds:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>Cons(<span class="constant" style="color:purple">4</span>, Nil)[tail <span style="color:blue">:=</span> Cons(<span class="constant" style="color:purple">3</span>, Nil)] == Cons(<span class="constant" style="color:purple">4</span>, Cons(<span class="constant" style="color:purple">3</span>, Nil))</code></pre>
+<p class="p noindent para-continued">The datatype update expression also accepts multiple field
+names, provided these are distinct. For example, a node of some
+inductive datatype for trees may be updated as follows:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>node[left <span style="color:blue">:=</span> L, right <span style="color:blue">:=</span> R]</code></pre><h3 id="sec-tuple-types" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">18.1</span>.&#8194;</span>Tuple types</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_tupletype_" class="ntdef" style="color:olive">TupleType_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> [ <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span> } ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> </code></pre>
+<p class="p noindent para-continued">Dafny builds in record types that correspond to tuples and gives these
+a convenient special syntax, namely parentheses. For example, what
+might have been declared as:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">datatype</span> Pair&lt;T,U&gt; = Pair(<span class="constant" style="color:purple">0</span>: T, <span class="constant" style="color:purple">1</span>: U)</code></pre>
+<p class="p noindent para-continued">Dafny provides as the type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(T, U)</code> and the constructor <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(t, u)</code>, as
+if the datatype&#39;s name were &#8220;&#8221; and its type arguments are given in
+round parentheses, and as if the constructor name were &#8220;&#8221;. Note that
+the destructor names are <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">1</span></code>, which are legal identifier names
+for members. For example, showing the use of a tuple destructor, here
+is a property that holds of 2-tuples (that is, <em class="em-low1">pairs</em>):
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>(<span class="constant" style="color:purple">5</span>, <span style="color:blue">true</span>).<span class="constant" style="color:purple">1</span> == <span style="color:blue">true</span></code></pre>
+<p class="p noindent para-continued">Dafny declares <em class="em-low1">n</em>-tuples where <em class="em-low1">n</em> is 0 or 2 or up. There are no
+1-tuples, since parentheses around a single type or a single value have
+no semantic meaning. The 0-tuple type, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">()</code>, is often known as the
+<em class="em-low1">unit type</em> and its single value, also written <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">()</code>, is known as <em class="em-low1">unit</em>.
+</p><h3 id="sec-co-inductive-datatypes" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">18.2</span>.&#8194;</span>Co-inductive datatypes</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_coinductivedatatypedecl_" class="ntdef" style="color:olive">CoinductiveDatatypeDecl_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"codatatype"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span class="code-escaped"><a href="#1_datatypename" class="ntref localref" style="color:maroon">DatatypeName</a></span> [ <span class="code-escaped"><a href="#1_genericparameters" class="ntref localref" style="color:maroon">GenericParameters</a></span> ]
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"="</span></span> <span class="code-escaped"><a href="#1_datatypememberdecl" class="ntref localref" style="color:maroon">DatatypeMemberDecl</a></span> { <span style="color:maroon">&quot;</span><span style="color:maroon">|</span><span style="color:maroon">&quot;</span> <span class="code-escaped"><a href="#1_datatypememberdecl" class="ntref localref" style="color:maroon">DatatypeMemberDecl</a></span> } [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span> ] </code></pre>
+<p class="p noindent para-continued">Whereas Dafny insists that there is a way to construct every inductive
+datatype value from the ground up, Dafny also supports
+<em class="em-low1">co-inductive datatypes</em>, whose constructors are evaluated lazily and
+hence allows infinite structures. A co-inductive datatype is declared
+using the keyword <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">codatatype</span></code>; other than that, it is declared and
+used like an inductive datatype.
+</p>
+<p class="p indent">For example,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">codatatype</span> IList&lt;T&gt; = Nil | Cons(head: T, tail: IList&lt;T&gt;)
+<span style="color:blue">codatatype</span> Stream&lt;T&gt; = More(head: T, tail: Stream&lt;T&gt;)
+<span style="color:blue">codatatype</span> Tree&lt;T&gt; = Node(left: Tree&lt;T&gt;, value: T, right: Tree&lt;T&gt;)</code></pre>
+<p class="p noindent para-continued">declare possibly infinite lists (that is, lists that can be either
+finite or infinite), infinite streams (that is, lists that are always
+infinite), and infinite binary trees (that is, trees where every
+branch goes on forever), respectively.
+</p>
+<p class="p indent">The paper&nbsp;<a href="http://research.microsoft.com/en-us/um/people/leino/papers/krml230.pdf" title="Co-induction Simply: Automatic Co-inductive Proofs in a Program Verifier" data-linkid="co-induction simply">Co-induction Simply</a>, by Leino and
+Moskal<span class="citations" style="target-element:bibitem">[<a href="#leino:dafny:coinduction" title="K.&#160;Rustan&#160;M. Leino and Michal Moskal.
+Co-induction simply: Automatic co-inductive proofs in a program verifier.
+Manuscript KRML 230, 2014a.
+Available at http://research.microsoft.com/en-us/um/people/leino/papers/krml230.pdf." class="bibref localref" style="target-element:bibitem"><span class="cite-number">20</span></a>]</span>, explains Dafny&#39;s implementation and
+verification of co-inductive types. We capture the key features from that
+paper in this section but the reader is referred to that paper for more
+complete details and to supply bibliographic references that we have
+omitted.
+</p>
+<p class="p indent">Mathematical induction is a cornerstone of programming and program
+verification. It arises in data definitions (e.g., some algebraic data
+structures can be described using induction), it underlies program
+semantics (e.g., it explains how to reason about finite iteration and
+recursion), and it gets used in proofs (e.g., supporting lemmas about
+data structures use inductive proofs). Whereas induction deals with
+finite things (data, behavior, etc.), its dual, co-induction, deals with
+possibly infinite things. Co-induction, too, is important in programming
+and program verification, where it arises in data definitions (e.g., lazy
+data structures), semantics (e.g., concurrency), and proofs (e.g.,
+showing refinement in a co-inductive big-step semantics). It is thus
+desirable to have good support for both induction and co-induction in a
+system for constructing and reasoning about programs.
+</p>
+<p class="p indent">Co-datatypes and co-recursive functions make it possible to use lazily
+evaluated data structures (like in Haskell or Agda). Co-predicates,
+defined by greatest fix-points, let programs state properties of such
+data structures (as can also be done in, for example, Coq). For the
+purpose of writing co-inductive proofs in the language, we introduce
+co-lemmas. Ostensibly, a co-lemma invokes the co-induction hypothesis
+much like an inductive proof invokes the induction hypothesis. Underneath
+the hood, our co-inductive proofs are actually approached via induction:
+co-lemmas provide a syntactic veneer around this approach.
+</p>
+<p class="p indent">The following example gives a taste of how the co-inductive features in
+Dafny come together to give straightforward definitions of infinite
+matters.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:darkgreen">// infinite streams</span>
+<span style="color:blue">codatatype</span> IStream&lt;T&gt; = ICons(head: T, tail: IStream)
+
+<span style="color:darkgreen">// pointwise product of streams</span>
+<span style="color:blue">function</span> Mult(a: IStream&lt;<span style="color:teal">int</span>&gt;, b: IStream&lt;<span style="color:teal">int</span>&gt;): IStream&lt;<span style="color:teal">int</span>&gt;
+{ ICons(a.head * b.head, Mult(a.tail, b.tail)) }
+
+<span style="color:darkgreen">// lexicographic order on streams</span>
+<span style="color:blue">copredicate</span> Below(a: IStream&lt;<span style="color:teal">int</span>&gt;, b: IStream&lt;<span style="color:teal">int</span>&gt;)
+{ a.head &lt;= b.head &amp;&amp; ((a.head == b.head) ==&gt; Below(a.tail, b.tail)) }
+
+<span style="color:darkgreen">// a stream is Below its Square</span>
+<span style="color:blue">colemma</span> Theorem_BelowSquare(a: IStream&lt;<span style="color:teal">int</span>&gt;)
+<span style="color:purple">ensures</span> Below(a, Mult(a, a))
+{ <span style="color:blue">assert</span> a.head &lt;= Mult(a, a).head;
+ <span style="color:blue">if</span> a.head == Mult(a, a).head {
+ Theorem_BelowSquare(a.tail);
+ }
+}
+
+<span style="color:darkgreen">// an incorrect property and a bogus proof attempt</span>
+<span style="color:blue">colemma</span> NotATheorem_SquareBelow(a: IStream&lt;<span style="color:teal">int</span>&gt;)
+ <span style="color:purple">ensures</span> Below(Mult(a, a), a); <span style="color:darkgreen">// ERROR</span>
+{
+ NotATheorem_SquareBelow(a);
+}</code></pre>
+<p class="p noindent para-continued">It defines a type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">IStream</code> of infinite streams, with constructor <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">ICons</code> and
+destructors <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">head</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">tail</code>. Function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Mult</code> performs pointwise
+multiplication on infinite streams of integers, defined using a
+co-recursive call (which is evaluated lazily). Co-predicate <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Below</code> is
+defined as a greatest fix-point, which intuitively means that the
+co-predicate will take on the value true if the recursion goes on forever
+without determining a different value. The co-lemma states the theorem
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Below(a, Mult(a, a))</code>. Its body gives the proof, where the recursive
+invocation of the co-lemma corresponds to an invocation of the
+co-induction hypothesis.
+</p>
+<p class="p indent">The proof of the theorem stated by the first co-lemma lends
+itself to the following intuitive reading: To prove that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">a</code> is below
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Mult(a, a)</code>, check that their heads are ordered and, if the heads are
+equal, also prove that the tails are ordered. The second co-lemma states
+a property that does not always hold; the verifier is not fooled by the
+bogus proof attempt and instead reports the property as unproved.
+</p>
+<p class="p indent">We argue that these definitions in Dafny are simple enough to level the
+playing field between induction (which is familiar) and co-induction
+(which, despite being the dual of induction, is often perceived as eerily
+mysterious). Moreover, the automation provided by our SMT-based verifier
+reduces the tedium in writing co-inductive proofs. For example, it
+verifies <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Theorem_BelowSquare</code> from the program text given above no
+additional lemmas or tactics are needed. In fact, as a consequence of the
+automatic-induction heuristic in Dafny, the verifier will
+automatically verify Theorem_BelowSquare even given an empty body.
+</p>
+<p class="p indent">Just like there are restrictions on when an <em class="em-low1">inductive hypothesis</em> can be
+invoked, there are restriction on how a <em class="em-low1">co-inductive</em> hypothesis can be
+<em class="em-low1">used</em>. These are, of course, taken into consideration by our verifier.
+For example, as illustrated by the second co-lemma above, invoking the
+co-inductive hypothesis in an attempt to obtain the entire proof goal is
+futile. (We explain how this works in section&nbsp;<a href="#sec-colemmas" title="18.2.4.1.&#8194;Colemmas" class="localref" style="target-element:h4"><span class="heading-label">18.2.4.1</span></a>) Our initial experience
+with co-induction in Dafny shows it to provide an intuitive, low-overhead
+user experience that compares favorably to even the best of today’s
+interactive proof assistants for co-induction. In addition, the
+co-inductive features and verification support in Dafny have other
+potential benefits. The features are a stepping stone for verifying
+functional lazy programs with Dafny. Co-inductive features have also
+shown to be useful in defining language semantics, as needed to verify
+the correctness of a compiler, so this opens the possibility that
+such verifications can benefit from SMT automation.
+</p><h4 id="sec-well-founded-functionmethod-definitions" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">18.2.0</span>.&#8194;</span>Well-Founded Function/Method Definitions</h4>
+<p class="p noindent">The Dafny programming language supports functions and methods. A <em class="em-low1">function</em>
+in Dafny is a mathematical function (i.e., it is well-defined,
+deterministic, and pure), whereas a <em class="em-low1">method</em> is a body of statements that
+can mutate the state of the program. A function is defined by its given
+body, which is an expression. To ensure that function definitions
+are mathematically consistent, Dafny insists that recursive calls be well-founded,
+enforced as follows: Dafny computes the call graph of functions. The strongly connected
+components within it are <em class="em-low1">clusters</em> of mutually recursive definitions arranged in
+a DAG. This stratifies the functions so that a call from one cluster in the DAG to a
+lower cluster is allowed arbitrarily. For an intra-cluster call, Dafny prescribes a proof
+obligation that gets taken through the program verifier’s reasoning engine. Semantically,
+each function activation is labeled by a <em class="em-low1">rank</em>â€â€a lexicographic tuple determined
+by evaluating the function’s <strong class="strong-star2">decreases</strong> clause upon invocation of the function. The
+proof obligation for an intra-cluster call is thus that the rank of the callee is strictly less
+(in a language-defined well-founded relation) than the rank of the caller. Because
+these well-founded checks correspond to proving termination of executable code, we
+will often refer to them as “termination checksâ€Â. The same process applies to methods.
+</p>
+<p class="p indent">Lemmas in Dafny are commonly introduced by declaring a method, stating
+the property of the lemma in the <em class="em-low1">postcondition</em> (keyword <strong class="strong-star2">ensures</strong>) of
+the method, perhaps restricting the domain of the lemma by also giving a
+<em class="em-low1">precondition</em> (keyword <strong class="strong-star2">requires</strong>), and using the lemma by invoking
+the method. Lemmas are stated, used, and proved as methods, but
+since they have no use at run time, such lemma methods are typically
+declared as <em class="em-low1">ghost</em>, meaning that they are not compiled into code. The
+keyword <strong class="strong-star2">lemma</strong> introduces such a method. Control flow statements
+correspond to proof techniquesâ€â€case splits are introduced with if
+statements, recursion and loops are used for induction, and method calls
+for structuring the proof. Additionally, the statement:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">forall</span> x | P(x) { Lemma(x); }</code></pre>
+<p class="p noindent para-continued">is used to invoke <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Lemma(x)</code> on all <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> for which <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P(x)</code> holds. If
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Lemma(x)</code> ensures <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q(x)</code>, then the forall statement establishes
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">forall</span> x :: P(x) ==&gt; Q(x).</code></pre><h4 id="sec-defining-co-inductive-datatypes" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">18.2.1</span>.&#8194;</span>Defining Co-inductive Datatypes</h4>
+<p class="p noindent">Each value of an inductive datatype is finite, in the sense that it can
+be constructed by a finite number of calls to datatype constructors. In
+contrast, values of a co-inductive datatype, or co-datatype for short,
+can be infinite. For example, a co-datatype can be used to represent
+infinite trees.
+</p>
+<p class="p indent">Syntactically, the declaration of a co-datatype in Dafny looks like that
+of a datatype, giving prominence to the constructors (following Coq). The
+following example defines a co-datatype Stream of possibly
+infinite lists.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">codatatype</span> Stream&lt;T&gt; = SNil | SCons(head: T, tail: Stream)
+<span style="color:blue">function</span> Up(n: <span style="color:teal">int</span>): Stream&lt;<span style="color:teal">int</span>&gt; { SCons(n, Up(n+<span class="constant" style="color:purple">1</span>)) }
+<span style="color:blue">function</span> FivesUp(n: <span style="color:teal">int</span>): Stream&lt;<span style="color:teal">int</span>&gt;
+ <span style="color:purple">decreases</span> <span class="constant" style="color:purple">4</span> - (n - <span class="constant" style="color:purple">1</span>) % <span class="constant" style="color:purple">5</span>
+{
+ <span style="color:blue">if</span> (n % <span class="constant" style="color:purple">5</span> == <span class="constant" style="color:purple">0</span>) <span style="color:blue">then</span>
+ SCons(n, FivesUp(n+<span class="constant" style="color:purple">1</span>))
+ <span style="color:blue">else</span>
+ FivesUp(n+<span class="constant" style="color:purple">1</span>)
+}</code></pre>
+<p class="p noindent para-continued"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Stream</code> is a co-inductive datatype whose values are possibly infinite
+lists. Function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Up</code> returns a stream consisting of all integers upwards
+of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">FivesUp</code> returns a stream consisting of all multiples of 5
+upwards of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n</code> . The self-call in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Up</code> and the first self-call in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">FivesUp</code>
+sit in productive positions and are therefore classified as co-recursive
+calls, exempt from termination checks. The second self-call in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">FivesUp</code> is
+not in a productive position and is therefore subject to termination
+checking; in particular, each recursive call must decrease the rank
+defined by the <strong class="strong-star2">decreases</strong> clause.
+</p>
+<p class="p indent">Analogous to the common finite list datatype, Stream declares two
+constructors, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">SNil</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">SCons</code>. Values can be destructed using match
+expressions and statements. In addition, like for inductive datatypes,
+each constructor <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C</code> automatically gives rise to a discriminator <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C?</code> and
+each parameter of a constructor can be named in order to introduce a
+corresponding destructor. For example, if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">xs</code> is the stream
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">SCons(x, ys)</code>, then <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">xs.SCons?</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">xs.head == x</code> hold. In contrast
+to datatype declarations, there is no grounding check for
+co-datatypesâ€â€since a codatatype admits infinite values, the type is
+nevertheless inhabited.
+</p><h4 id="sec-creating-values-of-co-datatypes" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">18.2.2</span>.&#8194;</span>Creating Values of Co-datatypes</h4>
+<p class="p noindent">To define values of co-datatypes, one could imagine a “co-functionâ€Â
+language feature: the body of a “co-function†could include possibly
+never-ending self-calls that are interpreted by a greatest fix-point
+semantics (akin to a <strong class="strong-star2">CoFixpoint</strong> in Coq). Dafny uses a different design:
+it offers only functions (not “co-functionsâ€Â), but it classifies each
+intra-cluster call as either <em class="em-low1">recursive</em> or <em class="em-low1">co-recursive</em>. Recursive calls
+are subject to termination checks. Co-recursive calls may be
+never-ending, which is what is needed to define infinite values of a
+co-datatype. For example, function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Up(n )</code> in the preceding example is defined as the
+stream of numbers from <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n</code> upward: it returns a stream that starts with <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n</code>
+and continues as the co-recursive call <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Up(n + <span class="constant" style="color:purple">1</span>)</code>.
+</p>
+<p class="p indent">To ensure that co-recursive calls give rise to mathematically consistent definitions,
+they must occur only in productive positions. This says that it must be possible to determine
+each successive piece of a co-datatype value after a finite amount of work. This
+condition is satisfied if every co-recursive call is syntactically guarded by a constructor
+of a co-datatype, which is the criterion Dafny uses to classify intra-cluster calls as being
+either co-recursive or recursive. Calls that are classified as co-recursive are exempt from
+termination checks.
+</p>
+<p class="p indent">A consequence of the productivity checks and termination checks is that, even in the
+absence of talking about least or greatest fix-points of self-calling functions, all functions
+in Dafny are deterministic. Since there is no issue of several possible fix-points,
+the language allows one function to be involved in both recursive and co-recursive calls,
+as we illustrate by the function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">FivesUp</code>.
+</p><h4 id="sec-copredicates" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">18.2.3</span>.&#8194;</span>Copredicates</h4>
+<p class="p noindent">Determining properties of co-datatype values may require an infinite
+number of observations. To that avail, Dafny provides <em class="em-low1">co-predicates</em>
+which are function declarations that use the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">copredicate</span></code> keyword.
+Self-calls to a co-predicate need not terminate. Instead, the value
+defined is the greatest fix-point of the given recurrence equations.
+Continuing the preceding example, the following code defines a
+co-predicate that holds for exactly those streams whose payload consists
+solely of positive integers. The co-predicate definition implicitly also
+gives rise to a corresponding prefix predicate, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos#</code>. The syntax for
+calling a prefix predicate sets apart the argument that specifies the
+prefix length, as shown in the last line; for this figure, we took the
+liberty of making up a coordinating syntax for the signature of the
+automatically generated prefix predicate (which is not part of
+Dafny syntax).
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">copredicate</span> Pos(s: Stream&lt;<span style="color:teal">int</span>&gt;)
+{
+ <span style="color:blue">match</span> s
+ <span style="color:blue">case</span> SNil =&gt; <span style="color:blue">true</span>
+ <span style="color:blue">case</span> SCons(x, rest) =&gt; x &gt; <span class="constant" style="color:purple">0</span> &amp;&amp; Pos(rest)
+}
+<span style="color:darkgreen">// Automatically generated by the Dafny compiler:</span>
+<span style="color:blue">predicate</span> Pos#[_k: <span style="color:teal">nat</span>](s: Stream&lt;<span style="color:teal">int</span>&gt;)
+ <span style="color:purple">decreases</span> _k
+{ <span style="color:blue">if</span> _k = <span class="constant" style="color:purple">0</span> <span style="color:blue">then</span> <span style="color:blue">true</span> <span style="color:blue">else</span>
+ <span style="color:blue">match</span> s
+ <span style="color:blue">case</span> SNil =&gt; <span style="color:blue">true</span>
+ <span style="color:blue">case</span> SCons(x, rest) =&gt; x &gt; <span class="constant" style="color:purple">0</span> &amp;&amp; Pos#[_k-<span class="constant" style="color:purple">1</span>](rest)
+}</code></pre>
+<p class="p noindent para-continued">Some restrictions apply. To guarantee that the greatest fix-point always
+exists, the (implicit functor defining the) co-predicate must be
+monotonic. This is enforced by a syntactic restriction on the form of the
+body of co-predicates: after conversion to negation normal form (i.e.,
+pushing negations down to the atoms), intra-cluster calls of
+co-predicates must appear only in <em class="em-low1">positive</em> positionsâ€â€that is, they must
+appear as atoms and must not be negated. Additionally, to guarantee
+soundness later on, we require that they appear in <em class="em-low1">co-friendly</em>
+positionsâ€â€that is, in negation normal form, when they appear under
+existential quantification, the quantification needs to be limited to a
+finite range<sup id="back-fn-fn-copredicate-restriction" ><a href="#fn-fn-copredicate-restriction" title="8.Higher-order function support in Dafny is
+rather modest and typical reasoning patterns do not involve them, so this
+restriction is not as limiting as it would have been in, e.g., Coq.
+&#8617;" class="footnote-ref localref" ><span class="footnote-label">8</span></a></sup>. Since the evaluation of a co-predicate might not
+terminate, co-predicates are always ghost. There is also a restriction on
+the call graph that a cluster containing a co-predicate must contain only
+co-predicates, no other kinds of functions.
+</p>
+<p class="p indent">A <strong class="strong-star2">copredicate</strong> declaration of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P</code> defines not just a co-predicate, but
+also a corresponding <em class="em-low1">prefix predicate</em> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P#</code>. A prefix predicate is a
+finite unrolling of a co-predicate. The prefix predicate is constructed
+from the co-predicate by
+</p>
+<ul class="ul list-star loose">
+<li class="li ul-li list-star-li loose-li">
+<p>adding a parameter _k of type nat to denote the prefix length,
+</p></li>
+<li class="li ul-li list-star-li loose-li">
+<p>adding the clause &#8220;<strong class="strong-star2">decreases</strong> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_k;</code>&#8221; to the prefix predicate (the
+co-predicate itself is not allowed to have a decreases clause),
+</p></li>
+<li class="li ul-li list-star-li loose-li">
+<p>replacing in the body of the co-predicate every intra-cluster
+call <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q(args)</code> to a copredicate by a call <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q#[_k - <span class="constant" style="color:purple">1</span>](args)</code>
+to the corresponding prefix predicate, and then
+</p></li>
+<li class="li ul-li list-star-li loose-li">
+<p>prepending the body with <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">if</span> _k = <span class="constant" style="color:purple">0</span> <span style="color:blue">then</span> <span style="color:blue">true</span> <span style="color:blue">else</span></code>.
+</p></li></ul>
+
+<p class="p noindent">For example, for co-predicate <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos</code>, the definition of the prefix
+predicate <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos#</code> is as suggested above. Syntactically, the prefix-length
+argument passed to a prefix predicate to indicate how many times to
+unroll the definition is written in square brackets, as in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos#[k](s)</code>.
+In the Dafny grammar this is called a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_hashcall" class="ntref localref" style="color:maroon">HashCall</a></span></code>. The definition of
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos#</code> is available only at clusters strictly higher than that of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos</code>;
+that is, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos#</code> must not be in the same cluster. In other
+words, the definition of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos</code> cannot depend on <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos#</code>.
+</p><h5 id="sec-co-equality" class="h4" data-heading-depth="4" style="display:block"><span class="heading-before"><span class="heading-label">18.2.3.0</span>.&#8194;</span>Co-Equality</h5>
+<p class="p noindent">Equality between two values of a co-datatype is a built-in co-predicate.
+It has the usual equality syntax <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s == t</code>, and the corresponding prefix
+equality is written <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s ==#[k] t</code>. And similarly for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s != t</code>
+and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s !=#[k] t</code>.
+</p><h4 id="sec-co-inductive-proofs" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">18.2.4</span>.&#8194;</span>Co-inductive Proofs</h4>
+<p class="p noindent">From what we have said so far, a program can make use of properties of
+co-datatypes. For example, a method that declares <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos(s)</code> as a
+precondition can rely on the stream <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code> containing only positive integers.
+In this section, we consider how such properties are established in the
+first place.
+</p><h5 id="sec-properties-about-prefix-predicates" class="h4" data-heading-depth="4" style="display:block"><span class="heading-before"><span class="heading-label">18.2.4.0</span>.&#8194;</span>Properties About Prefix Predicates</h5>
+<p class="p noindent">Among other possible strategies for establishing co-inductive properties
+we take the time-honored approach of reducing co-induction to
+induction. More precisely, Dafny passes to the SMT solver an
+assumption <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">D(P)</code> for every co-predicate <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P</code>, where:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>D(P) = ? x • P(x) &lt;==&gt; ? k • P#[k](x)</code></pre>
+<p class="p noindent para-continued">In other words, a co-predicate is true iff its corresponding prefix
+predicate is true for all finite unrollings.
+</p>
+<p class="p indent">In Sec. 4 of the paper&nbsp;<a href="http://research.microsoft.com/en-us/um/people/leino/papers/krml230.pdf" title="Co-induction Simply: Automatic Co-inductive Proofs in a Program Verifier" data-linkid="co-induction simply">Co-induction Simply</a> a soundness theorem of such
+assumptions is given, provided the co-predicates meet the co-friendly
+restrictions. An example proof of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos(Up(n))</code> for every <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n &gt; <span class="constant" style="color:purple">0</span></code> is
+here shown:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">lemma</span> UpPosLemma(n: <span style="color:teal">int</span>)
+ <span style="color:purple">requires</span> n &gt; <span class="constant" style="color:purple">0</span>
+ <span style="color:purple">ensures</span> Pos(Up(n))
+{
+ <span style="color:blue">forall</span> k | <span class="constant" style="color:purple">0</span> &lt;= k { UpPosLemmaK(k, n); }
+}
+
+<span style="color:blue">lemma</span> UpPosLemmaK(k: <span style="color:teal">nat</span>, n: <span style="color:teal">int</span>)
+ <span style="color:purple">requires</span> n &gt; <span class="constant" style="color:purple">0</span>
+ <span style="color:purple">ensures</span> Pos#[k](Up(n))
+ <span style="color:purple">decreases</span> k
+{
+ <span style="color:blue">if</span> k != <span class="constant" style="color:purple">0</span> {
+ <span style="color:darkgreen">// this establishes Pos#[k-1](Up(n).tail)</span>
+ UpPosLemmaK(k-<span class="constant" style="color:purple">1</span>, n+<span class="constant" style="color:purple">1</span>);
+ }
+}</code></pre>
+<p class="p noindent para-continued">The lemma <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">UpPosLemma</code> proves <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos(Up(n))</code> for every <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n &gt; <span class="constant" style="color:purple">0</span></code>. We first
+show <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos#[k](Up(n ))</code>, for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n &gt; <span class="constant" style="color:purple">0</span></code> and an arbitrary <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">k</code>, and then use
+the forall statement to show <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">? k • Pos#[k](Up(n))</code>. Finally, the axiom
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">D(Pos)</code> is used (automatically) to establish the co-predicate.
+</p><h5 id="sec-colemmas" class="h4" data-heading-depth="4" style="display:block"><span class="heading-before"><span class="heading-label">18.2.4.1</span>.&#8194;</span>Colemmas</h5>
+<p class="p noindent">As we just showed, with help of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">D</code> axiom we can now prove a
+co-predicate by inductively proving that the corresponding prefix
+predicate holds for all prefix lengths <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">k</code> . In this section, we introduce
+<em class="em-low1">co-lemma</em> declarations, which bring about two benefits. The first benefit
+is that co-lemmas are syntactic sugar and reduce the tedium of having to
+write explicit quantifications over <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">k</code> . The second benefit is that, in
+simple cases, the bodies of co-lemmas can be understood as co-inductive
+proofs directly. As an example consider the following co-lemma.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">colemma</span> UpPosLemma(n: <span style="color:teal">int</span>)
+ <span style="color:purple">requires</span> n &gt; <span class="constant" style="color:purple">0</span>
+ <span style="color:purple">ensures</span> Pos(Up(n))
+{
+ UpPosLemma(n+<span class="constant" style="color:purple">1</span>);
+}</code></pre>
+<p class="p noindent para-continued">This co-lemma can be understood as follows: <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">UpPosLemma</code> invokes itself
+co-recursively to obtain the proof for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos(Up(n).tail)</code> (since <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Up(n).tail</code>
+equals <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Up(n+<span class="constant" style="color:purple">1</span>)</code>). The proof glue needed to then conclude <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Pos(Up(n))</code> is
+provided automatically, thanks to the power of the SMT-based verifier.
+</p><h5 id="sec-prefix-lemmas" class="h4" data-heading-depth="4" style="display:block"><span class="heading-before"><span class="heading-label">18.2.4.2</span>.&#8194;</span>Prefix Lemmas</h5>
+<p class="p noindent">To understand why the above <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">UpPosLemma</code> co-lemma code is a sound proof,
+let us now describe the details of the desugaring of co-lemmas. In
+analogy to how a <strong class="strong-star2">copredicate</strong> declaration defines both a co-predicate and
+a prefix predicate, a <strong class="strong-star2">colemma</strong> declaration defines both a co-lemma and
+<em class="em-low1">prefix lemma</em>. In the call graph, the cluster containing a co-lemma must
+contain only co-lemmas and prefix lemmas, no other methods or function.
+By decree, a co-lemma and its corresponding prefix lemma are always
+placed in the same cluster. Both co-lemmas and prefix lemmas are always
+ghosts.
+</p>
+<p class="p indent">The prefix lemma is constructed from the co-lemma by
+</p>
+<ul class="ul list-star loose">
+<li class="li ul-li list-star-li loose-li">
+<p>adding a parameter <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_k</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">nat</span></code> to denote the prefix length,
+</p></li>
+<li class="li ul-li list-star-li loose-li">
+<p>replacing in the co-lemma’s postcondition the positive co-friendly
+occurrences of co-predicates by corresponding prefix predicates,
+passing in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_k</code> as the prefix-length argument,
+</p></li>
+<li class="li ul-li list-star-li loose-li">
+<p>prepending <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_k</code> to the (typically implicit) <strong class="strong-star2">decreases</strong> clause of the co-lemma,
+</p></li>
+<li class="li ul-li list-star-li loose-li">
+<p>replacing in the body of the co-lemma every intra-cluster call
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M(args)</code> to a colemma by a call <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M#[_k - <span class="constant" style="color:purple">1</span>](args)</code> to the
+corresponding prefix lemma, and then
+</p></li>
+<li class="li ul-li list-star-li loose-li">
+<p>making the body’s execution conditional on <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_k != <span class="constant" style="color:purple">0</span></code>.
+</p></li></ul>
+
+<p class="p noindent">Note that this rewriting removes all co-recursive calls of co-lemmas,
+replacing them with recursive calls to prefix lemmas. These recursive
+call are, as usual, checked to be terminating. We allow the pre-declared
+identifier <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_k</code> to appear in the original body of the
+co-lemma.<sup id="back-fn-fn-co-predicate-co-lemma-diffs" ><a href="#fn-fn-co-predicate-co-lemma-diffs" title="9.Note, two places where co-predicates
+and co-lemmas are not analogous are: co-predicates must not make
+recursive calls to their prefix predicates, and co-predicates cannot
+mention _k.
+&#8617;" class="footnote-ref localref" ><span class="footnote-label">9</span></a></sup>
+</p>
+<p class="p indent">We can now think of the body of the co-lemma as being replaced by a
+<strong class="strong-star2">forall</strong> call, for every <em class="em-low1">k</em> , to the prefix lemma. By construction,
+this new body will establish the colemma’s declared postcondition (on
+account of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">D</code> axiom, and remembering that only the positive
+co-friendly occurrences of co-predicates in the co-lemma’s postcondition
+are rewritten), so there is no reason for the program verifier to check
+it.
+</p>
+<p class="p indent">The actual desugaring of our co-lemma <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">UpPosLemma</code> is in fact the
+previous code for the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">UpPosLemma</code> lemma except that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">UpPosLemmaK</code> is
+named <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">UpPosLemma#</code> and modulo a minor syntactic difference in how the
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">k</code> argument is passed.
+</p>
+<p class="p indent">In the recursive call of the prefix lemma, there is a proof obligation
+that the prefixlength argument <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_k - <span class="constant" style="color:purple">1</span></code> is a natural number.
+Conveniently, this follows from the fact that the body has been wrapped
+in an <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">if</span> _k != <span class="constant" style="color:purple">0</span></code> statement. This also means that the postcondition must
+hold trivially when <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_k = <span class="constant" style="color:purple">0</span></code>, or else a postcondition violation will be
+reported. This is an appropriate design for our desugaring, because
+co-lemmas are expected to be used to establish co-predicates, whose
+corresponding prefix predicates hold trivially when <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_k = <span class="constant" style="color:purple">0</span></code>. (To prove
+other predicates, use an ordinary lemma, not a co-lemma.)
+</p>
+<p class="p indent">It is interesting to compare the intuitive understanding of the
+co-inductive proof in using a co-lemma with the inductive proof in using
+the lemma. Whereas the inductive proof is performing proofs for deeper
+and deeper equalities, the co-lemma can be understood as producing the
+infinite proof on demand.
+</p><h2 id="sec-newtypes" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">19</span>.&#8194;</span>Newtypes</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_newtypedecl" class="ntdef" style="color:olive">NewtypeDecl</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"newtype"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span class="code-escaped"><a href="#1_newtypename" class="ntref localref" style="color:maroon">NewtypeName</a></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"="</span></span>
+ ( <span class="code-escaped"><a href="#1_numerictypename" class="ntref localref" style="color:maroon">NumericTypeName</a></span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":"</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span> ] <span style="color:maroon">&quot;</span><span style="color:maroon">|</span><span style="color:maroon">&quot;</span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ | <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span>
+ ) </code></pre>
+<p class="p noindent para-continued">A new numeric type can be declared with the <em class="em-low1">newtype</em>
+declaration<sup id="back-fn-fn-newtype-name" ><a href="#fn-fn-newtype-name" title="10.Should newtype perhaps be renamed to numtype?
+&#8617;" class="footnote-ref localref" ><span class="footnote-label">10</span></a></sup>, for example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">newtype</span> N = x: M | Q</code></pre>
+<p class="p noindent para-continued">where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M</code> is a numeric type and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q</code> is a boolean expression that can
+use <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> as a free variable. If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M</code> is an integer-based numeric type,
+then so is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">N</code>; if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M</code> is real-based, then so is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">N</code>. If the type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M</code>
+can be inferred from <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q</code>, the &#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">: M</code>&#8221; can be omitted. If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q</code> is just
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code>, then the declaration can be given simply as:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">newtype</span> N = M</code></pre>
+<p class="p noindent para-continued">Type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M</code> is known as the <em class="em-low1">base type</em> of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">N</code>.
+</p>
+<p class="p indent">A newtype is a numeric type that supports the same operations as its
+base type. The newtype is distinct from and incompatible with other
+numeric types; in particular, it is not assignable to its base type
+without an explicit conversion. An important difference between the
+operations on a newtype and the operations on its base type is that
+the newtype operations are defined only if the result satisfies the
+predicate <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q</code>, and likewise for the literals of the
+newtype.<sup id="back-fn-fn-newtype-design-question" ><a href="#fn-fn-newtype-design-question" title="11.Would it be useful to also
+automatically define predicate N?(m: M) { Q }?
+&#8617;" class="footnote-ref localref" ><span class="footnote-label">11</span></a></sup>
+</p>
+<p class="p indent">For example, suppose <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">lo</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi</code> are integer-based numerics that
+satisfy <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0</span> &lt;= lo &lt;= hi</code> and consider the following code fragment:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> mid <span style="color:blue">:=</span> (lo + hi) / <span class="constant" style="color:purple">2</span>;</code></pre>
+<p class="p noindent para-continued">If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">lo</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi</code> have type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code>, then the code fragment is legal; in
+particular, it never overflows, since <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code> has no upper bound. In
+contrast, if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">lo</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi</code> are variables of a newtype <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">int32</code> declared
+as follows:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">newtype</span> int32 = x | -<span class="constant" style="color:purple">0x80000000</span> &lt;= x &lt; <span class="constant" style="color:purple">0x80000000</span></code></pre>
+<p class="p noindent para-continued">then the code fragment is erroneous, since the result of the addition
+may fail to satisfy the predicate in the definition of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">int32</code>. The
+code fragment can be rewritten as
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> mid <span style="color:blue">:=</span> lo + (hi - lo) / <span class="constant" style="color:purple">2</span>;</code></pre>
+<p class="p noindent para-continued">in which case it is legal for both <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">int32</code>.
+</p>
+<p class="p indent">Since a newtype is incompatible with its base type and since all
+results of the newtype&#39;s operations are members of the newtype, a
+compiler for Dafny is free to specialize the run-time representation
+of the newtype. For example, by scrutinizing the definition of
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">int32</code> above, a compiler may decide to store <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">int32</code> values using
+signed 32-bit integers in the target hardware.
+</p>
+<p class="p indent">Note that the bound variable <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q</code> has type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">M</code>, not <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">N</code>.
+Consequently, it may not be possible to state <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q</code> about the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">N</code>
+value. For example, consider the following type of 8-bit 2&#39;s
+complement integers:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">newtype</span> int8 = x: <span style="color:teal">int</span> | -<span class="constant" style="color:purple">128</span> &lt;= x &lt; <span class="constant" style="color:purple">128</span></code></pre>
+<p class="p noindent para-continued">and consider a variable <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">c</code> of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">int8</code>. The expression
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>-<span class="constant" style="color:purple">128</span> &lt;= c &lt; <span class="constant" style="color:purple">128</span></code></pre>
+<p class="p noindent para-continued">is not well-defined, because the comparisons require each operand to
+have type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">int8</code>, which means the literal <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">128</span></code> is checked to be of
+type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">int8</code>, which it is not. A proper way to write this expression
+would be to use a conversion operation, described next, on <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">c</code> to
+convert it to the base type:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>-<span class="constant" style="color:purple">128</span> &lt;= <span style="color:teal">int</span>(c) &lt; <span class="constant" style="color:purple">128</span></code></pre>
+<p class="p noindent para-continued">If possible Dafny will represent values of the newtype using
+a native data type for the sake of efficiency. This action can
+be inhibited or a specific native data type selected by
+using the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(:nativeType)</code> attribute, as explained in
+section&nbsp;<a href="#sec-nativetype" title="24.1.11.&#8194;nativeType" class="localref" style="target-element:h3"><span class="heading-label">24.1.11</span></a>.
+</p>
+<p class="p indent">There is a restriction that the value <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0</span></code> must be part of every
+newtype.<sup id="back-fn-fn-newtype-zero" ><a href="#fn-fn-newtype-zero" title="12.The restriction is due to a current limitation in
+the compiler. This will change in the future and will also open
+up the possibility for subset types and non-null reference
+types.
+&#8617;" class="footnote-ref localref" ><span class="footnote-label">12</span></a></sup>
+</p><h3 id="sec-numeric-conversion-operations" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">19.0</span>.&#8194;</span>Numeric conversion operations</h3>
+<p class="p noindent">For every numeric type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">N</code>, there is a conversion function with the
+same name. It is a partial identity function. It is defined when the
+given value, which can be of any numeric type, is a member of the type
+converted to. When the conversion is from a real-based numeric type
+to an integer-based numeric type, the operation requires that the
+real-based argument has no fractional part. (To round a real-based
+numeric value down to the nearest integer, use the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">.Trunc</code> member,
+see Section&nbsp;<a href="#sec-numeric-types" title="6.1.&#8194;Numeric types" class="localref" style="target-element:h2"><span class="heading-label">6.1</span></a>.)
+</p>
+<p class="p indent">To illustrate using the example from above, if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">lo</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi</code> have type
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">int32</code>, then the code fragment can legally be written as follows:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> mid <span style="color:blue">:=</span> (<span style="color:teal">int</span>(lo) + <span style="color:teal">int</span>(hi)) / <span class="constant" style="color:purple">2</span>;</code></pre>
+<p class="p noindent para-continued">where the type of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">mid</code> is inferred to be <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code>. Since the result
+value of the division is a member of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">int32</code>, one can introduce
+yet another conversion operation to make the type of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">mid</code> be <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">int32</code>:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> mid <span style="color:blue">:=</span> int32((<span style="color:teal">int</span>(lo) + <span style="color:teal">int</span>(hi)) / <span class="constant" style="color:purple">2</span>);</code></pre>
+<p class="p noindent para-continued">If the compiler does specialize the run-time representation for
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">int32</code>, then these statements come at the expense of two,
+respectively three, run-time conversions.
+</p><h2 id="sec-subset-types" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">20</span>.&#8194;</span>Subset types</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_nattype_" class="ntdef" style="color:olive">NatType_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"nat"</span></span> </code></pre>
+<p class="p noindent para-continued">A <em class="em-low1">subset type</em> is a restricted use of an existing type, called
+the <em class="em-low1">base type</em> of the subset type. A subset type is like a
+combined use of the base type and a predicate on the base
+type.
+</p>
+<p class="p indent">An assignment from a subset type to its base type is always
+allowed. An assignment in the other direction, from the base type to
+a subset type, is allowed provided the value assigned does indeed
+satisfy the predicate of the subset type.
+(Note, in contrast, assignments between a newtype and its base type
+are never allowed, even if the value assigned is a value of the target
+type. For such assignments, an explicit conversion must be used, see
+Section&nbsp;<a href="#sec-numeric-conversion-operations" title="19.0.&#8194;Numeric conversion operations" class="localref" style="target-element:h2"><span class="heading-label">19.0</span></a>.)
+</p>
+<p class="p indent">Dafny supports one subset type, namely the built-in type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">nat</span></code>,
+whose base type is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code>.<sup id="back-fn-fn-more-subset-types" ><a href="#fn-fn-more-subset-types" title="13.A future version of Dafny will support
+user-defined subset types.
+&#8617;" class="footnote-ref localref" ><span class="footnote-label">13</span></a></sup> Type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">nat</span></code>
+designates the non-negative subrange of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code>. A simple example that
+puts subset type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">nat</span></code> to good use is the standard Fibonacci
+function:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">function</span> Fib(n: <span style="color:teal">nat</span>): <span style="color:teal">nat</span>
+{
+ <span style="color:blue">if</span> n &lt; <span class="constant" style="color:purple">2</span> <span style="color:blue">then</span> n <span style="color:blue">else</span> Fib(n-<span class="constant" style="color:purple">2</span>) + Fib(n-<span class="constant" style="color:purple">1</span>)
+}</code></pre>
+<p class="p noindent para-continued">An equivalent, but clumsy, formulation of this function (modulo the
+wording of any error messages produced at call sites) would be to use
+type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code> and to write the restricting predicate in pre- and
+postconditions:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">function</span> Fib(n: <span style="color:teal">int</span>): <span style="color:teal">int</span>
+ <span style="color:purple">requires</span> <span class="constant" style="color:purple">0</span> &lt;= n; <span style="color:darkgreen">// the function argument must be non-negative</span>
+ <span style="color:purple">ensures</span> <span class="constant" style="color:purple">0</span> &lt;= Fib(n); <span style="color:darkgreen">// the function result is non-negative</span>
+{
+ <span style="color:blue">if</span> n &lt; <span class="constant" style="color:purple">2</span> <span style="color:blue">then</span> n <span style="color:blue">else</span> Fib(n-<span class="constant" style="color:purple">2</span>) + Fib(n-<span class="constant" style="color:purple">1</span>)
+}</code></pre>
+<p class="p noindent para-continued">Type inference will never infer the type of a variable to be a
+subset type. It will instead infer the type to be the base type
+of the subset type. For example, the type of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> in
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">forall</span> x :: P(x)</code></pre>
+<p class="p noindent para-continued">will be <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code>, even if predicate <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P</code> declares its argument to have
+type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">nat</span></code>.
+</p><h2 id="sec-statements" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">21</span>.&#8194;</span>Statements</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_stmt" class="ntdef" style="color:olive">Stmt</span></span> = ( <span class="code-escaped"><a href="#1_blockstmt" class="ntref localref" style="color:maroon">BlockStmt</a></span> | <span class="code-escaped"><a href="#1_assertstmt" class="ntref localref" style="color:maroon">AssertStmt</a></span> | <span class="code-escaped"><a href="#1_assumestmt" class="ntref localref" style="color:maroon">AssumeStmt</a></span> | <span class="code-escaped"><a href="#1_printstmt" class="ntref localref" style="color:maroon">PrintStmt</a></span> | <span class="code-escaped"><a href="#1_updatestmt" class="ntref localref" style="color:maroon">UpdateStmt</a></span>
+ | <span class="code-escaped"><a href="#1_vardeclstatement" class="ntref localref" style="color:maroon">VarDeclStatement</a></span> | <span class="code-escaped"><a href="#1_ifstmt" class="ntref localref" style="color:maroon">IfStmt</a></span> | <span class="code-escaped"><a href="#1_whilestmt" class="ntref localref" style="color:maroon">WhileStmt</a></span> | <span class="code-escaped"><a href="#1_matchstmt" class="ntref localref" style="color:maroon">MatchStmt</a></span> | <span class="code-escaped"><a href="#1_forallstmt" class="ntref localref" style="color:maroon">ForallStmt</a></span>
+ | <span class="code-escaped"><a href="#1_calcstmt" class="ntref localref" style="color:maroon">CalcStmt</a></span> | <span class="code-escaped"><a href="#1_modifystmt" class="ntref localref" style="color:maroon">ModifyStmt</a></span> | <span class="code-escaped"><a href="#1_labeledstmt_" class="ntref localref" style="color:maroon">LabeledStmt_</a></span> | <span class="code-escaped"><a href="#1_breakstmt_" class="ntref localref" style="color:maroon">BreakStmt_</a></span> | <span class="code-escaped"><a href="#1_returnstmt" class="ntref localref" style="color:maroon">ReturnStmt</a></span>
+ | <span class="code-escaped"><a href="#1_yieldstmt" class="ntref localref" style="color:maroon">YieldStmt</a></span> | <span class="code-escaped"><a href="#1_skeletonstmt" class="ntref localref" style="color:maroon">SkeletonStmt</a></span>
+ ) </code></pre>
+<p class="p noindent para-continued">Many of Dafny&#39;s statements are similar to those in traditional
+programming languages, but a number of them are significantly different.
+This grammar production shows the different kinds of Dafny statements.
+They are described in subsequent sections.
+</p><h3 id="sec-labeled-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.0</span>.&#8194;</span>Labeled Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_labeledstmt_" class="ntdef" style="color:olive">LabeledStmt_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"label"</span></span> <span class="code-escaped"><a href="#1_labelname" class="ntref localref" style="color:maroon">LabelName</a></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":"</span></span> <span class="code-escaped"><a href="#1_stmt" class="ntref localref" style="color:maroon">Stmt</a></span> </code></pre>
+<p class="p noindent para-continued">A labeled statement is just the keyword <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">label</span></code> followed by and
+identifier which is the label followed by a colon and a
+statement. The label may be referenced in a break statement
+to transfer control to the location after that statement.
+</p><h3 id="sec-break-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.1</span>.&#8194;</span>Break Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_breakstmt_" class="ntdef" style="color:olive">BreakStmt_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"break"</span></span> ( <span class="code-escaped"><a href="#1_labelname" class="ntref localref" style="color:maroon">LabelName</a></span> | { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"break"</span></span> } ) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span> </code></pre>
+<p class="p noindent para-continued">A break statement breaks out of one or more loops (if the
+statement consists solely of one or more <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">break</span></code> keywords),
+or else transfers control to just past the statement
+bearing the referenced label, if a label was used.
+</p><h3 id="sec-block-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.2</span>.&#8194;</span>Block Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_blockstmt" class="ntdef" style="color:olive">BlockStmt</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"{"</span></span> { <span class="code-escaped"><a href="#1_stmt" class="ntref localref" style="color:maroon">Stmt</a></span> } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"}"</span></span> </code></pre>
+<p class="p noindent para-continued">A block statement is just a sequence of statements enclosed by curly braces.
+</p><h3 id="sec-return-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.3</span>.&#8194;</span>Return Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_returnstmt" class="ntdef" style="color:olive">ReturnStmt</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"return"</span></span> [ <span class="code-escaped"><a href="#1_rhs" class="ntref localref" style="color:maroon">Rhs</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_rhs" class="ntref localref" style="color:maroon">Rhs</a></span> } ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span> </code></pre>
+<p class="p noindent para-continued">A return statement can only be used in a method. It is used
+to terminate the execution of the method.
+To return a value from a method, the value is assigned to one
+of the named return values sometime before a return statement.
+In fact, the return values act very much like local variables,
+and can be assigned to more than once. Return statements are
+used when one wants to return before reaching the end of the
+body block of the method. Return statements can be just the
+return keyword (where the current value of the out parameters
+are used), or they can take a list of values to return.
+If a list is given the number of values given must be the
+same as the number of named return values.
+</p><h3 id="sec-yield-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.4</span>.&#8194;</span>Yield Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_yieldstmt" class="ntdef" style="color:olive">YieldStmt</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"yield"</span></span> [ <span class="code-escaped"><a href="#1_rhs" class="ntref localref" style="color:maroon">Rhs</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_rhs" class="ntref localref" style="color:maroon">Rhs</a></span> } ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span> </code></pre>
+<p class="p noindent para-continued">A yield statement can only be used in an iterator.
+See section&nbsp;<a href="#sec-iterator-types" class="localref">Iterator types</a> for more details
+about iterators.
+</p>
+<p class="p indent">The body of an iterator is a <em class="em-low1">co-routine</em>. It is used
+to yield control to its caller, signaling that a new
+set of values for the iterator&#39;s yield parameters (if any)
+are available. Values are assigned to the yield parameters
+at or before a yield statement.
+In fact, the yield parameters act very much like local variables,
+and can be assigned to more than once. Yield statements are
+used when one wants to return new yield parameter values
+to the caller. Yield statements can be just the
+<strong class="strong-star2">yield</strong> keyword (where the current value of the yield parameters
+are used), or they can take a list of values to yield.
+If a list is given the number of values given must be the
+same as the number of named return yield parameters.
+</p><h3 id="sec-update-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.5</span>.&#8194;</span>Update Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_updatestmt" class="ntdef" style="color:olive">UpdateStmt</span></span> = <span class="code-escaped"><a href="#1_lhs" class="ntref localref" style="color:maroon">Lhs</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_lhs" class="ntref localref" style="color:maroon">Lhs</a></span> }
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":="</span></span> <span class="code-escaped"><a href="#1_rhs" class="ntref localref" style="color:maroon">Rhs</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_rhs" class="ntref localref" style="color:maroon">Rhs</a></span> }
+ | <span style="color:maroon">&quot;</span><span style="color:maroon">:|</span><span style="color:maroon">&quot;</span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"assume"</span></span> ] <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ )
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span><span style="color:red">&quot;</span></code></pre>
+<p class="p noindent para-continued">The update statement has two forms. The first more normal form
+allows for parallel assignment of right-hand-side values to the
+left-hand side. For example <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x,y <span style="color:blue">:=</span> y,x</code> to swap the values
+of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">y</code>. Of course the common case will have only one
+rhs and one lhs.
+</p>
+<p class="p indent">The form that uses &#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">:|</span></code>&#8221; assigns some values to the left-hand-side
+variables such that the boolean expression on the right hand side
+is satisfied. This can be used to make a choice as in the
+following example where we choose an element in a set.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">function</span> PickOne&lt;T&gt;(s: <span style="color:teal">set</span>&lt;T&gt;): T
+ <span style="color:purple">requires</span> s != {}
+{
+ <span style="color:blue">var</span> x <span style="color:blue">:|</span> x <span style="color:blue">in</span> s; x
+}</code></pre>
+<p class="p noindent para-continued">Dafny will report an error if it cannot prove that values
+exist which satisfy the condition.
+</p>
+<p class="p indent">In addition, though the choice is arbitrary, given identical
+circumstances the choice will be made consistently.
+</p>
+<p class="p indent">In the actual grammar two additional forms are recognized for
+purposes of error detection. The form:
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_lhs" class="ntdef" style="color:olive">Lhs</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span>} ;</code></pre>
+<p class="p noindent para-continued">is assumed to be a mal-formed call.
+</p>
+<p class="p indent">The form
+</p>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_lhs" class="ntdef" style="color:olive">Lhs</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":"</span></span></code></pre>
+<p class="p noindent para-continued">is diagnosed as a label in which the user forgot the <strong class="strong-star2">label</strong> keyword.
+</p><h3 id="sec-variable-declaration-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.6</span>.&#8194;</span>Variable Declaration Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_vardeclstatement" class="ntdef" style="color:olive">VarDeclStatement</span></span> = [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"ghost"</span></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"var"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ (
+ <span class="code-escaped"><a href="#1_localidenttypeoptional" class="ntref localref" style="color:maroon">LocalIdentTypeOptional</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span class="code-escaped"><a href="#1_localidenttypeoptional" class="ntref localref" style="color:maroon">LocalIdentTypeOptional</a></span> }
+ [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":="</span></span> <span class="code-escaped"><a href="#1_rhs" class="ntref localref" style="color:maroon">Rhs</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_rhs" class="ntref localref" style="color:maroon">Rhs</a></span> }
+ | { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span style="color:maroon">&quot;</span><span style="color:maroon">:|</span><span style="color:maroon">&quot;</span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"assume"</span></span> ] <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ ]
+ |
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> <span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span> } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span>
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":="</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ )
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span></code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_vardeclstatement" class="ntref localref" style="color:maroon">VarDeclStatement</a></span></code> is used to declare one or more local variables in a method or function.
+The type of each local variable must be given unless the variable is given an initial
+value in which case the type will be inferred. If initial values are given, the number of
+values must match the number of variables declared.
+</p>
+<p class="p indent">Note that the type of each variable must be given individually. The following code
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> x, y : <span style="color:teal">int</span>;</code></pre>
+<p class="p noindent para-continued">does not declare both <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">y</code> to be of type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code>. Rather it will give an
+error explaining that the type of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> is underspecified.
+</p>
+<p class="p indent">The lefthand side can also contain a tuple of patterns which will be
+matched against the right-hand-side. For example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">function</span> returnsTuple() : (<span style="color:teal">int</span>, <span style="color:teal">int</span>)
+{
+ (<span class="constant" style="color:purple">5</span>, <span class="constant" style="color:purple">10</span>)
+}
+
+<span style="color:blue">function</span> usesTuple() : <span style="color:teal">int</span>
+{
+ <span style="color:blue">var</span> (x, y) <span style="color:blue">:=</span> returnsTuple();
+ x + y
+}</code></pre><h3 id="sec-guards" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.7</span>.&#8194;</span>Guards</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_guard" class="ntdef" style="color:olive">Guard</span></span> = ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"*"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"*"</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> | <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) ) </code></pre>
+<p class="p noindent para-continued">Guards are used in <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">if</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">while</span></code> statements as boolean expressions. Guards
+take two forms.
+</p>
+<p class="p indent">The first and most common form is just a boolean expression.
+</p>
+<p class="p indent">The second form is either <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">*</code> or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(*)</code>. These have the same meaning. An
+unspecified boolean value is returned. The value returned
+may be different each time it is executed.
+</p><h3 id="sec-binding-guards" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.8</span>.&#8194;</span>Binding Guards</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_bindingguard" class="ntdef" style="color:olive">BindingGuard</span></span>(allowLambda) =
+ <span class="code-escaped"><a href="#1_identtypeoptional" class="ntref localref" style="color:maroon">IdentTypeOptional</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_identtypeoptional" class="ntref localref" style="color:maroon">IdentTypeOptional</a></span> } { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ <span style="color:maroon">&quot;</span><span style="color:maroon">:|</span><span style="color:maroon">&quot;</span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)</code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_bindingguard" class="ntref localref" style="color:maroon">BindingGuard</a></span></code> is used as a condition in an <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_ifstmt" class="ntref localref" style="color:maroon">IfStmt</a></span></code>.
+It binds the identifiers declared in the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_identtypeoptional" class="ntref localref" style="color:maroon">IdentTypeOptional</a></span></code>s.
+If there exists one or more assignments of values to the bound identifiers
+for which <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span></code> is true, then the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_bindingguard" class="ntref localref" style="color:maroon">BindingGuard</a></span></code>
+returns true and the identifiers are bound to values that make the
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span></code> true.
+</p>
+<p class="p indent">The identifiers bound by <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_bindingguard" class="ntref localref" style="color:maroon">BindingGuard</a></span></code> are ghost variables
+and cannot be assigned to non-ghost variables. They are only
+used in specification contexts.
+</p>
+<p class="p indent">Here is an example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">predicate</span> P(n: <span style="color:teal">int</span>)
+{
+ n % <span class="constant" style="color:purple">2</span> == <span class="constant" style="color:purple">0</span>
+}
+
+<span style="color:blue">method</span> M1() <span style="color:blue">returns</span> (<span style="color:blue">ghost</span> y: <span style="color:teal">int</span>)
+ <span style="color:purple">requires</span> <span class="code-escaped">&#8707;<span style="font-family:serif">&#8201;</span></span>x :: P(x)
+ <span style="color:purple">ensures</span> P(y)
+{
+ <span style="color:blue">if</span> x : <span style="color:teal">int</span> <span style="color:blue">:|</span> P(x) {
+ y <span style="color:blue">:=</span> x;
+ }
+}</code></pre><h3 id="sec-if-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.9</span>.&#8194;</span>If Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_ifstmt" class="ntdef" style="color:olive">IfStmt</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"if"</span></span>
+ ( <span class="code-escaped"><a href="#1_ifalternativeblock" class="ntref localref" style="color:maroon">IfAlternativeBlock</a></span>
+ |
+ ( <span class="code-escaped"><a href="#1_bindingguard" class="ntref localref" style="color:maroon">BindingGuard</a></span>(allowLambda: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ | <span class="code-escaped"><a href="#1_guard" class="ntref localref" style="color:maroon">Guard</a></span>
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&#8230;"</span></span>
+ )
+ <span class="code-escaped"><a href="#1_blockstmt" class="ntref localref" style="color:maroon">BlockStmt</a></span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"else"</span></span> ( <span class="code-escaped"><a href="#1_ifstmt" class="ntref localref" style="color:maroon">IfStmt</a></span> | <span class="code-escaped"><a href="#1_blockstmt" class="ntref localref" style="color:maroon">BlockStmt</a></span> ) ]
+ ) </code></pre>
+<p class="p noindent para-continued">In the simplest form an <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">if</span></code> statement uses a guard that is a boolean
+expression. It then has the same form as in C# and other common
+programming languages. For example
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:blue">if</span> x &lt; <span class="constant" style="color:purple">0</span> {
+ x <span style="color:blue">:=</span> -x;
+ } </code></pre>
+<p class="p noindent para-continued">If the guard is an asterisk then a non-deterministic choice is made:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:blue">if</span> * {
+ <span style="color:blue">print</span> <span style="color:maroon">&quot;</span><span style="color:maroon">True</span><span style="color:maroon">&quot;</span>;
+ } <span style="color:blue">else</span> {
+ <span style="color:blue">print</span> <span style="color:maroon">&quot;</span><span style="color:maroon">False</span><span style="color:maroon">&quot;</span>;
+ }</code></pre>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_ifalternativeblock" class="ntdef" style="color:olive">IfAlternativeBlock</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"{"</span></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"case"</span></span>
+ (
+ <span class="code-escaped"><a href="#1_bindingguard" class="ntref localref" style="color:maroon">BindingGuard</a></span>(allowLambda:<span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>)
+ | <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>)
+ ) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"=&gt;"</span></span> { <span class="code-escaped"><a href="#1_stmt" class="ntref localref" style="color:maroon">Stmt</a></span> } } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"}"</span></span> .</code></pre>
+<p class="p noindent para-continued">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">if</span></code> statement using the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">IfAlternativeBlock</code> form is similar to the
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">if</span> ... fi</code> construct used in the book &#8220;A Discipline of Programming&#8221; by
+Edsger W. Dijkstra. It is used for a multi-branch <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">if</span></code>.
+</p>
+<p class="p indent">For example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:blue">if</span> {
+ <span style="color:blue">case</span> x &lt;= y =&gt; max <span style="color:blue">:=</span> y;
+ <span style="color:blue">case</span> y &lt;= x =&gt; max <span style="color:blue">:=</span> y;
+ }</code></pre>
+<p class="p noindent para-continued">In this form the expressions following the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">case</span></code> keyword are called
+<em class="em-low1">guards</em>. The statement is evaluated by evaluating the guards in an
+undetermined order until one is found that is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code> or else all have
+evaluated to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">false</span></code>. If none of them evaluate to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code> then the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">if</span></code>
+statement does nothing. Otherwise the statements to the right of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">=&gt;</code>
+for the guard that evaluated to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code> are executed.
+</p><h3 id="sec-while-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.10</span>.&#8194;</span>While Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_whilestmt" class="ntdef" style="color:olive">WhileStmt</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"while"</span></span>
+ ( <span class="code-escaped"><a href="#1_loopspecwhile" class="ntref localref" style="color:maroon">LoopSpecWhile</a></span> <span class="code-escaped"><a href="#1_whilealternativeblock" class="ntref localref" style="color:maroon">WhileAlternativeBlock</a></span>
+ | ( <span class="code-escaped"><a href="#1_guard" class="ntref localref" style="color:maroon">Guard</a></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&#8230;"</span></span> ) <span class="code-escaped"><a href="#1_loopspec" class="ntref localref" style="color:maroon">LoopSpec</a></span>
+ ( <span class="code-escaped"><a href="#1_blockstmt" class="ntref localref" style="color:maroon">BlockStmt</a></span>
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&#8230;"</span></span>
+ | <span style="color:darkgreen">/*</span><span style="color:darkgreen"> go body-less </span><span style="color:darkgreen">*/</span>
+ )
+ ) </code></pre>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_whilealternativeblock" class="ntdef" style="color:olive">WhileAlternativeBlock</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"{"</span></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"case"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"=&gt;"</span></span> { <span class="code-escaped"><a href="#1_stmt" class="ntref localref" style="color:maroon">Stmt</a></span> } } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"}"</span></span> .</code></pre>
+<p class="p noindent para-continued">See section&nbsp;<a href="#sec-loop-specification" title="4.5.&#8194;Loop Specification" class="localref" style="target-element:h2"><span class="heading-label">4.5</span></a> for a description of <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_loopspec" class="ntref localref" style="color:maroon">LoopSpec</a></span></code>.
+</p>
+<p class="p indent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">while</span></code> statement is Dafny&#39;s only loop statement. It has two general
+forms.
+</p>
+<p class="p indent">The first form is similar to a while loop in a C-like language. For
+example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:blue">var</span> i <span style="color:blue">:=</span> <span class="constant" style="color:purple">0</span>;
+ <span style="color:blue">while</span> i &lt; <span class="constant" style="color:purple">5</span> {
+ i <span style="color:blue">:=</span> i + <span class="constant" style="color:purple">1</span>;
+ }</code></pre>
+<p class="p noindent para-continued">In this form the condition following the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">while</span></code> is one of these:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">A boolean expression. If true it means execute one more
+iteration of the loop. If false then terminate the loop.
+</li>
+<li class="li ul-li list-star-li compact-li">An asterisk (<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">*</code>), meaning non-deterministically yield either
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code> or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">false</span></code> as the value of the condition
+</li>
+<li class="li ul-li list-star-li compact-li">An ellipsis (<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">...</code>), which makes the while statement a <em class="em-low1">skeleton</em>
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">while</span></code> statement. TODO: What does that mean?
+</li></ul>
+
+<p class="p noindent">The <em class="em-low1">body</em> of the loop is usually a block statement, but it can also
+be a <em class="em-low1">skeleton</em>, denoted by ellipsis, or missing altogether.
+TODO: Wouldn&#39;t a missing body cause problems? Isn&#39;t it clearer to have
+a block statement with no statements inside?
+</p>
+<p class="p indent">The second form uses the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">WhileAlternativeBlock</code>. It is similar to the
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">do ... od</code> construct used in the book &#8220;A Discipline of Programming&#8221; by
+Edsger W. Dijkstra. For example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:blue">while</span>
+ <span style="color:purple">decreases</span> <span style="color:blue">if</span> <span class="constant" style="color:purple">0</span> &lt;= r <span style="color:blue">then</span> r <span style="color:blue">else</span> -r;
+ {
+ <span style="color:blue">case</span> r &lt; <span class="constant" style="color:purple">0</span> =&gt;
+ r <span style="color:blue">:=</span> r + <span class="constant" style="color:purple">1</span>;
+ <span style="color:blue">case</span> <span class="constant" style="color:purple">0</span> &lt; r =&gt;
+ r <span style="color:blue">:=</span> r - <span class="constant" style="color:purple">1</span>;
+ }</code></pre>
+<p class="p noindent para-continued">For this form the guards are evaluated in some undetermined order
+until one is found that is true, in which case the corresponding statements
+are executed. If none of the guards evaluates to true then the
+loop execution is terminated.
+</p><h4 id="sec-loop-specifications" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">21.10.0</span>.&#8194;</span>Loop Specifications</h4>
+<p class="p noindent">For some simple loops such as those mentioned previously Dafny can figure
+out what the loop is doing without more help. However in general the user
+must provide more information in order to help Dafny prove the effect of
+the loop. This information is provided by a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_loopspec" class="ntref localref" style="color:maroon">LoopSpec</a></span></code>. A
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_loopspec" class="ntref localref" style="color:maroon">LoopSpec</a></span></code> provides information about invariants, termination, and
+what the loop modifies. <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_loopspecs" class="ntref localref" style="color:maroon">LoopSpecs</a></span></code> are explained in
+section&nbsp;<a href="#sec-loop-specification" title="4.5.&#8194;Loop Specification" class="localref" style="target-element:h2"><span class="heading-label">4.5</span></a>. However the following sections
+present additional rationale and tutorial on loop specifications.
+</p><h5 id="sec-loop-invariants" class="h4" data-heading-depth="4" style="display:block"><span class="heading-before"><span class="heading-label">21.10.0.0</span>.&#8194;</span>Loop Invariants</h5>
+<p class="p noindent"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">While</code> loops present a problem for Dafny. There is no way for Dafny to
+know in advance how many times the code will go around the loop. But
+Dafny needs to consider all paths through a program, which could include
+going around the loop any number of times. To make it possible for Dafny
+to work with loops, you need to provide loop invariants, another kind of
+annotation.
+</p>
+<p class="p indent">A loop invariant is an expression that holds upon entering a loop, and
+after every execution of the loop body. It captures something that is
+invariant, i.e. does not change, about every step of the loop. Now,
+obviously we are going to want to change variables, etc. each time around
+the loop, or we wouldn&#39;t need the loop. Like pre- and postconditions, an
+invariant is a property that is preserved for each execution of the loop,
+expressed using the same boolean expressions we have seen. For example,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> i <span style="color:blue">:=</span> <span class="constant" style="color:purple">0</span>;
+<span style="color:blue">while</span> i &lt; n
+ <span style="color:purple">invariant</span> <span class="constant" style="color:purple">0</span> &lt;= i
+{
+ i <span style="color:blue">:=</span> i + <span class="constant" style="color:purple">1</span>;
+}</code></pre>
+<p class="p noindent para-continued">When you specify an invariant, Dafny proves two things: the invariant
+holds upon entering the loop, and it is preserved by the loop. By
+preserved, we mean that assuming that the invariant holds at the
+beginning of the loop, we must show that executing the loop body once
+makes the invariant hold again. Dafny can only know upon analyzing the
+loop body what the invariants say, in addition to the loop guard (the
+loop condition). Just as Dafny will not discover properties of a method
+on its own, it will not know any but the most basic properties of a loop
+are preserved unless it is told via an invariant.
+</p><h5 id="sec-loop-termination" class="h4" data-heading-depth="4" style="display:block"><span class="heading-before"><span class="heading-label">21.10.0.1</span>.&#8194;</span>Loop Termination</h5>
+<p class="p noindent">Dafny proves that code terminates, i.e. does not loop forever, by using
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">decreases</span></code> annotations. For many things, Dafny is able to guess the right
+annotations, but sometimes it needs to be made explicit. In fact, for all
+of the code we have seen so far, Dafny has been able to do this proof on
+its own, which is why we haven&#39;t seen the decreases annotation explicitly
+yet. There are two places Dafny proves termination: loops and recursion.
+Both of these situations require either an explicit annotation or a
+correct guess by Dafny.
+</p>
+<p class="p indent">A <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">decreases</span></code> annotation, as its name suggests, gives Dafny an expression
+that decreases with every loop iteration or recursive call. There are two
+conditions that Dafny needs to verify when using a decreases expression:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">that the expression actually gets smaller, and
+</li>
+<li class="li ul-li list-star-li compact-li">that it is bounded.
+</li></ul>
+
+<p class="p noindent">Many times, an integral value (natural or plain integer) is the quantity
+that decreases, but other things that can be used as well. In the case of
+integers, the bound is assumed to be zero. For example, the following is
+a proper use of decreases on a loop (with its own keyword, of course):
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:blue">while</span> <span class="constant" style="color:purple">0</span> &lt; i
+ <span style="color:purple">invariant</span> <span class="constant" style="color:purple">0</span> &lt;= i
+ <span style="color:purple">decreases</span> i
+ {
+ i <span style="color:blue">:=</span> i - <span class="constant" style="color:purple">1</span>;
+ }</code></pre>
+<p class="p noindent para-continued">Here Dafny has all the ingredients it needs to prove termination. The
+variable i gets smaller each loop iteration, and is bounded below by
+zero. This is fine, except the loop is backwards from most loops, which
+tend to count up instead of down. In this case, what decreases is not the
+counter itself, but rather the distance between the counter and the upper
+bound. A simple trick for dealing with this situation is given below:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:blue">while</span> i &lt; n
+ <span style="color:purple">invariant</span> <span class="constant" style="color:purple">0</span> &lt;= i &lt;= n
+ <span style="color:purple">decreases</span> n - i
+ {
+ i <span style="color:blue">:=</span> i + <span class="constant" style="color:purple">1</span>;
+ }</code></pre>
+<p class="p noindent para-continued">This is actually Dafny&#39;s guess for this situation, as it sees <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">i &lt; n</code> and
+assumes that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n - i</code> is the quantity that decreases. The upper bound of the
+loop invariant implies that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0</span> &lt;= n – i</code>, and gives Dafny a lower bound on
+the quantity. This also works when the bound <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">n</code> is not constant, such as
+in the binary search algorithm, where two quantities approach each other,
+and neither is fixed.
+</p>
+<p class="p indent">If the <strong class="strong-star2">decreases</strong> clause of a loop specified &#8220;*&#8221;, then no
+termination check will be performed. Use of this feature is sound only with
+respect to partial correctness.
+</p><h5 id="sec-loop-framing" class="h4" data-heading-depth="4" style="display:block"><span class="heading-before"><span class="heading-label">21.10.0.2</span>.&#8194;</span>Loop Framing</h5>
+<p class="p noindent">In some cases we also must specify what memory locations the loop body
+is allowed to modify. This is done using a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">modifies</span></code> clause.
+See the discussion of framing in methods for a fuller discussion.
+</p><h3 id="sec-match-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.11</span>.&#8194;</span>Match Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_matchstmt" class="ntdef" style="color:olive">MatchStmt</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"match"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"{"</span></span> { <span class="code-escaped"><a href="#1_casestatement" class="ntref localref" style="color:maroon">CaseStatement</a></span> } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"}"</span></span>
+ | { <span class="code-escaped"><a href="#1_casestatement" class="ntref localref" style="color:maroon">CaseStatement</a></span> }
+ )
+
+<span class="code-escaped"><span id="1_casestatement" class="ntdef" style="color:olive">CaseStatement</span></span> = <span class="code-escaped"><a href="#1_casebinding_" class="ntref localref" style="color:maroon">CaseBinding_</a></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"=&gt;"</span></span> { <span class="code-escaped"><a href="#1_stmt" class="ntref localref" style="color:maroon">Stmt</a></span> } </code></pre>
+<p class="p noindent para-continued">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">match</span></code> statement is used to do case analysis on a value of inductive
+or co-inductive type. The form with no leading <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_ident" class="ntref localref" style="color:maroon">Ident</a></span></code> is for matching
+tuples. The expression after the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">match</span></code> keyword is the (co)inductive
+value being matched. The expression is evaluated and then matched against
+each of the case clauses.
+</p>
+<p class="p indent">There must be a case clause for each constructor of the data type.
+The identifier after the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">case</span></code> keyword in a case clause, if present,
+must be the name of one of the data type&#39;s constructors.
+If the constructor takes parameters then a parenthesis-enclosed
+list of identifiers (with optional type) must follow the
+constructor. There must be as many identifiers as the constructor
+has parameters. If the optional type is given it must be the same
+as the type of the corresponding parameter of the constructor.
+If no type is given then the type of the corresponding parameter
+is the type assigned to the identifier.
+</p>
+<p class="p indent">When an inductive value that was created using constructor
+expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C1(v1, v2)</code> is matched against a case clause
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C2(x1, x2</code>), there is a match provided that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C1</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">C2</code> are the
+same constructor. In that case <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x1</code> is bound to value <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">v1</code> and
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x2</code> is bound to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">v2</code>. The identifiers in the case pattern
+are not mutable. Here is an example of the use of a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">match</span></code> statement.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">datatype</span> Tree = Empty | Node(left: Tree, data: <span style="color:teal">int</span>, right: Tree)
+
+<span style="color:darkgreen">// Return the sum of the data in a tree.</span>
+<span style="color:blue">method</span> Sum(x: Tree) <span style="color:blue">returns</span> (r: <span style="color:teal">int</span>)
+{
+ <span style="color:blue">match</span> x {
+ <span style="color:blue">case</span> Empty =&gt; r <span style="color:blue">:=</span> -<span class="constant" style="color:purple">1</span>;
+ <span style="color:blue">case</span> Node(t1 : Tree, d, t2) =&gt; {
+ <span style="color:blue">var</span> v1 <span style="color:blue">:=</span> Sum(t1);
+ <span style="color:blue">var</span> v2 <span style="color:blue">:=</span> Sum(t2);
+ r <span style="color:blue">:=</span> v1 + d + v2;
+ }
+ }
+}</code></pre>
+<p class="p noindent para-continued">Note that the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Sum</code> method is recursive yet has no <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">decreases</span></code> annotation.
+In this case it is not needed because Dafny is able to deduce that
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t1</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">t2</code> are <em class="em-low1">smaller</em> (structurally) than <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code>. If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Tree</code> had been
+coinductive this would not have been possible since <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> might have been
+infinite.
+</p><h3 id="sec-assert-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.12</span>.&#8194;</span>Assert Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_assertstmt" class="ntdef" style="color:olive">AssertStmt</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"assert"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ ( <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&#8230;"</span></span>
+ ) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span> </code></pre>
+<p class="p noindent para-continued"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Assert</code> statements are used to express logical proposition that are
+expected to be true. Dafny will attempt to prove that the assertion
+is true and give an error if not. Once it has proved the assertion
+it can then use its truth to aid in following deductions.
+Thus if Dafny is having a difficult time verifying a method
+the user may help by inserting assertions that Dafny can prove,
+and whose true may aid in the larger verification effort.
+</p>
+<p class="p indent">If the proposition is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">...</code> then (TODO: what does this mean?).
+</p><h3 id="sec-assume-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.13</span>.&#8194;</span>Assume Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_assumestmt" class="ntdef" style="color:olive">AssumeStmt</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"assume"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ ( <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&#8230;"</span></span>
+ ) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span> </code></pre>
+<p class="p noindent para-continued">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Assume</code> statement lets the user specify a logical proposition
+that Dafny may assume to be true without proof. If in fact the
+proposition is not true this may lead to invalid conclusions.
+</p>
+<p class="p indent">An <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Assume</code> statement would ordinarily be used as part of a larger
+verification effort where verification of some other part of
+the program required the proposition. By using the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Assume</code> statement
+the other verification can proceed. Then when that is completed the
+user would come back and replace the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">assume</span></code> with <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">assert</span></code>.
+</p>
+<p class="p indent">If the proposition is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">...</code> then (TODO: what does this mean?).
+</p><h3 id="sec-print-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.14</span>.&#8194;</span>Print Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_printstmt" class="ntdef" style="color:olive">PrintStmt</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"print"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span> </code></pre>
+<p class="p noindent para-continued">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">print</span></code> statement is used to print the values of a comma-separated
+list of expressions to the console. The generated C# code uses
+the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">System.Object.ToString()</code> method to convert the values to printable
+strings. The expressions may of course include strings that are used
+for captions. There is no implicit new line added, so to get a new
+line you should include &#8220;\n&#8221; as part of one of the expressions.
+Dafny automatically creates overrides for the ToString() method
+for Dafny data types. For example,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">datatype</span> Tree = Empty | Node(left: Tree, data: <span style="color:teal">int</span>, right: Tree)
+<span style="color:blue">method</span> Main()
+{
+ <span style="color:blue">var</span> x : Tree <span style="color:blue">:=</span> Node(Node(Empty, <span class="constant" style="color:purple">1</span>, Empty), <span class="constant" style="color:purple">2</span>, Empty);
+ <span style="color:blue">print</span> <span style="color:maroon">&quot;</span><span style="color:maroon">x=</span><span style="color:maroon">&quot;</span>, x, <span style="color:maroon">&quot;</span><span style="color:gray">\n</span><span style="color:maroon">&quot;</span>;
+}</code></pre>
+<p class="p noindent para-continued">produces this output:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>x=Tree.Node(Tree.Node(Tree.Empty, <span class="constant" style="color:purple">1</span>, Tree.Empty), <span class="constant" style="color:purple">2</span>, Tree.Empty)</code></pre><h3 id="sec-forall-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.15</span>.&#8194;</span>Forall Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_forallstmt" class="ntdef" style="color:olive">ForallStmt</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"forall"</span></span>
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> [ <span class="code-escaped"><a href="#1_quantifierdomain" class="ntref localref" style="color:maroon">QuantifierDomain</a></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span>
+ | [ <span class="code-escaped"><a href="#1_quantifierdomain" class="ntref localref" style="color:maroon">QuantifierDomain</a></span> ]
+ )
+ { [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"free"</span></span> ] <span class="code-escaped"><a href="#1_forallensuresclause_" class="ntref localref" style="color:maroon">ForAllEnsuresClause_</a></span> }
+ [ <span class="code-escaped"><a href="#1_blockstmt" class="ntref localref" style="color:maroon">BlockStmt</a></span> ] </code></pre>
+<p class="p noindent para-continued">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> statement executes ensures expressions or a body in
+parallel for all quantified values in the specified range.
+The use of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">parallel</code> keyword is deprecated. Use
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> instead. There are several variant uses of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code>
+statement. And there are a number of restrictions.
+</p>
+<p class="p indent">In particular a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> statement can be classified as one of the following:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li"><em class="em-low1">Assign</em> - the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> statement is used for simultaneous assignment.
+The target must be an array element or an object field.
+</li>
+<li class="li ul-li list-star-li compact-li"><em class="em-low1">Call</em> - The body consists of a single call to a method without side effects
+</li>
+<li class="li ul-li list-star-li compact-li"><em class="em-low1">Proof</em> - The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> has <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">ensure</code> expressions which are effectively
+quantified or proved by the body (if present).
+</li></ul>
+
+<p class="p noindent">An <em class="em-low1">assign</em> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> statement is to perform simultaneous assignment.
+The following is an excerpt of an example given by Leino in
+<a href="http://research.microsoft.com/en-us/um/people/leino/papers/krml233.pdf" data-linkid="leino233">Developing Verified Programs with Dafny</a>.
+When the buffer holding the queue needs to be resized,
+the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> statement is used to simultaneously copy the old contents
+into the new buffer.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">class</span> {<span style="color:purple">:autocontracts</span>} SimpleQueue&lt;Data&gt;
+{
+ <span style="color:blue">ghost</span> <span style="color:blue">var</span> Contents: <span style="color:teal">seq</span>&lt;Data&gt;;
+ <span style="color:blue">var</span> a: <span style="color:teal">array</span>&lt;Data&gt;; <span style="color:darkgreen">// Buffer holding contents of queue.</span>
+ <span style="color:blue">var</span> m: <span style="color:teal">int</span> <span style="color:darkgreen">// Index head of queue.</span>
+ <span style="color:blue">var</span> n: <span style="color:teal">int</span>; <span style="color:darkgreen">// Index just past end of queue</span>
+ ...
+ <span style="color:blue">method</span> Enqueue(d: Data)
+ <span style="color:purple">ensures</span> Contents == <span style="color:blue">old</span>(Contents) + [d]
+ {
+ <span style="color:blue">if</span> n == a.Length {
+ <span style="color:blue">var</span> b <span style="color:blue">:=</span> a;
+ <span style="color:blue">if</span> m == <span class="constant" style="color:purple">0</span> { b <span style="color:blue">:=</span> <span style="color:blue">new</span> Data[<span class="constant" style="color:purple">2</span> * a.Length]; }
+ <span style="color:blue">forall</span> (i | <span class="constant" style="color:purple">0</span> &lt;= i &lt; n - m) {
+ b[i] <span style="color:blue">:=</span> a[m + i];
+ }
+ a, m, n <span style="color:blue">:=</span> b, <span class="constant" style="color:purple">0</span>, n - m;
+ }
+ a[n], n, Contents <span style="color:blue">:=</span> d, n + <span class="constant" style="color:purple">1</span>, Contents + [d];
+ }
+}</code></pre>
+<p class="p noindent para-continued">Here is an example of a <em class="em-low1">call</em> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> statement and the
+callee. This is contained in the CloudMake-ConsistentBuilds.dfy
+test in the Dafny repository.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">forall</span> (cmd&#39;, deps&#39;, e&#39; | Hash(Loc(cmd&#39;, deps&#39;, e&#39;)) == Hash(Loc(cmd, deps, e))) {
+ HashProperty(cmd&#39;, deps&#39;, e&#39;, cmd, deps, e);
+}
+
+<span style="color:blue">ghost</span> <span style="color:blue">method</span> HashProperty(cmd: Expression, deps: Expression, ext: <span style="color:teal">string</span>,
+ cmd&#39;: Expression, deps&#39;: Expression, ext&#39;: <span style="color:teal">string</span>)
+ <span style="color:purple">requires</span> Hash(Loc(cmd, deps, ext)) == Hash(Loc(cmd&#39;, deps&#39;, ext&#39;))
+ <span style="color:purple">ensures</span> cmd == cmd&#39; &amp;&amp; deps == deps&#39; &amp;&amp; ext == ext&#39;</code></pre>
+<p class="p noindent para-continued">From the same file here is an example of a <em class="em-low1">proof</em> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> statement.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">forall</span> (p | p <span style="color:blue">in</span> DomSt(stCombinedC.st) &amp;&amp; p <span style="color:blue">in</span> DomSt(stExecC.st))
+ <span style="color:purple">ensures</span> GetSt(p, stCombinedC.st) == GetSt(p, stExecC.st)
+{
+ <span style="color:blue">assert</span> DomSt(stCombinedC.st) &lt;= DomSt(stExecC.st);
+ <span style="color:blue">assert</span> stCombinedC.st == Restrict(DomSt(stCombinedC.st), stExecC.st);
+}</code></pre>
+<p class="p noindent para-continued">More generally the statement
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">forall</span> x | P(x) { Lemma(x); }</code></pre>
+<p class="p noindent para-continued">is used to invoke <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Lemma(x)</code> on all <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> for which <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P(x)</code> holds. If
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Lemma(x)</code> ensures <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q(x)</code>, then the forall statement establishes
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">forall</span> x :: P(x) ==&gt; Q(x).</code></pre>
+<p class="p noindent para-continued">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> statement is also used extensively in the desugared forms of
+co-predicates and co-lemmas. See section&nbsp;<a href="#sec-co-inductive-datatypes" title="18.2.&#8194;Co-inductive datatypes" class="localref" style="target-element:h2"><span class="heading-label">18.2</span></a>.
+</p>
+<p class="p indent">TODO: List all of the restrictions on the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">forall</span></code> statement.
+</p><h3 id="sec-modify-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.16</span>.&#8194;</span>Modify Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_modifystmt" class="ntdef" style="color:olive">ModifyStmt</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"modify"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ ( <span class="code-escaped"><a href="#1_frameexpression" class="ntref localref" style="color:maroon">FrameExpression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_frameexpression" class="ntref localref" style="color:maroon">FrameExpression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) }
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&#8230;"</span></span>
+ )
+ ( <span class="code-escaped"><a href="#1_blockstmt" class="ntref localref" style="color:maroon">BlockStmt</a></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span> ) </code></pre>
+<p class="p noindent para-continued">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">modify</span></code> statement has two forms which have two different
+purposes.
+</p>
+<p class="p indent">When the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">modify</span></code> statement ends with a semi-colon rather than
+a block statement its effect is to say that some undetermined
+modifications have been made to any or all of the memory
+locations specified by the&nbsp;<a href="#sec-frame-expressions" class="localref">frame expressions</a>.
+In the following example, a value is assigned to field <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code>
+followed by a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">modify</span></code> statement that may modify any field
+in the object. After that we can no longer prove that the field
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> still has the value we assigned to it.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">class</span> MyClass {
+ <span style="color:blue">var</span> x: <span style="color:teal">int</span>;
+ <span style="color:blue">method</span> N()
+ <span style="color:purple">modifies</span> <span style="color:blue">this</span>
+ {
+ x <span style="color:blue">:=</span> <span class="constant" style="color:purple">18</span>;
+ <span style="color:blue">modify</span> <span style="color:blue">this</span>;
+ <span style="color:blue">assert</span> x == <span class="constant" style="color:purple">18</span>; <span style="color:darkgreen">// error: cannot conclude this here</span>
+ }
+}</code></pre>
+<p class="p noindent para-continued">When the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">modify</span></code> statement is followed by a block statement
+we are instead specifying what can be modified in that
+block statement. Namely, only memory locations specified
+by the frame expressions of the block <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">modify</span></code> statement
+may be modified. Consider the following example.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">class</span> ModifyBody {
+ <span style="color:blue">var</span> x: <span style="color:teal">int</span>;
+ <span style="color:blue">var</span> y: <span style="color:teal">int</span>;
+ <span style="color:blue">method</span> M0()
+ <span style="color:purple">modifies</span> <span style="color:blue">this</span>
+ {
+ <span style="color:blue">modify</span> {} {
+ x <span style="color:blue">:=</span> <span class="constant" style="color:purple">3</span>; <span style="color:darkgreen">// error: violates modifies clause of the modify statement</span>
+ }
+ }
+ <span style="color:blue">method</span> M1()
+ <span style="color:purple">modifies</span> <span style="color:blue">this</span>
+ {
+ <span style="color:blue">modify</span> {} {
+ <span style="color:blue">var</span> o <span style="color:blue">:=</span> <span style="color:blue">new</span> ModifyBody;
+ o.x <span style="color:blue">:=</span> <span class="constant" style="color:purple">3</span>; <span style="color:darkgreen">// fine</span>
+ }
+ }
+ <span style="color:blue">method</span> M2()
+ <span style="color:purple">modifies</span> <span style="color:blue">this</span>
+ {
+ <span style="color:blue">modify</span> <span style="color:blue">this</span> {
+ x <span style="color:blue">:=</span> <span class="constant" style="color:purple">3</span>;
+ }
+ }
+}</code></pre>
+<p class="p noindent para-continued">The first <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">modify</span></code> statement in the example has an empty
+frame expression so it cannot modify any memory locations.
+So an error is reported when it tries to modify field <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code>.
+</p>
+<p class="p indent">The second <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">modify</span></code> statement also has an empty frame
+expression. But it allocates a new object and modifies it.
+Thus we see that the frame expressions on a block <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">modify</span></code>
+statement only limits what may be modified of existing
+memory. It does not limit what may be modified in
+new memory that is allocated.
+</p>
+<p class="p indent">The third <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">modify</span></code> statement has a frame expression that
+allows it to modify any of the fields of the current object,
+so the modification of field <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> is allowed.
+</p><h3 id="sec-calc-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.17</span>.&#8194;</span>Calc Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_calcstmt" class="ntdef" style="color:olive">CalcStmt</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"calc"</span></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } [ <span class="code-escaped"><a href="#1_calcop" class="ntref localref" style="color:maroon">CalcOp</a></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"{"</span></span> <span class="code-escaped"><a href="#1_calcbody" class="ntref localref" style="color:maroon">CalcBody</a></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"}"</span></span>
+<span class="code-escaped"><span id="1_calcbody" class="ntdef" style="color:olive">CalcBody</span></span> = { <span class="code-escaped"><a href="#1_calcline" class="ntref localref" style="color:maroon">CalcLine</a></span> [ <span class="code-escaped"><a href="#1_calcop" class="ntref localref" style="color:maroon">CalcOp</a></span> ] <span class="code-escaped"><a href="#1_hints" class="ntref localref" style="color:maroon">Hints</a></span> }
+<span class="code-escaped"><span id="1_calcline" class="ntdef" style="color:olive">CalcLine</span></span> = <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span>
+<span class="code-escaped"><span id="1_hints" class="ntdef" style="color:olive">Hints</span></span> = { ( <span class="code-escaped"><a href="#1_blockstmt" class="ntref localref" style="color:maroon">BlockStmt</a></span> | <span class="code-escaped"><a href="#1_calcstmt" class="ntref localref" style="color:maroon">CalcStmt</a></span> ) }
+<span class="code-escaped"><span id="1_calcop" class="ntdef" style="color:olive">CalcOp</span></span> =
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"=="</span></span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"#"</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"["</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"]"</span></span> ]
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&lt;"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&gt;"</span></span>
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"!="</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&lt;="</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&gt;="</span></span>
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&lt;==&gt;"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"==&gt;"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&lt;=="</span></span>
+ ) </code></pre>
+<p class="p noindent para-continued">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">calc</span></code> statement supports <em class="em-low1">calculational proofs</em> using a language feature called <em class="em-low1">program-oriented calculations</em> (poC). This feature was introduced and explained in the&nbsp;<a href="http://research.microsoft.com/en-us/um/people/leino/papers/krml231.pdf" data-linkid="verified calculations">Verified Calculations</a> paper by
+Leino and Polikarpova<span class="citations" style="target-element:bibitem">[<a href="#leino:dafny:calc" title="K.&#160;Rustan&#160;M. Leino and Nadia Polikarpova.
+Verified calculations." class="bibref localref" style="target-element:bibitem"><span class="cite-number">22</span></a>]</span>. Please see that paper for a more complete explanation
+of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">calc</span></code> statement. We here mention only the highlights.
+</p>
+<p class="p noindent">Calculational proofs are proofs by stepwise formula manipulation
+as is taught in elementary algebra. The typical example is to prove
+an equality by starting with a left-hand-side, and through a series of
+transformations morph it into the desired right-hand-side.
+</p>
+<p class="p indent">Non-syntactic rules further restrict hints to only ghost and side-effect
+free statements, as well as impose a constraint that only
+chain-compatible operators can be used together in a calculation. The
+notion of chain-compatibility is quite intuitive for the operators
+supported by poC; for example, it is clear that &#8220;&lt;&#8221; and &#8220;&gt;&#8221; cannot be used within
+the same calculation, as there would be no relation to conclude between
+the first and the last line. See the&nbsp;<a href="http://research.microsoft.com/en-us/um/people/leino/papers/krml231.pdf" data-linkid="verified calculations">paper</a> for
+a more formal treatment of chain-compatibility.
+</p>
+<p class="p indent">Note that we allow a single occurrence of the intransitive operator &#8220;!=&#8221; to
+appear in a chain of equalities (that is, &#8220;!=&#8221; is chain-compatible with
+equality but not with any other operator, including itself). Calculations
+with fewer than two lines are allowed, but have no effect. If a step
+operator is omitted, it defaults to the calculation-wide operator,
+defined after the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">calc</span></code> keyword. If that operator if omitted, it defaults
+to equality.
+</p>
+<p class="p indent">Here is an example using <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">calc</span></code> statements to prove an elementary
+algebraic identity. As it turns out Dafny is able to prove this without
+the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">calc</span></code> statements, but it helps to illustrate the syntax.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">lemma</span> docalc(x : <span style="color:teal">int</span>, y: <span style="color:teal">int</span>)
+ <span style="color:purple">ensures</span> (x + y) * (x + y) == x * x + <span class="constant" style="color:purple">2</span> * x * y + y * y
+{
+ <span style="color:blue">calc</span> {
+ (x + y) * (x + y); ==
+ <span style="color:darkgreen">// distributive law: (a + b) * c == a * c + b * c</span>
+ x * (x + y) + y * (x + y); ==
+ <span style="color:darkgreen">// distributive law: a * (b + c) == a * b + a * c</span>
+ x * x + x * y + y * x + y * y; ==
+ <span style="color:blue">calc</span> {
+ y * x; ==
+ x * y;
+ }
+ x * x + x * y + x * y + y * y; ==
+ <span style="color:blue">calc</span> {
+ x * y + x * y; ==
+ <span style="color:darkgreen">// a = 1 * a</span>
+ <span class="constant" style="color:purple">1</span> * x * y + <span class="constant" style="color:purple">1</span> * x * y; ==
+ <span style="color:darkgreen">// Distributive law</span>
+ (<span class="constant" style="color:purple">1</span> + <span class="constant" style="color:purple">1</span>) * x * y; ==
+ <span class="constant" style="color:purple">2</span> * x * y;
+ }
+ x * x + <span class="constant" style="color:purple">2</span> * x * y + y * y;
+ }
+}</code></pre>
+<p class="p noindent para-continued">Here we started with <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(x + y) * (x + y)</code> as the left-hand-side
+expressions and gradually transformed it using distributive,
+commutative and other laws into the desired right-hand-side.
+</p>
+<p class="p indent">The justification for the steps are given as comments, or as
+nested <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">calc</span></code> statements that prove equality of some sub-parts
+of the expression.
+</p>
+<p class="p indent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">==</code> to the right of the semicolons show the relation between
+that expression and the next. Because of the transitivity of
+equality we can then conclude that the original left-hand-side is
+equal to the final expression.
+</p>
+<p class="p indent">We can avoid having to supply the relational operator between
+every pair of expressions by giving a default operator between
+the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">calc</span></code> keyword and the opening brace as shown in this abbreviated
+version of the above calc statement:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">calc</span> == {
+ (x + y) * (x + y);
+ x * (x + y) + y * (x + y);
+ x * x + x * y + y * x + y * y;
+ x * x + x * y + x * y + y * y;
+ x * x + <span class="constant" style="color:purple">2</span> * x * y + y * y;
+}</code></pre>
+<p class="p noindent para-continued">And since equality is the default operator we could have omitted
+it after the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">calc</span></code> keyword.
+The purpose of the block statements or the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">calc</span></code> statements between
+the expressions is to provide hints to aid Dafny in proving that
+step. As shown in the example, comments can also be used to aid
+the human reader in cases where Dafny can prove the step automatically.
+
+</p><h3 id="sec-skeleton-statement" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">21.18</span>.&#8194;</span>Skeleton Statement</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_skeletonstmt" class="ntdef" style="color:olive">SkeletonStmt</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&#8230;"</span></span>
+ [<span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"where"</span></span> <span class="code-escaped"><a href="#1_ident" class="ntref localref" style="color:maroon">Ident</a></span> {<span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_ident" class="ntref localref" style="color:maroon">Ident</a></span> } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":="</span></span>
+ <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ {<span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) }
+ ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span> </code></pre><h2 id="sec-expressions" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">22</span>.&#8194;</span>Expressions</h2>
+<p class="p noindent">The grammar of Dafny expressions follows a hierarchy that
+reflects the precedence of Dafny operators. The following
+table shows the Dafny operators and their precedence
+in order of increasing binding power.
+</p><table class="madoko block">
+<thead><tr><th class="thead tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></th><th class="thead tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></th></tr>
+<tr><th class="col cell-border-left thead tr-odd tr-last tr-first col-odd col-first" data-row="1" data-col="1" style="font-weight:bold"> operator </th><th class="col cell-border-left cell-border-right thead tr-odd tr-last tr-first col-even col-last" data-row="1" data-col="2" style="font-weight:bold"> description </th></tr></thead>
+<tbody><tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="0" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="0" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left col-odd col-first" data-row="1" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">;</code> </td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right col-even col-last" data-row="1" data-col="2"> In LemmaCall;Expression </td></tr>
+<tr><td class="tbody tr-odd tr-first col cell-border-left cell-line col-odd col-first" data-row="1" data-col="1"></td><td class="tbody tr-odd tr-first col cell-border-left cell-border-right cell-line col-even col-last" data-row="1" data-col="2"></td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="2" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;==&gt;</code>, &#8660; </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="2" data-col="2"> equivalence (if and only if) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="2" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="2" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="3" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">==&gt;</code>, &#8658; </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="3" data-col="2"> implication (implies) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="4" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;==</code>, &#8656; </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="4" data-col="2"> reverse implication (follows from) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="4" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="4" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="5" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&amp;&amp;</code>, &#8743; </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="5" data-col="2"> conjunction (and) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="6" data-col="1"> <span class="monospace">||</span>, &#8744; </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="6" data-col="2"> disjunction (or) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="6" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="6" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="7" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!</code>, &#172; </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="7" data-col="2"> negation (not) </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left cell-line col-odd col-first" data-row="7" data-col="1"></td><td class="tbody tr-odd col cell-border-left cell-border-right cell-line col-even col-last" data-row="7" data-col="2"></td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="8" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">==</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="8" data-col="2"> equality </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="9" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">==#[k]</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="9" data-col="2"> prefix equality (co-inductive) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="10" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!=</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="10" data-col="2"> disequality </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="11" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!=#[k]</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="11" data-col="2"> prefix disequality (co-inductive) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="12" data-col="1"> <span class="monospace">&lt;</span> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="12" data-col="2"> less than </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="13" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;=</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="13" data-col="2"> at most </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="14" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&gt;=</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="14" data-col="2"> at least </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="15" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&gt;</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="15" data-col="2"> greater than </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="16" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">in</span></code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="16" data-col="2"> collection membership </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="17" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!<span style="color:blue">in</span></code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="17" data-col="2"> collection non-membership </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="18" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!!</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="18" data-col="2"> disjointness </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="18" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="18" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="19" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">+</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="19" data-col="2"> addition (plus) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="20" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">-</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="20" data-col="2"> subtraction (minus) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="20" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="20" data-col="2"></td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="21" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">*</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="21" data-col="2"> multiplication (times) </td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="22" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">/</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="22" data-col="2"> division (divided by) </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="23" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">%</code> </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="23" data-col="2"> modulus (mod) </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left cell-line col-odd col-first" data-row="23" data-col="1"></td><td class="tbody tr-odd col cell-border-left cell-border-right cell-line col-even col-last" data-row="23" data-col="2"></td></tr>
+<tr><td class="tbody tr-even col cell-border-left col-odd col-first" data-row="24" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">-</code> </td><td class="tbody tr-even col cell-border-left cell-border-right col-even col-last" data-row="24" data-col="2"> arithmetic negation (unary minus) </td></tr>
+<tr><td class="tbody tr-odd col cell-border-left col-odd col-first" data-row="25" data-col="1"> <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!</code>, &#172; </td><td class="tbody tr-odd col cell-border-left cell-border-right col-even col-last" data-row="25" data-col="2"> logical negation </td></tr>
+<tr><td class="tbody tr-even tr-last col cell-border-left col-odd col-first" data-row="26" data-col="1"> Primary Expressions </td><td class="tbody tr-even tr-last col cell-border-left cell-border-right col-even col-last" data-row="26" data-col="2"> </td></tr>
+<tr><td class="tbody tr-even col cell-border-left cell-line col-odd col-first" data-row="26" data-col="1"></td><td class="tbody tr-even col cell-border-left cell-border-right cell-line col-even col-last" data-row="26" data-col="2"></td></tr></tbody></table>
+<p class="p noindent">We are calling the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_unaryexpression" class="ntref localref" style="color:maroon">UnaryExpression</a></span></code>s that are neither
+arithmetic nor logical negation the <em class="em-low1">primary expressions</em>.
+They are the most tightly bound.
+</p>
+<p class="p indent">In the grammar entries below we explain the meaning when the
+operator for that precedence level is present. If the
+operator is not present then we just descend to the
+next precedence level.
+</p><h3 id="sec-top-level-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.0</span>.&#8194;</span>Top-level expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_expression" class="ntdef" style="color:olive">Expression</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ <span class="code-escaped"><a href="#1_equivexpression" class="ntref localref" style="color:maroon">EquivExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) ] </code></pre>
+<p class="p noindent para-continued">The &#8220;allowLemma&#8221; argument says whether or not the expression
+to be parsed is allowed to have the form S;E where S is a call to a lemma.
+&#8220;allowLemma&#8221; should be passed in as &#8220;false&#8221; whenever the expression to
+be parsed sits in a context that itself is terminated by a semi-colon.
+</p>
+<p class="p indent">The &#8220;allowLambda&#8221; says whether or not the expression to be parsed is
+allowed to be a lambda expression. More precisely, an identifier or
+parenthesized-enclosed comma-delimited list of identifiers is allowed to
+continue as a lambda expression (that is, continue with a &#8220;reads&#8221;, &#8220;requires&#8221;,
+or &#8220;=&gt;&#8221;) only if &#8220;allowLambda&#8221; is true. This affects function/method/iterator
+specifications, if/while statements with guarded alternatives, and expressions
+in the specification of a lambda expression itself.
+</p>
+<p class="p indent">Sometimes an expression will fail unless some relevant fact is known.
+In the following example the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">F_Fails</code> function fails to verify
+because the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Fact(n)</code> divisor may be zero. But preceding
+the expression by a lemma that ensures that the denominator
+is not zero allows function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">F_Succeeds</code> to succeed.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">function</span> Fact(n: <span style="color:teal">nat</span>): <span style="color:teal">nat</span>
+{
+ <span style="color:blue">if</span> n == <span class="constant" style="color:purple">0</span> <span style="color:blue">then</span> <span class="constant" style="color:purple">1</span> <span style="color:blue">else</span> n * Fact(n-<span class="constant" style="color:purple">1</span>)
+}
+
+<span style="color:blue">lemma</span> L(n: <span style="color:teal">nat</span>)
+ <span style="color:purple">ensures</span> <span class="constant" style="color:purple">1</span> &lt;= Fact(n)
+{
+}
+
+<span style="color:blue">function</span> F_Fails(n: <span style="color:teal">nat</span>): <span style="color:teal">int</span>
+{
+ <span class="constant" style="color:purple">50</span> / Fact(n) <span style="color:darkgreen">// error: possible division by zero</span>
+}
+
+<span style="color:blue">function</span> F_Succeeds(n: <span style="color:teal">nat</span>): <span style="color:teal">int</span>
+{
+ L(n);
+ <span class="constant" style="color:purple">50</span> / Fact(n)
+}</code></pre><h3 id="sec-equivalence-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.1</span>.&#8194;</span>Equivalence Expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_equivexpression" class="ntdef" style="color:olive">EquivExpression</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ <span class="code-escaped"><a href="#1_impliesexpliesexpression" class="ntref localref" style="color:maroon">ImpliesExpliesExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&lt;==&gt;"</span></span> <span class="code-escaped"><a href="#1_impliesexpliesexpression" class="ntref localref" style="color:maroon">ImpliesExpliesExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) } </code></pre>
+<p class="p noindent para-continued">An <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_equivexpression" class="ntref localref" style="color:maroon">EquivExpression</a></span></code> that contains one or more &#8220;&lt;==&gt;&#8221;s is
+a boolean expression and all the contained <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_impliesexpliesexpression" class="ntref localref" style="color:maroon">ImpliesExpliesExpression</a></span></code>
+must also be boolean expressions. In that case each &#8220;&lt;==&gt;&#8221;
+operator tests for logical equality which is the same as
+ordinary equality.
+</p>
+<p class="p indent">See section&nbsp;<a href="#sec-equivalence-operator" title="6.0.0.&#8194;Equivalence Operator" class="localref" style="target-element:h3"><span class="heading-label">6.0.0</span></a> for an explanation of the
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;==&gt;</code> operator as compared with the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">==</code> operator.
+</p><h3 id="sec-implies-or-explies-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.2</span>.&#8194;</span>Implies or Explies Expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_impliesexpliesexpression" class="ntdef" style="color:olive">ImpliesExpliesExpression</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ <span class="code-escaped"><a href="#1_logicalexpression" class="ntref localref" style="color:maroon">LogicalExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ [ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"==&gt;"</span></span> <span class="code-escaped"><a href="#1_impliesexpression" class="ntref localref" style="color:maroon">ImpliesExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&lt;=="</span></span> <span class="code-escaped"><a href="#1_logicalexpression" class="ntref localref" style="color:maroon">LogicalExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&lt;=="</span></span> <span class="code-escaped"><a href="#1_logicalexpression" class="ntref localref" style="color:maroon">LogicalExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) }
+ )
+ ]
+
+<span class="code-escaped"><span id="1_impliesexpression" class="ntdef" style="color:olive">ImpliesExpression</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ <span class="code-escaped"><a href="#1_logicalexpression" class="ntref localref" style="color:maroon">LogicalExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"==&gt;"</span></span> <span class="code-escaped"><a href="#1_impliesexpression" class="ntref localref" style="color:maroon">ImpliesExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) ] </code></pre>
+<p class="p noindent para-continued">See section&nbsp;<a href="#sec-implication-and-reverse-implication" title="6.0.2.&#8194;Implication and Reverse Implication" class="localref" style="target-element:h3"><span class="heading-label">6.0.2</span></a> for an explanation
+of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">==&gt;</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;==</code> operators.
+</p><h3 id="sec-logical-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.3</span>.&#8194;</span>Logical Expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_logicalexpression" class="ntdef" style="color:olive">LogicalExpression</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ <span class="code-escaped"><a href="#1_relationalexpression" class="ntref localref" style="color:maroon">RelationalExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ [ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&amp;&amp;"</span></span> <span class="code-escaped"><a href="#1_relationalexpression" class="ntref localref" style="color:maroon">RelationalExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&amp;&amp;"</span></span> <span class="code-escaped"><a href="#1_relationalexpression" class="ntref localref" style="color:maroon">RelationalExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) }
+ | <span style="color:maroon">&quot;</span><span style="color:maroon">||</span><span style="color:maroon">&quot;</span> <span class="code-escaped"><a href="#1_relationalexpression" class="ntref localref" style="color:maroon">RelationalExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ { <span style="color:maroon">&quot;</span><span style="color:maroon">||</span><span style="color:maroon">&quot;</span> <span class="code-escaped"><a href="#1_relationalexpression" class="ntref localref" style="color:maroon">RelationalExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) }
+ )
+ ] </code></pre>
+<p class="p noindent para-continued">See section&nbsp;<a href="#sec-conjunction-and-disjunction" title="6.0.1.&#8194;Conjunction and Disjunction" class="localref" style="target-element:h3"><span class="heading-label">6.0.1</span></a> for an explanation
+of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&amp;&amp;</code> (or &#8743;) and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">||</code> (or &#8744;) operators.
+</p><h3 id="sec-relational-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.4</span>.&#8194;</span>Relational Expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_relationalexpression" class="ntdef" style="color:olive">RelationalExpression</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ <span class="code-escaped"><a href="#1_term" class="ntref localref" style="color:maroon">Term</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ [ <span class="code-escaped"><a href="#1_relop" class="ntref localref" style="color:maroon">RelOp</a></span> <span class="code-escaped"><a href="#1_term" class="ntref localref" style="color:maroon">Term</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ { <span class="code-escaped"><a href="#1_relop" class="ntref localref" style="color:maroon">RelOp</a></span> <span class="code-escaped"><a href="#1_term" class="ntref localref" style="color:maroon">Term</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) } ]
+
+<span class="code-escaped"><span id="1_relop" class="ntdef" style="color:olive">RelOp</span></span> =
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"=="</span></span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"#"</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"["</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"]"</span></span> ]
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&lt;"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&gt;"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&lt;="</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"&gt;="</span></span>
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"!="</span></span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"#"</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"["</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"]"</span></span> ]
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"in"</span></span>
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"!in"</span></span>
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"!!"</span></span>
+ )
+ </code></pre>
+<p class="p noindent para-continued">The relation expressions that have a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_relop" class="ntref localref" style="color:maroon">RelOp</a></span></code> compare two or more terms.
+As explained in section&nbsp;<a href="#sec-basic-types" title="6.&#8194;Basic types" class="localref" style="target-element:h1"><span class="heading-label">6</span></a>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">==</code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!=</code>, <code class="grammar-ref code code2 language-java lang-java java colorized">&lt;</code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&gt;</code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&lt;=</code>, and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">&gt;=</code>
+and their corresponding Unicode equivalents are <em class="em-low1">chaining</em>.
+</p>
+<p class="p indent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">in</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!<span style="color:blue">in</span></code> operators apply to collection types as explained in
+section&nbsp;<a href="#sec-collection-types" title="9.&#8194;Collection types" class="localref" style="target-element:h1"><span class="heading-label">9</span></a> and represent membership or non-membership
+respectively.
+</p>
+<p class="p indent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!!</code> represents disjointness for sets and multisets as explained in
+sections&nbsp;<a href="#sec-sets" title="9.0.&#8194;Sets" class="localref" style="target-element:h2"><span class="heading-label">9.0</span></a> and&nbsp;<a href="#sec-multisets" title="9.1.&#8194;Multisets" class="localref" style="target-element:h2"><span class="heading-label">9.1</span></a>.
+</p>
+<p class="p indent">Note that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x ==#[k] y</code> is the prefix equality operator that compares
+co-inductive values for equality to a nesting level of k, as
+explained in section&nbsp;<a href="#sec-co-equality" title="18.2.3.0.&#8194;Co-Equality" class="localref" style="target-element:h4"><span class="heading-label">18.2.3.0</span></a>.
+</p><h3 id="sec-terms" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.5</span>.&#8194;</span>Terms</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_term" class="ntdef" style="color:olive">Term</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ <span class="code-escaped"><a href="#1_factor" class="ntref localref" style="color:maroon">Factor</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ { <span class="code-escaped"><a href="#1_addop" class="ntref localref" style="color:maroon">AddOp</a></span> <span class="code-escaped"><a href="#1_factor" class="ntref localref" style="color:maroon">Factor</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) }
+<span class="code-escaped"><span id="1_addop" class="ntdef" style="color:olive">AddOp</span></span> = ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"+"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"-"</span></span> ) </code></pre>
+<p class="p noindent para-continued"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Terms</code> combine <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Factors</code> by adding or subtracting.
+Addition has these meanings for different types:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">Arithmetic addition for numeric types (section&nbsp;<a href="#sec-numeric-types" title="6.1.&#8194;Numeric types" class="localref" style="target-element:h2"><span class="heading-label">6.1</span></a>).
+</li>
+<li class="li ul-li list-star-li compact-li">Union for sets and multisets (sections&nbsp;<a href="#sec-sets" title="9.0.&#8194;Sets" class="localref" style="target-element:h2"><span class="heading-label">9.0</span></a> and&nbsp;<a href="#sec-multisets" title="9.1.&#8194;Multisets" class="localref" style="target-element:h2"><span class="heading-label">9.1</span></a>)
+</li>
+<li class="li ul-li list-star-li compact-li">Concatenation for sequences (section&nbsp;<a href="#sec-sequences" title="9.2.&#8194;Sequences" class="localref" style="target-element:h2"><span class="heading-label">9.2</span></a>)
+</li></ul>
+
+<p class="p noindent">Subtraction is arithmetic subtraction for numeric types, and set or multiset
+difference for sets and multisets.
+</p><h3 id="sec-factors" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.6</span>.&#8194;</span>Factors</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_factor" class="ntdef" style="color:olive">Factor</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ <span class="code-escaped"><a href="#1_unaryexpression" class="ntref localref" style="color:maroon">UnaryExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ { <span class="code-escaped"><a href="#1_mulop" class="ntref localref" style="color:maroon">MulOp</a></span> <span class="code-escaped"><a href="#1_unaryexpression" class="ntref localref" style="color:maroon">UnaryExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) }
+<span class="code-escaped"><span id="1_mulop" class="ntdef" style="color:olive">MulOp</span></span> = ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"*"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"/"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"%"</span></span> ) </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_factor" class="ntref localref" style="color:maroon">Factor</a></span></code> combines <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_unaryexpression" class="ntref localref" style="color:maroon">UnaryExpression</a></span></code>s using multiplication,
+division, or modulus. For numeric types these are explained in
+section&nbsp;<a href="#sec-numeric-types" title="6.1.&#8194;Numeric types" class="localref" style="target-element:h2"><span class="heading-label">6.1</span></a>.
+</p>
+<p class="p indent">Only <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">*</code> has a non-numeric application. It represents set or multiset
+intersection as explained in sections&nbsp;<a href="#sec-sets" title="9.0.&#8194;Sets" class="localref" style="target-element:h2"><span class="heading-label">9.0</span></a> and&nbsp;<a href="#sec-multisets" title="9.1.&#8194;Multisets" class="localref" style="target-element:h2"><span class="heading-label">9.1</span></a>.
+</p><h3 id="sec-unary-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.7</span>.&#8194;</span>Unary Expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_unaryexpression" class="ntdef" style="color:olive">UnaryExpression</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"-"</span></span> <span class="code-escaped"><a href="#1_unaryexpression" class="ntref localref" style="color:maroon">UnaryExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"!"</span></span> <span class="code-escaped"><a href="#1_unaryexpression" class="ntref localref" style="color:maroon">UnaryExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ | <span class="code-escaped"><a href="#1_primaryexpression_" class="ntref localref" style="color:maroon">PrimaryExpression_</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ )
+ </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_unaryexpression" class="ntref localref" style="color:maroon">UnaryExpression</a></span></code> applies either numeric (section&nbsp;<a href="#sec-numeric-types" title="6.1.&#8194;Numeric types" class="localref" style="target-element:h2"><span class="heading-label">6.1</span></a>)
+or logical (section&nbsp;<a href="#sec-booleans" title="6.0.&#8194;Booleans" class="localref" style="target-element:h2"><span class="heading-label">6.0</span></a>) negation to its operand.
+</p><h3 id="sec-primary-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.8</span>.&#8194;</span>Primary Expressions</h3><!-- These are introduced for explanatory purposes as are not in the grammar. -->
+
+
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_primaryexpression_" class="ntdef" style="color:olive">PrimaryExpression_</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ ( <span class="code-escaped"><a href="#1_mapdisplayexpr" class="ntref localref" style="color:maroon">MapDisplayExpr</a></span> { <span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span> }
+ | <span class="code-escaped"><a href="#1_lambdaexpression" class="ntref localref" style="color:maroon">LambdaExpression</a></span>(allowLemma)
+ | <span class="code-escaped"><a href="#1_endlessexpression" class="ntref localref" style="color:maroon">EndlessExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ | <span class="code-escaped"><a href="#1_namesegment" class="ntref localref" style="color:maroon">NameSegment</a></span> { <span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span> }
+ | <span class="code-escaped"><a href="#1_seqdisplayexpr" class="ntref localref" style="color:maroon">SeqDisplayExpr</a></span> { <span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span> }
+ | <span class="code-escaped"><a href="#1_setdisplayexpr" class="ntref localref" style="color:maroon">SetDisplayExpr</a></span> { <span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span> }
+ | <span class="code-escaped"><a href="#1_multisetexpr" class="ntref localref" style="color:maroon">MultiSetExpr</a></span> { <span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span> }
+ | <span class="code-escaped"><a href="#1_constatomexpression" class="ntref localref" style="color:maroon">ConstAtomExpression</a></span> { <span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span> }
+ )
+ </code></pre>
+<p class="p noindent para-continued">After descending through all the binary and unary operators we arrive at
+the primary expressions which are explained in subsequent sections. As
+can be seen, a number of these can be followed by 0 or more <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span></code>es
+to select a component of the value.
+</p>
+<p class="p indent">If the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">allowLambda</code> is false then <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_lambdaexpression" class="ntref localref" style="color:maroon">LambdaExpression</a></span></code>s are not
+recognized in this context.
+</p><h3 id="sec-lambda-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.9</span>.&#8194;</span>Lambda expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_lambdaexpression" class="ntdef" style="color:olive">LambdaExpression</span></span>(allowLemma) =
+ ( <span class="code-escaped"><a href="#1_wildident" class="ntref localref" style="color:maroon">WildIdent</a></span>
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> [ <span class="code-escaped"><a href="#1_identtypeoptional" class="ntref localref" style="color:maroon">IdentTypeOptional</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_identtypeoptional" class="ntref localref" style="color:maroon">IdentTypeOptional</a></span> } ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span>
+ )
+ <span class="code-escaped"><a href="#1_lambdaspec_" class="ntref localref" style="color:maroon">LambdaSpec_</a></span>
+ <span class="code-escaped"><a href="#1_lambdaarrow" class="ntref localref" style="color:maroon">LambdaArrow</a></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+
+<span class="code-escaped"><span id="1_lambdaarrow" class="ntdef" style="color:olive">LambdaArrow</span></span> = ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"=&gt;"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"-&gt;"</span></span> ) </code></pre>
+<p class="p noindent para-continued">See section&nbsp;<a href="#sec-lambda-specification" title="4.3.&#8194;Lambda Specification" class="localref" style="target-element:h2"><span class="heading-label">4.3</span></a> for a description of <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_lambdaspec" class="ntref localref" style="color:maroon">LambdaSpec</a></span></code>.
+</p>
+<p class="p indent">In addition to named functions, Dafny supports expressions that define
+functions. These are called <em class="em-low1">lambda (expression)s</em> (some languages
+know them as <em class="em-low1">anonymous functions</em>). A lambda expression has the
+form:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>(<span class="code-escaped"><em class="em-low1">params</em></span>) <span class="code-escaped"><em class="em-low1">specification</em></span> =&gt; <span class="code-escaped"><em class="em-low1">body</em></span></code></pre>
+<p class="p noindent para-continued">where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="code-escaped"><em class="em-low1">params</em></span></code> is a comma-delimited list of parameter
+declarations, each of which has the form <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x: T</code>. The type <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">T</code>
+of a parameter can be omitted when it can be inferred. If the
+identifier <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> is not needed, it can be replaced by &#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_</code>&#8221;. If
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="code-escaped"><em class="em-low1">params</em></span></code> consists of a single parameter <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x</code> (or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_</code>) without an
+explicit type, then the parentheses can be dropped; for example, the
+function that returns the successor of a given integer can be written
+as the following lambda expression:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>x =&gt; x + <span class="constant" style="color:purple">1</span></code></pre>
+<p class="p noindent para-continued">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="code-escaped"><em class="em-low1">specification</em></span></code> is a list of clauses <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">requires</span> E</code> or
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">reads</span> W</code>, where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">E</code> is a boolean expression and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">W</code> is a frame
+expression.
+</p>
+<p class="p indent"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="code-escaped"><em class="em-low1">body</em></span></code> is an expression that defines the function&#39;s return
+value. The body must be well-formed for all possible values of the
+parameters that satisfy the precondition (just like the bodies of
+named functions and methods). In some cases, this means it is
+necessary to write explicit <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">requires</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">reads</span></code> clauses. For
+example, the lambda expression
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>x <span style="color:purple">requires</span> x != <span class="constant" style="color:purple">0</span> =&gt; <span class="constant" style="color:purple">100</span> / x</code></pre>
+<p class="p noindent para-continued">would not be well-formed if the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">requires</span></code> clause were omitted,
+because of the possibility of division-by-zero.
+</p>
+<p class="p indent">In settings where functions cannot be partial and there are no
+restrictions on reading the heap, the <em class="em-low1">eta expansion</em> of a function
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">F: T -&gt; U</code> (that is, the wrapping of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">F</code> inside a lambda expression
+in such a way that the lambda expression is equivalent to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">F</code>) would
+be written <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x =&gt; F(x)</code>. In Dafny, eta expansion must also account for
+the precondition and reads set of the function, so the eta expansion
+of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">F</code> looks like:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>x <span style="color:purple">requires</span> F.<span style="color:purple">requires</span>(x) <span style="color:purple">reads</span> F.<span style="color:purple">reads</span>(x) =&gt; F(x)</code></pre><h3 id="sec-left-hand-side-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.10</span>.&#8194;</span>Left-Hand-Side Expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_lhs" class="ntdef" style="color:olive">Lhs</span></span> =
+ ( <span class="code-escaped"><a href="#1_namesegment" class="ntref localref" style="color:maroon">NameSegment</a></span> { <span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span> }
+ | <span class="code-escaped"><a href="#1_constatomexpression" class="ntref localref" style="color:maroon">ConstAtomExpression</a></span> <span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span> { <span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span> }
+ ) </code></pre>
+<p class="p noindent para-continued">A left-hand-side expression is only used on the left hand
+side of an <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_updatestmt" class="ntref localref" style="color:maroon">UpdateStmt</a></span></code>.
+</p>
+<p class="p indent">TODO: Try to give examples showing how these kinds of
+left-hand-sides are possible.
+</p><h3 id="sec-right-hand-side-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.11</span>.&#8194;</span>Right-Hand-Side Expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_rhs" class="ntdef" style="color:olive">Rhs</span></span> =
+ ( <span class="code-escaped"><a href="#1_arrayallocation_" class="ntref localref" style="color:maroon">ArrayAllocation_</a></span>
+ | <span class="code-escaped"><a href="#1_objectallocation_" class="ntref localref" style="color:maroon">ObjectAllocation_</a></span>
+ | <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ | <span class="code-escaped"><a href="#1_havocrhs_" class="ntref localref" style="color:maroon">HavocRhs_</a></span>
+ )
+ { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } </code></pre>
+<p class="p noindent para-continued">An <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_rhs" class="ntref localref" style="color:maroon">Rhs</a></span></code> is either array allocation, an object allocation,
+an expression, or a havoc right-hand-side, optionally followed
+by one or more <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span></code>s.
+</p>
+<p class="p indent">Right-hand-side expressions appear in the following constructs:
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_returnstmt" class="ntref localref" style="color:maroon">ReturnStmt</a></span></code>, <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_yieldstmt" class="ntref localref" style="color:maroon">YieldStmt</a></span></code>, <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_updatestmt" class="ntref localref" style="color:maroon">UpdateStmt</a></span></code>, or <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_vardeclstatement" class="ntref localref" style="color:maroon">VarDeclStatement</a></span></code>.
+These are the only contexts in which arrays or objects may be
+allocated, or in which havoc may be produced.
+</p><h3 id="sec-array-allocation" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.12</span>.&#8194;</span>Array Allocation</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_arrayallocation_" class="ntdef" style="color:olive">ArrayAllocation_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"new"</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"["</span></span> <span class="code-escaped"><a href="#1_expressions" class="ntref localref" style="color:maroon">Expressions</a></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"]"</span></span> </code></pre>
+<p class="p noindent para-continued">This allocates a new single or multi-dimensional array as explained in
+section&nbsp;<a href="#sec-array-types" title="14.&#8194;Array Types" class="localref" style="target-element:h1"><span class="heading-label">14</span></a>.
+</p><h3 id="sec-object-allocation" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.13</span>.&#8194;</span>Object Allocation</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_objectallocation_" class="ntdef" style="color:olive">ObjectAllocation_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"new"</span></span> <span class="code-escaped"><a href="#1_type" class="ntref localref" style="color:maroon">Type</a></span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> [ <span class="code-escaped"><a href="#1_expressions" class="ntref localref" style="color:maroon">Expressions</a></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> ] </code></pre>
+<p class="p noindent para-continued">This allocated a new object of a class type as explained
+in section&nbsp;<a href="#sec-class-types" title="12.&#8194;Class Types" class="localref" style="target-element:h1"><span class="heading-label">12</span></a>.
+</p><h3 id="sec-havoc-right-hand-side" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.14</span>.&#8194;</span>Havoc Right-Hand-Side</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_havocrhs_" class="ntdef" style="color:olive">HavocRhs_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"*"</span></span></code></pre>
+<p class="p noindent para-continued">A havoc right-hand-side produces an arbitrary value of its associated
+type. To get a more constrained arbitrary value the &#8220;assign-such-that&#8221;
+operator (<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">:|</span></code>) can be used. See section&nbsp;<a href="#sec-update-statement" title="21.5.&#8194;Update Statement" class="localref" style="target-element:h2"><span class="heading-label">21.5</span></a>.
+</p><h3 id="sec-constant-or-atomic-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.15</span>.&#8194;</span>Constant Or Atomic Expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_constatomexpression" class="ntdef" style="color:olive">ConstAtomExpression</span></span> =
+ ( <span class="code-escaped"><a href="#1_literalexpression_" class="ntref localref" style="color:maroon">LiteralExpression_</a></span>
+ | <span class="code-escaped"><a href="#1_freshexpression_" class="ntref localref" style="color:maroon">FreshExpression_</a></span>
+ | <span class="code-escaped"><a href="#1_oldexpression_" class="ntref localref" style="color:maroon">OldExpression_</a></span>
+ | <span class="code-escaped"><a href="#1_cardinalityexpression_" class="ntref localref" style="color:maroon">CardinalityExpression_</a></span>
+ | <span class="code-escaped"><a href="#1_numericconversionexpression_" class="ntref localref" style="color:maroon">NumericConversionExpression_</a></span>
+ | <span class="code-escaped"><a href="#1_parensexpression" class="ntref localref" style="color:maroon">ParensExpression</a></span>
+ ) </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_constatomexpression" class="ntref localref" style="color:maroon">ConstAtomExpression</a></span></code> represent either a constant of some type, or an
+atomic expression. A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_constatomexpression" class="ntref localref" style="color:maroon">ConstAtomExpression</a></span></code> is never an l-value. Also, a
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_constatomexpression" class="ntref localref" style="color:maroon">ConstAtomExpression</a></span></code> is never followed by an open parenthesis (but could
+very well have a suffix that starts with a period or a square bracket).
+(The &#8220;Also&#8230;&#8221; part may change if expressions in Dafny could yield
+functions.)
+</p><h3 id="sec-literal-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.16</span>.&#8194;</span>Literal Expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_literalexpression_" class="ntdef" style="color:olive">LiteralExpression_</span></span> =
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"false"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"true"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"null"</span></span> | <span class="code-escaped"><a href="#1_nat" class="ntref localref" style="color:maroon">Nat</a></span> | <span class="code-escaped"><a href="#1_dec" class="ntref localref" style="color:maroon">Dec</a></span> |
+ <span class="code-escaped"><a href="#2_chartoken" class="ntref localref" style="color:maroon">charToken</a></span> | <span class="code-escaped"><a href="#2_stringtoken" class="ntref localref" style="color:maroon">stringToken</a></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"this"</span></span>) </code></pre>
+<p class="p noindent para-continued">A literal expression is a boolean literal, a null object reference,
+an unsigned integer or real literal, a character or string literal,
+or &#8220;this&#8221; which denote the current object in the context of
+an instance method or function.
+</p><h3 id="sec-fresh-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.17</span>.&#8194;</span>Fresh Expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_freshexpression_" class="ntdef" style="color:olive">FreshExpression_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"fresh"</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> </code></pre>
+<p class="p noindent para-continued"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">fresh</span>(e)</code> returns a boolean value that is true if
+the objects referenced in expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e</code> were all
+freshly allocated in the current method invocation.
+The argument of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">fresh</span></code> must be either an object reference
+or a collection of object references.
+</p><h3 id="sec-old-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.18</span>.&#8194;</span>Old Expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_oldexpression_" class="ntdef" style="color:olive">OldExpression_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"old"</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> </code></pre>
+<p class="p noindent para-continued">An <em class="em-low1">old expression</em> is used in postconditions. <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">old</span>(e)</code> evaluates to
+the value expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">e</code> had on entry to the current method.
+</p><h3 id="sec-cardinality-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.19</span>.&#8194;</span>Cardinality Expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_cardinalityexpression_" class="ntdef" style="color:olive">CardinalityExpression_</span></span> = <span style="color:maroon">&quot;</span><span style="color:maroon">|</span><span style="color:maroon">&quot;</span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) <span style="color:maroon">&quot;</span><span style="color:maroon">|</span><span style="color:maroon">&quot;</span> </code></pre>
+<p class="p noindent para-continued">For a collection expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">c</code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">|c|</code> is the cardinality of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">c</code>. For a
+set or sequence the cardinality is the number of elements. For
+a multiset the cardinality is the sum of the multiplicities of the
+elements. For a map the cardinality is the cardinality of the
+domain of the map. Cardinality is not defined for infinite maps.
+For more see section&nbsp;<a href="#sec-collection-types" title="9.&#8194;Collection types" class="localref" style="target-element:h1"><span class="heading-label">9</span></a>.
+</p><h3 id="sec-numeric-conversion-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.20</span>.&#8194;</span>Numeric Conversion Expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_numericconversionexpression_" class="ntdef" style="color:olive">NumericConversionExpression_</span></span> =
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"int"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"real"</span></span> ) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> </code></pre>
+<p class="p noindent para-continued">Numeric conversion expressions give the name of the target type
+followed by the expression being converted in parentheses.
+This production is for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">int</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">real</span></code> as the target types
+but this also applies more generally to other numeric types,
+e.g. <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">newtypes</code>. See section&nbsp;<a href="#sec-numeric-conversion-operations" title="19.0.&#8194;Numeric conversion operations" class="localref" style="target-element:h2"><span class="heading-label">19.0</span></a>.
+</p><h3 id="sec-parenthesized-expression" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.21</span>.&#8194;</span>Parenthesized Expression</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_parensexpression" class="ntdef" style="color:olive">ParensExpression</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> [ <span class="code-escaped"><a href="#1_expressions" class="ntref localref" style="color:maroon">Expressions</a></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_parensexpression" class="ntref localref" style="color:maroon">ParensExpression</a></span></code> is a list of zero or more expressions
+enclosed in parentheses.
+</p>
+<p class="p indent">If there is exactly one expression enclosed then the value is just
+the value of that expression.
+</p>
+<p class="p indent">If there are zero or more than one the result is a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">tuple</code> value.
+See section&nbsp;<a href="#sec-tuple-types" title="18.1.&#8194;Tuple types" class="localref" style="target-element:h2"><span class="heading-label">18.1</span></a>.
+</p><h3 id="sec-sequence-display-expression" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.22</span>.&#8194;</span>Sequence Display Expression</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_seqdisplayexpr" class="ntdef" style="color:olive">SeqDisplayExpr</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"["</span></span> [ <span class="code-escaped"><a href="#1_expressions" class="ntref localref" style="color:maroon">Expressions</a></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"]"</span></span> </code></pre>
+<p class="p noindent para-continued">A sequence display expression provide a way to constructing
+a sequence with given values. For example
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>[<span class="constant" style="color:purple">1</span>, <span class="constant" style="color:purple">2</span>, <span class="constant" style="color:purple">3</span>]</code></pre>
+<p class="p noindent para-continued">is a sequence with three elements in it.
+See section&nbsp;<a href="#sec-sequences" title="9.2.&#8194;Sequences" class="localref" style="target-element:h2"><span class="heading-label">9.2</span></a> for more information on
+sequences.
+</p><h3 id="sec-set-display-expression" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.23</span>.&#8194;</span>Set Display Expression</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_setdisplayexpr" class="ntdef" style="color:olive">SetDisplayExpr</span></span> = [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"iset"</span></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"{"</span></span> [ <span class="code-escaped"><a href="#1_expressions" class="ntref localref" style="color:maroon">Expressions</a></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"}"</span></span> </code></pre>
+<p class="p noindent para-continued">A set display expression provide a way to constructing
+a set with given elements. If the keyword <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">iset</span></code> is present
+then a potentially infinite set is constructed.
+</p>
+<p class="p indent">For example
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>{<span class="constant" style="color:purple">1</span>, <span class="constant" style="color:purple">2</span>, <span class="constant" style="color:purple">3</span>}</code></pre>
+<p class="p noindent para-continued">is a set with three elements in it.
+See section&nbsp;<a href="#sec-sets" title="9.0.&#8194;Sets" class="localref" style="target-element:h2"><span class="heading-label">9.0</span></a> for more information on
+sets.
+</p><h3 id="sec-multiset-display-or-cast-expression" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.24</span>.&#8194;</span>Multiset Display or Cast Expression</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_multisetexpr" class="ntdef" style="color:olive">MultiSetExpr</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"multiset"</span></span>
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"{"</span></span> [ <span class="code-escaped"><a href="#1_expressions" class="ntref localref" style="color:maroon">Expressions</a></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"}"</span></span>
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span>
+ ) </code></pre>
+<p class="p noindent para-continued">A multiset display expression provide a way to constructing
+a multiset with given elements and multiplicity. For example
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:teal">multiset</span>{<span class="constant" style="color:purple">1</span>, <span class="constant" style="color:purple">1</span>, <span class="constant" style="color:purple">2</span>, <span class="constant" style="color:purple">3</span>}</code></pre>
+<p class="p noindent para-continued">is a multiset with three elements in it. The number 1 has a multiplicity of 2,
+the others a multiplicity of 1.
+</p>
+<p class="p indent">On the other hand, a multiset cast expression converts a set or a sequence
+into a multiset as shown here:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> s : <span style="color:teal">set</span>&lt;<span style="color:teal">int</span>&gt; <span style="color:blue">:=</span> {<span class="constant" style="color:purple">1</span>, <span class="constant" style="color:purple">2</span>, <span class="constant" style="color:purple">3</span>};
+<span style="color:blue">var</span> ms : <span style="color:teal">multiset</span>&lt;<span style="color:teal">int</span>&gt; <span style="color:blue">:=</span> <span style="color:teal">multiset</span>(s);
+ms <span style="color:blue">:=</span> ms + <span style="color:teal">multiset</span>{<span class="constant" style="color:purple">1</span>};
+<span style="color:blue">var</span> sq : <span style="color:teal">seq</span>&lt;<span style="color:teal">int</span>&gt; <span style="color:blue">:=</span> [<span class="constant" style="color:purple">1</span>, <span class="constant" style="color:purple">1</span>, <span class="constant" style="color:purple">2</span>, <span class="constant" style="color:purple">3</span>];
+<span style="color:blue">var</span> ms2 : <span style="color:teal">multiset</span>&lt;<span style="color:teal">int</span>&gt; <span style="color:blue">:=</span> <span style="color:teal">multiset</span>(sq);
+<span style="color:blue">assert</span> ms == ms2;</code></pre>
+<p class="p noindent para-continued">See section&nbsp;<a href="#sec-multisets" title="9.1.&#8194;Multisets" class="localref" style="target-element:h2"><span class="heading-label">9.1</span></a> for more information on
+multisets.
+</p><h3 id="sec-map-display-expression" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.25</span>.&#8194;</span>Map Display Expression</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_mapdisplayexpr" class="ntdef" style="color:olive">MapDisplayExpr</span></span> = (<span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"map"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"imap"</span></span> ) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"["</span></span> [ <span class="code-escaped"><a href="#1_mapliteralexpressions" class="ntref localref" style="color:maroon">MapLiteralExpressions</a></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"]"</span></span>
+<span class="code-escaped"><span id="1_mapliteralexpressions" class="ntdef" style="color:olive">MapLiteralExpressions</span></span> =
+ <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":="</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":="</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ }</code></pre>
+<p class="p noindent para-continued">A map display expression builds a finite or potentially infinite
+map from explicit <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_mapliteralexpressions" class="ntref localref" style="color:maroon">MapLiteralExpressions</a></span></code>. For example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> m <span style="color:blue">:=</span> <span style="color:teal">map</span>[<span class="constant" style="color:purple">1</span> <span style="color:blue">:=</span> <span style="color:maroon">&quot;</span><span style="color:maroon">a</span><span style="color:maroon">&quot;</span>, <span class="constant" style="color:purple">2</span> <span style="color:blue">:=</span> <span style="color:maroon">&quot;</span><span style="color:maroon">b</span><span style="color:maroon">&quot;</span>];
+<span style="color:blue">ghost</span> <span style="color:blue">var</span> im <span style="color:blue">:=</span> <span style="color:teal">imap</span>[<span class="constant" style="color:purple">1</span> <span style="color:blue">:=</span> <span style="color:maroon">&quot;</span><span style="color:maroon">a</span><span style="color:maroon">&quot;</span>, <span class="constant" style="color:purple">2</span> <span style="color:blue">:=</span> <span style="color:maroon">&quot;</span><span style="color:maroon">b</span><span style="color:maroon">&quot;</span>];</code></pre>
+<p class="p noindent para-continued">Note that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">imap</span></code>s may only appear in ghost contexts. See
+section&nbsp;<a href="#sec-finite-and-infinite-maps" title="9.3.&#8194;Finite and Infinite Maps" class="localref" style="target-element:h2"><span class="heading-label">9.3</span></a> for more details on maps and imaps.
+</p><h3 id="sec-endless-expression" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.26</span>.&#8194;</span>Endless Expression</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_endlessexpression" class="ntdef" style="color:olive">EndlessExpression</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ ( <span class="code-escaped"><a href="#1_ifexpression_" class="ntref localref" style="color:maroon">IfExpression_</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ | <span class="code-escaped"><a href="#1_matchexpression" class="ntref localref" style="color:maroon">MatchExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ | <span class="code-escaped"><a href="#1_quantifierexpression" class="ntref localref" style="color:maroon">QuantifierExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ | <span class="code-escaped"><a href="#1_setcomprehensionexpr" class="ntref localref" style="color:maroon">SetComprehensionExpr</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ | <span class="code-escaped"><a href="#1_stmtinexpr" class="ntref localref" style="color:maroon">StmtInExpr</a></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ | <span class="code-escaped"><a href="#1_letexpr" class="ntref localref" style="color:maroon">LetExpr</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ | <span class="code-escaped"><a href="#1_mapcomprehensionexpr" class="ntref localref" style="color:maroon">MapComprehensionExpr</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ ) </code></pre><!-- Experimental - do not document.
+ | NamedExpr(allowLemma, allowLambda)
+-->
+
+
+
+<p class="p noindent para-continued"><code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_endlessexpression" class="ntref localref" style="color:maroon">EndlessExpression</a></span></code> gets it name from the fact that all its alternate
+productions have no terminating symbol to end them, but rather they
+all end with an <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span></code> at the end. The various
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_endlessexpression" class="ntref localref" style="color:maroon">EndlessExpression</a></span></code> alternatives are described below.
+</p><h3 id="sec-if-expression" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.27</span>.&#8194;</span>If Expression</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_ifexpression_" class="ntdef" style="color:olive">IfExpression_</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"if"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"then"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"else"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) </code></pre>
+<p class="p noindent para-continued">The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_ifexpression" class="ntref localref" style="color:maroon">IfExpression</a></span></code> is a conditional expression. It first evaluates
+the expression following the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">if</span></code>. If it evaluates to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code> then
+it evaluates the expression following the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">then</span></code> and that is the
+result of the expression. If it evaluates to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">false</span></code> then the
+expression following the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">else</span></code> is evaluated and that is the result
+of the expression. It is important that only the selected expression
+is evaluated as the following example shows.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> k <span style="color:blue">:=</span> <span class="constant" style="color:purple">10</span> / x; <span style="color:darkgreen">// error, may divide by 0.</span>
+<span style="color:blue">var</span> m <span style="color:blue">:=</span> <span style="color:blue">if</span> x != <span class="constant" style="color:purple">0</span> <span style="color:blue">then</span> <span class="constant" style="color:purple">10</span> / x <span style="color:blue">else</span> <span class="constant" style="color:purple">1</span>; <span style="color:darkgreen">// ok, guarded</span></code></pre><h3 id="sec-case-bindings-and-patterns" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.28</span>.&#8194;</span>Case Bindings and Patterns</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_casebinding_" class="ntdef" style="color:olive">CaseBinding_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"case"</span></span>
+ ( <span class="code-escaped"><a href="#1_ident" class="ntref localref" style="color:maroon">Ident</a></span> [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> <span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span> } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> ]
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> <span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span> } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span>
+ )
+
+<span class="code-escaped"><span id="1_casepattern" class="ntdef" style="color:olive">CasePattern</span></span> =
+ ( <span class="code-escaped"><a href="#1_ident" class="ntref localref" style="color:maroon">Ident</a></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> [ <span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span> } ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span>
+ | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> [ <span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">Casepattern</a></span> } ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span>
+ | <span class="code-escaped"><a href="#1_identtypeoptional" class="ntref localref" style="color:maroon">IdentTypeOptional</a></span>
+ ) </code></pre>
+<p class="p noindent para-continued">Case bindings and patterns are used for (possibly nested)
+pattern matching on inductive or coinductive values.
+The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_casebinding_" class="ntref localref" style="color:maroon">CaseBinding_</a></span></code> construct is used in
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_casestatement" class="ntref localref" style="color:maroon">CaseStatement</a></span></code> and <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_caseexpression" class="ntref localref" style="color:maroon">CaseExpression</a></span></code>s.
+Besides its use in <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_casebinding_" class="ntref localref" style="color:maroon">CaseBinding_</a></span></code>, <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span></code>s are used
+in <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_letexpr" class="ntref localref" style="color:maroon">LetExpr</a></span></code>s and <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_vardeclstatement" class="ntref localref" style="color:maroon">VarDeclStatement</a></span></code>s.
+</p>
+<p class="p indent">When matching an inductive or coinductive value in
+a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_matchstmt" class="ntref localref" style="color:maroon">MatchStmt</a></span></code> or <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_matchexpression" class="ntref localref" style="color:maroon">MatchExpression</a></span></code>, there must be
+a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_casebinding_" class="ntref localref" style="color:maroon">CaseBinding_</a></span></code> for each constructor. A tuple is
+considered to have a single constructor.
+The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_ident" class="ntref localref" style="color:maroon">Ident</a></span></code> of the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_casebinding_" class="ntref localref" style="color:maroon">CaseBinding_</a></span></code> must match the name
+of a constructor (or in the case of a tuple the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_ident" class="ntref localref" style="color:maroon">Ident</a></span></code> is
+absent and the second alternative is chosen).
+The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span></code>s inside the parenthesis are then
+matched against the argument that were given to the
+constructor when the value was constructed.
+The number of <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span></code>s must match the number
+of parameters to the constructor (or the arity of the
+tuple).
+</p>
+<p class="p indent">The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span></code>s may be nested. The set of non-constructor-name
+identifiers contained in a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_casebinding_" class="ntref localref" style="color:maroon">CaseBinding_</a></span></code> must be distinct.
+They are bound to the corresponding values in the value being
+matched.
+</p><h3 id="sec-match-expression" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.29</span>.&#8194;</span>Match Expression</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_matchexpression" class="ntdef" style="color:olive">MatchExpression</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"match"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"{"</span></span> { <span class="code-escaped"><a href="#1_caseexpression" class="ntref localref" style="color:maroon">CaseExpression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"}"</span></span>
+ | { <span class="code-escaped"><a href="#1_caseexpression" class="ntref localref" style="color:maroon">CaseExpression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) }
+ )
+
+<span class="code-escaped"><span id="1_caseexpression" class="ntdef" style="color:olive">CaseExpression</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ <span class="code-escaped"><a href="#1_casebinding_" class="ntref localref" style="color:maroon">CaseBinding_</a></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"=&gt;"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_matchexpression" class="ntref localref" style="color:maroon">MatchExpression</a></span></code> is used to conditionally evaluate and select an
+expression depending on the value of an algebraic type, i.e. an inductive
+type, or a co-inductive type.
+</p>
+<p class="p indent">The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span></code> following the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">match</span></code> keyword is called the
+<em class="em-low1">selector</em>. There must be a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_caseexpression" class="ntref localref" style="color:maroon">CaseExpression</a></span></code> for each constructor of
+the type of the selector. The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_ident" class="ntref localref" style="color:maroon">Ident</a></span></code> following the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">case</span></code> keyword in a
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_caseexpression" class="ntref localref" style="color:maroon">CaseExpression</a></span></code> is the name of a constructor of the selector&#39;s type.
+It may be absent if the expression being matched is a tuple since these
+have no constructor name.
+</p>
+<p class="p indent">If the constructor has parameters then in the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_caseexpression" class="ntref localref" style="color:maroon">CaseExpression</a></span></code> the
+constructor name must be followed by a parenthesized list of <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span></code>s.
+If the constructor has no parameters then the
+<code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_caseexpression" class="ntref localref" style="color:maroon">CaseExpression</a></span></code> must not have a following <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span></code> list.
+All of the identifiers in the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span></code>s must be distinct.
+If types for the identifiers are not given then types are inferred
+from the types of the constructor&#39;s parameters. If types are
+given then they must agree with the types of the
+corresponding parameters.
+</p>
+<p class="p indent">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_matchexpression" class="ntref localref" style="color:maroon">MatchExpression</a></span></code> is evaluated by first evaluating the selector.
+Then the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_caseclause" class="ntref localref" style="color:maroon">CaseClause</a></span></code> is selected for the constructor that was
+used to construct the evaluated selector. If the constructor had
+parameters then the actual values used to construct the selector
+value are bound to the identifiers in the identifier list.
+The expression to the right of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">=&gt;</code> in the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_caseclause" class="ntref localref" style="color:maroon">CaseClause</a></span></code> is then
+evaluated in the environment enriched by this binding. The result
+of that evaluation is the result of the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_matchexpression" class="ntref localref" style="color:maroon">MatchExpression</a></span></code>.
+</p>
+<p class="p indent">Note that the braces enclosing the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_caseclause" class="ntref localref" style="color:maroon">CaseClause</a></span></code>s may be omitted.
+</p><h3 id="sec-quantifier-expression" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.30</span>.&#8194;</span>Quantifier Expression</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_quantifierexpression" class="ntdef" style="color:olive">QuantifierExpression</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"forall"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"exists"</span></span> ) <span class="code-escaped"><a href="#1_quantifierdomain" class="ntref localref" style="color:maroon">QuantifierDomain</a></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"::"</span></span>
+ <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+
+<span class="code-escaped"><span id="1_quantifierdomain" class="ntdef" style="color:olive">QuantifierDomain</span></span> =
+ <span class="code-escaped"><a href="#1_identtypeoptional" class="ntref localref" style="color:maroon">IdentTypeOptional</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_identtypeoptional" class="ntref localref" style="color:maroon">IdentTypeOptional</a></span> } { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ [ <span style="color:maroon">&quot;</span><span style="color:maroon">|</span><span style="color:maroon">&quot;</span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) ]</code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_quantifierexpression" class="ntref localref" style="color:maroon">QuantifierExpression</a></span></code> is a boolean expression that specifies that a
+given expression (the one following the &#8220;::&#8221;) is true for all (for
+<strong class="strong-star2">forall</strong>) or some (for <strong class="strong-star2">exists</strong>) combination of values of the
+quantified variables, namely those in the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_quantifierdomain" class="ntref localref" style="color:maroon">QuantifierDomain</a></span></code>.
+</p>
+<p class="p indent">Here are some examples:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">assert</span> <span style="color:blue">forall</span> x : <span style="color:teal">nat</span> | x &lt;= <span class="constant" style="color:purple">5</span> :: x * x &lt;= <span class="constant" style="color:purple">25</span>;
+(<span style="color:blue">forall</span> n :: <span class="constant" style="color:purple">2</span> &lt;= n ==&gt; (<span class="code-escaped">&#8707;<span style="font-family:serif">&#8201;</span></span>d :: n &lt; d &amp;&amp; d &lt; <span class="constant" style="color:purple">2</span>*n))</code></pre>
+<p class="p noindent para-continued">or using the Unicode symbols:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">assert</span> <span class="code-escaped">&#8704;</span> x : <span style="color:teal">nat</span> | x &lt;= <span class="constant" style="color:purple">5</span> <span class="code-escaped">&#8226;</span> x * x &lt;= <span class="constant" style="color:purple">25</span>;
+(<span class="code-escaped">&#8704;</span> n <span class="code-escaped">&#8226;</span> <span class="constant" style="color:purple">2</span> &lt;= n ==&gt; (<span class="code-escaped">&#8707;</span> d <span class="code-escaped">&#8226;</span> n &lt; d &amp;&amp; d &lt; <span class="constant" style="color:purple">2</span>*n))</code></pre>
+<p class="p noindent para-continued">The quantifier identifiers are <em class="em-low1">bound</em> within the scope of the
+expressions in the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_quantifierexpression" class="ntref localref" style="color:maroon">QuantifierExpression</a></span></code>.
+</p>
+<p class="p indent">It types are not given for the quantified identifiers then Dafny
+attempts to infer their types from the context of the expressions.
+It this is not possible the program is in error.
+</p><h3 id="sec-set-comprehension-expressions" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.31</span>.&#8194;</span>Set Comprehension Expressions</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_setcomprehensionexpr" class="ntdef" style="color:olive">SetComprehensionExpr</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"set"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"iset"</span></span> ]
+ <span class="code-escaped"><a href="#1_identtypeoptional" class="ntref localref" style="color:maroon">IdentTypeOptional</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_identtypeoptional" class="ntref localref" style="color:maroon">IdentTypeOptional</a></span> } { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ <span style="color:maroon">&quot;</span><span style="color:maroon">|</span><span style="color:maroon">&quot;</span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>)
+ [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"::"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) ] </code></pre>
+<p class="p noindent para-continued">A set comprehension expression is an expressions that yields a set
+(possibly infinite if <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">iset</span></code> is used) that
+satisfies specified conditions. There are two basic forms.
+</p>
+<p class="p indent">If there is only one quantified variable the optional <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"::"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span></code>
+need not be supplied, in which case it is as if it had been supplied
+and the expression consists solely of the quantified variable.
+That is,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:teal">set</span> x : T | P(x)</code></pre>
+<p class="p noindent para-continued">is equivalent to
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:teal">set</span> x : T | P(x) :: x</code></pre>
+<p class="p noindent para-continued">For the full form
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> S <span style="color:blue">:=</span> <span style="color:teal">set</span> x1:T1, x2:T2 ... | P(x1, x2, ...) :: Q(x1, x2, ...)</code></pre>
+<p class="p noindent para-continued">the elements of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">S</code> will be all values resulting from evaluation of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q(x1, x2, ...)</code>
+for all combinations of quantified variables <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">x1, x2, ...</code> such that
+predicate <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P(x1, x2, ...)</code> holds. For example,
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> S <span style="color:blue">:=</span> <span style="color:teal">set</span> x:<span style="color:teal">nat</span>, y:<span style="color:teal">nat</span> | x &lt; <span class="constant" style="color:purple">2</span> &amp;&amp; y &lt; <span class="constant" style="color:purple">2</span> :: (x, y)</code></pre>
+<p class="p noindent para-continued">would yield <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">S == {(<span class="constant" style="color:purple">0</span>, <span class="constant" style="color:purple">0</span>), (<span class="constant" style="color:purple">0</span>, <span class="constant" style="color:purple">1</span>), (<span class="constant" style="color:purple">1</span>, <span class="constant" style="color:purple">0</span>), (<span class="constant" style="color:purple">1</span>,<span class="constant" style="color:purple">1</span>) }</code>
+</p>
+<p class="p indent">The types on the quantified variables are optional and if not given Dafny
+will attempt to infer them from the contexts in which they are used in the
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">P</code> or <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Q</code> expressions.
+</p>
+<p class="p indent">If a finite set was specified (&#8220;set&#8221; keyword used), Dafny must be able to prove that the
+result is finite otherwise the set comprehension expression will not be
+accepted.
+</p>
+<p class="p indent">Set comprehensions involving reference types such as
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:teal">set</span> o: <span style="color:teal">object</span> | <span style="color:blue">true</span></code></pre>
+<p class="p noindent para-continued">are allowed in ghost contexts. In particular, in ghost contexts, the
+check that the result is finite should allow any set comprehension
+where the bound variable is of a reference type. In non-ghost contexts,
+it is not allowed, because&#8211;even though the resulting set would be
+finite&#8211;it is not pleasant or practical to compute at run time.
+</p><h3 id="sec-statements-in-an-expression" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.32</span>.&#8194;</span>Statements in an Expression</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_stmtinexpr" class="ntdef" style="color:olive">StmtInExpr</span></span> = ( <span class="code-escaped"><a href="#1_assertstmt" class="ntref localref" style="color:maroon">AssertStmt</a></span> | <span class="code-escaped"><a href="#1_assumestmt" class="ntref localref" style="color:maroon">AssumeStmt</a></span> | <span class="code-escaped"><a href="#1_calcstmt" class="ntref localref" style="color:maroon">CalcStmt</a></span> ) </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_stmtinexpr" class="ntref localref" style="color:maroon">StmtInExpr</a></span></code> is a kind of statement that is allowed to
+precede an expression in order to ensure that the expression
+can be evaluated without error. For example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">assume</span> x != <span class="constant" style="color:purple">0</span>; <span class="constant" style="color:purple">10</span>/x</code></pre>
+<p class="p noindent para-continued"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Assert</code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">assume</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">calc</span></code> statements can be used in this way.
+</p><h3 id="sec-let-expression" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.33</span>.&#8194;</span>Let Expression</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_letexpr" class="ntdef" style="color:olive">LetExpr</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"ghost"</span></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"var"</span></span> <span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span> }
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":="</span></span> | { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> } <span style="color:maroon">&quot;</span><span style="color:maroon">:|</span><span style="color:maroon">&quot;</span> )
+ <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_false" class="ntref localref" style="color:maroon">false</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">";"</span></span>
+ <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) </code></pre>
+<p class="p noindent para-continued">A <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">let</code> expression allows binding of intermediate values to identifiers
+for use in an expression. The start of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">let</code> expression is
+signaled by the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">var</span></code> keyword. They look much like a local variable
+declaration except the scope of the variable only extends to the
+enclosed expression.
+</p>
+<p class="p indent">For example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">var</span> sum <span style="color:blue">:=</span> x + y; sum * sum</code></pre>
+<p class="p noindent para-continued">In the simple case the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_casepattern" class="ntref localref" style="color:maroon">CasePattern</a></span></code> is just an identifier with optional
+type (which if missing is inferred from the rhs).
+</p>
+<p class="p indent">The more complex case allows destructuring of constructor expressions.
+For example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">datatype</span> Stuff = SCons(x: <span style="color:teal">int</span>, y: <span style="color:teal">int</span>) | Other
+<span style="color:blue">function</span> GhostF(z: Stuff): <span style="color:teal">int</span>
+ <span style="color:purple">requires</span> z.SCons?
+{
+ <span style="color:blue">var</span> SCons(u, v) <span style="color:blue">:=</span> z; <span style="color:blue">var</span> sum <span style="color:blue">:=</span> u + v; sum * sum
+}</code></pre><h3 id="sec-map-comprehension-expression" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.34</span>.&#8194;</span>Map Comprehension Expression</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_mapcomprehensionexpr" class="ntdef" style="color:olive">MapComprehensionExpr</span></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) =
+ ( <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"map"</span></span> | <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"imap"</span></span> ) <span class="code-escaped"><a href="#1_identtypeoptional" class="ntref localref" style="color:maroon">IdentTypeOptional</a></span> { <span class="code-escaped"><a href="#1_attribute" class="ntref localref" style="color:maroon">Attribute</a></span> }
+ [ <span style="color:maroon">&quot;</span><span style="color:maroon">|</span><span style="color:maroon">&quot;</span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) ]
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"::"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>) </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_mapcomprehensionexpr" class="ntref localref" style="color:maroon">MapComprehensionExpr</a></span></code> defines a finite or infinite map value
+by defining a domain (using the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_identtypeoptional" class="ntref localref" style="color:maroon">IdentTypeOptional</a></span></code> and the optional
+condition following the &#8220;|&#8221;) and for each value in the domain,
+giving the mapped value using the expression following the &#8220;::&#8221;.
+</p>
+<p class="p indent">For example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">function</span> square(x : <span style="color:teal">int</span>) : <span style="color:teal">int</span> { x * x }
+<span style="color:blue">method</span> test()
+{
+ <span style="color:blue">var</span> m <span style="color:blue">:=</span> <span style="color:teal">map</span> x : <span style="color:teal">int</span> | <span class="constant" style="color:purple">0</span> &lt;= x &lt;= <span class="constant" style="color:purple">10</span> :: x * x;
+ <span style="color:blue">ghost</span> <span style="color:blue">var</span> im <span style="color:blue">:=</span> <span style="color:teal">imap</span> x : <span style="color:teal">int</span> :: x * x;
+ <span style="color:blue">ghost</span> <span style="color:blue">var</span> im2 <span style="color:blue">:=</span> <span style="color:teal">imap</span> x : <span style="color:teal">int</span> :: square(x);
+}</code></pre>
+<p class="p noindent para-continued">Dafny maps must be finite, so the domain must be constrained to be finite.
+But imaps may be infinite as the example shows. The last example shows
+creation of an infinite map that gives the same results as a function.
+</p><!-- Experimental - do not document.
+
+## Named Expression
+````
+NamedExpr(allowLemma, allowLambda) =
+ "label" LabelName ":" Expression(allowLemma, allowLambda)
+````
+
+A ``NamedExpr`` is an expression that has been tagged with a name.
+For example:
+```
+label squareit: x * x
+```
+
+This is an experimental feature.
+TODO: When is this useful. Is there any way to refer to the label?
+Should we remove the description?
+-->
+
+
+<h3 id="sec-name-segment" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.35</span>.&#8194;</span>Name Segment</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_namesegment" class="ntdef" style="color:olive">NameSegment</span></span> = <span class="code-escaped"><a href="#1_ident" class="ntref localref" style="color:maroon">Ident</a></span> [ <span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span> | <span class="code-escaped"><a href="#1_hashcall" class="ntref localref" style="color:maroon">HashCall</a></span> ] </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_namesegment" class="ntref localref" style="color:maroon">NameSegment</a></span></code> names a Dafny entity by giving its declared
+name optionally followed by information to
+make the name more complete. For the simple case it is
+just an identifier.
+</p>
+<p class="p indent">If the identifier is for a generic entity it is followed by
+a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span></code> which provides actual types for
+the type parameters.
+</p>
+<p class="p indent">To reference a prefix predicate (see section&nbsp;<a href="#sec-copredicates" title="18.2.3.&#8194;Copredicates" class="localref" style="target-element:h3"><span class="heading-label">18.2.3</span></a>) or
+prefix lemma (see section&nbsp;<a href="#sec-prefix-lemmas" title="18.2.4.2.&#8194;Prefix Lemmas" class="localref" style="target-element:h4"><span class="heading-label">18.2.4.2</span></a>), the identifier
+must be the name of the copredicate or colemma and it must be
+followed by a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_hashcall" class="ntref localref" style="color:maroon">HashCall</a></span></code>.
+</p><h3 id="sec-hash-call" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.36</span>.&#8194;</span>Hash Call</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_hashcall" class="ntdef" style="color:olive">HashCall</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"#"</span></span> [ <span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span> ]
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"["</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"]"</span></span>
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> [ <span class="code-escaped"><a href="#1_expressions" class="ntref localref" style="color:maroon">Expressions</a></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> </code></pre>
+<p class="p noindent para-continued">A <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_hashcall" class="ntref localref" style="color:maroon">HashCall</a></span></code> is used to call the prefix for a copredicate or colemma.
+In the non-generic case it just insert <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">#[k]</span><span style="color:maroon">&quot;</span></code> before the call argument
+list where k is the number of recursion levels.
+</p>
+<p class="p indent">In the case where the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">colemma</span></code> is generic, the generic type
+argument is given before. Here is an example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">codatatype</span> Stream&lt;T&gt; = Nil | Cons(head: <span style="color:teal">int</span>, stuff: T, tail: Stream)
+
+<span style="color:blue">function</span> append(M: Stream, N: Stream): Stream
+{
+ <span style="color:blue">match</span> M
+ <span style="color:blue">case</span> Nil =&gt; N
+ <span style="color:blue">case</span> Cons(t, s, M&#39;) =&gt; Cons(t, s, append(M&#39;, N))
+}
+
+<span style="color:blue">function</span> zeros&lt;T&gt;(s : T): Stream&lt;T&gt;
+{
+ Cons(<span class="constant" style="color:purple">0</span>, s, zeros(s))
+}
+
+<span style="color:blue">function</span> ones&lt;T&gt;(s: T): Stream&lt;T&gt;
+{
+ Cons(<span class="constant" style="color:purple">1</span>, s, ones(s))
+}
+
+<span style="color:blue">copredicate</span> atmost(a: Stream, b: Stream)
+{
+ <span style="color:blue">match</span> a
+ <span style="color:blue">case</span> Nil =&gt; <span style="color:blue">true</span>
+ <span style="color:blue">case</span> Cons(h,s,t) =&gt; b.Cons? &amp;&amp; h &lt;= b.head &amp;&amp; atmost(t, b.tail)
+}
+
+<span style="color:blue">colemma</span> {<span style="color:purple">:induction</span> <span style="color:blue">false</span>} Theorem0&lt;T&gt;(s: T)
+ <span style="color:purple">ensures</span> atmost(zeros(s), ones(s))
+{
+ <span style="color:darkgreen">// the following shows two equivalent ways to getting essentially the</span>
+ <span style="color:darkgreen">// co-inductive hypothesis</span>
+ <span style="color:blue">if</span> (*) {
+ Theorem0#&lt;T&gt;[_k-<span class="constant" style="color:purple">1</span>](s);
+ } <span style="color:blue">else</span> {
+ Theorem0(s);
+ }
+}
+</code></pre>
+<p class="p noindent para-continued">where the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_hashcall" class="ntref localref" style="color:maroon">HashCall</a></span></code> is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:maroon">&quot;</span><span style="color:maroon">Theorem0#&lt;T&gt;[_k-1](s);</span><span style="color:maroon">&quot;</span></code>.
+See sections&nbsp;<a href="#sec-copredicates" title="18.2.3.&#8194;Copredicates" class="localref" style="target-element:h3"><span class="heading-label">18.2.3</span></a> and&nbsp;<a href="#sec-prefix-lemmas" title="18.2.4.2.&#8194;Prefix Lemmas" class="localref" style="target-element:h4"><span class="heading-label">18.2.4.2</span></a>.
+</p><h3 id="sec-suffix" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.37</span>.&#8194;</span>Suffix</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_suffix" class="ntdef" style="color:olive">Suffix</span></span> =
+ ( <span class="code-escaped"><a href="#1_augmenteddotsuffix_" class="ntref localref" style="color:maroon">AugmentedDotSuffix_</a></span>
+ | <span class="code-escaped"><a href="#1_datatypeupdatesuffix_" class="ntref localref" style="color:maroon">DatatypeUpdateSuffix_</a></span>
+ | <span class="code-escaped"><a href="#1_subsequencesuffix_" class="ntref localref" style="color:maroon">SubsequenceSuffix_</a></span>
+ | <span class="code-escaped"><a href="#1_slicesbylengthsuffix_" class="ntref localref" style="color:maroon">SlicesByLengthSuffix_</a></span>
+ | <span class="code-escaped"><a href="#1_sequenceupdatesuffix_" class="ntref localref" style="color:maroon">SequenceUpdateSuffix_</a></span>
+ | <span class="code-escaped"><a href="#1_selectionsuffix_" class="ntref localref" style="color:maroon">SelectionSuffix_</a></span>
+ | <span class="code-escaped"><a href="#1_argumentlistsuffix_" class="ntref localref" style="color:maroon">ArgumentListSuffix_</a></span>
+ ) </code></pre>
+<p class="p noindent para-continued">The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_suffix" class="ntref localref" style="color:maroon">Suffix</a></span></code> non-terminal describes ways of deriving a new value from
+the entity to which the suffix is appended. There are six kinds
+of suffixes which are described below.
+</p><h4 id="sec-augmented-dot-suffix" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">22.37.0</span>.&#8194;</span>Augmented Dot Suffix</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_augmenteddotsuffix_" class="ntdef" style="color:olive">AugmentedDotSuffix_</span></span> = <span style="color:maroon">&quot;</span><span style="color:maroon">. </span><span style="color:maroon">&quot;</span> <span class="code-escaped"><a href="#1_dotsuffix" class="ntref localref" style="color:maroon">DotSuffix</a></span> [ <span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span> | <span class="code-escaped"><a href="#1_hashcall" class="ntref localref" style="color:maroon">HashCall</a></span> ] </code></pre>
+<p class="p noindent para-continued">An augmented dot suffix consists of a simple <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_dotsuffix" class="ntref localref" style="color:maroon">DotSuffix</a></span></code> optionally
+followed by either
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_genericinstantiation" class="ntref localref" style="color:maroon">GenericInstantiation</a></span></code> (for the case where the item
+selected by the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_dotsuffix" class="ntref localref" style="color:maroon">DotSuffix</a></span></code> is generic), or
+</li>
+<li class="li ul-li list-star-li compact-li">a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_hashcall" class="ntref localref" style="color:maroon">HashCall</a></span></code> for the case where we want to call a prefix copredicate
+or colemma. The result is the result of calling the prefix copredicate
+or colemma.
+</li></ul>
+<h4 id="sec-datatype-update-suffix" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">22.37.1</span>.&#8194;</span>Datatype Update Suffix</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_datatypeupdatesuffix_" class="ntdef" style="color:olive">DatatypeUpdateSuffix_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"."</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> <span class="code-escaped"><a href="#1_memberbindingupdate" class="ntref localref" style="color:maroon">MemberBindingUpdate</a></span> { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_memberbindingupdate" class="ntref localref" style="color:maroon">MemberBindingUpdate</a></span> } <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span>
+
+<span class="code-escaped"><span id="1_memberbindingupdate" class="ntdef" style="color:olive">MemberBindingUpdate</span></span> =
+ ( <span class="code-escaped"><a href="#2_ident" class="ntref localref" style="color:maroon">ident</a></span> | <span class="code-escaped"><a href="#2_digits" class="ntref localref" style="color:maroon">digits</a></span> ) <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":="</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)</code></pre>
+<p class="p noindent para-continued">A datatype update suffix is used to produce a new datatype value
+that is the same as an old datatype value except that the
+value corresponding to a given destructor has the specified value.
+In a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_memberbindingupdate" class="ntref localref" style="color:maroon">MemberBindingUpdate</a></span></code>, the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#2_ident" class="ntref localref" style="color:maroon">ident</a></span></code> or <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#2_digits" class="ntref localref" style="color:maroon">digits</a></span></code> is the
+name of a destructor (i.e. formal parameter name) for one of the
+constructors of the datatype. The expression to the right of the
+&#8220;:=&#8221; is the new value for that formal.
+</p>
+<p class="p indent">All of the destructors in a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_datatypeupdatesuffix_" class="ntref localref" style="color:maroon">DatatypeUpdateSuffix_</a></span></code> must be
+for the same constructor, and if they do not cover all of the
+destructors for that constructor then the datatype value being
+updated must have a value derived from that same constructor.
+</p>
+<p class="p indent">Here is an example:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">module</span> NewSyntax {
+<span style="color:blue">datatype</span> MyDataType = MyConstructor(myint:<span style="color:teal">int</span>, mybool:<span style="color:teal">bool</span>)
+ | MyOtherConstructor(otherbool:<span style="color:teal">bool</span>)
+ | MyNumericConstructor(<span class="constant" style="color:purple">42</span>:<span style="color:teal">int</span>)
+
+<span style="color:blue">method</span> test(datum:MyDataType, x:<span style="color:teal">int</span>)
+ <span style="color:blue">returns</span> (abc:MyDataType, def:MyDataType, ghi:MyDataType, jkl:MyDataType)
+ <span style="color:purple">requires</span> datum.MyConstructor?;
+ <span style="color:purple">ensures</span> abc == datum.(myint <span style="color:blue">:=</span> x + <span class="constant" style="color:purple">2</span>);
+ <span style="color:purple">ensures</span> def == datum.(otherbool <span style="color:blue">:=</span> !datum.mybool);
+ <span style="color:purple">ensures</span> ghi == datum.(myint <span style="color:blue">:=</span> <span class="constant" style="color:purple">2</span>).(mybool <span style="color:blue">:=</span> <span style="color:blue">false</span>);
+ <span style="color:darkgreen">// Resolution error: no non_destructor in MyDataType</span>
+ <span style="color:darkgreen">//ensures jkl == datum.(non_destructor := 5);</span>
+ <span style="color:purple">ensures</span> jkl == datum.(<span class="constant" style="color:purple">42</span> <span style="color:blue">:=</span> <span class="constant" style="color:purple">7</span>);
+{
+ abc <span style="color:blue">:=</span> MyConstructor(x + <span class="constant" style="color:purple">2</span>, datum.mybool);
+ abc <span style="color:blue">:=</span> datum.(myint <span style="color:blue">:=</span> x + <span class="constant" style="color:purple">2</span>);
+ def <span style="color:blue">:=</span> MyOtherConstructor(!datum.mybool);
+ ghi <span style="color:blue">:=</span> MyConstructor(<span class="constant" style="color:purple">2</span>, <span style="color:blue">false</span>);
+ jkl <span style="color:blue">:=</span> datum.(<span class="constant" style="color:purple">42</span> <span style="color:blue">:=</span> <span class="constant" style="color:purple">7</span>);
+
+ <span style="color:blue">assert</span> abc.(myint <span style="color:blue">:=</span> abc.myint - <span class="constant" style="color:purple">2</span>) == datum.(myint <span style="color:blue">:=</span> x);
+}
+}</code></pre><h4 id="sec-subsequence-suffix" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">22.37.2</span>.&#8194;</span>Subsequence Suffix</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_subsequencesuffix_" class="ntdef" style="color:olive">SubsequenceSuffix_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"["</span></span> [ <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) ]
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">".."</span></span> [ <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) ]
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"]"</span></span> </code></pre>
+<p class="p noindent para-continued">A subsequence suffix applied to a sequence produces a new sequence whose
+elements are taken from a contiguous part of the original sequence. For
+example, expression <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[lo..hi]</code> for sequence <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code>, and integer-based
+numerics <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">lo</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">hi</code> satisfying <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span class="constant" style="color:purple">0</span> &lt;= lo &lt;= hi &lt;= |s|</code>. See
+section&nbsp;<a href="#sec-other-sequence-expressions" title="9.2.3.&#8194;Other Sequence Expressions" class="localref" style="target-element:h3"><span class="heading-label">9.2.3</span></a> for details.
+</p><h4 id="sec-slices-by-length-suffix" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">22.37.3</span>.&#8194;</span>Slices By Length Suffix</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_slicesbylengthsuffix_" class="ntdef" style="color:olive">SlicesByLengthSuffix_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"["</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":"</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) }
+ [ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":"</span></span> ]
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"]"</span></span> </code></pre>
+<p class="p noindent para-continued">Applying a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_slicesbylengthsuffix_" class="ntref localref" style="color:maroon">SlicesByLengthSuffix_</a></span></code> to a sequence produces a
+sequence of subsequences of the original sequence.
+See section&nbsp;<a href="#sec-other-sequence-expressions" title="9.2.3.&#8194;Other Sequence Expressions" class="localref" style="target-element:h3"><span class="heading-label">9.2.3</span></a> for details.
+</p><h4 id="sec-sequence-update-suffix" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">22.37.4</span>.&#8194;</span>Sequence Update Suffix</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_sequenceupdatesuffix_" class="ntdef" style="color:olive">SequenceUpdateSuffix_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"["</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":="</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"]"</span></span> </code></pre>
+<p class="p noindent para-continued">For a sequence <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code> and expressions <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">i</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">v</code>, the expression
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s[i <span style="color:blue">:=</span> v]</code> is the same as the sequence <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">s</code> except that at
+index <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">i</code> it has value <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">v</code>.
+</p><h4 id="sec-selection-suffix" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">22.37.5</span>.&#8194;</span>Selection Suffix</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_selectionsuffix_" class="ntdef" style="color:olive">SelectionSuffix_</span></span> =
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"["</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) }
+ <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"]"</span></span> </code></pre>
+<p class="p noindent para-continued">If a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_selectionsuffix_" class="ntref localref" style="color:maroon">SelectionSuffix_</a></span></code> has only one expression in it, it is a
+zero-based index that may be used to select a single element of a
+sequence or from a single-dimensional array.
+</p>
+<p class="p indent">If a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_selectionsuffix_" class="ntref localref" style="color:maroon">SelectionSuffix_</a></span></code> has more than one expression in it, then
+it is a list of indices to index into a multi-dimensional array.
+The rank of the array must be the same as the number of indices.
+</p><h4 id="sec-argument-list-suffix" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">22.37.6</span>.&#8194;</span>Argument List Suffix</h4>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_argumentlistsuffix_" class="ntdef" style="color:olive">ArgumentListSuffix_</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"("</span></span> [ <span class="code-escaped"><a href="#1_expressions" class="ntref localref" style="color:maroon">Expressions</a></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">")"</span></span> </code></pre>
+<p class="p noindent para-continued">An argument list suffix is a parenthesized list of expressions that
+are the arguments to pass to a method or function that is being
+called. Applying such a suffix caused the method or function
+to be called and the result is the result of the call.
+</p><h3 id="sec-expression-lists" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">22.38</span>.&#8194;</span>Expression Lists</h3>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_expressions" class="ntdef" style="color:olive">Expressions</span></span> =
+ <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>)
+ { <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">","</span></span> <span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span>(allowLemma: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>, <span class="code-escaped"><a href="#2_allowlambda" class="ntref localref" style="color:maroon">allowLambda</a></span>: <span class="code-escaped"><a href="#2_true" class="ntref localref" style="color:maroon">true</a></span>) }</code></pre>
+<p class="p noindent para-continued">The <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_expressions" class="ntref localref" style="color:maroon">Expressions</a></span></code> non-terminal represents a list of
+one or more expressions separated by a comma.
+</p><h2 id="sec-module-refinement" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">23</span>.&#8194;</span>Module Refinement</h2>
+<p class="p noindent">TODO: Write this section.
+</p><h2 id="sec-attributes" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">24</span>.&#8194;</span>Attributes</h2>
+<pre class="para-block grammar-def pre-fenced pre-fenced4 language-java lang-java java colorized" style="display:block;font-size:small;margin-left:1em"><code><span class="code-escaped"><span id="1_attribute" class="ntdef" style="color:olive">Attribute</span></span> = <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"{"</span></span> <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">":"</span></span> <span class="code-escaped"><a href="#1_attributename" class="ntref localref" style="color:maroon">AttributeName</a></span> [ <span class="code-escaped"><a href="#1_expressions" class="ntref localref" style="color:maroon">Expressions</a></span> ] <span class="code-escaped"><span class="terminal" style="font-weight:bold;color:black">"}"</span></span> </code></pre>
+<p class="p noindent para-continued">Dafny allows many of its entities to be annotated with <em class="em-low1">Attributes</em>.
+The grammar shows where the attribute annotations may appear.
+</p>
+<p class="p indent">Here is an example of an attribute from the Dafny test suite:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>{<span style="color:purple">:MyAttribute</span> <span style="color:maroon">&quot;</span><span style="color:maroon">hello</span><span style="color:maroon">&quot;</span>, <span style="color:maroon">&quot;</span><span style="color:maroon">hi</span><span style="color:maroon">&quot;</span> + <span style="color:maroon">&quot;</span><span style="color:maroon">there</span><span style="color:maroon">&quot;</span>, <span class="constant" style="color:purple">57</span>}</code></pre>
+<p class="p noindent para-continued">In general an attribute may have any name the user chooses. It may be
+followed by a comma separated list of expressions. These expressions will
+be resolved and type-checked in the context where the attribute appears.
+</p><h3 id="sec-dafny-attribute-implementation-details" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">24.0</span>.&#8194;</span>Dafny Attribute Implementation Details</h3>
+<p class="p noindent">In the Dafny implementation the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Attributes</code> type holds the name of
+the attribute, a list of <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_expression" class="ntref localref" style="color:maroon">Expression</a></span></code> arguments and a link to the
+previous Attributes object for that Dafny entity. So for each
+Dafny entity that has attributes we have a list of them.
+</p>
+<p class="p indent">Dafny stores attributes on the following kinds of entities:
+Declaration (base class), ModuleDefinition, Statement,
+AssignmentRhs, LocalVariable, LetExpr, ComprehensionExpr,
+MaybeFreeExpression, Specification.
+</p>
+<p class="p indent">TODO: Dafny internals information should go into a separate
+document on Dafny internals.
+</p><h3 id="sec-dafny-attributes" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">24.1</span>.&#8194;</span>Dafny Attributes</h3>
+<p class="p noindent">All entities that Dafny translates to Boogie have their attributes
+passed on to Boogie except for the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:axiom</span>}</code> attribute (which
+conflicts with Boogie usage) and the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:trigger</span>}</code> attribute which is
+instead converted into a Boogie quantifier <em class="em-low1">trigger</em>. See Section 11 of
+<span class="citations" style="target-element:bibitem">[<a href="#leino:boogie2-refman" title="K.&#160;Rustan&#160;M. Leino.
+This is Boogie 2." class="bibref localref" style="target-element:bibitem"><span class="cite-number">16</span></a>]</span>.
+</p>
+<p class="p indent">Dafny has special processing for some attributes. For some attributes the
+setting is only looked for on the entity of interest. For others we start
+at the entity and if the attribute is not there, look up in the hierarchy
+(enclosing class and enclosing modules). The latter case is checked by
+the ContainsBoolAtAnyLevel method in the Dafny source. The attribute
+declaration closest to the entity overrides those further away.
+</p>
+<p class="p indent">For attributes with a single boolean expression argument, the attribute
+with no argument is interpreted as if it were true.
+</p>
+<p class="p indent">The attributes that are processed specially by Dafny are described in the
+following sections.
+</p><h4 id="sec-assumption" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.0</span>.&#8194;</span>assumption</h4>
+<p class="p noindent">This attribute can only be placed on a local ghost bool
+variable of a method. Its declaration cannot have a rhs, but it is
+allowed to participate as the lhs of exactly one assignment of the
+form: <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">b <span style="color:blue">:=</span> b &amp;&amp; expr;</code>. Such a variable declaration translates in the
+Boogie output to a declaration followed by an <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">assume</span> b</code> command. TODO:
+What is the motivation for this?
+</p><h4 id="sec-autoreq-boolexpr" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.1</span>.&#8194;</span>autoReq boolExpr</h4>
+<p class="p noindent">For a function declaration, if this attribute is set true at the nearest
+level, then its <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">requires</span></code> clause is strengthed sufficiently so that
+it may call the functions that it calls.
+</p>
+<p class="p indent">For following example
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">function</span> f(x:<span style="color:teal">int</span>) : <span style="color:teal">bool</span>
+ <span style="color:purple">requires</span> x &gt; <span class="constant" style="color:purple">3</span>
+{
+ x &gt; <span class="constant" style="color:purple">7</span>
+}
+
+<span style="color:darkgreen">// Should succeed thanks to auto_reqs</span>
+<span style="color:blue">function</span> {<span style="color:purple">:autoReq</span>} g(y:<span style="color:teal">int</span>, b:<span style="color:teal">bool</span>) : <span style="color:teal">bool</span>
+{
+ <span style="color:blue">if</span> b <span style="color:blue">then</span> f(y + <span class="constant" style="color:purple">2</span>) <span style="color:blue">else</span> f(<span class="constant" style="color:purple">2</span>*y)
+}</code></pre>
+<p class="p noindent para-continued">the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:autoReq</span>}</code> attribute causes Dafny to
+deduce a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">requires</span></code> clause for g as if it had been
+declared
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">function</span> g(y:<span style="color:teal">int</span>, b:<span style="color:teal">bool</span>) : <span style="color:teal">bool</span>
+ <span style="color:purple">requires</span> <span style="color:blue">if</span> b <span style="color:blue">then</span> y + <span class="constant" style="color:purple">2</span> &gt; <span class="constant" style="color:purple">3</span> <span style="color:blue">else</span> <span class="constant" style="color:purple">2</span> * y &gt; <span class="constant" style="color:purple">3</span>
+{
+ <span style="color:blue">if</span> b <span style="color:blue">then</span> f(y + <span class="constant" style="color:purple">2</span>) <span style="color:blue">else</span> f(<span class="constant" style="color:purple">2</span>*y)
+}</code></pre><h4 id="sec-autocontracts" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.2</span>.&#8194;</span>autocontracts</h4>
+<p class="p noindent">Dynamic frames&nbsp;<span class="citations" style="target-element:bibitem">[<a href="#kassios:fm2006" title="Ioannis&#160;T. Kassios.
+Dynamic frames: Support for framing, dependencies and sharing without restrictions.
+In Jayadev Misra, Tobias Nipkow, and Emil Sekerinski, editors, FM 2006: Formal Methods, 14th International Symposium on Formal Methods, volume 4085, pages 268&#8211;283. Springer, August 2006." class="bibref localref" style="target-element:bibitem"><span class="cite-number">9</span></a>, <a href="#leino:dafny:dynamicframes" title="K.&#160;Rustan&#160;M. Leino.
+Dynamic-frame specifications in dafny." class="bibref localref" style="target-element:bibitem"><span class="cite-number">17</span></a>, <a href="#smansetal:vericool" title="Jan Smans, Bart Jacobs, Frank Piessens, and Wolfram Schulte.
+Automatic verifier for Java-like programs based on dynamic frames." class="bibref localref" style="target-element:bibitem"><span class="cite-number">32</span></a>, <a href="#smansetal:implicitdynamicframes" title="Jan Smans, Bart Jacobs, and Frank Piessens.
+Implicit dynamic frames: Combining dynamic frames and separation logic.
+In Sophia Drossopoulou, editor, ECOOP 2009 &#8212; Object-Oriented Programming, 23rd European Conference, volume 5653, pages 148&#8211;172. Springer, July 2009." class="bibref localref" style="target-element:bibitem"><span class="cite-number">33</span></a>]</span>
+are frame expressions that can vary dynamically during
+program execution. AutoContracts is an experimental feature that will
+fill much of the dynamic-frames boilerplate into a class.
+</p>
+<p class="p indent">From the user&#39;s perspective, what needs to be done is simply:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">mark the class with {:autocontracts}
+</li>
+<li class="li ul-li list-star-li compact-li">declare a function (or predicate) called Valid()
+</li></ul>
+
+<p class="p noindent">AutoContracts will then:
+</p>
+<ul class="ul list-star loose">
+<li class="li ul-li list-star-li loose-li">
+<p>Declare:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">ghost</span> <span style="color:blue">var</span> Repr: <span style="color:teal">set</span>(<span style="color:teal">object</span>);</code></pre></li>
+<li class="li ul-li list-star-li loose-li">
+<p>For function/predicate Valid(), insert:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:purple">reads</span> <span style="color:blue">this</span>, Repr</code></pre></li>
+<li class="li ul-li list-star-li loose-li">
+<p>Into body of Valid(), insert (at the beginning of the body):
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:blue">this</span> <span style="color:blue">in</span> Repr &amp;&amp; <span style="color:blue">null</span> !<span style="color:blue">in</span> Repr</code></pre></li>
+<li class="li ul-li list-star-li loose-li">
+<p>and also insert, for every array-valued field A declared in the class:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> (A != <span style="color:blue">null</span> ==&gt; A <span style="color:blue">in</span> Repr) &amp;&amp;</code></pre></li>
+<li class="li ul-li list-star-li loose-li">
+<p>and for every field F of a class type T where T has a field called Repr, also insert:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> (F != <span style="color:blue">null</span> ==&gt; F <span style="color:blue">in</span> Repr &amp;&amp; F.Repr SUBSET Repr &amp;&amp; <span style="color:blue">this</span> !<span style="color:blue">in</span> Repr)</code></pre></li>
+<li class="li ul-li list-star-li loose-li">
+<p>Except, if A or F is declared with {:autocontracts false}, then the implication will not
+be added.
+</p></li>
+<li class="li ul-li list-star-li loose-li">
+<p>For every constructor, add:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:purple">modifies</span> <span style="color:blue">this</span>
+ <span style="color:purple">ensures</span> Valid() &amp;&amp; <span style="color:blue">fresh</span>(Repr - {<span style="color:blue">this</span>})</code></pre></li>
+<li class="li ul-li list-star-li loose-li">
+<p>At the end of the body of the constructor, add:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> Repr <span style="color:blue">:=</span> {<span style="color:blue">this</span>};
+ <span style="color:blue">if</span> (A != <span style="color:blue">null</span>) { Repr <span style="color:blue">:=</span> Repr + {A}; }
+ <span style="color:blue">if</span> (F != <span style="color:blue">null</span>) { Repr <span style="color:blue">:=</span> Repr + {F} + F.Repr; }</code></pre></li>
+<li class="li ul-li list-star-li loose-li">
+<p>For every method, add:
+</p></li></ul>
+
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:purple">requires</span> Valid()
+ <span style="color:purple">modifies</span> Repr
+ <span style="color:purple">ensures</span> Valid() &amp;&amp; <span style="color:blue">fresh</span>(Repr - <span style="color:blue">old</span>(Repr))</code></pre>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">At the end of the body of the method, add:
+
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:blue">if</span> (A != <span style="color:blue">null</span>) { Repr <span style="color:blue">:=</span> Repr + {A}; }
+ <span style="color:blue">if</span> (F != <span style="color:blue">null</span>) { Repr <span style="color:blue">:=</span> Repr + {F} + F.Repr; }</code></pre></li></ul>
+<h4 id="sec-axiom" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.3</span>.&#8194;</span>axiom</h4>
+<p class="p noindent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:axiom</span>}</code> attribute may be placed on a function or method.
+It means that the post-condition may be assumed to be true
+without proof. In that case also the body of the function or
+method may be omitted.
+</p>
+<p class="p indent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:axiom</span>}</code> attribute is also used for generated <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">reveal_*</code>
+lemmas as shown in Section&nbsp;<a href="#sec-opaque" title="24.1.12.&#8194;opaque" class="localref" style="target-element:h3"><span class="heading-label">24.1.12</span></a>.
+</p><h4 id="sec-compile" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.4</span>.&#8194;</span>compile</h4>
+<p class="p noindent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:compile</span>}</code> attribute takes a boolean argument. It may be applied to
+any top-level declaration. If that argument is false then that declaration
+will not be compiled into .Net code.
+</p><h4 id="sec-decl" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.5</span>.&#8194;</span>decl</h4>
+<p class="p noindent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:decl</span>}</code> attribute may be placed on a method declaration. It
+inhibits the error message that has would be given when the method has a
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">ensures</span></code> clauses but no body.
+</p>
+<p class="p indent">TODO: There are no examples of this in the Dafny tests. What is the motivation
+for this?
+</p><h4 id="sec-fuel" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.6</span>.&#8194;</span>fuel</h4>
+<p class="p noindent">The fuel attributes is used to specify how much &#8220;fuel&#8221; a function should have,
+i.e., how many times Z3 is permitted to unfold it&#39;s definition. The
+new {:fuel} annotation can be added to the function itself, it which
+case it will apply to all uses of that function, or it can overridden
+within the scope of a module, function, method, iterator, calc, forall,
+while, assert, or assume. The general format is:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>{<span style="color:purple">:fuel</span> functionName,lowFuel,highFuel}</code></pre>
+<p class="p noindent para-continued">When applied as an annotation to the function itself, omit
+functionName. If highFuel is omitted, it defaults to lowFuel + 1.
+</p>
+<p class="p indent">The default fuel setting for recursive functions is 1,2. Setting the
+fuel higher, say, to 3,4, will give more unfoldings, which may make
+some proofs go through with less programmer assistance (e.g., with
+fewer assert statements), but it may also increase verification time,
+so use it with care. Setting the fuel to 0,0 is similar to making the
+definition opaque, except when used with all literal arguments.
+</p><h4 id="sec-heapquantifier" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.7</span>.&#8194;</span>heapQuantifier</h4>
+<p class="p noindent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:heapQuantifier</span>}</code> attribute may be used on a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_quantifierexpression" class="ntref localref" style="color:maroon">QuantifierExpression</a></span></code>.
+When it appears in a quantifier expression it is as if a new heap-valued
+quantifier variable was added to the quantification. Consider this code
+that is one of the invariants of a while loop.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:purple">invariant</span> <span style="color:blue">forall</span> u {<span style="color:purple">:heapQuantifier</span>} :: f(u) == u + r</code></pre>
+<p class="p noindent para-continued">The quantifier is translated into the following Boogie:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>(<span style="color:blue">forall</span> q$heap#<span class="constant" style="color:purple">8</span>: Heap, u#<span class="constant" style="color:purple">5</span>: <span style="color:teal">int</span> ::
+ {<span style="color:purple">:heapQuantifier</span>}
+ $IsGoodHeap(q$heap#<span class="constant" style="color:purple">8</span>) &amp;&amp; ($Heap == q$heap#<span class="constant" style="color:purple">8</span> || $HeapSucc($Heap, q$heap#<span class="constant" style="color:purple">8</span>))
+ ==&gt; $Unbox(Apply1(TInt, TInt, f#<span class="constant" style="color:purple">0</span>, q$heap#<span class="constant" style="color:purple">8</span>, $Box(u#<span class="constant" style="color:purple">5</span>))): <span style="color:teal">int</span> == u#<span class="constant" style="color:purple">5</span> + r#<span class="constant" style="color:purple">0</span>);</code></pre>
+<p class="p noindent para-continued">What this is saying is that the quantified expression, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f(u) == u + r</code>,
+which may depend on the heap, is also valid for any good heap that is either the
+same as the current heap, or that is derived from it by heap update operations.
+</p>
+<p class="p indent">TODO: I think this means that the quantified expression is actually independent of the
+heap. Is that true?
+</p><h4 id="sec-imported" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.8</span>.&#8194;</span>imported</h4>
+<p class="p noindent">If a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_methoddecl" class="ntref localref" style="color:maroon">MethodDecl</a></span></code> or <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_functiondecl" class="ntref localref" style="color:maroon">FunctionDecl</a></span></code> has an <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:imported</span>}</code> attribute,
+then it is allowed to have a empty body even though it has an <strong class="strong-star2">ensures</strong>
+clause. Ordinarily a body would be required in order to provide the
+proof of the <strong class="strong-star2">ensures</strong> clause (but the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(:axiom)</code> attribute also
+provides this facility, so the need for <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(:imported)</code> is not clear.)
+A method or function declaration may be given the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(:imported)</code> attribute. This suppresses
+the error message that would be given if a method or function with an <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">ensures</span></code> clause
+does not have a body.
+</p>
+<p class="p indent">TODO: When would this be used? An example would be helpful.
+</p>
+<p class="p indent">TODO: When is this useful or valid?
+</p><h4 id="sec-induction" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.9</span>.&#8194;</span>induction</h4>
+<p class="p noindent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:induction</span>}</code> attribute controls the application of
+proof by induction to two contexts. Given a list of
+variables on which induction might be applied, the
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:induction</span>}</code> attribute selects a sub-list of those
+variables (in the same order) to which to apply induction.
+</p>
+<p class="p indent">TODO: Would there be any advantage to taking the order
+from the attribute, rather than preserving the original
+order? That would seem to give the user more control.
+</p>
+<p class="p indent">The two contexts are:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">A method, in which case the bound variables are all the
+in-parameters of the method.
+</li>
+<li class="li ul-li list-star-li compact-li">A quantifier expression, in which case the bound variables
+are the bound variables of the quantifier expression.
+</li></ul>
+
+<p class="p noindent">The form of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:induction</span>}</code> attribute is one of the following:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:induction</span>}</code> &#8211; apply induction to all bound variables
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:induction</span> <span style="color:blue">false</span>}</code> &#8211; suppress induction, that is, don&#39;t apply it to any bound variable
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:induction</span> L}</code> where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">L</code> is a list consisting entirely of bound variables
+&#8211; apply induction to the specified bound variables
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:induction</span> X}</code> where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">X</code> is anything else &#8211; treat the same as
+{:induction}, that is, apply induction to all bound variables. For this
+usage conventionally <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">X</code> is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">true</span></code>.
+</li></ul>
+
+<p class="p noindent">Here is an example of using it on a quantifier expression:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:blue">ghost</span> <span style="color:blue">method</span> Fill_J(s: <span style="color:teal">seq</span>&lt;<span style="color:teal">int</span>&gt;)
+ <span style="color:purple">requires</span> <span style="color:blue">forall</span> i :: <span class="constant" style="color:purple">1</span> &lt;= i &lt; |s| ==&gt; s[i-<span class="constant" style="color:purple">1</span>] &lt;= s[i]
+ <span style="color:purple">ensures</span> <span style="color:blue">forall</span> i,j {<span style="color:purple">:induction</span> j} :: <span class="constant" style="color:purple">0</span> &lt;= i &lt; j &lt; |s| ==&gt; s[i] &lt;= s[j]
+{
+}</code></pre><h4 id="sec-layerquantifier" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.10</span>.&#8194;</span>layerQuantifier</h4>
+<p class="p noindent">When Dafny is translating a quantified expression, if it has
+a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:layerQuantifier</span>}</code> attribute an additional quantifier
+variable is added to the quantifier bound variables.
+This variable as the predefined <em class="em-low1">LayerType</em>.
+A <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:layerQuantifier</span>}</code> attribute may be placed on a quantifier expression.
+Translation of Dafny into Boogie defines a <em class="em-low1">LayerType</em> which has defined zero and
+successor constructors.
+</p>
+<p class="p indent">The Dafny source has the comment that &#8220;if a function is recursive,
+then make the reveal lemma quantifier a layerQuantifier.&#8221;
+And in that case it adds the attribute to the quantifier.
+</p>
+<p class="p indent">There is no explicit user of the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:layerQuantifier</span>}</code> attribute
+in the Dafny tests. So I believe this attribute is only used
+internally by Dafny and not externally.
+</p>
+<p class="p indent">TODO: Need more complete explanation of this attribute.
+</p><h4 id="sec-nativetype" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.11</span>.&#8194;</span>nativeType</h4>
+<p class="p noindent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:nativeType</span>}</code> attribute may only be used on a <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_newtypedecl" class="ntref localref" style="color:maroon">NewtypeDecl</a></span></code>
+where the base type is an integral type. It can take one of the following
+forms:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:nativeType</span>}</code> - With no parameters it has no effect and the <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_newtypedecl" class="ntref localref" style="color:maroon">NewtypeDecl</a></span></code>
+have its default behavior which is to choose a native type that can hold any
+value satisfying the constraints, if possible, otherwise BigInteger is used.
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:nativeType</span> <span style="color:blue">true</span>}</code> - Also gives default <code class="grammar-ref code code2 language-java lang-java java colorized"><span class="code-escaped"><a href="#1_newtypedecl" class="ntref localref" style="color:maroon">NewtypeDecl</a></span></code> behavior,
+but gives an error if base type is not integral.
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:nativeType</span> <span style="color:blue">false</span>}</code> - Inhibits using a native type. BigInteger is used
+for integral types and BitRational for real types.
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:nativeType</span> <span style="color:maroon">&quot;</span><span style="color:maroon">typename</span><span style="color:maroon">&quot;</span>}</code> - This form has an native integral
+type name as a string literal. Acceptable values are: &#8220;byte&#8221;,
+&#8220;sbyte&#8221;, &#8220;ushort&#8221;, &#8220;short&#8221;, &#8220;uint&#8221;, &#8220;int&#8221;, &#8220;ulong&#8221; and &#8220;long&#8221;.
+An error is reported if the given data type cannot hold all the
+values that satisfy the constraint.
+</li></ul>
+<h4 id="sec-opaque" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.12</span>.&#8194;</span>opaque</h4>
+<p class="p noindent">Ordinarily the body of a function is transparent to its users but
+sometimes it is useful to hide it. If a function <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">f</code> is given the
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:opaque</span>}</code> attribute then Dafny hides the body of the function,
+so that it can only be seen within its recursive clique (if any),
+or if the programmer specifically asks to see it via the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">reveal_f()</code> lemma.
+</p>
+<p class="p indent">We create a lemma to allow the user to selectively reveal the function&#39;s body <br>
+That is, given:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:blue">function</span> {<span style="color:purple">:opaque</span>} foo(x:<span style="color:teal">int</span>, y:<span style="color:teal">int</span>) : <span style="color:teal">int</span>
+ <span style="color:purple">requires</span> <span class="constant" style="color:purple">0</span> &lt;= x &lt; <span class="constant" style="color:purple">5</span>
+ <span style="color:purple">requires</span> <span class="constant" style="color:purple">0</span> &lt;= y &lt; <span class="constant" style="color:purple">5</span>
+ <span style="color:purple">ensures</span> foo(x, y) &lt; <span class="constant" style="color:purple">10</span>
+ { x + y }</code></pre>
+<p class="p noindent para-continued">We produce:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> <span style="color:blue">lemma</span> {<span style="color:purple">:axiom</span>} reveal_foo()
+ <span style="color:purple">ensures</span> <span style="color:blue">forall</span> x:<span style="color:teal">int</span>, y:<span style="color:teal">int</span> {<span style="color:purple">:trigger</span> foo(x,y)} ::
+ <span class="constant" style="color:purple">0</span> &lt;= x &lt; <span class="constant" style="color:purple">5</span> &amp;&amp; <span class="constant" style="color:purple">0</span> &lt;= y &lt; <span class="constant" style="color:purple">5</span> ==&gt; foo(x,y) == foo_FULL(x,y)</code></pre>
+<p class="p noindent para-continued">where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">foo_FULL</code> is a copy of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">foo</code> which does not have its body
+hidden. In addition <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">foo_FULL</code> is given the
+<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:opaque_full</span>}</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:auto_generated</span>}</code> attributes in addition
+to the <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:opaque</span>}</code> attribute (which it got because it is a copy of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">foo</code>).
+</p><h4 id="sec-opaque-full" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.13</span>.&#8194;</span>opaque full</h4>
+<p class="p noindent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:opaque_full</span>}</code> attribute is used to mark the <em class="em-low1">full</em> version
+of an opaque function. See Section&nbsp;<a href="#sec-opaque" title="24.1.12.&#8194;opaque" class="localref" style="target-element:h3"><span class="heading-label">24.1.12</span></a>.
+</p><h4 id="sec-prependasserttoken" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.14</span>.&#8194;</span>prependAssertToken</h4>
+<p class="p noindent">This is used internally in Dafny as part of module refinement.
+It is an attribute on an assert statement.
+The Dafny code has the following comment:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code><span style="color:darkgreen">// Clone the expression, but among the new assert&#39;s attributes, indicate</span>
+<span style="color:darkgreen">// that this assertion is supposed to be translated into a check. That is,</span>
+<span style="color:darkgreen">// it is not allowed to be just assumed in the translation, despite the fact</span>
+<span style="color:darkgreen">// that the condition is inherited.</span></code></pre>
+<p class="p noindent para-continued">TODO: Decide if we want to describe this in more detail, or whether
+the functionality is already adequately described where
+refinement is described.
+</p><h4 id="sec-tailrecursion" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.15</span>.&#8194;</span>tailrecursion</h4>
+<p class="p noindent">This attribute is used on a method declarations. It has a boolean argument.
+</p>
+<p class="p indent">If specified with a false value it means the user specifically
+requested no tail recursion, so none is done.
+</p>
+<p class="p indent">If specified with a true value, or if not specified
+then tail recursive optimization will be attempted subject to
+the following conditions:
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li">It is an error if the method is a ghost method and tail
+recursion was explicitly requested.
+</li>
+<li class="li ul-li list-star-li compact-li">Only direct recursion is supported, not mutually recursive methods.
+</li>
+<li class="li ul-li list-star-li compact-li">If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:tailrecursion</span> <span style="color:blue">true</span>}</code> was specified but the code does not allow it
+an error message is given.
+</li></ul>
+<h4 id="sec-timelimitmultiplier" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.16</span>.&#8194;</span>timeLimitMultiplier</h4>
+<p class="p noindent">This attribute may be placed on a method or function declaration
+and has an integer argument. If <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:timeLimitMultiplier</span> X}</code> was
+specified a <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:timelimit</span> Y}</code> attributed is passed on to Boogie
+where <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Y</code> is <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">X</code> times either the default verification time limit
+for a function or method, or times the value specified by the
+Boogie <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">timelimit</code> command-line option.
+</p><h4 id="sec-trigger" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.17</span>.&#8194;</span>trigger</h4>
+<p class="p noindent">Trigger attributes are used on quantifiers and comprehensions.
+They are translated into Boogie triggers.
+</p><h4 id="sec-typequantifier" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">24.1.18</span>.&#8194;</span>typeQuantifier</h4>
+<p class="p noindent">The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:typeQuantifier</span>}</code> must be used on a quantifier if it
+quantifies over types.
+</p><h3 id="sec-boogie-attributes" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">24.2</span>.&#8194;</span>Boogie Attributes</h3>
+<p class="p noindent">Use the Boogie &#8220;/attrHelp&#8221; option to get the list of attributes
+that Boogie recognizes and their meaning. Here is the output at
+the time of this writing. Dafny passes attributes that have
+been specified to the Boogie.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>Boogie: The following attributes are supported by <span style="color:blue">this</span> implementation.
+
+ ---- On top-level declarations ---------------------------------------------
+
+ {<span style="color:purple">:ignore</span>}
+ Ignore the declaration (after checking for duplicate names).
+
+ {<span style="color:purple">:extern</span>}
+ If two top-level declarations introduce the same name (for example, two
+ constants with the same name or two procedures with the same name), <span style="color:blue">then</span>
+ Boogie usually produces an error message. However, <span style="color:blue">if</span> at least one of
+ the declarations is declared with :extern, one of the declarations is
+ ignored. If both declarations are :extern, Boogie arbitrarily chooses
+ one of them to keep; otherwise, Boogie ignore the :extern declaration
+ and keeps the other.
+
+ {<span style="color:purple">:checksum</span> &lt;<span style="color:teal">string</span>&gt;}
+ Attach a checksum to be used for verification result caching.
+
+ ---- On implementations and procedures -------------------------------------
+
+ {<span style="color:purple">:inline</span> N}
+ Inline given procedure (can be also used on implementation).
+ N should be a non-negative number and represents the inlining depth.
+ With /inline:<span style="color:blue">assume</span> call is replaced with <span style="color:maroon">&quot;</span><span style="color:maroon">assume false</span><span style="color:maroon">&quot;</span> once inlining depth is reached.
+ With /inline:<span style="color:blue">assert</span> call is replaced with <span style="color:maroon">&quot;</span><span style="color:maroon">assert false</span><span style="color:maroon">&quot;</span> once inlining depth is reached.
+ With /inline:spec call is left <span style="color:blue">as</span> is once inlining depth is reached.
+ With the above three options, methods with the attribute {<span style="color:purple">:inline</span> N} are not verified.
+ With /inline:none the entire attribute is ignored.
+
+ {<span style="color:purple">:verify</span> <span style="color:blue">false</span>}
+ Skip verification of an implementation.
+
+ {<span style="color:purple">:vcs_max_cost</span> N}
+ {<span style="color:purple">:vcs_max_splits</span> N}
+ {<span style="color:purple">:vcs_max_keep_going_splits</span> N}
+ Per-implementation versions of
+ /vcsMaxCost, /vcsMaxSplits and /vcsMaxKeepGoingSplits.
+
+ {<span style="color:purple">:selective_checking</span> <span style="color:blue">true</span>}
+ Turn all asserts into assumes except for the ones reachable from
+ assumptions marked with the attribute {<span style="color:purple">:start_checking_here</span>}.
+ Thus, <span style="color:maroon">&quot;</span><span style="color:maroon">assume {:start_checking_here} something;</span><span style="color:maroon">&quot;</span> becomes an inverse
+ of <span style="color:maroon">&quot;</span><span style="color:maroon">assume false;</span><span style="color:maroon">&quot;</span>: the first one disables all verification before
+ it, and the second one disables all verification after.
+
+ {<span style="color:purple">:priority</span> N}
+ Assign a positive priority <span style="color:maroon">&#39;N&#39;</span> to an implementation to control the order
+ <span style="color:blue">in</span> which implementations are verified (<span style="color:blue">default</span>: N = <span class="constant" style="color:purple">1</span>).
+
+ {<span style="color:purple">:id</span> &lt;<span style="color:teal">string</span>&gt;}
+ Assign a unique ID to an implementation to be used for verification
+ result caching (<span style="color:blue">default</span>: <span style="color:maroon">&quot;</span><span style="color:maroon">&lt;impl. name&gt;:0</span><span style="color:maroon">&quot;</span>).
+
+ {<span style="color:purple">:timeLimit</span> N}
+ Set the time limit for a given implementation.
+
+ ---- On functions ----------------------------------------------------------
+
+ {<span style="color:purple">:builtin</span> <span style="color:maroon">&quot;</span><span style="color:maroon">spec</span><span style="color:maroon">&quot;</span>}
+ {<span style="color:purple">:bvbuiltin</span> <span style="color:maroon">&quot;</span><span style="color:maroon">spec</span><span style="color:maroon">&quot;</span>}
+ Rewrite the <span style="color:blue">function</span> to built-<span style="color:blue">in</span> prover <span style="color:blue">function</span> symbol &#39;fn&#39;.
+
+ {<span style="color:purple">:inline</span>}
+ {<span style="color:purple">:inline</span> <span style="color:blue">true</span>}
+ Expand <span style="color:blue">function</span> according to its definition before going to the prover.
+
+ {<span style="color:purple">:never_pattern</span> <span style="color:blue">true</span>}
+ Terms starting with <span style="color:blue">this</span> <span style="color:blue">function</span> symbol will never be
+ automatically selected <span style="color:blue">as</span> patterns. It does not prevent them
+ from being used inside the triggers, and does not affect explicit
+ trigger annotations. Internally it works by adding {<span style="color:purple">:nopats</span> ...}
+ annotations to quantifiers.
+
+ {<span style="color:purple">:identity</span>}
+ {<span style="color:purple">:identity</span> <span style="color:blue">true</span>}
+ If the <span style="color:blue">function</span> has <span class="constant" style="color:purple">1</span> argument and the use of it has <span style="color:blue">type</span> X-&gt;X for
+ some X, <span style="color:blue">then</span> the <span style="color:blue">abstract</span> interpreter will treat the <span style="color:blue">function</span> <span style="color:blue">as</span> an
+ identity <span style="color:blue">function</span>. Note, the <span style="color:blue">abstract</span> interpreter trusts the
+ attribute--it does not try to verify that the <span style="color:blue">function</span> really is an
+ identity <span style="color:blue">function</span>.
+
+ ---- On variables ----------------------------------------------------------
+
+ {<span style="color:purple">:existential</span> <span style="color:blue">true</span>}
+ Marks a global Boolean variable <span style="color:blue">as</span> existentially quantified. If
+ used <span style="color:blue">in</span> combination with option /contractInfer Boogie will check
+ whether there <span class="code-escaped">&#8707;<span style="font-family:serif">&#8201;</span></span>a Boolean assignment to the existentials
+ that makes all verification conditions valid. Without option
+ /contractInfer the attribute is ignored.
+
+ ---- On <span style="color:blue">assert</span> statements --------------------------------------------------
+
+ {<span style="color:purple">:subsumption</span> n}
+ Overrides the /subsumption command-line setting for <span style="color:blue">this</span> assertion.
+
+ {<span style="color:purple">:split_here</span>}
+ Verifies code leading to <span style="color:blue">this</span> point and code leading from <span style="color:blue">this</span> point
+ to the next split_here <span style="color:blue">as</span> separate pieces. May help with timeouts.
+ May also occasionally double-report errors.
+
+ ---- The end ---------------------------------------------------------------
+</code></pre>
+<p class="p noindent para-continued">However a scan of Boogie&#39;s sources shows it checks for the
+following attributes.
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{:$}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{:$renamed$}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:InlineAssume</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:PossiblyUnreachable</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:__dominator_enabled</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:__enabled</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:a</span>##post##}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:absdomain</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:ah</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:assumption</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:assumption_variable_initialization</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:atomic</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:aux</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:both</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:bvbuiltin</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:candidate</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:captureState</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:checksum</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:constructor</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:datatype</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:do_not_predicate</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:entrypoint</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:existential</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:exitAssert</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:expand</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:extern</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:hidden</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:ignore</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:inline</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:left</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:linear</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:linear_in</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:linear_out</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:msg</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:name</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:originated_from_invariant</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:partition</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:positive</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:post</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:pre</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:precondition_previous_snapshot</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:qid</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:right</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:selective_checking</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:si_fcall</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:si_unique_call</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:sourcefile</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:sourceline</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:split_here</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:stage_active</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:stage_complete</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:staged_houdini_tag</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:start_checking_here</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:subsumption</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:template</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:terminates</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:upper</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:verified_under</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:weight</span>}</code>
+</li>
+<li class="li ul-li list-star-li compact-li"><code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">{<span style="color:purple">:yields</span>}</code>
+</li></ul>
+<h2 id="sec-dafny-users-guide" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">25</span>.&#8194;</span>Dafny User&#39;s Guide</h2><h3 id="sec-installing-dafny-from-binaries" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">25.0</span>.&#8194;</span>Installing Dafny From Binaries</h3><h3 id="sec-building-dafny-from-source" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">25.1</span>.&#8194;</span>Building Dafny from Source</h3>
+<p class="p noindent">The current version of Dafny only works with Visual Studio 2012,
+so if you intend to run Dafny from withing Visual Studio you must
+install Visual Studio 2012.
+</p>
+<p class="p indent">Dafny performs its verification by translating the Dafny source into
+the Boogie intermediate verification language. So Dafny references
+data structures defined in the Boogie project. So the first step
+is to clone and build Boogie from sources. See
+<a href="https://github.com/boogie-org/boogie">https://github.com/boogie-org/boogie</a>.
+</p>
+<p class="p indent">Follow these steps.
+</p>
+<p class="p indent">Let <em class="em-low1">work</em> be a working directory.
+</p>
+<p class="p indent">Clone Boogie using
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>cd work
+git clone https:<span style="color:darkgreen">//github.com/boogie-org/boogie.git</span></code></pre>
+<p class="p noindent para-continued">Build Boogie using the directions from the Boogie web site,
+which for Windows currently are:
+</p>
+<ol class="ol compact">
+<li class="li ol-li compact-li">Open Source\Boogie.sln in Visual Studio
+</li>
+<li class="li ol-li compact-li">Right click the Boogie solution in the Solution Explorer and click Enable NuGet Package Restore. You will probably get a prompt asking to confirm this. Choose Yes.
+</li>
+<li class="li ol-li compact-li">Click BUILD &gt; Build Solution.
+</li></ol>
+
+<p class="p noindent">Clone Dafny using Mercurial. The Dafny directory must be a sibling
+of the Boogie directory in order for it to find the Boogie files it needs.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>cd work
+hg clone https:<span style="color:darkgreen">//hg.codeplex.com/dafny </span></code></pre>
+<p class="p noindent para-continued">Download and install the Visual Studio 2012 SDK from
+</p>
+<ul class="ul list-star compact">
+<li class="li ul-li list-star-li compact-li"><a href="https://www.microsoft.com/en-us/download/details.aspx?id=30668">https://www.microsoft.com/en-us/download/details.aspx?id=30668</a>.
+</li></ul>
+
+<p class="p noindent">This is needed to build the Visual Studio Extension that
+runs Dafny from within Visual Studio 2012.
+</p>
+<p class="p indent">Build the command-line Dafny executables.
+1. Open dafny\Source\Dafny.sln in Visual Studio
+2. Click BUILD &gt; Build Solution.
+</p>
+<p class="p indent">Build and install the Dafny Visual Studio extensions
+</p>
+<ol class="ol compact">
+<li class="li ol-li compact-li">Open dafny/Source/DafnyExtension.sln in Visual Studio
+</li>
+<li class="li ol-li compact-li">Click BUILD &gt; Build Solution.
+</li>
+<li class="li ol-li compact-li">This builds DafnyLanguageService.vsix and DafnyMenu.vsix
+in the dafny/Binaries directory.
+</li>
+<li class="li ol-li compact-li">Install these by clicking on them from Windows Explorer. When
+prompted, only check installing into Visual Studio 2012.
+</li></ol>
+<h3 id="sec-using-dafny-from-visual-studio" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">25.2</span>.&#8194;</span>Using Dafny From Visual Studio</h3>
+<p class="p noindent">To test your installation, you can open Dafny test files
+from the dafny/Test subdirectory in Visual Studio 2012.
+You will want to use &#8220;VIEW/Error List&#8221; to ensure that
+you see any errors that Dafny detects, and
+&#8220;VIEW/Output&#8221; to see the result of any compilation.
+</p>
+<p class="p indent">An example of a valid Dafny test is
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>dafny\Test\vstte2012\Tree.dfy</code></pre>
+<p class="p noindent para-continued">You can choose &#8220;Dafny/Compile&#8221; to compile the Dafny
+program to C#. Doing that for the above test
+produces <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Tree.cs</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Tree.dll</code> (since this test does
+not have a main program).
+</p>
+<p class="p indent">The following file:
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code>D:\gh\dafny\Test\dafny0\Array.dfy</code></pre>
+<p class="p noindent para-continued">is an example of a Dafny file with verification errors.
+The source will show red squiggles or dots where there
+are errors, and the Error List window will describe the
+errors.
+</p><h3 id="sec-using-dafny-from-the-command-line" class="h2" data-heading-depth="2" style="display:block"><span class="heading-before"><span class="heading-label">25.3</span>.&#8194;</span>Using Dafny From the Command Line</h3><h4 id="sec-dafny-command-line-options" class="h3" data-heading-depth="3" style="display:block"><span class="heading-before"><span class="heading-label">25.3.0</span>.&#8194;</span>Dafny Command Line Options</h4>
+<p class="p noindent">The command <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">Dafny.exe /?</code> gives the following description of
+options that can be passed to Dafny.
+</p>
+<pre class="para-block pre-fenced pre-fenced3 language-dafnyx lang-dafnyx dafnyx colorized" style="display:block;font-size:small;margin-left:1em"><code> ---- Dafny options ---------------------------------------------------------
+
+ Multiple .dfy files supplied on the command line are concatenated into one
+ Dafny program.
+
+ /dprelude:&lt;file&gt;
+ choose Dafny prelude file
+ /dprint:&lt;file&gt;
+ <span style="color:blue">print</span> Dafny program after parsing it
+ (use - <span style="color:blue">as</span> &lt;file&gt; to <span style="color:blue">print</span> to console)
+ /printMode:&lt;Everything|NoIncludes|NoGhost&gt;
+ NoIncludes disables printing of {<span style="color:purple">:verify</span> <span style="color:blue">false</span>} methods incorporated via the
+ <span style="color:blue">include</span> mechanism, <span style="color:blue">as</span> well <span style="color:blue">as</span> datatypes and fields included from other files.
+ NoGhost disables printing of functions, <span style="color:blue">ghost</span> methods, and proof statements
+ <span style="color:blue">in</span> implementation methods. It also disables anything NoIncludes disables.
+ /rprint:&lt;file&gt;
+ <span style="color:blue">print</span> Dafny program after resolving it
+ (use - <span style="color:blue">as</span> &lt;file&gt; to <span style="color:blue">print</span> to console)
+ /dafnyVerify:&lt;n&gt;
+ <span class="constant" style="color:purple">0</span> - stop after typechecking
+ <span class="constant" style="color:purple">1</span> - continue on to translation, verification, and compilation
+ /compile:&lt;n&gt; <span class="constant" style="color:purple">0</span> - do not compile Dafny program
+ <span class="constant" style="color:purple">1</span> (<span style="color:blue">default</span>) - upon successful verification of the Dafny
+ program, compile Dafny program to .NET assembly
+ Program.exe (<span style="color:blue">if</span> the program has a Main <span style="color:blue">method</span>) or
+ Program.dll (othewise), <span style="color:blue">where</span> Program.dfy is the name
+ of the last .dfy file on the command line
+ <span class="constant" style="color:purple">2</span> - always attempt to compile Dafny program to C# program
+ out.cs, regardless of verification outcome
+ <span class="constant" style="color:purple">3</span> - <span style="color:blue">if</span> there is a Main <span style="color:blue">method</span> and there are no verification
+ errors, compiles program <span style="color:blue">in</span> memory (i.e., does not write
+ an output file) and runs it
+ /spillTargetCode:&lt;n&gt;
+ <span class="constant" style="color:purple">0</span> (<span style="color:blue">default</span>) - don&#39;t write the compiled Dafny program (but
+ still compile it, <span style="color:blue">if</span> /compile indicates to do so)
+ <span class="constant" style="color:purple">1</span> - write the compiled Dafny program <span style="color:blue">as</span> a .cs file
+ /dafnycc Disable features not supported by DafnyCC
+ /noCheating:&lt;n&gt;
+ <span class="constant" style="color:purple">0</span> (<span style="color:blue">default</span>) - allow <span style="color:blue">assume</span> statements and <span style="color:purple">free</span> invariants
+ <span class="constant" style="color:purple">1</span> - treat all assumptions <span style="color:blue">as</span> asserts, and drop <span style="color:purple">free</span>.
+ /induction:&lt;n&gt;
+ <span class="constant" style="color:purple">0</span> - never do induction, not even when attributes request it
+ <span class="constant" style="color:purple">1</span> - only apply induction when attributes request it
+ <span class="constant" style="color:purple">2</span> - apply induction <span style="color:blue">as</span> requested (by attributes) and also
+ for heuristically chosen quantifiers
+ <span class="constant" style="color:purple">3</span> (<span style="color:blue">default</span>) - apply induction <span style="color:blue">as</span> requested, and for
+ heuristically chosen quantifiers and <span style="color:blue">ghost</span> methods
+ /inductionHeuristic:&lt;n&gt;
+ <span class="constant" style="color:purple">0</span> - least discriminating induction heuristic (that is, lean
+ toward applying induction more often)
+ <span class="constant" style="color:purple">1</span>,<span class="constant" style="color:purple">2</span>,<span class="constant" style="color:purple">3</span>,<span class="constant" style="color:purple">4</span>,<span class="constant" style="color:purple">5</span> - levels <span style="color:blue">in</span> between, ordered <span style="color:blue">as</span> follows <span style="color:blue">as</span> far <span style="color:blue">as</span>
+ how discriminating they are: <span class="constant" style="color:purple">0</span> &lt; <span class="constant" style="color:purple">1</span> &lt; <span class="constant" style="color:purple">2</span> &lt; (<span class="constant" style="color:purple">3</span>,<span class="constant" style="color:purple">4</span>) &lt; <span class="constant" style="color:purple">5</span> &lt; <span class="constant" style="color:purple">6</span>
+ <span class="constant" style="color:purple">6</span> (<span style="color:blue">default</span>) - most discriminating
+ /noIncludes Ignore <span style="color:blue">include</span> directives
+ /noNLarith Reduce Z3&#39;s knowledge of non-linear arithmetic (*,/,%).
+ Results <span style="color:blue">in</span> more manual work, but also produces more predictable behavior.
+ /autoReqPrint:&lt;file&gt;
+ Print out requirements that were automatically generated by autoReq.
+ /noAutoReq Ignore autoReq attributes
+ /allowGlobals Allow the implicit <span style="color:blue">class</span> &#39;_default&#39; to contain fields, instance functions,
+ and instance methods. These <span style="color:blue">class</span> members are declared at the <span style="color:blue">module</span> scope,
+ outside of explicit classes. This command-line option is provided to simply
+ a transition from the behavior <span style="color:blue">in</span> the language prior to version <span class="constant" style="color:purple">1.9</span>.<span class="constant" style="color:purple">3</span>, from
+ which point onward all functions and methods declared at the <span style="color:blue">module</span> scope are
+ implicitly <span style="color:blue">static</span> and fields declarations are not allowed at the <span style="color:blue">module</span> scope.
+ The reference manual is written assuming <span style="color:blue">this</span> option is not given.
+
+
+ /nologo suppress printing of version number, copyright message
+ /env:&lt;n&gt; <span style="color:blue">print</span> command line arguments
+ <span class="constant" style="color:purple">0</span> - never, <span class="constant" style="color:purple">1</span> (<span style="color:blue">default</span>) - during BPL <span style="color:blue">print</span> and prover log,
+ <span class="constant" style="color:purple">2</span> - like <span class="constant" style="color:purple">1</span> and also to standard output
+ /wait <span style="color:blue">await</span> Enter from keyboard before terminating program
+ /xml:&lt;file&gt; also produce output <span style="color:blue">in</span> XML format to &lt;file&gt;
+
+ ---- Boogie options --------------------------------------------------------
+
+ Multiple .bpl files supplied on the command line are concatenated into one
+ Boogie program.
+
+ /proc:&lt;p&gt; : limits which procedures to check
+ /noResolve : parse only
+ /noTypecheck : parse and resolve only
+
+ /<span style="color:blue">print</span>:&lt;file&gt; : <span style="color:blue">print</span> Boogie program after parsing it
+ (use - <span style="color:blue">as</span> &lt;file&gt; to <span style="color:blue">print</span> to console)
+ /pretty:&lt;n&gt;
+ <span class="constant" style="color:purple">0</span> - <span style="color:blue">print</span> each Boogie statement on one line (faster).
+ <span class="constant" style="color:purple">1</span> (<span style="color:blue">default</span>) - pretty-<span style="color:blue">print</span> with some line breaks.
+ /printWithUniqueIds : <span style="color:blue">print</span> augmented information that uniquely
+ identifies variables
+ /printUnstructured : with /<span style="color:blue">print</span> option, desugars all structured statements
+ /printDesugared : with /<span style="color:blue">print</span> option, desugars calls
+
+ /overlookTypeErrors : skip any implementation with resolution or <span style="color:blue">type</span>
+ checking errors
+
+ /loopUnroll:&lt;n&gt;
+ unroll loops, following up to n back edges (and <span style="color:blue">then</span> some)
+ /soundLoopUnrolling
+ sound loop unrolling
+ /printModel:&lt;n&gt;
+ <span class="constant" style="color:purple">0</span> (<span style="color:blue">default</span>) - do not <span style="color:blue">print</span> Z3&#39;s error model
+ <span class="constant" style="color:purple">1</span> - <span style="color:blue">print</span> Z3&#39;s error model
+ <span class="constant" style="color:purple">2</span> - <span style="color:blue">print</span> Z3&#39;s error model plus reverse mappings
+ <span class="constant" style="color:purple">4</span> - <span style="color:blue">print</span> Z3&#39;s error model <span style="color:blue">in</span> a more human readable way
+ /printModelToFile:&lt;file&gt;
+ <span style="color:blue">print</span> model to &lt;file&gt; instead of console
+ /mv:&lt;file&gt; Specify file <span style="color:blue">where</span> to save the model <span style="color:blue">in</span> BVD format
+ /enhancedErrorMessages:&lt;n&gt;
+ <span class="constant" style="color:purple">0</span> (<span style="color:blue">default</span>) - no enhanced error messages
+ <span class="constant" style="color:purple">1</span> - Z3 error model enhanced error messages
+
+ /printCFG:&lt;prefix&gt; : <span style="color:blue">print</span> control flow graph of each implementation <span style="color:blue">in</span>
+ Graphviz format to files named:
+ &lt;prefix&gt;.&lt;procedure name&gt;.dot
+
+ /useBaseNameForFileName : When parsing use basename of file for tokens instead
+ of the path supplied on the command line
+
+ ---- Inference options -----------------------------------------------------
+
+ /infer:&lt;flags&gt;
+ use <span style="color:blue">abstract</span> interpretation to infer invariants
+ The <span style="color:blue">default</span> is /infer:i
+ &lt;flags&gt; are <span style="color:blue">as</span> follows (missing &lt;flags&gt; means all)
+ i = intervals
+ c = constant propagation
+ d = dynamic <span style="color:blue">type</span>
+ n = nullness
+ p = polyhedra for linear inequalities
+ t = trivial bottom/top lattice (cannot be combined with
+ other domains)
+ j = stronger intervals (cannot be combined with other
+ domains)
+ or the following (which denote options, not domains):
+ s = debug statistics
+ <span class="constant" style="color:purple">0</span>..<span class="constant" style="color:purple">9</span> = number of iterations before applying a widen (<span style="color:blue">default</span>=<span class="constant" style="color:purple">0</span>)
+ /noinfer turn off the <span style="color:blue">default</span> inference, and overrides the /infer
+ switch on its left
+ /checkInfer instrument inferred invariants <span style="color:blue">as</span> asserts to be checked by
+ theorem prover
+ /interprocInfer
+ perform interprocedural inference (deprecated, not supported)
+ /contractInfer
+ perform procedure contract inference
+ /instrumentInfer
+ h - instrument inferred invariants only at beginning of
+ loop headers (<span style="color:blue">default</span>)
+ e - instrument inferred invariants at beginning and end
+ of every block (<span style="color:blue">this</span> mode is intended for use <span style="color:blue">in</span>
+ debugging of <span style="color:blue">abstract</span> domains)
+ /printInstrumented
+ <span style="color:blue">print</span> Boogie program after it has been instrumented with
+ invariants
+
+ ---- Debugging and general tracing options ---------------------------------
+
+ /trace blurt out various debug trace information
+ /traceTimes output timing information at certain points <span style="color:blue">in</span> the pipeline
+ /tracePOs output information about the number of proof obligations
+ (also included <span style="color:blue">in</span> the /trace output)
+ /log[:<span style="color:blue">method</span>] Print debug output during translation
+
+ /<span style="color:blue">break</span> launch and <span style="color:blue">break</span> into debugger
+
+ ---- Verification-condition generation options -----------------------------
+
+ /liveVariableAnalysis:&lt;c&gt;
+ <span class="constant" style="color:purple">0</span> = do not perform live variable analysis
+ <span class="constant" style="color:purple">1</span> = perform live variable analysis (<span style="color:blue">default</span>)
+ <span class="constant" style="color:purple">2</span> = perform interprocedural live variable analysis
+ /noVerify skip VC generation and invocation of the theorem prover
+ /verifySnapshots:&lt;n&gt;
+ verify several program snapshots (named &lt;filename&gt;.v0.bpl
+ to &lt;filename&gt;.vN.bpl) using verification result caching:
+ <span class="constant" style="color:purple">0</span> - do not use any verification result caching (<span style="color:blue">default</span>)
+ <span class="constant" style="color:purple">1</span> - use the basic verification result caching
+ <span class="constant" style="color:purple">2</span> - use the more advanced verification result caching
+ /verifySeparately
+ verify each input program separately
+ /removeEmptyBlocks:&lt;c&gt;
+ <span class="constant" style="color:purple">0</span> - do not remove empty blocks during VC generation
+ <span class="constant" style="color:purple">1</span> - remove empty blocks (<span style="color:blue">default</span>)
+ /coalesceBlocks:&lt;c&gt;
+ <span class="constant" style="color:purple">0</span> = do not coalesce blocks
+ <span class="constant" style="color:purple">1</span> = coalesce blocks (<span style="color:blue">default</span>)
+ /vc:&lt;variety&gt; n = nested block (<span style="color:blue">default</span> for /prover:Simplify),
+ m = nested block reach,
+ b = flat block, r = flat block reach,
+ s = structured, l = local,
+ d = dag (<span style="color:blue">default</span>, except with /prover:Simplify)
+ doomed = doomed
+ /traceverify <span style="color:blue">print</span> debug output during verification condition generation
+ /subsumption:&lt;c&gt;
+ apply subsumption to asserted conditions:
+ <span class="constant" style="color:purple">0</span> - never, <span class="constant" style="color:purple">1</span> - not for quantifiers, <span class="constant" style="color:purple">2</span> (<span style="color:blue">default</span>) - always
+ /alwaysAssumeFreeLoopInvariants
+ usually, a <span style="color:purple">free</span> loop <span style="color:purple">invariant</span> (or <span style="color:blue">assume</span>
+ statement <span style="color:blue">in</span> that position) is ignored <span style="color:blue">in</span> checking contexts
+ (like other <span style="color:purple">free</span> things); <span style="color:blue">this</span> option includes these <span style="color:purple">free</span>
+ loop invariants <span style="color:blue">as</span> assumes <span style="color:blue">in</span> both contexts
+ /inline:&lt;i&gt; use inlining strategy &lt;i&gt; for procedures with the :inline
+ attribute, see /attrHelp for details:
+ none
+ <span style="color:blue">assume</span> (<span style="color:blue">default</span>)
+ <span style="color:blue">assert</span>
+ spec
+ /printInlined
+ <span style="color:blue">print</span> the implementation after inlining calls to
+ procedures with the :inline attribute (works with /inline)
+ /lazyInline:<span class="constant" style="color:purple">1</span>
+ Use the lazy inlining algorithm
+ /stratifiedInline:<span class="constant" style="color:purple">1</span>
+ Use the stratified inlining algorithm
+ /fixedPointEngine:&lt;engine&gt;
+ Use the specified fixed point engine for inference
+ /recursionBound:&lt;n&gt;
+ Set the recursion bound for stratified inlining to
+ be n (<span style="color:blue">default</span> <span class="constant" style="color:purple">500</span>)
+ /inferLeastForUnsat:&lt;str&gt;
+ Infer the least number of constants (whose names
+ are prefixed by &lt;str&gt;) that need to be <span style="color:teal">set</span> to
+ <span style="color:blue">true</span> for the program to be correct. This turns
+ on stratified inlining.
+ /smoke Soundness Smoke Test: try to stick <span style="color:blue">assert</span> <span style="color:blue">false</span>; <span style="color:blue">in</span> some
+ places <span style="color:blue">in</span> the BPL and see <span style="color:blue">if</span> we can still prove it
+ /smokeTimeout:&lt;n&gt;
+ Timeout, <span style="color:blue">in</span> seconds, for a single theorem prover
+ invocation during smoke test, defaults to <span class="constant" style="color:purple">10</span>.
+ /causalImplies
+ Translate Boogie&#39;s A ==&gt; B into prover&#39;s A ==&gt; A &amp;&amp; B.
+ /typeEncoding:&lt;m&gt;
+ how to encode types when sending VC to theorem prover
+ n = none (unsound)
+ p = predicates (<span style="color:blue">default</span>)
+ a = arguments
+ m = monomorphic
+ /monomorphize
+ Do not <span style="color:blue">abstract</span> <span style="color:teal">map</span> types <span style="color:blue">in</span> the encoding (<span style="color:blue">this</span> is an
+ experimental feature that will not do the right thing <span style="color:blue">if</span>
+ the program uses polymorphism)
+ /reflectAdd In the VC, generate an auxiliary symbol, elsewhere defined
+ to be +, instead of +.
+
+ ---- Verification-condition splitting --------------------------------------
+
+ /vcsMaxCost:&lt;f&gt;
+ VC will not be split unless the cost of a VC exceeds <span style="color:blue">this</span>
+ number, defaults to <span class="constant" style="color:purple">2000.0</span>. This does NOT apply <span style="color:blue">in</span> the
+ keep-going mode after first round of splitting.
+ /vcsMaxSplits:&lt;n&gt;
+ Maximal number of VC generated per <span style="color:blue">method</span>. In keep
+ going mode only applies to the first round.
+ Defaults to <span class="constant" style="color:purple">1</span>.
+ /vcsMaxKeepGoingSplits:&lt;n&gt;
+ If <span style="color:teal">set</span> to more than <span class="constant" style="color:purple">1</span>, activates the keep
+ going mode, <span style="color:blue">where</span> after the first round of splitting,
+ VCs that timed out are split into &lt;n&gt; pieces and retried
+ until we succeed proving them, or there is only one
+ assertion on a single path and it timeouts (<span style="color:blue">in</span> which
+ <span style="color:blue">case</span> error is reported for that assertion).
+ Defaults to <span class="constant" style="color:purple">1</span>.
+ /vcsKeepGoingTimeout:&lt;n&gt;
+ Timeout <span style="color:blue">in</span> seconds for a single theorem prover
+ invocation <span style="color:blue">in</span> keep going mode, except for the final
+ single-assertion <span style="color:blue">case</span>. Defaults to <span class="constant" style="color:purple">1</span>s.
+ /vcsFinalAssertTimeout:&lt;n&gt;
+ Timeout <span style="color:blue">in</span> seconds for the single last
+ assertion <span style="color:blue">in</span> the keep going mode. Defaults to <span class="constant" style="color:purple">30</span>s.
+ /vcsPathJoinMult:&lt;f&gt;
+ If more than one path join at a block, by how much
+ multiply the number of paths <span style="color:blue">in</span> that block, to accomodate
+ for the fact that the prover will learn something on one
+ paths, before proceeding to another. Defaults to <span class="constant" style="color:purple">0.8</span>.
+ /vcsPathCostMult:&lt;f1&gt;
+ /vcsAssumeMult:&lt;f2&gt;
+ The cost of a block is
+ (&lt;<span style="color:blue">assert</span>-cost&gt; + &lt;f2&gt;*&lt;<span style="color:blue">assume</span>-cost&gt;) *
+ (<span class="constant" style="color:purple">1.0</span> + &lt;f1&gt;*&lt;entering-paths&gt;)
+ &lt;f1&gt; defaults to <span class="constant" style="color:purple">1.0</span>, &lt;f2&gt; defaults to <span class="constant" style="color:purple">0.01</span>.
+ The cost of a single assertion or assumption is
+ currently always <span class="constant" style="color:purple">1.0</span>.
+ /vcsPathSplitMult:&lt;f&gt;
+ If the best path split of a VC of cost A is into
+ VCs of cost B and C, <span style="color:blue">then</span> the split is applied <span style="color:blue">if</span>
+ A &gt;= &lt;f&gt;*(B+C), otherwise assertion splitting will be
+ applied. Defaults to <span class="constant" style="color:purple">0.5</span> (always do path splitting <span style="color:blue">if</span>
+ possible), <span style="color:teal">set</span> to more to do less path splitting
+ and more assertion splitting.
+ /vcsDumpSplits
+ For split #n dump split.n.dot and split.n.bpl.
+ Warning: Affects error reporting.
+ /vcsCores:&lt;n&gt;
+ Try to verify &lt;n&gt; VCs at once. Defaults to <span class="constant" style="color:purple">1</span>.
+ /vcsLoad:&lt;f&gt; Sets vcsCores to the machine&#39;s ProcessorCount * f,
+ rounded to the nearest integer (<span style="color:blue">where</span> <span class="constant" style="color:purple">0.0</span> &lt;= f &lt;= <span class="constant" style="color:purple">3.0</span>),
+ but never to less than <span class="constant" style="color:purple">1</span>.
+
+ ---- Prover options --------------------------------------------------------
+
+ /errorLimit:&lt;num&gt;
+ Limit the number of errors produced for each procedure
+ (<span style="color:blue">default</span> is <span class="constant" style="color:purple">5</span>, some provers may support only <span class="constant" style="color:purple">1</span>)
+ /timeLimit:&lt;num&gt;
+ Limit the number of seconds spent trying to verify
+ each procedure
+ /errorTrace:&lt;n&gt;
+ <span class="constant" style="color:purple">0</span> - no Trace labels <span style="color:blue">in</span> the error output,
+ <span class="constant" style="color:purple">1</span> (<span style="color:blue">default</span>) - <span style="color:blue">include</span> useful Trace labels <span style="color:blue">in</span> error output,
+ <span class="constant" style="color:purple">2</span> - <span style="color:blue">include</span> all Trace labels <span style="color:blue">in</span> the error output
+ /vcBrackets:&lt;b&gt;
+ bracket odd-charactered identifier names with |&#39;s. &lt;b&gt; is:
+ <span class="constant" style="color:purple">0</span> - no (<span style="color:blue">default</span> with non-/prover:Simplify),
+ <span class="constant" style="color:purple">1</span> - yes (<span style="color:blue">default</span> with /prover:Simplify)
+ /prover:&lt;tp&gt; use theorem prover &lt;tp&gt;, <span style="color:blue">where</span> &lt;tp&gt; is either the name of
+ a DLL containing the prover interface located <span style="color:blue">in</span> the
+ Boogie directory, or a full path to a DLL containing such
+ an interface. The standard interfaces shipped <span style="color:blue">include</span>:
+ SMTLib (<span style="color:blue">default</span>, uses the SMTLib2 format and calls Z3)
+ Z3 (uses Z3 with the Simplify format)
+ Simplify
+ ContractInference (uses Z3)
+ Z3api (Z3 using Managed .NET API)
+ /proverOpt:KEY[=VALUE]
+ Provide a prover-specific option (short form /p).
+ /proverLog:&lt;file&gt;
+ Log input for the theorem prover. Like filenames
+ supplied <span style="color:blue">as</span> arguments to other options, &lt;file&gt; can use the
+ following macros:
+ @TIME@ expands to the current time
+ @PREFIX@ expands to the concatenation of strings given
+ by /logPrefix options
+ @FILE@ expands to the last filename specified on the
+ command line
+ In addition, /proverLog can also use the macro &#39;@PROC@&#39;,
+ which causes there to be one prover log file per
+ verification condition, and the macro <span style="color:blue">then</span> expands to the
+ name of the procedure that the verification condition is for.
+ /logPrefix:&lt;str&gt;
+ Defines the expansion of the macro &#39;@PREFIX@&#39;, which can
+ be used <span style="color:blue">in</span> various filenames specified by other options.
+ /proverLogAppend
+ Append (not overwrite) the specified prover log file
+ /proverWarnings
+ <span class="constant" style="color:purple">0</span> (<span style="color:blue">default</span>) - don&#39;t <span style="color:blue">print</span>, <span class="constant" style="color:purple">1</span> - <span style="color:blue">print</span> to stdout,
+ <span class="constant" style="color:purple">2</span> - <span style="color:blue">print</span> to stderr
+ /proverMemoryLimit:&lt;num&gt;
+ Limit on the virtual memory for prover before
+ restart <span style="color:blue">in</span> MB (<span style="color:blue">default</span>:<span class="constant" style="color:purple">100</span>MB)
+ /restartProver
+ Restart the prover after each query
+ /proverShutdownLimit&lt;num&gt;
+ Time between closing the stream to the prover and
+ killing the prover process (<span style="color:blue">default</span>: <span class="constant" style="color:purple">0</span>s)
+ /platform:&lt;ptype&gt;,&lt;location&gt;
+ ptype = v11,v2,cli1
+ location = platform libraries directory
+
+ Simplify specific options:
+ /simplifyMatchDepth:&lt;num&gt;
+ Set Simplify prover&#39;s matching depth limit
+
+ Z3 specific options:
+ /z3opt:&lt;arg&gt; specify additional Z3 options
+ /z3multipleErrors
+ report multiple counterexamples for each error
+ /useArrayTheory
+ use Z3&#39;s native theory (<span style="color:blue">as</span> opposed to axioms). Currently
+ implies /monomorphize.
+ /useSmtOutputFormat
+ Z3 outputs a model <span style="color:blue">in</span> the SMTLIB2 format.
+ /z3types generate multi-sorted VC that make use of Z3 types
+ /z3lets:&lt;n&gt; <span class="constant" style="color:purple">0</span> - no LETs, <span class="constant" style="color:purple">1</span> - only LET TERM, <span class="constant" style="color:purple">2</span> - only LET FORMULA,
+ <span class="constant" style="color:purple">3</span> - (<span style="color:blue">default</span>) any
+ /z3exe:&lt;path&gt;
+ path to Z3 executable
+
+ CVC4 specific options:
+ /cvc4exe:&lt;path&gt;
+ path to CVC4 executable
+</code></pre><h2 id="sec-references" class="h1" data-heading-depth="1" style="display:block"><span class="heading-before"><span class="heading-label">26</span>.&#8194;</span>References</h2>
+<div class="bibl" style="bbl-file:out/DafnyRef-bib.bbl.mdk">
+<div class="bibliography bib-numeric" data-style="plainnat" style="bibstyle:madoko-numeric"><h2 id="sec-references" class="clearnum h1 heading-references" data-heading-depth="1" style="display:block">References</h2>
+<div id="boogie:architecture" class="bibitem" data-cite-label="0" data-cite-year="2006" data-cite-authors="Barnett et\ al." data-cite-authors-long="Barnett, Chang, DeLine, Jacobs, and
+ Leino" style="searchterm:Mike+Barnett+Evan+Chang+Robert+DeLine+Bart+Jacobs+Rustan+Leino+Boogie+modular+reusable+verifier+object+oriented+programs+Frank+Boer+Marcello+Bonsangue+Susanne+Graf+Willem+Paul+Roever+editors+_Formal+Methods+Components+Objects+International+Symposium+FMCO+volume+pages+Springer+September++"><span class="bibitem-before">[0]&#160;&#160;</span>Mike Barnett, Bor-Yuh&#160;Evan Chang, Robert DeLine, Bart Jacobs, and K.&#160;Rustan&#160;M.
+ Leino.
+<span class="newblock"></span> Boogie: A modular reusable verifier for object-oriented programs.
+<span class="newblock"></span> In Frank&#160;S. de&#160;Boer, Marcello&#160;M. Bonsangue, Susanne Graf, and
+ Willem-Paul de&#160;Roever, editors, <em class="em-low1">Formal Methods for Components and
+ Objects: 4th International Symposium, FMCO 2005</em>, volume 4111, pages
+ 364&#8211;387. Springer, September 2006.&nbsp;<a href="http://www.bing.com/search?q=Mike+Barnett+Evan+Chang+Robert+DeLine+Bart+Jacobs+Rustan+Leino+Boogie+modular+reusable+verifier+object+oriented+programs+Frank+Boer+Marcello+Bonsangue+Susanne+Graf+Willem+Paul+Roever+editors+_Formal+Methods+Components+Objects+International+Symposium+FMCO+volume+pages+Springer+September++" class="bibsearch">&#128270;</a></div>
+<div id="coq:book" class="bibitem" data-cite-label="1" data-cite-year="2004" data-cite-authors="Bertot and Cast&#233;ran" style="searchterm:Yves+Bertot+Pierre+Cast+_Interactive+Theorem+Proving+Program+Development+Calculus+Inductive+Constructions_+Texts+Theoretical+Computer+Science+Springer++"><span class="bibitem-before">[1]&#160;&#160;</span>Yves Bertot and Pierre Cast&#233;ran.
+<span class="newblock"></span> <em class="em-low1">Interactive Theorem Proving and Program Development &#8212;
+ Coq&#39;Art: The Calculus of Inductive Constructions</em>.
+<span class="newblock"></span> Texts in Theoretical Computer Science. Springer, 2004.&nbsp;<a href="http://www.bing.com/search?q=Yves+Bertot+Pierre+Cast+_Interactive+Theorem+Proving+Program+Development+Calculus+Inductive+Constructions_+Texts+Theoretical+Computer+Science+Springer++" class="bibsearch">&#128270;</a></div>
+<div id="bovedybjernorell:briefagda" class="bibitem" data-cite-label="2" data-cite-year="2009" data-cite-authors="Bove et\ al." data-cite-authors-long="Bove, Dybjer, and Norell" style="searchterm:+Bove+Peter+Dybjer+Norell+brief+overview+Agda+functional+language+with+dependent+types+Stefan+Berghofer+Tobias+Nipkow+Christian+Urban+Makarius+Wenzel+editors+_Theorem+Proving+Higher+Order+Logics+International+Conference+TPHOLs+volume+_Lecture+Notes+Computer+Science_+pages+Springer+August++"><span class="bibitem-before">[2]&#160;&#160;</span>Ana Bove, Peter Dybjer, and Ulf Norell.
+<span class="newblock"></span> A brief overview of Agda &#8212; a functional language with dependent
+ types.
+<span class="newblock"></span> In Stefan Berghofer, Tobias Nipkow, Christian Urban, and Makarius
+ Wenzel, editors, <em class="em-low1">Theorem Proving in Higher Order Logics, 22nd
+ International Conference, TPHOLs 2009</em>, volume 5674 of <em class="em-low1">Lecture Notes in
+ Computer Science</em>, pages 73&#8211;78. Springer, August 2009.&nbsp;<a href="http://www.bing.com/search?q=+Bove+Peter+Dybjer+Norell+brief+overview+Agda+functional+language+with+dependent+types+Stefan+Berghofer+Tobias+Nipkow+Christian+Urban+Makarius+Wenzel+editors+_Theorem+Proving+Higher+Order+Logics+International+Conference+TPHOLs+volume+_Lecture+Notes+Computer+Science_+pages+Springer+August++" class="bibsearch">&#128270;</a></div>
+<div id="camillerimelham:inductiverelations" class="bibitem" data-cite-label="3" data-cite-year="1992" data-cite-authors="Camilleri and Melham" style="searchterm:Juanito+Camilleri+Melham+Reasoning+with+inductively+defined+relations+theorem+prover+Technical+Report+University+Cambridge+Computer+Laboratory++"><span class="bibitem-before">[3]&#160;&#160;</span>Juanito Camilleri and Tom Melham.
+<span class="newblock"></span> Reasoning with inductively defined relations in the HOL theorem
+ prover.
+<span class="newblock"></span> Technical Report 265, University of Cambridge Computer Laboratory,
+ 1992.&nbsp;<a href="http://www.bing.com/search?q=Juanito+Camilleri+Melham+Reasoning+with+inductively+defined+relations+theorem+prover+Technical+Report+University+Cambridge+Computer+Laboratory++" class="bibsearch">&#128270;</a></div>
+<div id="demourabjorner:z3:overview" class="bibitem" data-cite-label="4" data-cite-year="2008" data-cite-authors="de\ Moura and Bj&#248;rner" style="searchterm:+efficient+solver++Leonardo+Moura+Nikolaj+rner+"><span class="bibitem-before">[4]&#160;&#160;</span>Leonardo de&#160;Moura and Nikolaj Bj&#248;rner.
+<span class="newblock"></span> Z3: An efficient SMT solver.
+<span class="newblock"></span> In C.&#160;R. Ramakrishnan and Jakob Rehof, editors, <em class="em-low1">Tools and
+ Algorithms for the Construction and Analysis of Systems, 14th International
+ Conference, TACAS 2008</em>, volume 4963, pages 337&#8211;340. Springer, March&#8211;April
+ 2008.&nbsp;<a href="http://www.bing.com/search?q=+efficient+solver++Leonardo+Moura+Nikolaj+rner+" class="bibsearch">&#128270;</a></div>
+<div id="msr:dafny:source" class="bibitem" data-cite-label="5" data-cite-authors="et\ al" style="searchterm:Dafny+source+code+++Rustan+Leino+"><span class="bibitem-before">[5]&#160;&#160;</span>K.&#160;Rustan M.&#160;Leino et&#160;al.
+<span class="newblock"></span> Dafny source code.
+<span class="newblock"></span> Available at <a href="http://dafny.codeplex.com">http://dafny.codeplex.com</a>.&nbsp;<a href="http://www.bing.com/search?q=Dafny+source+code+++Rustan+Leino+" class="bibsearch">&#128270;</a></div>
+<div id="harrison:inductivedefs" class="bibitem" data-cite-label="6" data-cite-year="1995" data-cite-authors="Harrison" style="searchterm:Inductive+definitions+Automation+application++John+Harrison+"><span class="bibitem-before">[6]&#160;&#160;</span>John Harrison.
+<span class="newblock"></span> Inductive definitions: Automation and application.
+<span class="newblock"></span> In E.&#160;Thomas Schubert, Phillip&#160;J. Windley, and Jim Alves-Foss,
+ editors, <em class="em-low1">TPHOLs 1995</em>, volume 971 of <em class="em-low1">LNCS</em>, pages 200&#8211;213.
+ Springer, 1995.&nbsp;<a href="http://www.bing.com/search?q=Inductive+definitions+Automation+application++John+Harrison+" class="bibsearch">&#128270;</a></div>
+<div id="hoare:axiomaticbasis" class="bibitem" data-cite-label="7" data-cite-year="1969" data-cite-authors="Hoare" style="searchterm:+axiomatic+basis+computer+programming+++Hoare+"><span class="bibitem-before">[7]&#160;&#160;</span>C.&#160;A.&#160;R. Hoare.
+<span class="newblock"></span> An axiomatic basis for computer programming.
+<span class="newblock"></span> <em class="em-low1">Communications of the ACM</em>, 12<span style="penalty:0"></span> (10):<span style="penalty:0"></span>
+ 576&#8211;580,583, October 1969.&nbsp;<a href="http://www.bing.com/search?q=+axiomatic+basis+computer+programming+++Hoare+" class="bibsearch">&#128270;</a></div>
+<div id="jacobsrutten:introductioncoalgebra" class="bibitem" data-cite-label="8" data-cite-year="2011" data-cite-authors="Jacobs and Rutten" style="searchterm:+introduction+algebra+induction++Bart+Jacobs+Rutten+"><span class="bibitem-before">[8]&#160;&#160;</span>Bart Jacobs and Jan Rutten.
+<span class="newblock"></span> An introduction to (co)algebra and (co)induction.
+<span class="newblock"></span> In Davide Sangiorgi and Jan Rutten, editors, <em class="em-low1">Advanced Topics in
+ Bisimulation and Coinduction</em>, number&#160;52 in Cambridge Tracts in Theoretical
+ Computer Science, pages 38&#8211;99. Cambridge University Press, October 2011.&nbsp;<a href="http://www.bing.com/search?q=+introduction+algebra+induction++Bart+Jacobs+Rutten+" class="bibsearch">&#128270;</a></div>
+<div id="kassios:fm2006" class="bibitem" data-cite-label="9" data-cite-year="2006" data-cite-authors="Kassios" style="searchterm:Ioannis+Kassios+Dynamic+frames+Support+framing+dependencies+sharing+without+restrictions+Jayadev+Misra+Tobias+Nipkow+Emil+Sekerinski+editors+Formal+Methods+International+Symposium+Formal+Methods_+volume+pages+Springer+August++"><span class="bibitem-before">[9]&#160;&#160;</span>Ioannis&#160;T. Kassios.
+<span class="newblock"></span> Dynamic frames: Support for framing, dependencies and sharing without
+ restrictions.
+<span class="newblock"></span> In Jayadev Misra, Tobias Nipkow, and Emil Sekerinski, editors,
+ <em class="em-low1">FM 2006: Formal Methods, 14th International Symposium on Formal
+ Methods</em>, volume 4085, pages 268&#8211;283. Springer, August 2006.&nbsp;<a href="http://www.bing.com/search?q=Ioannis+Kassios+Dynamic+frames+Support+framing+dependencies+sharing+without+restrictions+Jayadev+Misra+Tobias+Nipkow+Emil+Sekerinski+editors+Formal+Methods+International+Symposium+Formal+Methods_+volume+pages+Springer+August++" class="bibsearch">&#128270;</a></div>
+<div id="acl2:book" class="bibitem" data-cite-label="10" data-cite-year="2000" data-cite-authors="Kaufmann et\ al." data-cite-authors-long="Kaufmann, Manolios, and Moore" style="searchterm:_Computer+Aided+Reasoning+Approach_++Matt+Kaufmann+Panagiotis+Manolios+Strother+Moore+"><span class="bibitem-before">[10]&#160;&#160;</span>Matt Kaufmann, Panagiotis Manolios, and J&#160;Strother Moore.
+<span class="newblock"></span> <em class="em-low1">Computer-Aided Reasoning: An Approach</em>.
+<span class="newblock"></span> Kluwer Academic Publishers, 2000.&nbsp;<a href="http://www.bing.com/search?q=_Computer+Aided+Reasoning+Approach_++Matt+Kaufmann+Panagiotis+Manolios+Strother+Moore+" class="bibsearch">&#128270;</a></div>
+<div id="kozensilva:coinduction" class="bibitem" data-cite-label="11" data-cite-year="2012" data-cite-authors="Kozen and Silva" style="searchterm:Practical+coinduction++Dexter+Kozen+Alexandra+Silva+"><span class="bibitem-before">[11]&#160;&#160;</span>Dexter Kozen and Alexandra Silva.
+<span class="newblock"></span> Practical coinduction.
+<span class="newblock"></span> Technical Report <a href="http://hdl.handle.net/1813/30510">http://hdl.handle.net/1813/30510</a>, Comp. and
+ Inf. Science, Cornell Univ., 2012.&nbsp;<a href="http://www.bing.com/search?q=Practical+coinduction++Dexter+Kozen+Alexandra+Silva+" class="bibsearch">&#128270;</a></div>
+<div id="krauss:phd" class="bibitem" data-cite-label="12" data-cite-year="2009" data-cite-authors="Krauss" style="searchterm:Alexander+Krauss+_Automating+Recursive+Definitions+Termination+Proofs+Higher+Order+Logic_+thesis+Technische+Universit+nchen++"><span class="bibitem-before">[12]&#160;&#160;</span>Alexander Krauss.
+<span class="newblock"></span> <em class="em-low1">Automating Recursive Definitions and Termination Proofs in
+ Higher-Order Logic</em>.
+<span class="newblock"></span> PhD thesis, Technische Universit&#228;t M&#252;nchen, 2009.&nbsp;<a href="http://www.bing.com/search?q=Alexander+Krauss+_Automating+Recursive+Definitions+Termination+Proofs+Higher+Order+Logic_+thesis+Technische+Universit+nchen++" class="bibsearch">&#128270;</a></div>
+<div id="msr:dafny:main" class="bibitem" data-cite-label="13" data-cite-authors="Leino" data-cite-authors-long="a)" style="searchterm:Main+microsoft+research+dafny+page+++Rustan+Leino+"><span class="bibitem-before">[13]&#160;&#160;</span>K.&#160;Rustan&#160;M. Leino.
+<span class="newblock"></span> Main microsoft research dafny web page, a.
+<span class="newblock"></span> Available at
+ <a href="http://research.microsoft.com/en-us/projects/dafny">http://research.microsoft.com/en-us/projects/dafny</a>.&nbsp;<a href="http://www.bing.com/search?q=Main+microsoft+research+dafny+page+++Rustan+Leino+" class="bibsearch">&#128270;</a></div>
+<div id="msr:dafny:quickref" class="bibitem" data-cite-label="14" data-cite-authors="Leino" data-cite-authors-long="b)" style="searchterm:Dafny+quick+reference+++Rustan+Leino+"><span class="bibitem-before">[14]&#160;&#160;</span>K.&#160;Rustan&#160;M. Leino.
+<span class="newblock"></span> Dafny quick reference, b.
+<span class="newblock"></span> Available at
+ <a href="http://research.microsoft.com/en-us/projects/dafny/reference.aspx">http://research.microsoft.com/en-us/projects/dafny/reference.aspx</a>.&nbsp;<a href="http://www.bing.com/search?q=Dafny+quick+reference+++Rustan+Leino+" class="bibsearch">&#128270;</a></div>
+<div id="rise4fun:dafny" class="bibitem" data-cite-label="15" data-cite-authors="Leino" data-cite-authors-long="c)" style="searchterm:+dafny+your+browser+++Rustan+Leino+"><span class="bibitem-before">[15]&#160;&#160;</span>K.&#160;Rustan&#160;M. Leino.
+<span class="newblock"></span> Try dafny in your browser, c.
+<span class="newblock"></span> Available at <a href="http://rise4fun.com/Dafny">http://rise4fun.com/Dafny</a>.&nbsp;<a href="http://www.bing.com/search?q=+dafny+your+browser+++Rustan+Leino+" class="bibsearch">&#128270;</a></div>
+<div id="leino:boogie2-refman" class="bibitem" data-cite-label="16" data-cite-year="2008" data-cite-authors="Leino" style="searchterm:This+Boogie+++Rustan+Leino+"><span class="bibitem-before">[16]&#160;&#160;</span>K.&#160;Rustan&#160;M. Leino.
+<span class="newblock"></span> This is Boogie 2.
+<span class="newblock"></span> Manuscript KRML 178, 2008.
+<span class="newblock"></span> Available at <a href="http://research.microsoft.com/~leino/papers.html">http://research.microsoft.com/~leino/papers.html</a>.&nbsp;<a href="http://www.bing.com/search?q=This+Boogie+++Rustan+Leino+" class="bibsearch">&#128270;</a></div>
+<div id="leino:dafny:dynamicframes" class="bibitem" data-cite-label="17" data-cite-year="2009" data-cite-authors="Leino" style="searchterm:Dynamic+frame+specifications+dafny+++Rustan+Leino+"><span class="bibitem-before">[17]&#160;&#160;</span>K.&#160;Rustan&#160;M. Leino.
+<span class="newblock"></span> Dynamic-frame specifications in dafny.
+<span class="newblock"></span> JML seminar, Dagstuhl, Germany, 2009.
+<span class="newblock"></span> Available at
+ <a href="http://research.microsoft.com/en-us/um/people/leino/papers/dafny-jml-dagstuhl-2009.pptx">http://research.microsoft.com/en-us/um/people/leino/papers/dafny-jml-dagstuhl-2009.pptx</a>.&nbsp;<a href="http://www.bing.com/search?q=Dynamic+frame+specifications+dafny+++Rustan+Leino+" class="bibsearch">&#128270;</a></div>
+<div id="leino:dafny:lpar16" class="bibitem" data-cite-label="18" data-cite-year="2010" data-cite-authors="Leino" style="searchterm:Dafny+automatic+program+verifier+functional+correctness+++Rustan+Leino+"><span class="bibitem-before">[18]&#160;&#160;</span>K.&#160;Rustan&#160;M. Leino.
+<span class="newblock"></span> Dafny: An automatic program verifier for functional correctness.
+<span class="newblock"></span> In Edmund&#160;M. Clarke and Andrei Voronkov, editors, <em class="em-low1">LPAR-16</em>,
+ volume 6355, pages 348&#8211;370. Springer, April 2010.&nbsp;<a href="http://www.bing.com/search?q=Dafny+automatic+program+verifier+functional+correctness+++Rustan+Leino+" class="bibsearch">&#128270;</a></div>
+<div id="leino:induction" class="bibitem" data-cite-label="19" data-cite-year="2012" data-cite-authors="Leino" style="searchterm:Automating+induction+with+solver+++Rustan+Leino+"><span class="bibitem-before">[19]&#160;&#160;</span>K.&#160;Rustan&#160;M. Leino.
+<span class="newblock"></span> Automating induction with an SMT solver.
+<span class="newblock"></span> In Viktor Kuncak and Andrey Rybalchenko, editors, <em class="em-low1">Verification,
+ Model Checking, and Abstract Interpretation &#8212; 13th International
+ Conference, VMCAI 2012</em>, volume 7148, pages 315&#8211;331. Springer, January 2012.&nbsp;<a href="http://www.bing.com/search?q=Automating+induction+with+solver+++Rustan+Leino+" class="bibsearch">&#128270;</a></div>
+<div id="leino:dafny:coinduction" class="bibitem" data-cite-label="20" data-cite-year="2014a" data-cite-authors="Leino and Moskal" style="searchterm:+Rustan+Leino+Michal+Moskal+induction+simply+Automatic+inductive+proofs+program+verifier+Manuscript+KRML+Available+http+research+microsoft+people+leino+papers+krml230++"><span class="bibitem-before">[20]&#160;&#160;</span>K.&#160;Rustan&#160;M. Leino and Michal Moskal.
+<span class="newblock"></span> Co-induction simply: Automatic co-inductive proofs in a program
+ verifier.
+<span class="newblock"></span> Manuscript KRML 230, 2014a.
+<span class="newblock"></span> Available at
+ <a href="http://research.microsoft.com/en-us/um/people/leino/papers/krml230.pdf">http://research.microsoft.com/en-us/um/people/leino/papers/krml230.pdf</a>.&nbsp;<a href="http://www.bing.com/search?q=+Rustan+Leino+Michal+Moskal+induction+simply+Automatic+inductive+proofs+program+verifier+Manuscript+KRML+Available+http+research+microsoft+people+leino+papers+krml230++" class="bibsearch">&#128270;</a></div>
+<div id="leinomoskal:coinduction" class="bibitem" data-cite-label="21" data-cite-year="2014b" data-cite-authors="Leino and Moskal" style="searchterm:+Rustan+Leino+Micha+Moskal+induction+simply+automatic+inductive+proofs+program+verifier+volume+_LNCS_+pages+Springer++"><span class="bibitem-before">[21]&#160;&#160;</span>K.&#160;Rustan&#160;M. Leino and Micha&#322; Moskal.
+<span class="newblock"></span> Co-induction simply &#8212; automatic co-inductive proofs in a program
+ verifier.
+<span class="newblock"></span> In <em class="em-low1">FM 2014</em>, volume 8442 of <em class="em-low1">LNCS</em>, pages 382&#8211;398.
+ Springer, May 2014b.&nbsp;<a href="http://www.bing.com/search?q=+Rustan+Leino+Micha+Moskal+induction+simply+automatic+inductive+proofs+program+verifier+volume+_LNCS_+pages+Springer++" class="bibsearch">&#128270;</a></div>
+<div id="leino:dafny:calc" class="bibitem" data-cite-label="22" data-cite-year="2013" data-cite-authors="Leino and Polikarpova" style="searchterm:Verified+calculations+++Rustan+Leino+Nadia+Polikarpova+"><span class="bibitem-before">[22]&#160;&#160;</span>K.&#160;Rustan&#160;M. Leino and Nadia Polikarpova.
+<span class="newblock"></span> Verified calculations.
+<span class="newblock"></span> Manuscript KRML 231, 2013.
+<span class="newblock"></span> Available at
+ <a href="http://research.microsoft.com/en-us/um/people/leino/papers/krml231.pdf">http://research.microsoft.com/en-us/um/people/leino/papers/krml231.pdf</a>.&nbsp;<a href="http://www.bing.com/search?q=Verified+calculations+++Rustan+Leino+Nadia+Polikarpova+" class="bibsearch">&#128270;</a></div>
+<div id="leinoruemmer:boogie2" class="bibitem" data-cite-label="23" data-cite-year="2010" data-cite-authors="Leino and R&#252;mmer" style="searchterm:+Rustan+Leino+Philipp+mmer+polymorphic+intermediate+verification+language+Design+logical+encoding+Javier+Esparza+Rupak+Majumdar+editors+_Tools+Algorithms+Construction+Analysis+Systems+International+Conference+TACAS+volume+pages+Springer+March++"><span class="bibitem-before">[23]&#160;&#160;</span>K.&#160;Rustan&#160;M. Leino and Philipp R&#252;mmer.
+<span class="newblock"></span> A polymorphic intermediate verification language: Design and logical
+ encoding.
+<span class="newblock"></span> In Javier Esparza and Rupak Majumdar, editors, <em class="em-low1">Tools and
+ Algorithms for the Construction and Analysis of Systems, 16th International
+ Conference, TACAS 2010</em>, volume 6015, pages 312&#8211;327. Springer, March 2010.&nbsp;<a href="http://www.bing.com/search?q=+Rustan+Leino+Philipp+mmer+polymorphic+intermediate+verification+language+Design+logical+encoding+Javier+Esparza+Rupak+Majumdar+editors+_Tools+Algorithms+Construction+Analysis+Systems+International+Conference+TACAS+volume+pages+Springer+March++" class="bibsearch">&#128270;</a></div>
+<div id="leroygrall:coinductivebigstep" class="bibitem" data-cite-label="24" data-cite-year="2009" data-cite-authors="Leroy and Grall" style="searchterm:Coinductive+step+operational+semantics++Xavier+Leroy+Herv+Grall+"><span class="bibitem-before">[24]&#160;&#160;</span>Xavier Leroy and Herv&#233; Grall.
+<span class="newblock"></span> Coinductive big-step operational semantics.
+<span class="newblock"></span> <em class="em-low1">Information and Computation</em>, 207<span style="penalty:0"></span> (2):<span style="penalty:0"></span>
+ 284&#8211;304, February 2009.&nbsp;<a href="http://www.bing.com/search?q=Coinductive+step+operational+semantics++Xavier+Leroy+Herv+Grall+" class="bibsearch">&#128270;</a></div>
+<div id="manoliosmoore:partialfunctions" class="bibitem" data-cite-label="25" data-cite-year="2003" data-cite-authors="Manolios and Moore" style="searchterm:Partial+functions+ACL2++Panagiotis+Manolios+Strother+Moore+"><span class="bibitem-before">[25]&#160;&#160;</span>Panagiotis Manolios and J&#160;Strother Moore.
+<span class="newblock"></span> Partial functions in ACL2.
+<span class="newblock"></span> <em class="em-low1">Journal of Automated Reasoning</em>, 31<span style="penalty:0"></span> (2):<span style="penalty:0"></span>
+ 107&#8211;127, 2003.&nbsp;<a href="http://www.bing.com/search?q=Partial+functions+ACL2++Panagiotis+Manolios+Strother+Moore+" class="bibsearch">&#128270;</a></div>
+<div id="milner:ccs" class="bibitem" data-cite-label="26" data-cite-year="1982" data-cite-authors="Milner" style="searchterm:+Calculus+Communicating+Systems_++Robin+Milner+"><span class="bibitem-before">[26]&#160;&#160;</span>Robin Milner.
+<span class="newblock"></span> <em class="em-low1">A Calculus of Communicating Systems</em>.
+<span class="newblock"></span> Springer-Verlag New York, Inc., 1982.
+<span class="newblock"></span> ISBN 0387102353.&nbsp;<a href="http://www.bing.com/search?q=+Calculus+Communicating+Systems_++Robin+Milner+" class="bibsearch">&#128270;</a></div>
+<div id="linz:coco" class="bibitem" data-cite-label="27" data-cite-year="2013" data-cite-authors="M&#246;ssenb&#246;ck et\ al." data-cite-authors-long="M&#246;ssenb&#246;ck,
+ L&#246;berbauer, and W&#246;&#223;" style="searchterm:Hanspeter+ssenb+Markus+berbauer+Albrecht+compiler+generator+coco+Open+source+from+University+Linz+Available+http+linz+Research+Projects+Coco++"><span class="bibitem-before">[27]&#160;&#160;</span>Hanspeter M&#246;ssenb&#246;ck, Markus L&#246;berbauer, and Albrecht
+ W&#246;&#223;.
+<span class="newblock"></span> The compiler generator coco/r.
+<span class="newblock"></span> Open source from University of Linz, 2013.
+<span class="newblock"></span> Available at
+ <a href="http://www.ssw.uni-linz.ac.at/Research/Projects/Coco/">http://www.ssw.uni-linz.ac.at/Research/Projects/Coco/</a>.&nbsp;<a href="http://www.bing.com/search?q=Hanspeter+ssenb+Markus+berbauer+Albrecht+compiler+generator+coco+Open+source+from+University+Linz+Available+http+linz+Research+Projects+Coco++" class="bibsearch">&#128270;</a></div>
+<div id="nipkowklein:concretesemantics" class="bibitem" data-cite-label="28" data-cite-year="2014" data-cite-authors="Nipkow and Klein" style="searchterm:_Concrete+Semantics+with+Isabelle+HOL_++Tobias+Nipkow+Gerwin+Klein+"><span class="bibitem-before">[28]&#160;&#160;</span>Tobias Nipkow and Gerwin Klein.
+<span class="newblock"></span> <em class="em-low1">Concrete Semantics with Isabelle/HOL</em>.
+<span class="newblock"></span> Springer, 2014.&nbsp;<a href="http://www.bing.com/search?q=_Concrete+Semantics+with+Isabelle+HOL_++Tobias+Nipkow+Gerwin+Klein+" class="bibsearch">&#128270;</a></div>
+<div id="paulinmohring:inductivecoq" class="bibitem" data-cite-label="29" data-cite-year="1993" data-cite-authors="Paulin-Mohring" style="searchterm:Inductive+definitions+system+rules+properties++Christine+Paulin+Mohring+"><span class="bibitem-before">[29]&#160;&#160;</span>Christine Paulin-Mohring.
+<span class="newblock"></span> Inductive definitions in the system Coq &#8212; rules and properties.
+<span class="newblock"></span> In <em class="em-low1">TLCA &#39;93</em>, volume 664 of <em class="em-low1">LNCS</em>, pages 328&#8211;345.
+ Springer, 1993.&nbsp;<a href="http://www.bing.com/search?q=Inductive+definitions+system+rules+properties++Christine+Paulin+Mohring+" class="bibsearch">&#128270;</a></div>
+<div id="paulson:cade1994" class="bibitem" data-cite-label="30" data-cite-year="1994" data-cite-authors="Paulson" style="searchterm:+fixedpoint+approach+implementing+inductive+definitions++Lawrence+Paulson+"><span class="bibitem-before">[30]&#160;&#160;</span>Lawrence&#160;C. Paulson.
+<span class="newblock"></span> A fixedpoint approach to implementing (co)inductive definitions.
+<span class="newblock"></span> In Alan Bundy, editor, <em class="em-low1">CADE-12</em>, volume 814 of <em class="em-low1">LNCS</em>,
+ pages 148&#8211;161. Springer, 1994.&nbsp;<a href="http://www.bing.com/search?q=+fixedpoint+approach+implementing+inductive+definitions++Lawrence+Paulson+" class="bibsearch">&#128270;</a></div>
+<div id="pierce:softwarefoundations" class="bibitem" data-cite-label="31" data-cite-year="2015" data-cite-authors="Pierce et\ al." data-cite-authors-long="Pierce, Casinghino, Gaboardi, Greenberg,
+ Hri&#355;cu, Sj&#246;berg, and Yorgey" style="searchterm:Benjamin+Pierce+Chris+Casinghino+Marco+Gaboardi+Michael+Greenberg+Catalin+Vilhelm+berg+Brent+Yorgey+_Software+Foundations_+http+upenn+bcpierce+version+edition+January++"><span class="bibitem-before">[31]&#160;&#160;</span>Benjamin&#160;C. Pierce, Chris Casinghino, Marco Gaboardi, Michael Greenberg,
+ Catalin Hri&#355;cu, Vilhelm Sj&#246;berg, and Brent Yorgey.
+<span class="newblock"></span> <em class="em-low1">Software Foundations</em>.
+<span class="newblock"></span> <a href="http://www.cis.upenn.edu/~bcpierce/sf">http://www.cis.upenn.edu/~bcpierce/sf</a>, version 3.2 edition,
+ January 2015.&nbsp;<a href="http://www.bing.com/search?q=Benjamin+Pierce+Chris+Casinghino+Marco+Gaboardi+Michael+Greenberg+Catalin+Vilhelm+berg+Brent+Yorgey+_Software+Foundations_+http+upenn+bcpierce+version+edition+January++" class="bibsearch">&#128270;</a></div>
+<div id="smansetal:vericool" class="bibitem" data-cite-label="32" data-cite-year="2008" data-cite-authors="Smans et\ al." data-cite-authors-long="Smans, Jacobs, Piessens, and
+ Schulte" style="searchterm:Automatic+verifier+Java+like+programs+based+dynamic+frames+++Smans+Bart+Jacobs+Frank+Piessens+Wolfram+Schulte+"><span class="bibitem-before">[32]&#160;&#160;</span>Jan Smans, Bart Jacobs, Frank Piessens, and Wolfram Schulte.
+<span class="newblock"></span> Automatic verifier for Java-like programs based on dynamic frames.
+<span class="newblock"></span> In Jos&#233;&#160;Luiz Fiadeiro and Paola Inverardi, editors,
+ <em class="em-low1">Fundamental Approaches to Software Engineering, 11th International
+ Conference, FASE 2008</em>, volume 4961, pages 261&#8211;275. Springer, March&#8211;April
+ 2008.&nbsp;<a href="http://www.bing.com/search?q=Automatic+verifier+Java+like+programs+based+dynamic+frames+++Smans+Bart+Jacobs+Frank+Piessens+Wolfram+Schulte+" class="bibsearch">&#128270;</a></div>
+<div id="smansetal:implicitdynamicframes" class="bibitem" data-cite-label="33" data-cite-year="2009" data-cite-authors="Smans et\ al." data-cite-authors-long="Smans, Jacobs, and
+ Piessens" style="searchterm:+Smans+Bart+Jacobs+Frank+Piessens+Implicit+dynamic+frames+Combining+dynamic+frames+separation+logic+Sophia+Drossopoulou+editor+_ECOOP+Object+Oriented+Programming+European+Conference_+volume+pages+Springer+July++"><span class="bibitem-before">[33]&#160;&#160;</span>Jan Smans, Bart Jacobs, and Frank Piessens.
+<span class="newblock"></span> Implicit dynamic frames: Combining dynamic frames and separation
+ logic.
+<span class="newblock"></span> In Sophia Drossopoulou, editor, <em class="em-low1">ECOOP 2009 &#8212; Object-Oriented
+ Programming, 23rd European Conference</em>, volume 5653, pages 148&#8211;172.
+ Springer, July 2009.&nbsp;<a href="http://www.bing.com/search?q=+Smans+Bart+Jacobs+Frank+Piessens+Implicit+dynamic+frames+Combining+dynamic+frames+separation+logic+Sophia+Drossopoulou+editor+_ECOOP+Object+Oriented+Programming+European+Conference_+volume+pages+Springer+July++" class="bibsearch">&#128270;</a></div>
+<div id="swamyetal:fstar2011" class="bibitem" data-cite-label="34" data-cite-year="2011" data-cite-authors="Swamy et\ al." data-cite-authors-long="Swamy, Chen, Fournet, Strub, Bhargavan, and
+ Yang" style="searchterm:Nikhil+Swamy+Juan+Chen+dric+Fournet+Pierre+Yves+Strub+Karthikeyan+Bhargavan+Jean+Yang+Secure+distributed+programming+with+value+dependent+types+_ICFP+pages+September++"><span class="bibitem-before">[34]&#160;&#160;</span>Nikhil Swamy, Juan Chen, C&#233;dric Fournet, Pierre-Yves Strub, Karthikeyan
+ Bhargavan, and Jean Yang.
+<span class="newblock"></span> Secure distributed programming with value-dependent types.
+<span class="newblock"></span> In <em class="em-low1">ICFP 2011</em>, pages 266&#8211;278. ACM, September 2011.&nbsp;<a href="http://www.bing.com/search?q=Nikhil+Swamy+Juan+Chen+dric+Fournet+Pierre+Yves+Strub+Karthikeyan+Bhargavan+Jean+Yang+Secure+distributed+programming+with+value+dependent+types+_ICFP+pages+September++" class="bibsearch">&#128270;</a></div>
+<div id="tarski:theorem" class="bibitem" data-cite-label="35" data-cite-year="1955" data-cite-authors="Tarski" style="searchterm:+lattice+theoretical+fixpoint+theorem+applications++Alfred+Tarski+"><span class="bibitem-before">[35]&#160;&#160;</span>Alfred Tarski.
+<span class="newblock"></span> A lattice-theoretical fixpoint theorem and its applications.
+<span class="newblock"></span> <em class="em-low1">Pacific Journal of Mathematics</em>, 5:<span style="penalty:0"></span> 285&#8211;309, 1955.&nbsp;<a href="http://www.bing.com/search?q=+lattice+theoretical+fixpoint+theorem+applications++Alfred+Tarski+" class="bibsearch">&#128270;</a></div>
+<div id="winskel:formalsemantics" class="bibitem" data-cite-label="36" data-cite-year="1993" data-cite-authors="Winskel" style="searchterm:Glynn+Winskel+_The+Formal+Semantics+Programming+Languages+Introduction_+Press++"><span class="bibitem-before">[36]&#160;&#160;</span>Glynn Winskel.
+<span class="newblock"></span> <em class="em-low1">The Formal Semantics of Programming Languages: An
+ Introduction</em>.
+<span class="newblock"></span> MIT Press, 1993.&nbsp;<a href="http://www.bing.com/search?q=Glynn+Winskel+_The+Formal+Semantics+Programming+Languages+Introduction_+Press++" class="bibsearch">&#128270;</a></div></div></div><span data-line=""></span>
+<div class="footnotes madoko">
+<hr >
+
+<div id="fn-fn-nullable" class="footnote" style="line-adjust:0">
+<p class="p noindent"><span class="footnote-before"><sup><span class="footnote-label">0</span>.</sup></span>This will change in a future version of Dafny that
+will support both nullable and (by default) non-null reference
+types.
+<a href="#back-fn-fn-nullable" class="footnote-backref localref">&#8617;</a></p></div>
+<div id="fn-fn-type-mode" class="footnote" style="line-adjust:0">
+<p class="p noindent"><span class="footnote-before"><sup><span class="footnote-label">1</span>.</sup></span>Being equality-supporting is just one of many
+<em class="em-low1">modes</em> that one can imagine types in a rich type system to have.
+For example, other modes could include having a total order,
+being zero-initializable, and possibly being uninhabited. If
+Dafny were to support more modes in the future, the &#8220;<code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">(<span class="code-escaped">&#160;</span>)</code>&#8221;-suffix
+syntax may be extended. For now, the suffix can only indicate the
+equality-supporting mode.
+<a href="#back-fn-fn-type-mode" class="footnote-backref localref">&#8617;</a></p></div>
+<div id="fn-fn-slice-into-tuple" class="footnote" style="line-adjust:0">
+<p class="p noindent"><span class="footnote-before"><sup><span class="footnote-label">2</span>.</sup></span>Now that Dafny supports built-in tuples, the
+plan is to change the sequence slice operation to return not a
+sequence of subsequences, but a tuple of subsequences.
+<a href="#back-fn-fn-slice-into-tuple" class="footnote-backref localref">&#8617;</a></p></div>
+<div id="fn-fn-map-display" class="footnote" style="line-adjust:0">
+<p class="p noindent"><span class="footnote-before"><sup><span class="footnote-label">3</span>.</sup></span>This is likely to change in the future to disallow
+multiple occurrences of the same key.
+<a href="#back-fn-fn-map-display" class="footnote-backref localref">&#8617;</a></p></div>
+<div id="fn-fn-map-membership" class="footnote" style="line-adjust:0">
+<p class="p noindent"><span class="footnote-before"><sup><span class="footnote-label">4</span>.</sup></span>This is likely to change in the future as
+follows: The <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">in</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">!<span style="color:blue">in</span></code> operations will no longer be
+supported on maps. Instead, for any map <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m</code>, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m.Domain</code> will
+return its domain as a set and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m.Range</code> will return, also as a
+set, the image of <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">m</code> under its domain.
+<a href="#back-fn-fn-map-membership" class="footnote-backref localref">&#8617;</a></p></div>
+<div id="fn-fn-on-da-web" class="footnote" style="line-adjust:0">
+<p class="p noindent"><span class="footnote-before"><sup><span class="footnote-label">5</span>.</sup></span>Dafny is open source at&nbsp;<a href="http://dafny.codeplex.com">dafny.codeplex.com</a> and can also be used online at&nbsp;<a href="http://rise4fun.com/dafny">rise4fun.com/dafny</a>.
+<a href="#back-fn-fn-on-da-web" class="footnote-backref localref">&#8617;</a></p></div>
+<div id="fn-fn-object-trait" class="footnote" style="line-adjust:0">
+<p class="p noindent"><span class="footnote-before"><sup><span class="footnote-label">6</span>.</sup></span>The current compiler restriction that <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:teal">object</span></code> cannot
+be used as a type parameter needs to be removed.
+<a href="#back-fn-fn-object-trait" class="footnote-backref localref">&#8617;</a></p></div>
+<div id="fn-fn-iterator-field-names" class="footnote" style="line-adjust:0">
+<p class="p noindent"><span class="footnote-before"><sup><span class="footnote-label">7</span>.</sup></span>It would make sense to rename the special
+fields <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_reads</code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_modifies</code> to have the same names as the
+corresponding keywords, <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">reads</span></code> and <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">modifies</span></code>, as is done for
+function values. Also, the various <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">_decreases<span class="code-escaped"><em class="em-low1">i</em></span></code> fields can
+combined into one field named <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:purple">decreases</span></code> whose type is a
+<em class="em-low1">n</em>-tuple. Thse changes may be incorporated into a future version
+of Dafny.
+</p>
+<p class="p indent"><a href="#back-fn-fn-iterator-field-names" class="footnote-backref localref">&#8617;</a></p></div>
+<div id="fn-fn-copredicate-restriction" class="footnote" style="line-adjust:0">
+<p class="p noindent"><span class="footnote-before"><sup><span class="footnote-label">8</span>.</sup></span>Higher-order function support in Dafny is
+rather modest and typical reasoning patterns do not involve them, so this
+restriction is not as limiting as it would have been in, e.g., Coq.
+<a href="#back-fn-fn-copredicate-restriction" class="footnote-backref localref">&#8617;</a></p></div>
+<div id="fn-fn-co-predicate-co-lemma-diffs" class="footnote" style="line-adjust:0">
+<p class="p noindent"><span class="footnote-before"><sup><span class="footnote-label">9</span>.</sup></span>Note, two places where co-predicates
+and co-lemmas are not analogous are: co-predicates must not make
+recursive calls to their prefix predicates, and co-predicates cannot
+mention _k.
+<a href="#back-fn-fn-co-predicate-co-lemma-diffs" class="footnote-backref localref">&#8617;</a></p></div>
+<div id="fn-fn-newtype-name" class="footnote" style="line-adjust:0">
+<p class="p noindent"><span class="footnote-before"><sup><span class="footnote-label">10</span>.</sup></span>Should <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">newtype</span></code> perhaps be renamed to <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small">numtype</code>?
+<a href="#back-fn-fn-newtype-name" class="footnote-backref localref">&#8617;</a></p></div>
+<div id="fn-fn-newtype-design-question" class="footnote" style="line-adjust:0">
+<p class="p noindent"><span class="footnote-before"><sup><span class="footnote-label">11</span>.</sup></span>Would it be useful to also
+automatically define <code class="code code1 language-dafnyx lang-dafnyx dafnyx colorized" style="font-size:small"><span style="color:blue">predicate</span> N?(m: M) { Q }</code>?
+<a href="#back-fn-fn-newtype-design-question" class="footnote-backref localref">&#8617;</a></p></div>
+<div id="fn-fn-newtype-zero" class="footnote" style="line-adjust:0">
+<p class="p noindent"><span class="footnote-before"><sup><span class="footnote-label">12</span>.</sup></span>The restriction is due to a current limitation in
+the compiler. This will change in the future and will also open
+up the possibility for subset types and non-null reference
+types.
+<a href="#back-fn-fn-newtype-zero" class="footnote-backref localref">&#8617;</a></p></div>
+<div id="fn-fn-more-subset-types" class="footnote" style="line-adjust:0">
+<p class="p noindent"><span class="footnote-before"><sup><span class="footnote-label">13</span>.</sup></span>A future version of Dafny will support
+user-defined subset types.
+<a href="#back-fn-fn-more-subset-types" class="footnote-backref localref">&#8617;</a></p></div></div></div>
+<svg id='math-svg-paths' style='display:none' version='1.1' viewBox='0 0 0 0' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
+<defs>
+<path d='M1.913 -3.766C1.913 -4.055 1.674 -4.294 1.385 -4.294S0.857 -4.055 0.857 -3.766S1.096 -3.238 1.385 -3.238S1.913 -3.477 1.913 -3.766ZM1.913 -0.528C1.913 -0.817 1.674 -1.056 1.385 -1.056S0.857 -0.817 0.857 -0.528S1.096 0 1.385 0S1.913 -0.239 1.913 -0.528Z' id='g5-58'/>
+<path d='M8.309 -2.291C7.761 -1.873 7.492 -1.465 7.412 -1.335C6.964 -0.648 6.884 -0.02 6.884 -0.01C6.884 0.11 7.004 0.11 7.083 0.11C7.253 0.11 7.263 0.09 7.303 -0.09C7.532 -1.066 8.12 -1.903 9.245 -2.361C9.365 -2.401 9.395 -2.421 9.395 -2.491S9.335 -2.59 9.315 -2.6C8.877 -2.77 7.671 -3.268 7.293 -4.941C7.263 -5.061 7.253 -5.091 7.083 -5.091C7.004 -5.091 6.884 -5.091 6.884 -4.971C6.884 -4.951 6.974 -4.324 7.392 -3.656C7.592 -3.357 7.88 -3.009 8.309 -2.69H0.907C0.727 -2.69 0.548 -2.69 0.548 -2.491S0.727 -2.291 0.907 -2.291H8.309Z' id='g1-33'/>
+<path d='M4.832 -4.095L3.995 -6.077C3.965 -6.157 3.945 -6.197 3.945 -6.207C3.945 -6.267 4.115 -6.456 4.533 -6.496C4.633 -6.506 4.732 -6.516 4.732 -6.685C4.732 -6.804 4.613 -6.804 4.583 -6.804C4.174 -6.804 3.746 -6.775 3.328 -6.775C3.078 -6.775 2.461 -6.804 2.212 -6.804C2.152 -6.804 2.032 -6.804 2.032 -6.605C2.032 -6.496 2.132 -6.496 2.262 -6.496C2.859 -6.496 2.919 -6.396 3.009 -6.177L4.184 -3.397L2.082 -1.136L1.953 -1.026C1.465 -0.498 0.996 -0.339 0.488 -0.309C0.359 -0.299 0.269 -0.299 0.269 -0.11C0.269 -0.1 0.269 0 0.399 0C0.697 0 1.026 -0.03 1.335 -0.03C1.704 -0.03 2.092 0 2.451 0C2.511 0 2.63 0 2.63 -0.199C2.63 -0.299 2.531 -0.309 2.511 -0.309C2.421 -0.319 2.112 -0.339 2.112 -0.618C2.112 -0.777 2.262 -0.936 2.381 -1.066L3.397 -2.142L4.294 -3.118L5.3 -0.737C5.34 -0.628 5.35 -0.618 5.35 -0.598C5.35 -0.518 5.161 -0.349 4.772 -0.309C4.663 -0.299 4.573 -0.289 4.573 -0.12C4.573 0 4.682 0 4.722 0C5.001 0 5.699 -0.03 5.978 -0.03C6.227 -0.03 6.834 0 7.083 0C7.153 0 7.273 0 7.273 -0.189C7.273 -0.309 7.173 -0.309 7.093 -0.309C6.426 -0.319 6.406 -0.349 6.237 -0.747C5.848 -1.674 5.181 -3.228 4.951 -3.826C5.629 -4.523 6.675 -5.709 6.994 -5.988C7.283 -6.227 7.661 -6.466 8.259 -6.496C8.389 -6.506 8.478 -6.506 8.478 -6.695C8.478 -6.705 8.478 -6.804 8.349 -6.804C8.05 -6.804 7.721 -6.775 7.412 -6.775C7.044 -6.775 6.665 -6.804 6.306 -6.804C6.247 -6.804 6.117 -6.804 6.117 -6.605C6.117 -6.535 6.167 -6.506 6.237 -6.496C6.326 -6.486 6.635 -6.466 6.635 -6.187C6.635 -6.047 6.526 -5.918 6.446 -5.828L4.832 -4.095Z' id='g3-88'/>
+<path d='M5.948 -5.669L6.087 -5.808C6.386 -6.107 6.715 -6.446 7.392 -6.496C7.502 -6.506 7.592 -6.506 7.592 -6.685C7.592 -6.765 7.542 -6.804 7.462 -6.804C7.203 -6.804 6.914 -6.775 6.645 -6.775C6.316 -6.775 5.968 -6.804 5.649 -6.804C5.589 -6.804 5.46 -6.804 5.46 -6.615C5.46 -6.506 5.559 -6.496 5.609 -6.496C5.679 -6.496 5.968 -6.476 5.968 -6.267C5.968 -6.097 5.738 -5.838 5.699 -5.788L3.387 -3.128L2.291 -6.087C2.232 -6.227 2.232 -6.247 2.232 -6.257C2.232 -6.496 2.72 -6.496 2.819 -6.496C2.959 -6.496 3.059 -6.496 3.059 -6.695C3.059 -6.804 2.939 -6.804 2.909 -6.804C2.63 -6.804 1.933 -6.775 1.654 -6.775C1.405 -6.775 0.787 -6.804 0.538 -6.804C0.478 -6.804 0.349 -6.804 0.349 -6.605C0.349 -6.496 0.448 -6.496 0.578 -6.496C1.176 -6.496 1.215 -6.406 1.305 -6.157L2.531 -2.879C2.54 -2.849 2.57 -2.74 2.57 -2.71S2.172 -1.076 2.122 -0.897C1.993 -0.349 1.983 -0.319 1.186 -0.309C0.996 -0.309 0.917 -0.309 0.917 -0.11C0.917 0 1.036 0 1.056 0C1.335 0 2.032 -0.03 2.311 -0.03S3.308 0 3.587 0C3.656 0 3.776 0 3.776 -0.199C3.776 -0.309 3.686 -0.309 3.497 -0.309C3.477 -0.309 3.288 -0.309 3.118 -0.329C2.909 -0.349 2.849 -0.369 2.849 -0.488C2.849 -0.558 2.939 -0.907 2.989 -1.116L3.337 -2.521C3.387 -2.71 3.397 -2.74 3.477 -2.829L5.948 -5.669Z' id='g3-89'/>
+<path d='M3.656 -3.985H4.513C4.712 -3.985 4.812 -3.985 4.812 -4.184C4.812 -4.294 4.712 -4.294 4.543 -4.294H3.716L3.925 -5.43C3.965 -5.639 4.105 -6.346 4.164 -6.466C4.254 -6.655 4.423 -6.804 4.633 -6.804C4.672 -6.804 4.932 -6.804 5.121 -6.625C4.682 -6.585 4.583 -6.237 4.583 -6.087C4.583 -5.858 4.762 -5.738 4.951 -5.738C5.21 -5.738 5.499 -5.958 5.499 -6.336C5.499 -6.795 5.041 -7.024 4.633 -7.024C4.294 -7.024 3.666 -6.844 3.367 -5.858C3.308 -5.649 3.278 -5.549 3.039 -4.294H2.351C2.162 -4.294 2.052 -4.294 2.052 -4.105C2.052 -3.985 2.142 -3.985 2.331 -3.985H2.989L2.242 -0.05C2.062 0.917 1.893 1.823 1.375 1.823C1.335 1.823 1.086 1.823 0.897 1.644C1.355 1.614 1.445 1.255 1.445 1.106C1.445 0.877 1.265 0.757 1.076 0.757C0.817 0.757 0.528 0.976 0.528 1.355C0.528 1.803 0.966 2.042 1.375 2.042C1.923 2.042 2.321 1.455 2.501 1.076C2.819 0.448 3.049 -0.757 3.059 -0.827L3.656 -3.985Z' id='g3-102'/>
+<path d='M3.298 2.391C3.298 2.361 3.298 2.341 3.128 2.172C1.883 0.917 1.564 -0.966 1.564 -2.491C1.564 -4.224 1.943 -5.958 3.168 -7.203C3.298 -7.323 3.298 -7.342 3.298 -7.372C3.298 -7.442 3.258 -7.472 3.198 -7.472C3.098 -7.472 2.202 -6.795 1.614 -5.529C1.106 -4.433 0.986 -3.328 0.986 -2.491C0.986 -1.714 1.096 -0.508 1.644 0.618C2.242 1.843 3.098 2.491 3.198 2.491C3.258 2.491 3.298 2.461 3.298 2.391Z' id='g5-40'/>
+<path d='M2.879 -2.491C2.879 -3.268 2.77 -4.473 2.222 -5.599C1.624 -6.824 0.767 -7.472 0.667 -7.472C0.608 -7.472 0.568 -7.432 0.568 -7.372C0.568 -7.342 0.568 -7.323 0.757 -7.143C1.733 -6.157 2.301 -4.573 2.301 -2.491C2.301 -0.787 1.933 0.966 0.697 2.222C0.568 2.341 0.568 2.361 0.568 2.391C0.568 2.451 0.608 2.491 0.667 2.491C0.767 2.491 1.664 1.813 2.252 0.548C2.76 -0.548 2.879 -1.654 2.879 -2.491Z' id='g5-41'/>
+<path d='M6.844 -3.258C6.994 -3.258 7.183 -3.258 7.183 -3.457S6.994 -3.656 6.854 -3.656H0.887C0.747 -3.656 0.558 -3.656 0.558 -3.457S0.747 -3.258 0.897 -3.258H6.844ZM6.854 -1.325C6.994 -1.325 7.183 -1.325 7.183 -1.524S6.994 -1.724 6.844 -1.724H0.897C0.747 -1.724 0.558 -1.724 0.558 -1.524S0.747 -1.325 0.887 -1.325H6.854Z' id='g5-61'/>
+<path d='M8.269 -6.426C8.269 -6.884 7.502 -6.814 7.193 -6.814H3.437C2.939 -6.814 2.052 -6.526 1.893 -5.988C1.913 -5.928 1.933 -5.918 1.993 -5.918C2.212 -5.918 2.511 -6.097 2.65 -6.247C2.959 -6.247 3.258 -6.267 3.567 -6.267H4.184C4.184 -6.267 4.144 -6.087 4.144 -6.077C3.756 -4.254 3.168 -2.71 2.73 -1.733C2.67 -1.604 2.132 -0.359 1.993 -0.239C1.554 -0.239 1.225 -0.478 1.046 -0.867C1.016 -0.927 1.016 -0.966 0.946 -0.966C0.717 -0.966 0.169 -0.648 0.169 -0.478C0.169 -0.458 0.179 -0.438 0.189 -0.418C0.369 0.05 0.817 0.319 1.305 0.319C1.813 0.319 2.401 -0.05 2.72 -0.418C3.049 -0.797 3.706 -2.431 3.965 -3.029H5.888C5.878 -3.019 5.868 -3.009 5.868 -2.999C5.868 -2.959 5.928 -2.929 5.968 -2.929C6.187 -2.929 6.755 -3.208 6.755 -3.517C6.755 -3.587 6.685 -3.577 6.565 -3.577H4.174C4.433 -4.473 4.772 -5.35 4.951 -6.267H6.267C6.516 -6.267 7.213 -6.316 7.392 -6.097C7.412 -6.037 7.382 -5.938 7.412 -5.888C7.432 -5.868 7.462 -5.858 7.492 -5.858C7.731 -5.858 8.269 -6.157 8.269 -6.426Z' id='g1-70'/>
+<path d='M2.75 -3.985H4.742L4.025 -1.126C4.015 -1.086 3.975 -0.927 3.975 -0.787C3.975 -0.259 4.334 0.11 4.812 0.11C5.181 0.11 5.39 -0.139 5.539 -0.448C5.719 -0.827 5.838 -1.405 5.838 -1.425C5.838 -1.524 5.758 -1.524 5.689 -1.524C5.569 -1.524 5.559 -1.514 5.499 -1.295C5.36 -0.737 5.171 -0.11 4.832 -0.11C4.573 -0.11 4.573 -0.379 4.573 -0.518C4.573 -0.588 4.573 -0.747 4.643 -1.026L5.4 -4.055C5.41 -4.115 5.43 -4.164 5.43 -4.184C5.43 -4.294 5.36 -4.294 5.191 -4.294H2.809L2.989 -5.22C3.188 -6.177 3.318 -6.804 4.533 -6.804C4.812 -6.804 5.28 -6.775 5.529 -6.526C5.091 -6.436 5.091 -6.047 5.091 -6.037C5.091 -5.908 5.181 -5.719 5.44 -5.719C5.659 -5.719 5.938 -5.898 5.938 -6.276C5.938 -7.024 4.762 -7.024 4.513 -7.024C3.357 -7.024 2.62 -6.635 2.351 -5.23L2.172 -4.294H1.524C1.355 -4.294 1.245 -4.294 1.245 -4.105C1.245 -3.985 1.335 -3.985 1.494 -3.985H2.112L1.425 -0.428C1.325 0.06 1.215 0.548 1.096 1.036C1.026 1.295 0.897 1.823 0.538 1.823C0.518 1.823 0.279 1.823 0.149 1.644C0.448 1.594 0.588 1.355 0.588 1.156C0.588 0.936 0.408 0.837 0.249 0.837C0 0.837 -0.249 1.036 -0.249 1.385C-0.249 1.853 0.189 2.042 0.538 2.042C1.435 2.042 1.873 0.588 2.152 -0.857L2.75 -3.985Z' id='g0-12'/>
+<path d='M3.019 -6.665C3.029 -6.695 3.049 -6.775 3.049 -6.795C3.049 -6.884 2.989 -6.914 2.909 -6.914C2.879 -6.914 2.78 -6.904 2.75 -6.894L1.763 -6.814C1.644 -6.804 1.534 -6.795 1.534 -6.605C1.534 -6.496 1.634 -6.496 1.773 -6.496C2.252 -6.496 2.271 -6.426 2.271 -6.326C2.271 -6.296 2.242 -6.167 2.242 -6.157L1.255 -2.222C1.245 -2.192 1.146 -1.783 1.146 -1.405C1.146 -0.568 1.564 0.11 2.291 0.11C3.407 0.11 4.573 -1.385 4.573 -2.849C4.573 -3.915 3.975 -4.403 3.377 -4.403C2.999 -4.403 2.66 -4.194 2.321 -3.875L3.019 -6.665ZM2.291 -0.11C2.042 -0.11 1.714 -0.319 1.714 -1.066C1.714 -1.504 1.803 -1.853 2.092 -2.989C2.162 -3.228 2.162 -3.248 2.311 -3.447C2.6 -3.846 2.979 -4.184 3.357 -4.184C3.806 -4.184 3.905 -3.616 3.905 -3.298C3.905 -2.879 3.636 -1.694 3.347 -1.056C3.228 -0.807 2.8 -0.11 2.291 -0.11Z' id='g0-98'/>
+<path d='M4.075 -2.291H6.854C6.994 -2.291 7.183 -2.291 7.183 -2.491S6.994 -2.69 6.854 -2.69H4.075V-5.479C4.075 -5.619 4.075 -5.808 3.875 -5.808S3.676 -5.619 3.676 -5.479V-2.69H0.887C0.747 -2.69 0.558 -2.69 0.558 -2.491S0.747 -2.291 0.887 -2.291H3.676V0.498C3.676 0.638 3.676 0.827 3.875 0.827S4.075 0.638 4.075 0.498V-2.291Z' id='g5-43'/>
+<path d='M2.929 -6.376C2.929 -6.615 2.929 -6.635 2.7 -6.635C2.082 -5.998 1.205 -5.998 0.887 -5.998V-5.689C1.086 -5.689 1.674 -5.689 2.192 -5.948V-0.787C2.192 -0.428 2.162 -0.309 1.265 -0.309H0.946V0C1.295 -0.03 2.162 -0.03 2.56 -0.03S3.826 -0.03 4.174 0V-0.309H3.856C2.959 -0.309 2.929 -0.418 2.929 -0.787V-6.376Z' id='g5-49'/>
+<path d='M1.265 -0.767L2.321 -1.793C3.875 -3.168 4.473 -3.706 4.473 -4.702C4.473 -5.838 3.577 -6.635 2.361 -6.635C1.235 -6.635 0.498 -5.719 0.498 -4.832C0.498 -4.274 0.996 -4.274 1.026 -4.274C1.196 -4.274 1.544 -4.394 1.544 -4.802C1.544 -5.061 1.365 -5.32 1.016 -5.32C0.936 -5.32 0.917 -5.32 0.887 -5.31C1.116 -5.958 1.654 -6.326 2.232 -6.326C3.138 -6.326 3.567 -5.519 3.567 -4.702C3.567 -3.905 3.068 -3.118 2.521 -2.501L0.608 -0.369C0.498 -0.259 0.498 -0.239 0.498 0H4.194L4.473 -1.733H4.224C4.174 -1.435 4.105 -0.996 4.005 -0.847C3.935 -0.767 3.278 -0.767 3.059 -0.767H1.265Z' id='g5-50'/>
+<path d='M1.116 -2.511C1.176 -3.995 2.012 -4.244 2.351 -4.244C3.377 -4.244 3.477 -2.899 3.477 -2.511H1.116ZM1.106 -2.301H3.885C4.105 -2.301 4.134 -2.301 4.134 -2.511C4.134 -3.497 3.597 -4.463 2.351 -4.463C1.196 -4.463 0.279 -3.437 0.279 -2.192C0.279 -0.857 1.325 0.11 2.471 0.11C3.686 0.11 4.134 -0.996 4.134 -1.186C4.134 -1.285 4.055 -1.305 4.005 -1.305C3.915 -1.305 3.895 -1.245 3.875 -1.166C3.527 -0.139 2.63 -0.139 2.531 -0.139C2.032 -0.139 1.634 -0.438 1.405 -0.807C1.106 -1.285 1.106 -1.943 1.106 -2.301Z' id='g5-101'/>
+<path d='M1.743 -4.294V-5.45C1.743 -6.326 2.222 -6.804 2.66 -6.804C2.69 -6.804 2.839 -6.804 2.989 -6.735C2.869 -6.695 2.69 -6.565 2.69 -6.316C2.69 -6.087 2.849 -5.888 3.118 -5.888C3.407 -5.888 3.557 -6.087 3.557 -6.326C3.557 -6.695 3.188 -7.024 2.66 -7.024C1.963 -7.024 1.116 -6.496 1.116 -5.44V-4.294H0.329V-3.985H1.116V-0.757C1.116 -0.309 1.006 -0.309 0.339 -0.309V0C0.727 -0.01 1.196 -0.03 1.474 -0.03C1.873 -0.03 2.341 -0.03 2.74 0V-0.309H2.531C1.793 -0.309 1.773 -0.418 1.773 -0.777V-3.985H2.909V-4.294H1.743Z' id='g5-102'/>
+<path d='M1.096 -0.757C1.096 -0.309 0.986 -0.309 0.319 -0.309V0C0.667 -0.01 1.176 -0.03 1.445 -0.03C1.704 -0.03 2.222 -0.01 2.56 0V-0.309C1.893 -0.309 1.783 -0.309 1.783 -0.757V-2.59C1.783 -3.626 2.491 -4.184 3.128 -4.184C3.756 -4.184 3.866 -3.646 3.866 -3.078V-0.757C3.866 -0.309 3.756 -0.309 3.088 -0.309V0C3.437 -0.01 3.945 -0.03 4.214 -0.03C4.473 -0.03 4.991 -0.01 5.33 0V-0.309C4.812 -0.309 4.563 -0.309 4.553 -0.608V-2.511C4.553 -3.367 4.553 -3.676 4.244 -4.035C4.105 -4.204 3.776 -4.403 3.198 -4.403C2.361 -4.403 1.923 -3.806 1.753 -3.427V-6.914L0.319 -6.804V-6.496C1.016 -6.496 1.096 -6.426 1.096 -5.938V-0.757Z' id='g5-104'/>
+<path d='M1.763 -4.403L0.369 -4.294V-3.985C1.016 -3.985 1.106 -3.925 1.106 -3.437V-0.757C1.106 -0.309 0.996 -0.309 0.329 -0.309V0C0.648 -0.01 1.186 -0.03 1.425 -0.03C1.773 -0.03 2.122 -0.01 2.461 0V-0.309C1.803 -0.309 1.763 -0.359 1.763 -0.747V-4.403ZM1.803 -6.137C1.803 -6.456 1.554 -6.665 1.275 -6.665C0.966 -6.665 0.747 -6.396 0.747 -6.137C0.747 -5.868 0.966 -5.609 1.275 -5.609C1.554 -5.609 1.803 -5.818 1.803 -6.137Z' id='g5-105'/>
+<path d='M1.763 -6.914L0.329 -6.804V-6.496C1.026 -6.496 1.106 -6.426 1.106 -5.938V-0.757C1.106 -0.309 0.996 -0.309 0.329 -0.309V0C0.658 -0.01 1.186 -0.03 1.435 -0.03S2.172 -0.01 2.54 0V-0.309C1.873 -0.309 1.763 -0.309 1.763 -0.757V-6.914Z' id='g5-108'/>
+<path d='M1.096 -3.427V-0.757C1.096 -0.309 0.986 -0.309 0.319 -0.309V0C0.667 -0.01 1.176 -0.03 1.445 -0.03C1.704 -0.03 2.222 -0.01 2.56 0V-0.309C1.893 -0.309 1.783 -0.309 1.783 -0.757V-2.59C1.783 -3.626 2.491 -4.184 3.128 -4.184C3.756 -4.184 3.866 -3.646 3.866 -3.078V-0.757C3.866 -0.309 3.756 -0.309 3.088 -0.309V0C3.437 -0.01 3.945 -0.03 4.214 -0.03C4.473 -0.03 4.991 -0.01 5.33 0V-0.309C4.812 -0.309 4.563 -0.309 4.553 -0.608V-2.511C4.553 -3.367 4.553 -3.676 4.244 -4.035C4.105 -4.204 3.776 -4.403 3.198 -4.403C2.471 -4.403 2.002 -3.975 1.724 -3.357V-4.403L0.319 -4.294V-3.985C1.016 -3.985 1.096 -3.915 1.096 -3.427Z' id='g5-110'/>
+<path d='M2.072 -1.933C2.291 -1.893 3.108 -1.733 3.108 -1.016C3.108 -0.508 2.76 -0.11 1.983 -0.11C1.146 -0.11 0.787 -0.677 0.598 -1.524C0.568 -1.654 0.558 -1.694 0.458 -1.694C0.329 -1.694 0.329 -1.624 0.329 -1.445V-0.13C0.329 0.04 0.329 0.11 0.438 0.11C0.488 0.11 0.498 0.1 0.687 -0.09C0.707 -0.11 0.707 -0.13 0.887 -0.319C1.325 0.1 1.773 0.11 1.983 0.11C3.128 0.11 3.587 -0.558 3.587 -1.275C3.587 -1.803 3.288 -2.102 3.168 -2.222C2.839 -2.54 2.451 -2.62 2.032 -2.7C1.474 -2.809 0.807 -2.939 0.807 -3.517C0.807 -3.866 1.066 -4.274 1.923 -4.274C3.019 -4.274 3.068 -3.377 3.088 -3.068C3.098 -2.979 3.188 -2.979 3.208 -2.979C3.337 -2.979 3.337 -3.029 3.337 -3.218V-4.224C3.337 -4.394 3.337 -4.463 3.228 -4.463C3.178 -4.463 3.158 -4.463 3.029 -4.344C2.999 -4.304 2.899 -4.214 2.859 -4.184C2.481 -4.463 2.072 -4.463 1.923 -4.463C0.707 -4.463 0.329 -3.796 0.329 -3.238C0.329 -2.889 0.488 -2.61 0.757 -2.391C1.076 -2.132 1.355 -2.072 2.072 -1.933Z' id='g5-115'/>
+<path d='M1.724 -3.985H3.148V-4.294H1.724V-6.127H1.474C1.465 -5.31 1.166 -4.244 0.189 -4.204V-3.985H1.036V-1.235C1.036 -0.01 1.963 0.11 2.321 0.11C3.029 0.11 3.308 -0.598 3.308 -1.235V-1.803H3.059V-1.255C3.059 -0.518 2.76 -0.139 2.391 -0.139C1.724 -0.139 1.724 -1.046 1.724 -1.215V-3.985Z' id='g5-116'/>
+<path d='M6.565 -2.291C6.735 -2.291 6.914 -2.291 6.914 -2.491S6.735 -2.69 6.565 -2.69H1.176C1.006 -2.69 0.827 -2.69 0.827 -2.491S1.006 -2.291 1.176 -2.291H6.565Z' id='g1-0'/>
+<path d='M4.423 -2.491C4.423 -3.557 3.537 -4.423 2.491 -4.423C1.415 -4.423 0.548 -3.537 0.548 -2.491C0.548 -1.435 1.415 -0.558 2.491 -0.558C3.537 -0.558 4.423 -1.425 4.423 -2.491Z' id='g1-15'/>
+<path d='M3.557 -2.909C3.965 -1.863 4.443 -0.339 4.603 -0.11C4.762 0.11 4.862 0.11 5.131 0.11H5.35C5.45 0.1 5.46 0.04 5.46 0.01S5.44 -0.04 5.41 -0.08C5.31 -0.189 5.25 -0.339 5.181 -0.538L3.148 -6.207C2.939 -6.785 2.401 -6.914 1.933 -6.914C1.883 -6.914 1.753 -6.914 1.753 -6.804C1.753 -6.725 1.833 -6.705 1.843 -6.705C2.172 -6.645 2.242 -6.585 2.491 -5.908L3.457 -3.198L0.707 -0.468C0.588 -0.349 0.528 -0.289 0.528 -0.159C0.528 0.01 0.667 0.13 0.827 0.13S1.076 0.02 1.156 -0.08L3.557 -2.909Z' id='g3-21'/>
+<path d='M6.725 -4.961C6.844 -5.021 6.914 -5.071 6.914 -5.181S6.824 -5.38 6.715 -5.38C6.685 -5.38 6.665 -5.38 6.535 -5.31L1.016 -2.71C0.907 -2.66 0.827 -2.61 0.827 -2.491S0.907 -2.321 1.016 -2.271L6.535 0.329C6.665 0.399 6.685 0.399 6.715 0.399C6.824 0.399 6.914 0.309 6.914 0.199S6.844 0.04 6.725 -0.02L1.494 -2.491L6.725 -4.961Z' id='g3-60'/>
+<path d='M0.877 -0.588C0.847 -0.438 0.787 -0.209 0.787 -0.159C0.787 0.02 0.927 0.11 1.076 0.11C1.196 0.11 1.375 0.03 1.445 -0.169C1.455 -0.189 1.574 -0.658 1.634 -0.907L1.853 -1.803C1.913 -2.022 1.973 -2.242 2.022 -2.471C2.062 -2.64 2.142 -2.929 2.152 -2.969C2.301 -3.278 2.829 -4.184 3.776 -4.184C4.224 -4.184 4.314 -3.816 4.314 -3.487C4.314 -2.869 3.826 -1.594 3.666 -1.166C3.577 -0.936 3.567 -0.817 3.567 -0.707C3.567 -0.239 3.915 0.11 4.384 0.11C5.32 0.11 5.689 -1.345 5.689 -1.425C5.689 -1.524 5.599 -1.524 5.569 -1.524C5.469 -1.524 5.469 -1.494 5.42 -1.345C5.22 -0.667 4.892 -0.11 4.403 -0.11C4.234 -0.11 4.164 -0.209 4.164 -0.438C4.164 -0.687 4.254 -0.927 4.344 -1.146C4.533 -1.674 4.951 -2.77 4.951 -3.337C4.951 -4.005 4.523 -4.403 3.806 -4.403C2.909 -4.403 2.421 -3.766 2.252 -3.537C2.202 -4.095 1.793 -4.403 1.335 -4.403S0.687 -4.015 0.588 -3.836C0.428 -3.497 0.289 -2.909 0.289 -2.869C0.289 -2.77 0.389 -2.77 0.408 -2.77C0.508 -2.77 0.518 -2.78 0.578 -2.999C0.747 -3.706 0.946 -4.184 1.305 -4.184C1.504 -4.184 1.614 -4.055 1.614 -3.726C1.614 -3.517 1.584 -3.407 1.455 -2.889L0.877 -0.588Z' id='g3-110'/>
+<path d='M6.167 -5.22C6.286 -5.29 6.366 -5.33 6.366 -5.44C6.366 -5.579 6.257 -5.639 6.167 -5.639C6.097 -5.639 6.027 -5.599 5.988 -5.579L0.737 -2.71C0.618 -2.64 0.548 -2.61 0.548 -2.491S0.618 -2.341 0.737 -2.271L5.978 0.588C6.097 0.658 6.127 0.658 6.167 0.658C6.296 0.658 6.366 0.558 6.366 0.458C6.366 0.349 6.286 0.309 6.167 0.239L1.166 -2.491L6.167 -5.22ZM9.205 -5.22C9.325 -5.29 9.405 -5.33 9.405 -5.44C9.405 -5.579 9.295 -5.639 9.205 -5.639C9.136 -5.639 9.066 -5.599 9.026 -5.579L3.776 -2.71C3.656 -2.64 3.587 -2.61 3.587 -2.491S3.656 -2.341 3.776 -2.271L9.016 0.588C9.136 0.658 9.166 0.658 9.205 0.658C9.335 0.658 9.405 0.558 9.405 0.458C9.405 0.349 9.325 0.309 9.205 0.239L4.204 -2.491L9.205 -5.22Z' id='g1-28'/>
+<path d='M2.022 -3.292C2.078 -3.41 2.085 -3.466 2.085 -3.515C2.085 -3.731 1.89 -3.898 1.674 -3.898C1.409 -3.898 1.325 -3.682 1.29 -3.571L0.37 -0.551C0.363 -0.537 0.335 -0.446 0.335 -0.439C0.335 -0.356 0.551 -0.286 0.607 -0.286C0.656 -0.286 0.663 -0.3 0.711 -0.404L2.022 -3.292Z' id='g2-48'/>
+<path d='M3.328 -3.009C3.387 -3.268 3.616 -4.184 4.314 -4.184C4.364 -4.184 4.603 -4.184 4.812 -4.055C4.533 -4.005 4.334 -3.756 4.334 -3.517C4.334 -3.357 4.443 -3.168 4.712 -3.168C4.932 -3.168 5.25 -3.347 5.25 -3.746C5.25 -4.264 4.663 -4.403 4.324 -4.403C3.746 -4.403 3.397 -3.875 3.278 -3.646C3.029 -4.304 2.491 -4.403 2.202 -4.403C1.166 -4.403 0.598 -3.118 0.598 -2.869C0.598 -2.77 0.697 -2.77 0.717 -2.77C0.797 -2.77 0.827 -2.79 0.847 -2.879C1.186 -3.935 1.843 -4.184 2.182 -4.184C2.371 -4.184 2.72 -4.095 2.72 -3.517C2.72 -3.208 2.55 -2.54 2.182 -1.146C2.022 -0.528 1.674 -0.11 1.235 -0.11C1.176 -0.11 0.946 -0.11 0.737 -0.239C0.986 -0.289 1.205 -0.498 1.205 -0.777C1.205 -1.046 0.986 -1.126 0.837 -1.126C0.538 -1.126 0.289 -0.867 0.289 -0.548C0.289 -0.09 0.787 0.11 1.225 0.11C1.883 0.11 2.242 -0.588 2.271 -0.648C2.391 -0.279 2.75 0.11 3.347 0.11C4.374 0.11 4.941 -1.176 4.941 -1.425C4.941 -1.524 4.852 -1.524 4.822 -1.524C4.732 -1.524 4.712 -1.484 4.692 -1.415C4.364 -0.349 3.686 -0.11 3.367 -0.11C2.979 -0.11 2.819 -0.428 2.819 -0.767C2.819 -0.986 2.879 -1.205 2.989 -1.644L3.328 -3.009Z' id='g3-120'/>
+<path d='M7.054 -2.321C7.073 -2.371 7.103 -2.441 7.103 -2.461C7.103 -2.471 7.103 -2.57 6.984 -2.57C6.894 -2.57 6.874 -2.511 6.854 -2.451C6.207 -0.976 5.838 -0.309 4.134 -0.309H2.68C2.54 -0.309 2.521 -0.309 2.461 -0.319C2.361 -0.329 2.331 -0.339 2.331 -0.418C2.331 -0.448 2.331 -0.468 2.381 -0.648L3.059 -3.367H4.045C4.892 -3.367 4.892 -3.158 4.892 -2.909C4.892 -2.839 4.892 -2.72 4.822 -2.421C4.802 -2.371 4.792 -2.341 4.792 -2.311C4.792 -2.262 4.832 -2.202 4.922 -2.202C5.001 -2.202 5.031 -2.252 5.071 -2.401L5.639 -4.732C5.639 -4.792 5.589 -4.842 5.519 -4.842C5.43 -4.842 5.41 -4.782 5.38 -4.663C5.171 -3.905 4.991 -3.676 4.075 -3.676H3.138L3.736 -6.077C3.826 -6.426 3.836 -6.466 4.274 -6.466H5.679C6.894 -6.466 7.193 -6.177 7.193 -5.36C7.193 -5.121 7.193 -5.101 7.153 -4.832C7.153 -4.772 7.143 -4.702 7.143 -4.653S7.173 -4.533 7.263 -4.533C7.372 -4.533 7.382 -4.593 7.402 -4.782L7.601 -6.506C7.631 -6.775 7.582 -6.775 7.333 -6.775H2.301C2.102 -6.775 2.002 -6.775 2.002 -6.575C2.002 -6.466 2.092 -6.466 2.281 -6.466C2.65 -6.466 2.929 -6.466 2.929 -6.286C2.929 -6.247 2.929 -6.227 2.879 -6.047L1.564 -0.777C1.465 -0.389 1.445 -0.309 0.658 -0.309C0.488 -0.309 0.379 -0.309 0.379 -0.12C0.379 0 0.468 0 0.658 0H5.828C6.057 0 6.067 -0.01 6.137 -0.169L7.054 -2.321Z' id='g3-69'/>
+<path d='M6.725 -5.918C6.834 -5.968 6.914 -6.017 6.914 -6.137C6.914 -6.247 6.834 -6.336 6.715 -6.336C6.665 -6.336 6.575 -6.296 6.535 -6.276L1.026 -3.676C0.857 -3.597 0.827 -3.527 0.827 -3.447C0.827 -3.357 0.887 -3.288 1.026 -3.228L6.535 -0.638C6.665 -0.568 6.685 -0.568 6.715 -0.568C6.824 -0.568 6.914 -0.658 6.914 -0.767C6.914 -0.857 6.874 -0.917 6.705 -0.996L1.494 -3.447L6.725 -5.918ZM6.565 1.365C6.735 1.365 6.914 1.365 6.914 1.166S6.705 0.966 6.555 0.966H1.186C1.036 0.966 0.827 0.966 0.827 1.166S1.006 1.365 1.176 1.365H6.565Z' id='g1-20'/>
+<path d='M7.233 -3.258C7.651 -2.899 8.159 -2.64 8.488 -2.491C8.13 -2.331 7.641 -2.072 7.233 -1.724H0.907C0.737 -1.724 0.548 -1.724 0.548 -1.524S0.727 -1.325 0.897 -1.325H6.785C6.306 -0.867 5.788 0.01 5.788 0.139C5.788 0.249 5.918 0.249 5.978 0.249C6.057 0.249 6.127 0.249 6.167 0.169C6.376 -0.209 6.655 -0.737 7.303 -1.315C7.99 -1.923 8.658 -2.192 9.176 -2.341C9.345 -2.401 9.355 -2.411 9.375 -2.431C9.395 -2.441 9.395 -2.471 9.395 -2.491S9.395 -2.531 9.385 -2.55L9.355 -2.57C9.335 -2.58 9.325 -2.59 9.136 -2.65C7.791 -3.049 6.795 -3.955 6.237 -5.021C6.127 -5.22 6.117 -5.23 5.978 -5.23C5.918 -5.23 5.788 -5.23 5.788 -5.121C5.788 -4.991 6.296 -4.125 6.785 -3.656H0.897C0.727 -3.656 0.548 -3.656 0.548 -3.457S0.737 -3.258 0.907 -3.258H7.233Z' id='g1-41'/>
+<path d='M3.547 -5.748C3.467 -5.918 3.407 -5.958 3.318 -5.958C3.188 -5.958 3.158 -5.888 3.098 -5.748L0.618 -0.179C0.558 -0.05 0.548 -0.03 0.548 0.02C0.548 0.13 0.638 0.219 0.747 0.219C0.817 0.219 0.897 0.199 0.976 0.01L3.318 -5.27L5.659 0.01C5.748 0.219 5.848 0.219 5.888 0.219C5.998 0.219 6.087 0.13 6.087 0.02C6.087 0 6.087 -0.02 6.027 -0.139L3.547 -5.748Z' id='g1-94'/>
+<path d='M3.019 -3.148H4.712C6.127 -3.148 7.512 -4.184 7.512 -5.3C7.512 -6.067 6.854 -6.804 5.549 -6.804H2.321C2.132 -6.804 2.022 -6.804 2.022 -6.615C2.022 -6.496 2.112 -6.496 2.311 -6.496C2.441 -6.496 2.62 -6.486 2.74 -6.476C2.899 -6.456 2.959 -6.426 2.959 -6.316C2.959 -6.276 2.949 -6.247 2.919 -6.127L1.584 -0.777C1.484 -0.389 1.465 -0.309 0.677 -0.309C0.508 -0.309 0.399 -0.309 0.399 -0.12C0.399 0 0.518 0 0.548 0C0.827 0 1.534 -0.03 1.813 -0.03C2.022 -0.03 2.242 -0.02 2.451 -0.02C2.67 -0.02 2.889 0 3.098 0C3.168 0 3.298 0 3.298 -0.199C3.298 -0.309 3.208 -0.309 3.019 -0.309C2.65 -0.309 2.371 -0.309 2.371 -0.488C2.371 -0.548 2.391 -0.598 2.401 -0.658L3.019 -3.148ZM3.736 -6.117C3.826 -6.466 3.846 -6.496 4.274 -6.496H5.23C6.057 -6.496 6.585 -6.227 6.585 -5.539C6.585 -5.151 6.386 -4.294 5.998 -3.935C5.499 -3.487 4.902 -3.407 4.463 -3.407H3.059L3.736 -6.117Z' id='g3-80'/>
+<path d='M1.714 -0.727C1.634 -0.389 1.574 -0.309 0.897 -0.309C0.737 -0.309 0.628 -0.309 0.628 -0.12C0.628 0 0.717 0 0.877 0H3.935C5.818 0 7.721 -2.122 7.721 -4.364C7.721 -5.788 6.914 -6.804 5.629 -6.804H2.521C2.361 -6.804 2.252 -6.804 2.252 -6.615C2.252 -6.496 2.331 -6.496 2.521 -6.496C2.829 -6.496 3.098 -6.496 3.098 -6.326C3.098 -6.296 3.098 -6.276 3.059 -6.137L1.714 -0.727ZM3.846 -6.137C3.935 -6.486 4.005 -6.496 4.334 -6.496H5.3C6.107 -6.496 6.894 -6.047 6.894 -4.742C6.894 -4.154 6.605 -2.291 5.758 -1.295C5.27 -0.727 4.563 -0.309 3.766 -0.309H2.74C2.431 -0.309 2.431 -0.339 2.431 -0.408C2.431 -0.468 2.461 -0.568 2.471 -0.628L3.846 -6.137Z' id='g0-68'/>
+<path d='M3.128 -3.148H4.672C5.928 -3.148 7.273 -4.164 7.273 -5.33C7.273 -6.167 6.595 -6.804 5.509 -6.804H2.521C2.361 -6.804 2.252 -6.804 2.252 -6.615C2.252 -6.496 2.331 -6.496 2.521 -6.496C2.829 -6.496 3.098 -6.496 3.098 -6.326C3.098 -6.296 3.098 -6.276 3.059 -6.137L1.714 -0.727C1.634 -0.389 1.574 -0.309 0.897 -0.309C0.737 -0.309 0.628 -0.309 0.628 -0.12C0.628 0 0.737 0 0.767 0C1.146 0 1.554 -0.03 1.943 -0.03C2.341 -0.03 2.75 0 3.138 0C3.208 0 3.328 0 3.328 -0.179C3.328 -0.309 3.258 -0.309 3.059 -0.309C2.481 -0.309 2.481 -0.389 2.481 -0.478C2.481 -0.518 2.481 -0.538 2.521 -0.677L3.128 -3.148ZM3.846 -6.137C3.935 -6.496 4.005 -6.496 4.334 -6.496H5.191C5.888 -6.496 6.396 -6.237 6.396 -5.599C6.396 -5.4 6.247 -4.423 5.868 -3.985C5.519 -3.597 5.021 -3.407 4.423 -3.407H3.168L3.846 -6.137Z' id='g0-80'/>
+<path d='M3.477 -0.588C3.587 -0.1 3.955 0.11 4.304 0.11C4.672 0.11 4.882 -0.139 5.031 -0.448C5.21 -0.827 5.33 -1.405 5.33 -1.425C5.33 -1.524 5.25 -1.524 5.181 -1.524C5.061 -1.524 5.051 -1.514 4.991 -1.295C4.852 -0.737 4.663 -0.11 4.324 -0.11C4.065 -0.11 4.065 -0.379 4.065 -0.518C4.065 -0.588 4.065 -0.747 4.134 -1.026L4.812 -3.736C4.852 -3.875 4.852 -3.895 4.852 -3.945C4.852 -4.154 4.682 -4.204 4.583 -4.204C4.264 -4.204 4.194 -3.866 4.184 -3.816C3.995 -4.244 3.676 -4.403 3.357 -4.403C2.252 -4.403 1.076 -2.889 1.076 -1.435C1.076 -0.588 1.534 0.11 2.281 0.11C2.64 0.11 3.078 -0.1 3.477 -0.588ZM4.015 -3.118L3.547 -1.235C3.467 -0.917 2.849 -0.11 2.301 -0.11C1.833 -0.11 1.753 -0.697 1.753 -0.996C1.753 -1.494 2.062 -2.66 2.242 -3.078C2.491 -3.686 2.949 -4.184 3.357 -4.184C3.796 -4.184 4.045 -3.666 4.045 -3.248C4.045 -3.228 4.035 -3.178 4.015 -3.118Z' id='g0-97'/>
+<path d='M5.549 -6.665C5.559 -6.695 5.579 -6.775 5.579 -6.795C5.579 -6.884 5.519 -6.914 5.44 -6.914C5.41 -6.914 5.31 -6.904 5.28 -6.894L4.294 -6.814C4.174 -6.804 4.065 -6.795 4.065 -6.605C4.065 -6.496 4.164 -6.496 4.304 -6.496C4.782 -6.496 4.802 -6.426 4.802 -6.326C4.802 -6.296 4.772 -6.167 4.772 -6.157L4.194 -3.826C4.045 -4.134 3.776 -4.403 3.357 -4.403C2.252 -4.403 1.076 -2.889 1.076 -1.435C1.076 -0.588 1.534 0.11 2.281 0.11C2.64 0.11 3.078 -0.1 3.477 -0.588C3.587 -0.1 3.955 0.11 4.304 0.11C4.672 0.11 4.882 -0.139 5.031 -0.448C5.21 -0.827 5.33 -1.405 5.33 -1.425C5.33 -1.524 5.25 -1.524 5.181 -1.524C5.061 -1.524 5.051 -1.514 4.991 -1.295C4.852 -0.737 4.663 -0.11 4.324 -0.11C4.065 -0.11 4.065 -0.379 4.065 -0.518C4.065 -0.588 4.065 -0.737 4.125 -0.976L5.549 -6.665ZM3.537 -1.215C3.457 -0.907 2.849 -0.11 2.301 -0.11C1.833 -0.11 1.753 -0.697 1.753 -0.996C1.753 -1.494 2.062 -2.66 2.242 -3.078C2.491 -3.686 2.949 -4.184 3.357 -4.184C3.437 -4.184 3.666 -4.174 3.846 -3.895C3.945 -3.736 4.045 -3.447 4.045 -3.258C4.045 -3.228 4.035 -3.188 4.015 -3.128L3.537 -1.215Z' id='g0-100'/>
+<path d='M2.291 -1.743C2.341 -1.963 2.401 -2.172 2.451 -2.391C2.58 -2.899 2.58 -2.909 2.75 -3.198C2.889 -3.437 3.337 -4.184 4.095 -4.184C4.533 -4.184 4.553 -3.736 4.553 -3.527C4.553 -2.909 4.115 -1.724 3.975 -1.325C3.846 -0.986 3.816 -0.897 3.816 -0.687C3.816 -0.249 4.095 0.11 4.573 0.11C5.499 0.11 5.838 -1.375 5.838 -1.425C5.838 -1.524 5.758 -1.524 5.689 -1.524C5.559 -1.524 5.559 -1.504 5.519 -1.355C5.44 -1.086 5.171 -0.11 4.593 -0.11C4.384 -0.11 4.374 -0.259 4.374 -0.399C4.374 -0.648 4.473 -0.907 4.553 -1.146C4.752 -1.674 5.161 -2.79 5.161 -3.367C5.161 -4.184 4.613 -4.403 4.125 -4.403C3.308 -4.403 2.829 -3.806 2.7 -3.616C2.63 -4.105 2.291 -4.403 1.863 -4.403C1.504 -4.403 1.305 -4.174 1.146 -3.875C0.956 -3.477 0.827 -2.889 0.827 -2.869C0.827 -2.77 0.927 -2.77 0.976 -2.77C1.106 -2.77 1.116 -2.78 1.166 -2.999C1.345 -3.696 1.534 -4.184 1.843 -4.184C2.102 -4.184 2.102 -3.895 2.102 -3.786C2.102 -3.626 2.072 -3.437 2.032 -3.278L1.285 -0.289C1.265 -0.229 1.255 -0.179 1.255 -0.149C1.255 -0.04 1.335 0.11 1.534 0.11C1.654 0.11 1.823 0.04 1.893 -0.149L2.291 -1.743Z' id='g0-110'/>
+<path d='M5.071 -2.77C5.071 -3.716 4.483 -4.403 3.616 -4.403C2.381 -4.403 1.096 -2.989 1.096 -1.534C1.096 -0.508 1.724 0.11 2.55 0.11C3.786 0.11 5.071 -1.305 5.071 -2.77ZM2.55 -0.11C2.162 -0.11 1.793 -0.418 1.793 -1.136C1.793 -1.634 2.052 -2.74 2.371 -3.278C2.74 -3.885 3.218 -4.184 3.606 -4.184C4.095 -4.184 4.374 -3.746 4.374 -3.158C4.374 -2.73 4.154 -1.704 3.836 -1.106C3.547 -0.558 3.039 -0.11 2.55 -0.11Z' id='g0-111'/>
+<path d='M2.57 -2.869C2.58 -2.899 3.029 -4.184 3.885 -4.184C3.935 -4.184 4.214 -4.184 4.413 -4.045C4.065 -3.935 4.035 -3.616 4.035 -3.567C4.035 -3.437 4.125 -3.248 4.384 -3.248C4.563 -3.248 4.872 -3.387 4.872 -3.776C4.872 -4.294 4.224 -4.403 3.895 -4.403C3.208 -4.403 2.849 -3.905 2.69 -3.686C2.58 -4.214 2.192 -4.403 1.863 -4.403C1.504 -4.403 1.305 -4.174 1.146 -3.875C0.956 -3.477 0.827 -2.889 0.827 -2.869C0.827 -2.77 0.927 -2.77 0.976 -2.77C1.106 -2.77 1.116 -2.78 1.166 -2.999C1.345 -3.696 1.534 -4.184 1.843 -4.184C2.102 -4.184 2.102 -3.895 2.102 -3.786C2.102 -3.626 2.072 -3.437 2.032 -3.278L1.285 -0.289C1.265 -0.229 1.255 -0.179 1.255 -0.149C1.255 -0.04 1.335 0.11 1.534 0.11C1.833 0.11 1.903 -0.179 1.923 -0.259L2.57 -2.869Z' id='g0-114'/>
+<path d='M3.786 -1.674C3.716 -1.395 3.706 -1.186 3.706 -1.046C3.706 -0.936 3.397 -0.11 2.869 -0.11C2.172 -0.11 2.172 -0.847 2.172 -0.976C2.172 -1.514 2.431 -2.252 2.74 -3.068C2.809 -3.258 2.859 -3.407 2.859 -3.597C2.859 -4.095 2.531 -4.403 2.102 -4.403C1.176 -4.403 0.827 -2.929 0.827 -2.869C0.827 -2.77 0.927 -2.77 0.976 -2.77C1.106 -2.77 1.116 -2.79 1.156 -2.929C1.235 -3.238 1.504 -4.184 2.082 -4.184C2.192 -4.184 2.301 -4.154 2.301 -3.895C2.301 -3.656 2.202 -3.387 2.062 -2.999C1.564 -1.644 1.564 -1.385 1.564 -1.116C1.564 -0.777 1.644 -0.438 1.903 -0.199C2.222 0.08 2.67 0.11 2.839 0.11C3.158 0.11 3.507 -0.02 3.806 -0.528C3.955 -0.159 4.354 0.11 4.941 0.11C5.539 0.11 5.958 -0.299 6.257 -1.006C6.555 -1.684 6.944 -3.218 6.944 -3.746C6.944 -4.095 6.824 -4.403 6.535 -4.403C6.306 -4.403 6.057 -4.174 6.057 -3.945C6.057 -3.816 6.127 -3.746 6.187 -3.676C6.426 -3.417 6.456 -3.098 6.456 -2.889C6.456 -2.491 6.137 -1.445 5.968 -1.036C5.748 -0.518 5.43 -0.11 4.971 -0.11C4.473 -0.11 4.314 -0.518 4.314 -0.936C4.314 -1.026 4.324 -1.265 4.433 -1.704L4.852 -3.377C4.912 -3.597 5.011 -3.995 5.011 -4.035C5.011 -4.144 4.932 -4.294 4.732 -4.294C4.443 -4.294 4.374 -4.025 4.354 -3.945L3.786 -1.674Z' id='g0-119'/>
+<path d='M5.489 -6.535C5.539 -6.645 5.539 -6.665 5.539 -6.715C5.539 -6.814 5.46 -6.914 5.34 -6.914C5.21 -6.914 5.151 -6.795 5.111 -6.685L4.284 -4.493H1.255L0.428 -6.685C0.379 -6.824 0.329 -6.914 0.199 -6.914C0.1 -6.914 0 -6.814 0 -6.715C0 -6.695 0 -6.675 0.06 -6.535L2.54 -0.01C2.59 0.13 2.64 0.219 2.77 0.219C2.909 0.219 2.949 0.11 2.989 0.01L5.489 -6.535ZM1.415 -4.095H4.125L2.77 -0.548L1.415 -4.095Z' id='g1-56'/>
+<path d='M4.672 -2.72C4.672 -3.756 3.975 -4.403 3.078 -4.403C1.743 -4.403 0.408 -2.989 0.408 -1.574C0.408 -0.588 1.076 0.11 2.002 0.11C3.328 0.11 4.672 -1.265 4.672 -2.72ZM2.012 -0.11C1.584 -0.11 1.146 -0.418 1.146 -1.196C1.146 -1.684 1.405 -2.76 1.724 -3.268C2.222 -4.035 2.79 -4.184 3.068 -4.184C3.646 -4.184 3.945 -3.706 3.945 -3.108C3.945 -2.72 3.746 -1.674 3.367 -1.026C3.019 -0.448 2.471 -0.11 2.012 -0.11Z' id='g3-111'/>
+<path d='M0.448 1.215C0.369 1.554 0.349 1.624 -0.09 1.624C-0.209 1.624 -0.319 1.624 -0.319 1.813C-0.319 1.893 -0.269 1.933 -0.189 1.933C0.08 1.933 0.369 1.903 0.648 1.903C0.976 1.903 1.315 1.933 1.634 1.933C1.684 1.933 1.813 1.933 1.813 1.733C1.813 1.624 1.714 1.624 1.574 1.624C1.076 1.624 1.076 1.554 1.076 1.465C1.076 1.345 1.494 -0.279 1.564 -0.528C1.694 -0.239 1.973 0.11 2.481 0.11C3.636 0.11 4.882 -1.345 4.882 -2.809C4.882 -3.746 4.314 -4.403 3.557 -4.403C3.059 -4.403 2.58 -4.045 2.252 -3.656C2.152 -4.194 1.724 -4.403 1.355 -4.403C0.897 -4.403 0.707 -4.015 0.618 -3.836C0.438 -3.497 0.309 -2.899 0.309 -2.869C0.309 -2.77 0.408 -2.77 0.428 -2.77C0.528 -2.77 0.538 -2.78 0.598 -2.999C0.767 -3.706 0.966 -4.184 1.325 -4.184C1.494 -4.184 1.634 -4.105 1.634 -3.726C1.634 -3.497 1.604 -3.387 1.564 -3.218L0.448 1.215ZM2.202 -3.108C2.271 -3.377 2.54 -3.656 2.72 -3.806C3.068 -4.115 3.357 -4.184 3.527 -4.184C3.925 -4.184 4.164 -3.836 4.164 -3.248S3.836 -1.514 3.656 -1.136C3.318 -0.438 2.839 -0.11 2.471 -0.11C1.813 -0.11 1.684 -0.936 1.684 -0.996C1.684 -1.016 1.684 -1.036 1.714 -1.156L2.202 -3.108Z' id='g3-112'/>
+<path d='M6.027 -3.706C6.027 -4.154 5.848 -4.403 5.629 -4.403C5.37 -4.403 5.101 -4.164 5.101 -3.935C5.101 -3.836 5.151 -3.726 5.25 -3.636C5.42 -3.487 5.599 -3.218 5.599 -2.8C5.599 -2.401 5.41 -1.833 5.101 -1.375C4.802 -0.946 4.433 -0.608 3.965 -0.608C3.397 -0.608 3.088 -0.966 2.999 -1.504C3.108 -1.763 3.337 -2.401 3.337 -2.68C3.337 -2.8 3.288 -2.899 3.158 -2.899C3.078 -2.899 2.969 -2.879 2.889 -2.73C2.78 -2.531 2.66 -1.883 2.66 -1.524C2.331 -1.056 1.933 -0.608 1.305 -0.608C0.648 -0.608 0.438 -1.196 0.438 -1.753C0.438 -2.999 1.455 -4.045 1.455 -4.174C1.455 -4.284 1.375 -4.364 1.265 -4.364C1.136 -4.364 1.066 -4.234 1.006 -4.144C0.498 -3.407 0.12 -2.222 0.12 -1.315C0.12 -0.628 0.349 0.11 1.166 0.11C1.873 0.11 2.341 -0.389 2.7 -0.936C2.79 -0.359 3.178 0.11 3.796 0.11C4.573 0.11 5.051 -0.498 5.41 -1.245C5.649 -1.733 6.027 -3.088 6.027 -3.706Z' id='g3-33'/>
+<path d='M2.889 -3.507C3.706 -3.776 4.284 -4.473 4.284 -5.26C4.284 -6.077 3.407 -6.635 2.451 -6.635C1.445 -6.635 0.687 -6.037 0.687 -5.28C0.687 -4.951 0.907 -4.762 1.196 -4.762C1.504 -4.762 1.704 -4.981 1.704 -5.27C1.704 -5.768 1.235 -5.768 1.086 -5.768C1.395 -6.257 2.052 -6.386 2.411 -6.386C2.819 -6.386 3.367 -6.167 3.367 -5.27C3.367 -5.151 3.347 -4.573 3.088 -4.134C2.79 -3.656 2.451 -3.626 2.202 -3.616C2.122 -3.606 1.883 -3.587 1.813 -3.587C1.733 -3.577 1.664 -3.567 1.664 -3.467C1.664 -3.357 1.733 -3.357 1.903 -3.357H2.341C3.158 -3.357 3.527 -2.68 3.527 -1.704C3.527 -0.349 2.839 -0.06 2.401 -0.06C1.973 -0.06 1.225 -0.229 0.877 -0.817C1.225 -0.767 1.534 -0.986 1.534 -1.365C1.534 -1.724 1.265 -1.923 0.976 -1.923C0.737 -1.923 0.418 -1.783 0.418 -1.345C0.418 -0.438 1.345 0.219 2.431 0.219C3.646 0.219 4.553 -0.687 4.553 -1.704C4.553 -2.521 3.925 -3.298 2.889 -3.507Z' id='g5-51'/>
+<path d='M2.022 -0.01C2.022 -0.667 1.773 -1.056 1.385 -1.056C1.056 -1.056 0.857 -0.807 0.857 -0.528C0.857 -0.259 1.056 0 1.385 0C1.504 0 1.634 -0.04 1.733 -0.13C1.763 -0.149 1.773 -0.159 1.783 -0.159S1.803 -0.149 1.803 -0.01C1.803 0.727 1.455 1.325 1.126 1.654C1.016 1.763 1.016 1.783 1.016 1.813C1.016 1.883 1.066 1.923 1.116 1.923C1.225 1.923 2.022 1.156 2.022 -0.01Z' id='g3-59'/>
+<path d='M1.913 -6.137C1.913 -6.406 1.684 -6.665 1.385 -6.665C1.046 -6.665 0.847 -6.386 0.847 -6.137C0.847 -5.868 1.076 -5.609 1.375 -5.609C1.714 -5.609 1.913 -5.888 1.913 -6.137Z' id='g5-95'/>
+<path d='M4.692 -3.756C4.702 -3.816 4.722 -3.866 4.722 -3.935C4.722 -4.105 4.603 -4.204 4.433 -4.204C4.334 -4.204 4.065 -4.134 4.025 -3.776C3.846 -4.144 3.497 -4.403 3.098 -4.403C1.963 -4.403 0.727 -3.009 0.727 -1.574C0.727 -0.588 1.335 0 2.052 0C2.64 0 3.108 -0.468 3.208 -0.578L3.218 -0.568C3.009 0.319 2.889 0.727 2.889 0.747C2.849 0.837 2.511 1.823 1.455 1.823C1.265 1.823 0.936 1.813 0.658 1.724C0.956 1.634 1.066 1.375 1.066 1.205C1.066 1.046 0.956 0.857 0.687 0.857C0.468 0.857 0.149 1.036 0.149 1.435C0.149 1.843 0.518 2.042 1.474 2.042C2.72 2.042 3.437 1.265 3.587 0.667L4.692 -3.756ZM3.397 -1.275C3.337 -1.016 3.108 -0.767 2.889 -0.578C2.68 -0.399 2.371 -0.219 2.082 -0.219C1.584 -0.219 1.435 -0.737 1.435 -1.136C1.435 -1.614 1.724 -2.79 1.993 -3.298C2.262 -3.786 2.69 -4.184 3.108 -4.184C3.766 -4.184 3.905 -3.377 3.905 -3.328S3.885 -3.218 3.875 -3.178L3.397 -1.275Z' id='g3-103'/>
+<path d='M6.834 -4.224C7.004 -4.224 7.193 -4.224 7.193 -4.423S7.014 -4.623 6.844 -4.623H0.897C0.727 -4.623 0.548 -4.623 0.548 -4.423S0.747 -4.224 0.907 -4.224H6.834ZM6.844 -0.359C7.014 -0.359 7.193 -0.359 7.193 -0.558S7.004 -0.757 6.834 -0.757H0.907C0.747 -0.757 0.548 -0.757 0.548 -0.558S0.727 -0.359 0.897 -0.359H6.844ZM6.844 -2.291C7.014 -2.291 7.193 -2.291 7.193 -2.491S7.014 -2.69 6.844 -2.69H0.897C0.727 -2.69 0.548 -2.69 0.548 -2.491S0.727 -2.291 0.897 -2.291H6.844Z' id='g1-17'/>
+<path d='M4.503 -4.294C4.503 -4.334 4.473 -4.394 4.403 -4.394C4.294 -4.394 3.895 -3.995 3.726 -3.706C3.507 -4.244 3.118 -4.403 2.8 -4.403C1.624 -4.403 0.399 -2.929 0.399 -1.484C0.399 -0.508 0.986 0.11 1.714 0.11C2.142 0.11 2.531 -0.13 2.889 -0.488C2.8 -0.139 2.471 1.205 2.441 1.295C2.361 1.574 2.281 1.614 1.724 1.624C1.594 1.624 1.494 1.624 1.494 1.823C1.494 1.833 1.494 1.933 1.624 1.933C1.943 1.933 2.291 1.903 2.62 1.903C2.959 1.903 3.318 1.933 3.646 1.933C3.696 1.933 3.826 1.933 3.826 1.733C3.826 1.624 3.726 1.624 3.567 1.624C3.088 1.624 3.088 1.554 3.088 1.465C3.088 1.395 3.108 1.335 3.128 1.245L4.503 -4.294ZM1.743 -0.11C1.146 -0.11 1.106 -0.877 1.106 -1.046C1.106 -1.524 1.395 -2.61 1.564 -3.029C1.873 -3.766 2.391 -4.184 2.8 -4.184C3.447 -4.184 3.587 -3.377 3.587 -3.308C3.587 -3.248 3.039 -1.066 3.009 -1.026C2.859 -0.747 2.301 -0.11 1.743 -0.11Z' id='g3-113'/>
+<path d='M2.381 -2.301C2.68 -2.301 3.347 -2.331 3.826 -2.521C4.613 -2.839 4.613 -3.487 4.613 -3.557C4.613 -4.015 4.244 -4.403 3.606 -4.403C2.56 -4.403 1.136 -3.397 1.136 -1.634C1.136 -0.737 1.614 0.11 2.56 0.11C3.836 0.11 4.663 -0.887 4.663 -1.036C4.663 -1.086 4.583 -1.196 4.503 -1.196C4.463 -1.196 4.453 -1.186 4.374 -1.086C3.636 -0.149 2.75 -0.11 2.58 -0.11C1.933 -0.11 1.823 -0.817 1.823 -1.205C1.823 -1.584 1.923 -2.032 1.993 -2.301H2.381ZM2.052 -2.521C2.481 -4.154 3.507 -4.184 3.606 -4.184C4.005 -4.184 4.234 -3.915 4.234 -3.577C4.234 -2.521 2.59 -2.521 2.262 -2.521H2.052Z' id='g0-101'/>
+<path d='M2.76 -3.985H3.577C3.746 -3.985 3.846 -3.985 3.846 -4.164C3.846 -4.294 3.766 -4.294 3.597 -4.294H2.819C2.959 -5.031 2.989 -5.21 3.138 -5.908C3.218 -6.276 3.328 -6.804 3.686 -6.804C3.766 -6.804 3.975 -6.785 4.105 -6.625C3.806 -6.575 3.666 -6.336 3.666 -6.137C3.666 -5.918 3.846 -5.818 4.005 -5.818C4.254 -5.818 4.503 -6.017 4.503 -6.366C4.503 -6.795 4.095 -7.024 3.686 -7.024C2.71 -7.024 2.461 -5.729 2.391 -5.39L2.182 -4.294H1.524C1.365 -4.294 1.255 -4.294 1.255 -4.095C1.255 -3.985 1.355 -3.985 1.504 -3.985H2.122L1.395 -0.269C1.275 0.349 1.176 0.757 1.116 0.966C1.036 1.305 0.907 1.823 0.548 1.823C0.448 1.823 0.259 1.793 0.149 1.644C0.448 1.594 0.588 1.355 0.588 1.156C0.588 0.936 0.408 0.837 0.249 0.837C0 0.837 -0.249 1.036 -0.249 1.385C-0.249 1.833 0.179 2.042 0.548 2.042C1.584 2.042 1.993 -0.05 2.062 -0.408L2.76 -3.985Z' id='g0-102'/>
+<path d='M3.019 -6.665C3.029 -6.695 3.049 -6.775 3.049 -6.795C3.049 -6.884 2.989 -6.914 2.909 -6.914C2.879 -6.914 2.78 -6.904 2.75 -6.894L1.763 -6.814C1.644 -6.804 1.534 -6.795 1.534 -6.605C1.534 -6.496 1.634 -6.496 1.773 -6.496C2.252 -6.496 2.271 -6.426 2.271 -6.326C2.271 -6.296 2.242 -6.167 2.242 -6.157L0.996 -1.176C0.986 -1.136 0.936 -0.936 0.936 -0.787C0.936 -0.259 1.295 0.11 1.773 0.11C2.152 0.11 2.351 -0.159 2.481 -0.408C2.65 -0.757 2.79 -1.395 2.79 -1.425C2.79 -1.524 2.71 -1.524 2.64 -1.524C2.59 -1.524 2.531 -1.524 2.501 -1.474L2.431 -1.225C2.262 -0.528 2.072 -0.11 1.793 -0.11C1.534 -0.11 1.534 -0.379 1.534 -0.518C1.534 -0.588 1.534 -0.737 1.594 -0.976L3.019 -6.665Z' id='g0-108'/>
+<path d='M2.461 -1.963C2.899 -1.863 3.347 -1.763 3.347 -1.225C3.347 -0.907 3.078 -0.11 2.052 -0.11C1.833 -0.11 1.255 -0.159 1.096 -0.667C1.604 -0.717 1.604 -1.136 1.604 -1.156C1.604 -1.345 1.474 -1.474 1.265 -1.474C1.036 -1.474 0.757 -1.305 0.757 -0.857C0.757 -0.249 1.335 0.11 2.042 0.11C3.537 0.11 3.915 -1.106 3.915 -1.564C3.915 -2.431 3.138 -2.61 2.73 -2.7C2.451 -2.76 2.092 -2.839 2.092 -3.268C2.092 -3.507 2.301 -4.184 3.098 -4.184C3.367 -4.184 3.736 -4.085 3.846 -3.696C3.527 -3.656 3.447 -3.387 3.447 -3.288C3.447 -3.178 3.507 -3.009 3.746 -3.009C3.915 -3.009 4.174 -3.128 4.174 -3.557C4.174 -4.015 3.766 -4.403 3.108 -4.403C1.943 -4.403 1.534 -3.447 1.534 -2.929C1.534 -2.162 2.182 -2.022 2.461 -1.963Z' id='g0-115'/>
+<path d='M2.59 -3.985H3.437C3.606 -3.985 3.716 -3.985 3.716 -4.174C3.716 -4.294 3.626 -4.294 3.467 -4.294H2.67L3.039 -5.768C3.078 -5.908 3.078 -5.928 3.078 -5.978C3.078 -6.187 2.909 -6.237 2.809 -6.237C2.56 -6.237 2.461 -6.027 2.421 -5.878L2.032 -4.294H1.186C1.016 -4.294 0.907 -4.294 0.907 -4.105C0.907 -3.985 0.996 -3.985 1.156 -3.985H1.953L1.235 -1.126C1.225 -1.086 1.186 -0.927 1.186 -0.787C1.186 -0.289 1.514 0.11 2.032 0.11C3.039 0.11 3.547 -1.375 3.547 -1.425C3.547 -1.524 3.467 -1.524 3.397 -1.524C3.278 -1.524 3.278 -1.514 3.198 -1.335C3.019 -0.857 2.62 -0.11 2.052 -0.11C1.783 -0.11 1.783 -0.359 1.783 -0.518C1.783 -0.588 1.783 -0.747 1.853 -1.026L2.59 -3.985Z' id='g0-116'/>
+<path d='M5.111 -3.895C5.131 -3.955 5.141 -4.005 5.141 -4.035C5.141 -4.144 5.061 -4.294 4.862 -4.294C4.563 -4.294 4.493 -4.005 4.473 -3.925L3.746 -1.016C3.696 -0.837 3.696 -0.817 3.606 -0.687C3.417 -0.408 3.128 -0.11 2.69 -0.11C2.242 -0.11 2.152 -0.548 2.152 -0.877C2.152 -1.484 2.481 -2.381 2.73 -3.059C2.809 -3.268 2.859 -3.407 2.859 -3.597C2.859 -4.095 2.531 -4.403 2.102 -4.403C1.176 -4.403 0.827 -2.929 0.827 -2.869C0.827 -2.77 0.927 -2.77 0.976 -2.77C1.106 -2.77 1.116 -2.79 1.156 -2.929C1.235 -3.238 1.504 -4.184 2.082 -4.184C2.192 -4.184 2.301 -4.154 2.301 -3.895C2.301 -3.656 2.202 -3.387 2.062 -2.999C1.803 -2.301 1.544 -1.564 1.544 -1.046C1.544 -0.179 2.122 0.11 2.66 0.11C3.188 0.11 3.527 -0.189 3.766 -0.488C3.945 0.05 4.374 0.11 4.563 0.11C4.932 0.11 5.141 -0.139 5.29 -0.448C5.469 -0.827 5.589 -1.405 5.589 -1.425C5.589 -1.524 5.509 -1.524 5.44 -1.524C5.32 -1.524 5.31 -1.514 5.25 -1.295C5.111 -0.737 4.922 -0.11 4.583 -0.11C4.324 -0.11 4.324 -0.379 4.324 -0.518C4.324 -0.588 4.324 -0.747 4.394 -1.026L5.111 -3.895Z' id='g0-117'/>
+<path d='M4.583 -3.188C4.583 -3.985 4.533 -4.782 4.184 -5.519C3.726 -6.476 2.909 -6.635 2.491 -6.635C1.893 -6.635 1.166 -6.376 0.757 -5.45C0.438 -4.762 0.389 -3.985 0.389 -3.188C0.389 -2.441 0.428 -1.544 0.837 -0.787C1.265 0.02 1.993 0.219 2.481 0.219C3.019 0.219 3.776 0.01 4.214 -0.936C4.533 -1.624 4.583 -2.401 4.583 -3.188ZM2.481 0C2.092 0 1.504 -0.249 1.325 -1.205C1.215 -1.803 1.215 -2.72 1.215 -3.308C1.215 -3.945 1.215 -4.603 1.295 -5.141C1.484 -6.326 2.232 -6.416 2.481 -6.416C2.809 -6.416 3.467 -6.237 3.656 -5.25C3.756 -4.692 3.756 -3.935 3.756 -3.308C3.756 -2.56 3.756 -1.883 3.646 -1.245C3.497 -0.299 2.929 0 2.481 0Z' id='g5-48'/>
+<path d='M6.017 -5.559C6.077 -5.689 6.087 -5.709 6.087 -5.758C6.087 -5.868 5.998 -5.958 5.888 -5.958C5.788 -5.958 5.729 -5.888 5.659 -5.738L3.318 -0.468L0.976 -5.748C0.897 -5.938 0.817 -5.958 0.747 -5.958C0.638 -5.958 0.548 -5.868 0.548 -5.758C0.548 -5.738 0.548 -5.719 0.608 -5.599L3.088 0.01C3.168 0.179 3.228 0.219 3.318 0.219C3.447 0.219 3.477 0.149 3.537 0.01L6.017 -5.559Z' id='g1-95'/>
+<path d='M4.603 -3.377C4.653 -3.597 4.752 -3.965 4.752 -4.025C4.752 -4.204 4.613 -4.294 4.463 -4.294C4.344 -4.294 4.164 -4.214 4.095 -4.015C4.065 -3.945 3.597 -2.042 3.527 -1.783C3.457 -1.484 3.437 -1.305 3.437 -1.126C3.437 -1.016 3.437 -0.996 3.447 -0.946C3.218 -0.418 2.919 -0.11 2.531 -0.11C1.733 -0.11 1.733 -0.847 1.733 -1.016C1.733 -1.335 1.783 -1.724 2.252 -2.949C2.361 -3.248 2.421 -3.387 2.421 -3.587C2.421 -4.035 2.092 -4.403 1.604 -4.403C0.658 -4.403 0.289 -2.959 0.289 -2.869C0.289 -2.77 0.389 -2.77 0.408 -2.77C0.508 -2.77 0.518 -2.79 0.568 -2.949C0.837 -3.875 1.225 -4.184 1.574 -4.184C1.664 -4.184 1.823 -4.174 1.823 -3.856C1.823 -3.606 1.714 -3.328 1.644 -3.158C1.205 -1.983 1.086 -1.524 1.086 -1.146C1.086 -0.239 1.753 0.11 2.501 0.11C2.67 0.11 3.138 0.11 3.537 -0.588C3.796 0.05 4.483 0.11 4.782 0.11C5.529 0.11 5.968 -0.518 6.227 -1.116C6.565 -1.893 6.884 -3.228 6.884 -3.706C6.884 -4.254 6.615 -4.403 6.446 -4.403C6.197 -4.403 5.948 -4.144 5.948 -3.925C5.948 -3.796 6.007 -3.736 6.097 -3.656C6.207 -3.547 6.456 -3.288 6.456 -2.809C6.456 -2.471 6.167 -1.494 5.908 -0.986C5.649 -0.458 5.3 -0.11 4.812 -0.11C4.344 -0.11 4.075 -0.408 4.075 -0.976C4.075 -1.255 4.144 -1.564 4.184 -1.704L4.603 -3.377Z' id='g3-119'/>
+<path d='M3.786 -0.548V0.11L5.25 0V-0.309C4.553 -0.309 4.473 -0.379 4.473 -0.867V-6.914L3.039 -6.804V-6.496C3.736 -6.496 3.816 -6.426 3.816 -5.938V-3.786C3.527 -4.144 3.098 -4.403 2.56 -4.403C1.385 -4.403 0.339 -3.427 0.339 -2.142C0.339 -0.877 1.315 0.11 2.451 0.11C3.088 0.11 3.537 -0.229 3.786 -0.548ZM3.786 -3.218V-1.176C3.786 -0.996 3.786 -0.976 3.676 -0.807C3.377 -0.329 2.929 -0.11 2.501 -0.11C2.052 -0.11 1.694 -0.369 1.455 -0.747C1.196 -1.156 1.166 -1.724 1.166 -2.132C1.166 -2.501 1.186 -3.098 1.474 -3.547C1.684 -3.856 2.062 -4.184 2.6 -4.184C2.949 -4.184 3.367 -4.035 3.676 -3.587C3.786 -3.417 3.786 -3.397 3.786 -3.218Z' id='g5-100'/>
+<path d='M4.692 -2.132C4.692 -3.407 3.696 -4.463 2.491 -4.463C1.245 -4.463 0.279 -3.377 0.279 -2.132C0.279 -0.847 1.315 0.11 2.481 0.11C3.686 0.11 4.692 -0.867 4.692 -2.132ZM2.491 -0.139C2.062 -0.139 1.624 -0.349 1.355 -0.807C1.106 -1.245 1.106 -1.853 1.106 -2.212C1.106 -2.6 1.106 -3.138 1.345 -3.577C1.614 -4.035 2.082 -4.244 2.481 -4.244C2.919 -4.244 3.347 -4.025 3.606 -3.597S3.866 -2.59 3.866 -2.212C3.866 -1.853 3.866 -1.315 3.646 -0.877C3.427 -0.428 2.989 -0.139 2.491 -0.139Z' id='g5-111'/>
+<path d='M4.144 -3.318C4.234 -3.547 4.403 -3.975 5.061 -3.985V-4.294C4.832 -4.274 4.543 -4.264 4.314 -4.264C4.075 -4.264 3.616 -4.284 3.447 -4.294V-3.985C3.816 -3.975 3.925 -3.746 3.925 -3.557C3.925 -3.467 3.905 -3.427 3.866 -3.318L2.849 -0.777L1.733 -3.557C1.674 -3.686 1.674 -3.706 1.674 -3.726C1.674 -3.985 2.062 -3.985 2.242 -3.985V-4.294C1.943 -4.284 1.385 -4.264 1.156 -4.264C0.887 -4.264 0.488 -4.274 0.189 -4.294V-3.985C0.817 -3.985 0.857 -3.925 0.986 -3.616L2.421 -0.08C2.481 0.06 2.501 0.11 2.63 0.11S2.8 0.02 2.839 -0.08L4.144 -3.318Z' id='g5-118'/>
+<path d='M5.46 -2.291C5.629 -2.291 5.808 -2.291 5.808 -2.491S5.629 -2.69 5.46 -2.69H1.235C1.355 -4.025 2.501 -4.981 3.905 -4.981H5.46C5.629 -4.981 5.808 -4.981 5.808 -5.181S5.629 -5.38 5.46 -5.38H3.885C2.182 -5.38 0.827 -4.085 0.827 -2.491S2.182 0.399 3.885 0.399H5.46C5.629 0.399 5.808 0.399 5.808 0.199S5.629 0 5.46 0H3.905C2.501 0 1.355 -0.956 1.235 -2.291H5.46Z' id='g1-50'/>
+<path d='M2.819 -6.147C2.819 -6.545 3.078 -7.183 4.164 -7.253C4.214 -7.263 4.254 -7.303 4.254 -7.362C4.254 -7.472 4.174 -7.472 4.065 -7.472C3.068 -7.472 2.162 -6.964 2.152 -6.227V-3.955C2.152 -3.567 2.152 -3.248 1.753 -2.919C1.405 -2.63 1.026 -2.61 0.807 -2.6C0.757 -2.59 0.717 -2.55 0.717 -2.491C0.717 -2.391 0.777 -2.391 0.877 -2.381C1.534 -2.341 2.012 -1.983 2.122 -1.494C2.152 -1.385 2.152 -1.365 2.152 -1.006V0.966C2.152 1.385 2.152 1.704 2.63 2.082C3.019 2.381 3.676 2.491 4.065 2.491C4.174 2.491 4.254 2.491 4.254 2.381C4.254 2.281 4.194 2.281 4.095 2.271C3.467 2.232 2.979 1.913 2.849 1.405C2.819 1.315 2.819 1.295 2.819 0.936V-1.156C2.819 -1.614 2.74 -1.783 2.421 -2.102C2.212 -2.311 1.923 -2.411 1.644 -2.491C2.461 -2.72 2.819 -3.178 2.819 -3.756V-6.147Z' id='g1-102'/>
+<path d='M2.152 1.166C2.152 1.564 1.893 2.202 0.807 2.271C0.757 2.281 0.717 2.321 0.717 2.381C0.717 2.491 0.827 2.491 0.917 2.491C1.883 2.491 2.809 2.002 2.819 1.245V-1.026C2.819 -1.415 2.819 -1.733 3.218 -2.062C3.567 -2.351 3.945 -2.371 4.164 -2.381C4.214 -2.391 4.254 -2.431 4.254 -2.491C4.254 -2.59 4.194 -2.59 4.095 -2.6C3.437 -2.64 2.959 -2.999 2.849 -3.487C2.819 -3.597 2.819 -3.616 2.819 -3.975V-5.948C2.819 -6.366 2.819 -6.685 2.341 -7.064C1.943 -7.372 1.255 -7.472 0.917 -7.472C0.827 -7.472 0.717 -7.472 0.717 -7.362C0.717 -7.263 0.777 -7.263 0.877 -7.253C1.504 -7.213 1.993 -6.894 2.122 -6.386C2.152 -6.296 2.152 -6.276 2.152 -5.918V-3.826C2.152 -3.367 2.232 -3.198 2.55 -2.879C2.76 -2.67 3.049 -2.57 3.328 -2.491C2.511 -2.262 2.152 -1.803 2.152 -1.225V1.166Z' id='g1-103'/>
+<path d='M1.584 -7.113C1.584 -7.293 1.584 -7.472 1.385 -7.472S1.186 -7.293 1.186 -7.113V2.132C1.186 2.311 1.186 2.491 1.385 2.491S1.584 2.311 1.584 2.132V-7.113Z' id='g1-106'/>
+<path d='M1.315 -3.268V-3.507C1.315 -6.027 2.55 -6.386 3.059 -6.386C3.298 -6.386 3.716 -6.326 3.935 -5.988C3.786 -5.988 3.387 -5.988 3.387 -5.539C3.387 -5.23 3.626 -5.081 3.846 -5.081C4.005 -5.081 4.304 -5.171 4.304 -5.559C4.304 -6.157 3.866 -6.635 3.039 -6.635C1.763 -6.635 0.418 -5.35 0.418 -3.148C0.418 -0.488 1.574 0.219 2.501 0.219C3.606 0.219 4.553 -0.717 4.553 -2.032C4.553 -3.298 3.666 -4.254 2.56 -4.254C1.883 -4.254 1.514 -3.746 1.315 -3.268ZM2.501 -0.06C1.873 -0.06 1.574 -0.658 1.514 -0.807C1.335 -1.275 1.335 -2.072 1.335 -2.252C1.335 -3.029 1.654 -4.025 2.55 -4.025C2.71 -4.025 3.168 -4.025 3.477 -3.407C3.656 -3.039 3.656 -2.531 3.656 -2.042C3.656 -1.564 3.656 -1.066 3.487 -0.707C3.188 -0.11 2.73 -0.06 2.501 -0.06Z' id='g5-54'/>
+<path d='M2.929 -1.644V-0.777C2.929 -0.418 2.909 -0.309 2.172 -0.309H1.963V0C2.371 -0.03 2.889 -0.03 3.308 -0.03S4.254 -0.03 4.663 0V-0.309H4.453C3.716 -0.309 3.696 -0.418 3.696 -0.777V-1.644H4.692V-1.953H3.696V-6.486C3.696 -6.685 3.696 -6.745 3.537 -6.745C3.447 -6.745 3.417 -6.745 3.337 -6.625L0.279 -1.953V-1.644H2.929ZM2.989 -1.953H0.558L2.989 -5.669V-1.953Z' id='g5-52'/>
+<path d='M1.913 -0.528C1.913 -0.817 1.674 -1.056 1.385 -1.056S0.857 -0.817 0.857 -0.528S1.096 0 1.385 0S1.913 -0.239 1.913 -0.528Z' id='g5-46'/>
+<path d='M4.473 -2.002C4.473 -3.188 3.656 -4.184 2.58 -4.184C2.102 -4.184 1.674 -4.025 1.315 -3.676V-5.619C1.514 -5.559 1.843 -5.489 2.162 -5.489C3.387 -5.489 4.085 -6.396 4.085 -6.526C4.085 -6.585 4.055 -6.635 3.985 -6.635C3.975 -6.635 3.955 -6.635 3.905 -6.605C3.706 -6.516 3.218 -6.316 2.55 -6.316C2.152 -6.316 1.694 -6.386 1.225 -6.595C1.146 -6.625 1.126 -6.625 1.106 -6.625C1.006 -6.625 1.006 -6.545 1.006 -6.386V-3.437C1.006 -3.258 1.006 -3.178 1.146 -3.178C1.215 -3.178 1.235 -3.208 1.275 -3.268C1.385 -3.427 1.753 -3.965 2.56 -3.965C3.078 -3.965 3.328 -3.507 3.407 -3.328C3.567 -2.959 3.587 -2.57 3.587 -2.072C3.587 -1.724 3.587 -1.126 3.347 -0.707C3.108 -0.319 2.74 -0.06 2.281 -0.06C1.554 -0.06 0.986 -0.588 0.817 -1.176C0.847 -1.166 0.877 -1.156 0.986 -1.156C1.315 -1.156 1.484 -1.405 1.484 -1.644S1.315 -2.132 0.986 -2.132C0.847 -2.132 0.498 -2.062 0.498 -1.604C0.498 -0.747 1.186 0.219 2.301 0.219C3.457 0.219 4.473 -0.737 4.473 -2.002Z' id='g5-53'/>
+<path d='M4.742 -6.067C4.832 -6.187 4.832 -6.207 4.832 -6.416H2.411C1.196 -6.416 1.176 -6.545 1.136 -6.735H0.887L0.558 -4.682H0.807C0.837 -4.842 0.927 -5.469 1.056 -5.589C1.126 -5.649 1.903 -5.649 2.032 -5.649H4.095C3.985 -5.489 3.198 -4.403 2.979 -4.075C2.082 -2.73 1.753 -1.345 1.753 -0.329C1.753 -0.229 1.753 0.219 2.212 0.219S2.67 -0.229 2.67 -0.329V-0.837C2.67 -1.385 2.7 -1.933 2.78 -2.471C2.819 -2.7 2.959 -3.557 3.397 -4.174L4.742 -6.067Z' id='g5-55'/>
+<path d='M0.774 -5.021C0.774 -5.161 0.774 -5.23 0.656 -5.23S0.537 -5.161 0.537 -5.014V-0.07C0.537 0.07 0.537 0.139 0.656 0.139C0.76 0.139 1.22 -0.181 1.36 -0.279C1.897 -0.656 2.678 -1.297 2.678 -2.239C2.678 -2.838 2.148 -3.208 1.632 -3.208C1.59 -3.208 1.102 -3.208 0.774 -2.943V-5.021ZM0.774 -2.357C0.774 -2.518 0.774 -2.65 0.983 -2.769C1.081 -2.817 1.269 -2.887 1.534 -2.887C2.029 -2.887 2.057 -2.35 2.057 -2.239C2.057 -1.381 1.381 -0.663 0.774 -0.195V-2.357Z' id='g4-91'/>
+<path d='M2.183 -4.631C2.19 -4.645 2.211 -4.735 2.211 -4.742C2.211 -4.777 2.183 -4.84 2.099 -4.84C1.96 -4.84 1.381 -4.784 1.206 -4.77C1.151 -4.763 1.053 -4.756 1.053 -4.61C1.053 -4.512 1.151 -4.512 1.234 -4.512C1.569 -4.512 1.569 -4.463 1.569 -4.407C1.569 -4.359 1.555 -4.317 1.541 -4.254L0.558 -0.307C0.523 -0.181 0.523 -0.167 0.523 -0.153C0.523 -0.049 0.607 0.07 0.76 0.07C0.948 0.07 1.039 -0.07 1.081 -0.223C1.095 -0.251 1.395 -1.478 1.423 -1.576C1.918 -1.527 2.315 -1.367 2.315 -1.004C2.315 -0.969 2.315 -0.934 2.301 -0.865C2.273 -0.76 2.273 -0.725 2.273 -0.649C2.273 -0.153 2.678 0.07 3.013 0.07C3.689 0.07 3.898 -0.99 3.898 -0.997C3.898 -1.088 3.808 -1.088 3.787 -1.088C3.689 -1.088 3.682 -1.053 3.647 -0.921C3.564 -0.621 3.375 -0.126 3.034 -0.126C2.845 -0.126 2.79 -0.3 2.79 -0.488C2.79 -0.607 2.79 -0.621 2.831 -0.802C2.838 -0.823 2.866 -0.941 2.866 -1.018C2.866 -1.639 2.029 -1.736 1.736 -1.757C1.939 -1.883 2.197 -2.113 2.315 -2.218C2.671 -2.552 3.02 -2.88 3.41 -2.88C3.494 -2.88 3.585 -2.859 3.64 -2.79C3.34 -2.741 3.278 -2.504 3.278 -2.399C3.278 -2.246 3.396 -2.141 3.557 -2.141C3.745 -2.141 3.954 -2.294 3.954 -2.587C3.954 -2.817 3.787 -3.075 3.417 -3.075C3.02 -3.075 2.657 -2.79 2.301 -2.462C2.008 -2.183 1.778 -1.967 1.492 -1.848L2.183 -4.631Z' id='g4-107'/>
+<path d='M2.127 0.146V0.886C2.127 1.011 2.127 1.095 2.225 1.095C2.364 1.095 2.364 0.997 2.364 0.872V0.07L2.587 0C2.678 -0.028 2.678 -0.063 2.678 -0.209V-0.411C2.678 -0.53 2.678 -0.621 2.58 -0.621C2.532 -0.621 2.406 -0.572 2.364 -0.558V-2.936L2.587 -3.006C2.678 -3.034 2.678 -3.068 2.678 -3.215V-3.417C2.678 -3.536 2.678 -3.626 2.58 -3.626C2.532 -3.626 2.406 -3.578 2.364 -3.564V-4.777C2.364 -4.903 2.364 -4.993 2.267 -4.993C2.127 -4.993 2.127 -4.896 2.127 -4.77V-3.487L1.088 -3.152V-4.373C1.088 -4.498 1.088 -4.582 0.99 -4.582C0.851 -4.582 0.851 -4.484 0.851 -4.359V-3.075C0.746 -3.048 0.711 -3.034 0.628 -3.006C0.537 -2.971 0.537 -2.95 0.537 -2.79V-2.594C0.537 -2.476 0.537 -2.385 0.635 -2.385C0.683 -2.385 0.809 -2.434 0.851 -2.448V-0.07C0.746 -0.042 0.711 -0.028 0.628 0C0.537 0.035 0.537 0.056 0.537 0.216V0.411C0.537 0.53 0.537 0.621 0.635 0.621C0.683 0.621 0.809 0.572 0.851 0.558V1.29C0.851 1.416 0.851 1.506 0.948 1.506C1.088 1.506 1.088 1.409 1.088 1.283V0.481L2.127 0.146ZM2.127 -2.859V-0.481L1.088 -0.146V-2.525L2.127 -2.859Z' id='g4-93'/>
+<path d='M2.859 -6.804C2.859 -6.814 2.859 -6.914 2.73 -6.914C2.501 -6.914 1.773 -6.834 1.514 -6.814C1.435 -6.804 1.325 -6.795 1.325 -6.615C1.325 -6.496 1.415 -6.496 1.564 -6.496C2.042 -6.496 2.062 -6.426 2.062 -6.326L2.032 -6.127L0.588 -0.389C0.548 -0.249 0.548 -0.229 0.548 -0.169C0.548 0.06 0.747 0.11 0.837 0.11C0.966 0.11 1.116 0.02 1.176 -0.1C1.225 -0.189 1.674 -2.032 1.733 -2.281C2.072 -2.252 2.889 -2.092 2.889 -1.435C2.889 -1.365 2.889 -1.325 2.859 -1.225C2.839 -1.106 2.819 -0.986 2.819 -0.877C2.819 -0.289 3.218 0.11 3.736 0.11C4.035 0.11 4.304 -0.05 4.523 -0.418C4.772 -0.857 4.882 -1.405 4.882 -1.425C4.882 -1.524 4.792 -1.524 4.762 -1.524C4.663 -1.524 4.653 -1.484 4.623 -1.345C4.423 -0.618 4.194 -0.11 3.756 -0.11C3.567 -0.11 3.437 -0.219 3.437 -0.578C3.437 -0.747 3.477 -0.976 3.517 -1.136C3.557 -1.305 3.557 -1.345 3.557 -1.445C3.557 -2.092 2.929 -2.381 2.082 -2.491C2.391 -2.67 2.71 -2.989 2.939 -3.228C3.417 -3.756 3.875 -4.184 4.364 -4.184C4.423 -4.184 4.433 -4.184 4.453 -4.174C4.573 -4.154 4.583 -4.154 4.663 -4.095C4.682 -4.085 4.682 -4.075 4.702 -4.055C4.224 -4.025 4.134 -3.636 4.134 -3.517C4.134 -3.357 4.244 -3.168 4.513 -3.168C4.772 -3.168 5.061 -3.387 5.061 -3.776C5.061 -4.075 4.832 -4.403 4.384 -4.403C4.105 -4.403 3.646 -4.324 2.929 -3.527C2.59 -3.148 2.202 -2.75 1.823 -2.6L2.859 -6.804Z' id='g3-107'/>
+<path d='M4.144 14.834C4.144 14.366 4.144 13.629 3.517 12.822C3.128 12.324 2.56 11.866 1.873 11.557C3.816 10.62 4.144 9.186 4.144 8.618V2.879C4.144 2.262 4.144 0.986 6.077 0.03C6.157 -0.01 6.157 -0.03 6.157 -0.179C6.157 -0.389 6.157 -0.399 5.938 -0.399C5.798 -0.399 5.778 -0.399 5.519 -0.279C4.533 0.209 3.557 0.996 3.347 2.212C3.318 2.411 3.318 2.501 3.318 3.168V7.771C3.318 8.08 3.318 8.598 3.308 8.707C3.218 9.753 2.61 10.66 1.474 11.288C1.315 11.377 1.305 11.387 1.305 11.547C1.305 11.716 1.315 11.726 1.455 11.806C2.122 12.174 3.078 12.892 3.278 14.137C3.318 14.366 3.318 14.386 3.318 14.496V20.413C3.318 21.858 4.314 22.804 5.549 23.392C5.768 23.502 5.788 23.502 5.938 23.502C6.147 23.502 6.157 23.502 6.157 23.283C6.157 23.123 6.147 23.113 6.067 23.064C5.659 22.864 4.374 22.217 4.164 20.782C4.144 20.643 4.144 20.533 4.144 19.935V14.834Z' id='g7-26'/>
+<path d='M5.189 -1.576C5.3 -1.576 5.467 -1.576 5.467 -1.743C5.467 -1.918 5.307 -1.918 5.189 -1.918H1.032C0.921 -1.918 0.753 -1.918 0.753 -1.75C0.753 -1.576 0.914 -1.576 1.032 -1.576H5.189Z' id='g2-0'/>
+<path d='M2.336 -4.435C2.336 -4.624 2.322 -4.631 2.127 -4.631C1.681 -4.191 1.046 -4.184 0.76 -4.184V-3.933C0.928 -3.933 1.388 -3.933 1.771 -4.129V-0.572C1.771 -0.342 1.771 -0.251 1.074 -0.251H0.809V0C0.934 -0.007 1.792 -0.028 2.05 -0.028C2.267 -0.028 3.145 -0.007 3.299 0V-0.251H3.034C2.336 -0.251 2.336 -0.342 2.336 -0.572V-4.435Z' id='g6-49'/>
+<path d='M6.725 -2.271C6.834 -2.321 6.914 -2.371 6.914 -2.491S6.834 -2.66 6.725 -2.71L1.205 -5.31C1.076 -5.38 1.056 -5.38 1.026 -5.38C0.917 -5.38 0.827 -5.29 0.827 -5.181C0.827 -5.091 0.877 -5.031 1.016 -4.961L6.247 -2.491L1.016 -0.02C0.877 0.05 0.827 0.11 0.827 0.199C0.827 0.309 0.917 0.399 1.026 0.399C1.056 0.399 1.076 0.399 1.205 0.329L6.725 -2.271Z' id='g3-62'/>
+<path d='M4.075 -6.296C4.075 -6.476 4.075 -6.655 3.875 -6.655S3.676 -6.446 3.676 -6.296V-0.399H0.907C0.757 -0.399 0.548 -0.399 0.548 -0.199S0.757 0 0.907 0H6.854C7.024 0 7.203 0 7.203 -0.199S7.024 -0.399 6.854 -0.399H4.075V-6.296Z' id='g1-63'/>
+<path d='M4.075 -6.257H6.854C7.024 -6.257 7.203 -6.257 7.203 -6.456S7.024 -6.655 6.854 -6.655H0.907C0.757 -6.655 0.548 -6.655 0.548 -6.456S0.757 -6.257 0.907 -6.257H3.676V-0.359C3.676 -0.209 3.676 0 3.875 0S4.075 -0.179 4.075 -0.359V-6.257Z' id='g1-62'/>
+<path d='M3.599 -2.225C3.599 -2.992 3.508 -3.543 3.187 -4.031C2.971 -4.352 2.538 -4.631 1.981 -4.631C0.363 -4.631 0.363 -2.727 0.363 -2.225S0.363 0.139 1.981 0.139S3.599 -1.723 3.599 -2.225ZM1.981 -0.056C1.66 -0.056 1.234 -0.244 1.095 -0.816C0.997 -1.227 0.997 -1.799 0.997 -2.315C0.997 -2.824 0.997 -3.354 1.102 -3.738C1.248 -4.289 1.695 -4.435 1.981 -4.435C2.357 -4.435 2.72 -4.205 2.845 -3.801C2.957 -3.424 2.964 -2.922 2.964 -2.315C2.964 -1.799 2.964 -1.283 2.873 -0.844C2.734 -0.209 2.26 -0.056 1.981 -0.056Z' id='g6-48'/>
+<path d='M3.522 -1.269H3.285C3.264 -1.116 3.194 -0.704 3.103 -0.635C3.048 -0.593 2.511 -0.593 2.413 -0.593H1.13C1.862 -1.241 2.106 -1.437 2.525 -1.764C3.041 -2.176 3.522 -2.608 3.522 -3.271C3.522 -4.115 2.783 -4.631 1.89 -4.631C1.025 -4.631 0.439 -4.024 0.439 -3.382C0.439 -3.027 0.739 -2.992 0.809 -2.992C0.976 -2.992 1.179 -3.11 1.179 -3.361C1.179 -3.487 1.13 -3.731 0.767 -3.731C0.983 -4.226 1.458 -4.38 1.785 -4.38C2.483 -4.38 2.845 -3.836 2.845 -3.271C2.845 -2.664 2.413 -2.183 2.19 -1.932L0.509 -0.272C0.439 -0.209 0.439 -0.195 0.439 0H3.313L3.522 -1.269Z' id='g6-50'/>
+<path d='M0.159 -0.986C0.139 -0.956 0.11 -0.927 0.11 -0.887C0.11 -0.837 0.169 -0.757 0.229 -0.757C0.279 -0.757 0.309 -0.787 0.588 -1.066C0.667 -1.136 0.867 -1.325 0.946 -1.405C1.046 -0.618 1.335 0.12 2.062 0.12C2.461 0.12 2.809 -0.11 3.019 -0.259C3.158 -0.359 3.626 -0.747 3.626 -0.847C3.626 -0.877 3.597 -0.966 3.507 -0.966C3.477 -0.966 3.467 -0.956 3.377 -0.877C2.74 -0.249 2.371 -0.1 2.082 -0.1C1.634 -0.1 1.474 -0.618 1.474 -1.395C1.474 -1.455 1.494 -1.913 1.524 -1.963C1.544 -1.993 1.544 -2.012 1.743 -2.212C2.55 -3.019 3.965 -4.702 3.965 -6.247C3.965 -6.416 3.965 -7.024 3.377 -7.024C2.55 -7.024 1.813 -5.38 1.714 -5.151C1.235 -4.065 0.927 -2.899 0.927 -1.714L0.159 -0.986ZM1.574 -2.381C1.594 -2.491 2.022 -4.702 2.59 -5.868C2.859 -6.406 3.068 -6.804 3.387 -6.804C3.726 -6.804 3.726 -6.446 3.726 -6.286C3.726 -4.623 2.052 -2.879 1.574 -2.381Z' id='g3-96'/>
+<path d='M0.83 -0.955C0.9 -0.502 1.13 0.084 1.723 0.084C1.946 0.084 2.197 -0.007 2.448 -0.167C2.566 -0.237 2.929 -0.495 2.929 -0.579C2.929 -0.621 2.88 -0.711 2.817 -0.711C2.783 -0.711 2.769 -0.711 2.664 -0.607C2.364 -0.342 2.043 -0.112 1.73 -0.112C1.255 -0.112 1.255 -0.928 1.255 -1.06C1.255 -1.151 1.255 -1.165 1.269 -1.297C1.276 -1.367 1.276 -1.381 1.36 -1.458C1.743 -1.82 3.062 -3.089 3.062 -4.303C3.062 -4.407 3.062 -4.91 2.601 -4.91C2.022 -4.91 1.555 -4.01 1.444 -3.801C1.137 -3.208 0.976 -2.636 0.9 -2.287C0.795 -1.82 0.795 -1.576 0.795 -1.234C0.746 -1.186 0.349 -0.83 0.279 -0.774C0.167 -0.676 0.16 -0.669 0.16 -0.628C0.16 -0.579 0.223 -0.502 0.279 -0.502C0.328 -0.502 0.516 -0.676 0.565 -0.725L0.83 -0.955ZM1.332 -1.75C1.458 -2.462 2.008 -4.714 2.594 -4.714C2.755 -4.714 2.838 -4.603 2.838 -4.352C2.838 -4.268 2.838 -3.647 2.239 -2.79C1.883 -2.287 1.499 -1.911 1.332 -1.75Z' id='g4-96'/>
+<path d='M2.211 -4.561C2.211 -4.672 2.211 -4.84 2.043 -4.84C1.869 -4.84 1.869 -4.679 1.869 -4.561V0.335C1.262 -0.516 0.37 -0.656 0.335 -0.656C0.23 -0.656 0.23 -0.558 0.23 -0.488C0.23 -0.411 0.23 -0.342 0.321 -0.321C0.544 -0.258 0.969 -0.146 1.36 0.23C1.764 0.628 1.876 1.032 1.932 1.206C1.953 1.297 1.967 1.353 2.043 1.353C2.113 1.353 2.12 1.311 2.162 1.172C2.343 0.551 2.817 -0.07 3.689 -0.3C3.829 -0.335 3.85 -0.342 3.85 -0.488C3.85 -0.558 3.85 -0.656 3.745 -0.656C3.71 -0.656 2.817 -0.516 2.211 0.335V-4.561Z' id='g2-35'/>
+<path d='M2.211 -3.822C2.817 -2.971 3.71 -2.831 3.745 -2.831C3.85 -2.831 3.85 -2.929 3.85 -2.999C3.85 -3.075 3.85 -3.145 3.759 -3.166C3.473 -3.243 2.497 -3.494 2.155 -4.693C2.12 -4.798 2.113 -4.84 2.043 -4.84C1.988 -4.84 1.96 -4.798 1.946 -4.756C1.862 -4.463 1.59 -3.501 0.391 -3.187C0.251 -3.152 0.23 -3.145 0.23 -2.999C0.23 -2.929 0.23 -2.831 0.335 -2.831C0.37 -2.831 1.262 -2.971 1.869 -3.822V1.074C1.869 1.186 1.869 1.353 2.036 1.353C2.211 1.353 2.211 1.193 2.211 1.074V-3.822Z' id='g2-34'/>
+<path d='M4.981 -6.555C4.981 -6.884 4.951 -6.914 4.633 -6.914H0.897C0.727 -6.914 0.548 -6.914 0.548 -6.715S0.727 -6.516 0.897 -6.516H4.583V-3.656H1.036C0.867 -3.656 0.687 -3.656 0.687 -3.457S0.867 -3.258 1.036 -3.258H4.583V-0.399H0.897C0.727 -0.399 0.548 -0.399 0.548 -0.199S0.727 0 0.897 0H4.633C4.951 0 4.981 -0.03 4.981 -0.359V-6.555Z' id='g1-57'/>
+<path d='M4.364 -0.06C5.908 -0.648 7.372 -2.421 7.372 -4.344C7.372 -5.948 6.316 -7.024 4.832 -7.024C2.68 -7.024 0.488 -4.762 0.488 -2.441C0.488 -0.787 1.604 0.219 3.039 0.219C3.288 0.219 3.626 0.179 4.015 0.07C3.975 0.687 3.975 0.707 3.975 0.837C3.975 1.156 3.975 1.933 4.802 1.933C5.988 1.933 6.466 0.11 6.466 0C6.466 -0.07 6.396 -0.1 6.356 -0.1C6.276 -0.1 6.257 -0.05 6.237 0.01C5.998 0.717 5.42 0.966 5.071 0.966C4.613 0.966 4.463 0.697 4.364 -0.06ZM2.481 -0.139C1.704 -0.448 1.365 -1.225 1.365 -2.122C1.365 -2.809 1.624 -4.224 2.381 -5.3C3.108 -6.316 4.045 -6.775 4.772 -6.775C5.768 -6.775 6.496 -5.998 6.496 -4.663C6.496 -3.666 5.988 -1.335 4.314 -0.399C4.264 -0.747 4.164 -1.474 3.437 -1.474C2.909 -1.474 2.421 -0.976 2.421 -0.458C2.421 -0.259 2.481 -0.149 2.481 -0.139ZM3.098 -0.03C2.959 -0.03 2.64 -0.03 2.64 -0.458C2.64 -0.857 3.019 -1.255 3.437 -1.255S4.045 -1.016 4.045 -0.408C4.045 -0.259 4.035 -0.249 3.935 -0.209C3.676 -0.1 3.377 -0.03 3.098 -0.03Z' id='g3-81'/>
+<path d='M3.736 -6.117C3.796 -6.356 3.826 -6.456 4.015 -6.486C4.105 -6.496 4.423 -6.496 4.623 -6.496C5.33 -6.496 6.436 -6.496 6.436 -5.509C6.436 -5.171 6.276 -4.483 5.888 -4.095C5.629 -3.836 5.101 -3.517 4.204 -3.517H3.088L3.736 -6.117ZM5.171 -3.387C6.177 -3.606 7.362 -4.304 7.362 -5.31C7.362 -6.167 6.466 -6.804 5.161 -6.804H2.321C2.122 -6.804 2.032 -6.804 2.032 -6.605C2.032 -6.496 2.122 -6.496 2.311 -6.496C2.331 -6.496 2.521 -6.496 2.69 -6.476C2.869 -6.456 2.959 -6.446 2.959 -6.316C2.959 -6.276 2.949 -6.247 2.919 -6.127L1.584 -0.777C1.484 -0.389 1.465 -0.309 0.677 -0.309C0.498 -0.309 0.408 -0.309 0.408 -0.11C0.408 0 0.528 0 0.548 0C0.827 0 1.524 -0.03 1.803 -0.03S2.79 0 3.068 0C3.148 0 3.268 0 3.268 -0.199C3.268 -0.309 3.178 -0.309 2.989 -0.309C2.62 -0.309 2.341 -0.309 2.341 -0.488C2.341 -0.548 2.361 -0.598 2.371 -0.658L3.029 -3.298H4.214C5.121 -3.298 5.3 -2.74 5.3 -2.391C5.3 -2.242 5.22 -1.933 5.161 -1.704C5.091 -1.425 5.001 -1.056 5.001 -0.857C5.001 0.219 6.197 0.219 6.326 0.219C7.173 0.219 7.522 -0.787 7.522 -0.927C7.522 -1.046 7.412 -1.046 7.402 -1.046C7.313 -1.046 7.293 -0.976 7.273 -0.907C7.024 -0.169 6.595 0 6.366 0C6.037 0 5.968 -0.219 5.968 -0.608C5.968 -0.917 6.027 -1.425 6.067 -1.743C6.087 -1.883 6.107 -2.072 6.107 -2.212C6.107 -2.979 5.44 -3.288 5.171 -3.387Z' id='g3-82'/>
+<path d='M4.842 -3.796C4.882 -3.935 4.882 -3.955 4.882 -4.025C4.882 -4.204 4.742 -4.294 4.593 -4.294C4.493 -4.294 4.334 -4.234 4.244 -4.085C4.224 -4.035 4.144 -3.726 4.105 -3.547C4.035 -3.288 3.965 -3.019 3.905 -2.75L3.457 -0.956C3.417 -0.807 2.989 -0.11 2.331 -0.11C1.823 -0.11 1.714 -0.548 1.714 -0.917C1.714 -1.375 1.883 -1.993 2.222 -2.869C2.381 -3.278 2.421 -3.387 2.421 -3.587C2.421 -4.035 2.102 -4.403 1.604 -4.403C0.658 -4.403 0.289 -2.959 0.289 -2.869C0.289 -2.77 0.389 -2.77 0.408 -2.77C0.508 -2.77 0.518 -2.79 0.568 -2.949C0.837 -3.885 1.235 -4.184 1.574 -4.184C1.654 -4.184 1.823 -4.184 1.823 -3.866C1.823 -3.616 1.724 -3.357 1.654 -3.168C1.255 -2.112 1.076 -1.544 1.076 -1.076C1.076 -0.189 1.704 0.11 2.291 0.11C2.68 0.11 3.019 -0.06 3.298 -0.339C3.168 0.179 3.049 0.667 2.65 1.196C2.391 1.534 2.012 1.823 1.554 1.823C1.415 1.823 0.966 1.793 0.797 1.405C0.956 1.405 1.086 1.405 1.225 1.285C1.325 1.196 1.425 1.066 1.425 0.877C1.425 0.568 1.156 0.528 1.056 0.528C0.827 0.528 0.498 0.687 0.498 1.176C0.498 1.674 0.936 2.042 1.554 2.042C2.58 2.042 3.606 1.136 3.885 0.01L4.842 -3.796Z' id='g3-121'/>
+<path d='M1.913 -0.528C1.913 -0.817 1.674 -1.056 1.385 -1.056S0.857 -0.817 0.857 -0.528S1.096 0 1.385 0S1.913 -0.239 1.913 -0.528Z' id='g3-58'/>
+</defs>
+</svg>
+
+</body>
+
+</html>
diff --git a/Docs/DafnyRef/out/DafnyRef.pdf b/Docs/DafnyRef/out/DafnyRef.pdf
new file mode 100644
index 00000000..b4e94885
--- /dev/null
+++ b/Docs/DafnyRef/out/DafnyRef.pdf
Binary files differ
diff --git a/Docs/DafnyRef/paper-full.bib b/Docs/DafnyRef/paper-full.bib
new file mode 100644
index 00000000..c382df3b
--- /dev/null
+++ b/Docs/DafnyRef/paper-full.bib
@@ -0,0 +1,577 @@
+@string{lncs = "Lecture Notes in Computer Science"}
+@string{lnai = "Lecture Notes in Artificial Intelligence"}
+
+@InProceedings{Dafny:LPAR16,
+ author = {K. Rustan M. Leino},
+ title = {Dafny: An Automatic Program Verifier for Functional Correctness},
+ booktitle = {LPAR-16},
+ year = {2010},
+ volume = {6355},
+ series = lncs,
+ publisher = {Springer},
+ month = apr,
+ editor = {Edmund M. Clarke and Andrei Voronkov},
+ pages = {348-370},
+}
+
+@InCollection{LeinoMoskal:UsableProgramVerification,
+ author = {K. Rustan M. Leino and Micha{\l} Moskal},
+ title = {Usable Auto-Active Verification},
+ booktitle = {UV10 (Usable Verification) workshop},
+ year = {2010},
+ editor = {Tom Ball and Lenore Zuck and N. Shankar},
+ month = nov,
+ publisher = {\url{http://fm.csl.sri.com/UV10/}},
+}
+
+@InProceedings{Leino:VMCAI2012,
+ author = {K. Rustan M. Leino},
+ title = {Automating Induction with an {SMT} Solver},
+ booktitle = {Verification, Model Checking, and Abstract Interpretation --- 13th International Conference, VMCAI 2012},
+ pages = {315-331},
+ year = {2012},
+ editor = {Viktor Kuncak and Andrey Rybalchenko},
+ volume = {7148},
+ series = lncs,
+ month = jan,
+ publisher = {Springer},
+}
+
+@InProceedings{LeinoMonahan:Comprehensions,
+ author = {K. Rustan M. Leino and Rosemary Monahan},
+ title = {Reasoning about Comprehensions with First-Order {SMT} Solvers},
+ booktitle = {Proceedings of the 2009 ACM Symposium on Applied Computing (SAC)},
+ editor = {Sung Y. Shin and Sascha Ossowski},
+ publisher = {ACM},
+ month = mar,
+ year = 2009,
+ pages = {615-622},
+}
+
+@TechReport{VeriFast:TR,
+ author = {Bart Jacobs and Frank Piessens},
+ title = {The {VeriFast} program verifier},
+ institution = {Department of Computer Science, Katholieke Universiteit Leuven},
+ year = {2008},
+ number = {CW-520},
+ month = aug,
+}
+
+@InProceedings{LGLM:BVD,
+ author = {Le Goues, Claire and K. Rustan M. Leino and Micha{\l} Moskal},
+ title = {The {B}oogie {V}erification {D}ebugger (Tool Paper)},
+ booktitle = {Software Engineering and Formal Methods --- 9th International Conference, SEFM 2011},
+ pages = {407-414},
+ year = {2011},
+ editor = {Gilles Barthe and Alberto Pardo and Gerardo Schneider},
+ volume = {7041},
+ series = lncs,
+ month = nov,
+ publisher = {Springer},
+}
+
+@inproceedings{Paulson:coinduction2000,
+ author = {Lawrence C. Paulson},
+ title = {A fixedpoint approach to (co)inductive and (co)datatype
+ definitions},
+ booktitle = {Proof, Language, and Interaction},
+ year = {2000},
+ pages = {187-212},
+ crossref = {DBLP:conf/birthday/1999milner},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+@proceedings{DBLP:conf/birthday/1999milner,
+ editor = {Gordon D. Plotkin and
+ Colin Stirling and
+ Mads Tofte},
+ title = {Proof, Language, and Interaction, Essays in Honour of Robin
+ Milner},
+ booktitle = {Proof, Language, and Interaction},
+ publisher = {The MIT Press},
+ year = {2000},
+ isbn = {978-0-262-16188-6},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+
+@inproceedings{Paulson:coinduction1994,
+ author = {Lawrence C. Paulson},
+ title = {A Fixedpoint Approach to Implementing (Co)Inductive Definitions},
+ booktitle = {CADE},
+ year = {1994},
+ pages = {148-161},
+ ee = {http://dx.doi.org/10.1007/3-540-58156-1_11},
+ crossref = {DBLP:conf/cade/1994},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+@proceedings{DBLP:conf/cade/1994,
+ editor = {Alan Bundy},
+ title = {Automated Deduction - CADE-12, 12th International Conference
+ on Automated Deduction, Nancy, France, June 26 - July 1,
+ 1994, Proceedings},
+ booktitle = {CADE},
+ publisher = {Springer},
+ series = lncs,
+ volume = {814},
+ year = {1994},
+ isbn = {3-540-58156-1},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+
+@inproceedings{Hausmann:CoCasl,
+ author = {Daniel Hausmann and
+ Till Mossakowski and
+ Lutz Schr{\"o}der},
+ title = {Iterative Circular Coinduction for {CoCasl} in {I}sabelle/{HOL}},
+ booktitle = {Fundamental Approaches to Software Engineering, 8th International
+ Conference, FASE 2005},
+ editor = {Maura Cerioli},
+ series = lncs,
+ volume = {3442},
+ publisher = {Springer},
+ year = {2005},
+ pages = {341-356},
+}
+
+@inproceedings{Rosu:CIRC,
+ author = {Dorel Lucanu and
+ Grigore Rosu},
+ title = {{CIRC}: A Circular Coinductive Prover},
+ booktitle = {CALCO},
+ year = {2007},
+ pages = {372-378},
+ ee = {http://dx.doi.org/10.1007/978-3-540-73859-6_25},
+ crossref = {DBLP:conf/calco/2007},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+@proceedings{DBLP:conf/calco/2007,
+ editor = {Till Mossakowski and
+ Ugo Montanari and
+ Magne Haveraaen},
+ title = {Algebra and Coalgebra in Computer Science, Second International
+ Conference, CALCO 2007, Bergen, Norway, August 20-24, 2007,
+ Proceedings},
+ booktitle = {CALCO},
+ publisher = {Springer},
+ series = lncs,
+ volume = {4624},
+ year = {2007},
+ isbn = {978-3-540-73857-2},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+
+@inproceedings{Rosu:circRule,
+ author = {Grigore Rosu and
+ Dorel Lucanu},
+ title = {Circular Coinduction: A Proof Theoretical Foundation},
+ booktitle = {CALCO},
+ year = {2009},
+ pages = {127-144},
+ ee = {http://dx.doi.org/10.1007/978-3-642-03741-2_10},
+ crossref = {DBLP:conf/calco/2009},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+@proceedings{DBLP:conf/calco/2009,
+ editor = {Alexander Kurz and
+ Marina Lenisa and
+ Andrzej Tarlecki},
+ title = {Algebra and Coalgebra in Computer Science, Third International
+ Conference, CALCO 2009, Udine, Italy, September 7-10, 2009.
+ Proceedings},
+ booktitle = {CALCO},
+ publisher = {Springer},
+ series = lncs,
+ volume = {5728},
+ year = {2009},
+ isbn = {978-3-642-03740-5},
+ ee = {http://dx.doi.org/10.1007/978-3-642-03741-2},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+@inproceedings{VCC,
+ author = {Ernie Cohen and
+ Markus Dahlweid and
+ Mark A. Hillebrand and
+ Dirk Leinenbach and
+ Micha{\l} Moskal and
+ Thomas Santen and
+ Wolfram Schulte and
+ Stephan Tobies},
+ title = {{VCC}: A Practical System for Verifying Concurrent {C}},
+ booktitle = {TPHOLs 2009},
+ series = LNCS,
+ publisher = {Springer},
+ volume = {5674},
+ year = {2009},
+ pages = {23-42},
+}
+
+@InProceedings{VeriFast:ProgramsAsProofs,
+ author = {Bart Jacobs and Jan Smans and Frank Piessens},
+ title = {{VeriFast}: Imperative Programs as Proofs},
+ booktitle = {VS-Tools workshop at VSTTE 2010},
+ year = {2010},
+ month = aug,
+}
+
+@book{DijkstraScholten:book,
+ author = "Edsger W. Dijkstra and Carel S. Scholten",
+ title = "Predicate Calculus and Program Semantics",
+ publisher = "Springer-Verlag",
+ series = "Texts and Monographs in Computer Science",
+ year = 1990
+}
+
+@Book{BirdWadler:IntroFunctionalProgramming,
+ author = {Richard Bird and Philip Wadler},
+ title = {Introduction to Functional Programming},
+ publisher = {Prentice Hall},
+ series = {International Series in Computing Science},
+ year = {1992},
+}
+
+@inproceedings{Z3,
+ author = "de Moura, Leonardo and Nikolaj Bj{\o}rner",
+ title = {{Z3}: An efficient {SMT} solver},
+ booktitle = {Tools and Algorithms for the Construction and
+ Analysis of Systems, 14th International Conference,
+ TACAS 2008},
+ editor = {C. R. Ramakrishnan and Jakob Rehof},
+ series = lncs,
+ volume = 4963,
+ publisher = {Springer},
+ year = 2008,
+ pages = {337-340},
+}
+
+@phdthesis{moy09phd,
+ author = {Yannick Moy},
+ title = {Automatic Modular Static Safety Checking for C Programs},
+ school = {Universit{\'e} Paris-Sud},
+ year = 2009,
+ month = jan,
+ topics = {team},
+ x-equipes = {demons PROVAL},
+ x-type = {these},
+ x-support = {rapport},
+ url = {http://www.lri.fr/~marche/moy09phd.pdf}
+}
+@Book{PeytonJones:Haskell,
+ author = {Peyton Jones, Simon},
+ title = {Haskell 98 language and libraries: the Revised Report},
+ publisher = {Cambridge University Press},
+ year = {2003},
+}
+
+@InProceedings{Park:InfSeq,
+ author = {David Park},
+ title = {Concurrency and automata on infinite sequences},
+ booktitle = {Theoretical Computer Science, 5th GI-Conference},
+ editor = {Peter Deussen},
+ volume = {104},
+ series = lncs,
+ publisher = {Springer},
+ year = {1981},
+ pages = {167-183},
+}
+
+@Article{Leroy:CompCert:CACM,
+ author = {Xavier Leroy},
+ title = {Formal verification of a realistic compiler},
+ journal = cacm,
+ volume = {52},
+ number = {7},
+ month = jul,
+ year = {2009},
+ pages = {107-115},
+}
+
+@Book{KeY:book,
+ author = {Bernhard Beckert and Reiner H{\"a}hnle and Peter H. Schmitt},
+ title = {Verification of Object-Oriented Software: The {KeY} Approach},
+ volume = 4334,
+ series = lnai,
+ publisher = {Springer},
+ year = 2007,
+}
+
+@Book{Nipkow-Paulson-Menzel02,
+ author = {Tobias Nipkow and Lawrence Paulson and Markus Menzel},
+ title = {{Isabelle/HOL} --- A Proof Assistant for Higher-Order Logic},
+ publisher = {Springer},
+ year = 2002,
+ volume = 2283,
+ series = LNCS,
+}
+
+@Book{Coq:book,
+ author = {Yves Bertot and Pierre Cast{\'e}ran},
+ title = {Interactive Theorem Proving and Program Development --- {C}oq'{A}rt: The Calculus of Inductive Constructions},
+ publisher = {Springer},
+ year = {2004},
+ series = {Texts in Theoretical Computer Science},
+}
+
+@Book{ACL2:book,
+ author = {Matt Kaufmann and Panagiotis Manolios and J Strother Moore},
+ title = {Computer-Aided Reasoning: An Approach},
+ publisher = {Kluwer Academic Publishers},
+ year = {2000},
+}
+
+@Book{BoyerMoore:book,
+ author = {Robert S. Boyer and J Strother Moore},
+ title = {A Computational Logic},
+ publisher = {Academic Press},
+ series = {ACM Monograph Series},
+ year = {1979},
+}
+
+@article{Bertot:CoinductionInCoq,
+ location = {http://www.scientificcommons.org/8157029},
+ title = {CoInduction in {C}oq},
+ author = {Bertot, Yves},
+ year = {2005},
+ publisher = {HAL - CCSd - CNRS},
+ url = {http://hal.inria.fr/inria-00001174/en/},
+ institution = {CCSd/HAL : e-articles server (based on gBUS) [http://hal.ccsd.cnrs.fr/oai/oai.php] (France)},
+}
+
+@InProceedings{Coq:Coinduction,
+ author = {Eduardo Gim{\'e}nez},
+ title = {An Application of Co-inductive Types in {Coq}: Verification of the Alternating Bit Protocol},
+ booktitle = {Types for Proofs and Programs, International Workshop TYPES'95},
+ pages = {135-152},
+ year = {1996},
+ editor = {Stefano Berardi and Mario Coppo},
+ volume = 1158,
+ series = lncs,
+ publisher = {Springer},
+}
+
+@InProceedings{Boogie:Architecture,
+ author = "Mike Barnett and Bor-Yuh Evan Chang and Robert DeLine and
+ Bart Jacobs and K. Rustan M. Leino",
+ title = "{B}oogie: A Modular Reusable Verifier for Object-Oriented Programs",
+ booktitle = "Formal Methods for Components and Objects: 4th
+ International Symposium, FMCO 2005",
+ editor = "de Boer, Frank S. and Marcello M. Bonsangue and
+ Susanne Graf and de Roever, Willem-Paul",
+ series = lncs,
+ volume = 4111,
+ publisher = "Springer",
+ month = sep,
+ year = 2006,
+ pages = "364-387"
+}
+
+@InCollection{JacobsRutten:IntroductionCoalgebra,
+ author = {Bart Jacobs and Jan Rutten},
+ title = {An Introduction to (Co)Algebra and (Co)Induction},
+ booktitle = {Advanced Topics in Bisimulation and Coinduction},
+ editor = {Davide Sangiorgi and Jan Rutten},
+ series = {Cambridge Tracts in Theoretical Computer Science},
+ number = {52},
+ publisher = {Cambridge University Press},
+ month = oct,
+ year = {2011},
+ pages = {38-99},
+}
+
+@Book{Chlipala,
+ author = {Adam Chlipala},
+ title = {Certified Programming with Dependent Types},
+ publisher = {MIT Press},
+ year = {To appear},
+ note = {http://adam.chlipala.net/cpdt/}
+}
+
+@Misc{Charity,
+ author = {Robin Cockett},
+ title = {The {CHARITY} home page},
+ howpublished = {\url{http://pll.cpsc.ucalgary.ca/charity1/www/home.html}},
+ year = {1996},
+}
+
+@Article{Tarski:theorem,
+ author = "Alfred Tarski",
+ title = "A lattice-theoretical fixpoint theorem and its applications",
+ journal = "Pacific Journal of Mathematics",
+ year = 1955,
+ volume = 5,
+ pages = "285-309"
+}
+
+@InProceedings{PVS,
+ author = "Sam Owre and S. Rajan and John M. Rushby and Natarajan
+ Shankar and Mandayam K. Srivas",
+ title = "{PVS}: Combining Specification, Proof Checking, and Model
+ Checking",
+ editor = "Rajeev Alur and Thomas A. Henzinger",
+ booktitle = "Computer Aided Verification, 8th International
+ Conference, CAV '96",
+ volume = 1102,
+ series = lncs,
+ publisher = "Springer",
+ year = 1996,
+ pages = "411-414"
+}
+
+@InProceedings{SonnexEtAl:Zeno,
+ author = {William Sonnex and Sophia Drossopoulou and Susan Eisenbach},
+ title = {Zeno: An Automated Prover for Properties of Recursive
+ Data Structures},
+ booktitle = {Tools and Algorithms for the Construction and Analysis of
+ Systems --- 18th International Conference, TACAS 2012},
+ editor = {Cormac Flanagan and Barbara K{\"o}nig},
+ volume = {7214},
+ series = lncs,
+ year = {2012},
+ month = mar # "--" # apr,
+ publisher = {Springer},
+ pages = {407-421},
+}
+
+@InProceedings{JohanssonEtAl:IPT2010,
+ author = {Moa Johansson and Lucas Dixon and Alan Bundy},
+ title = {Case-Analysis for {R}ippling and Inductive Proof},
+ booktitle = {Interactive Theorem Proving, First International Conference, ITP 2010},
+ editor = {Matt Kaufmann and Lawrence C. Paulson},
+ volume = {6172},
+ series = lncs,
+ publisher = {Springer},
+ month = jul,
+ year = {2010},
+ pages = {291-306},
+}
+
+@book{Milner:CCS,
+ author = "Robin Milner",
+ title = {A Calculus of Communicating Systems},
+ year = {1982},
+ isbn = {0387102353},
+ publisher = {Springer-Verlag New York, Inc.},
+}
+
+@InProceedings{BoehmeNipkow:Sledgehammer,
+ author = {Sascha B{\"o}hme and Tobias Nipkow},
+ title = {Sledgehammer: {J}udgement {D}ay},
+ booktitle = {Automated Reasoning, 5th International Joint Conference, IJCAR 2010},
+ editor = {J{\"u}rgen Giesl and Reiner H{\"a}hnle},
+ year = {2010},
+ pages = {107-121},
+ volume = {6173},
+ series = lncs,
+ month = jul,
+ publisher = {Springer},
+}
+
+@PhdThesis{Norell:PhD,
+ author = {Ulf Norell},
+ title = {Towards a practical programming language based on dependent type theory},
+ school = {Department of Computer Science and Engineering, Chalmers
+ University of Technology},
+ year = {2007},
+ month = sep,
+}
+
+@Article{Paulson:MechanizingCoRecursion,
+ author = {Lawrence C. Paulson},
+ title = {Mechanizing Coinduction and Corecursion in Higher-order Logic},
+ journal = {Journal of Logic and Computation},
+ year = {1997},
+ volume = {7},
+}
+
+@InProceedings{Bertot:sieve,
+ author = {Yves Bertot},
+ title = {Filters on CoInductive Streams, an Application to {E}ratosthenes' Sieve},
+ booktitle = {Typed Lambda Calculi and Applications, 7th International Conference,
+ TLCA 2005},
+ editor = {Pawel Urzyczyn},
+ series = lncs,
+ volume = {3461},
+ month = apr,
+ year = {2005},
+ pages = {102-115},
+ publisher = {Springer},
+}
+
+@Misc{AltenkirchDanielsson:QuantifierInversion,
+ author = {Thorsten Altenkirch and Nils Anders Danielsson},
+ title = {Termination Checking in the Presence of Nested Inductive and Coinductive Types},
+ howpublished = {Short note supporting a talk given at PAR 2010, Workshop on Partiality and Recursion in Interactive Theorem Provers},
+ year = {2010},
+ note = {Available from \url{http://www.cse.chalmers.se/~nad/publications/}.},
+}
+
+@InProceedings{HurEtAl:Paco,
+ author = {Hur, Chung-Kil and Neis, Georg and Dreyer, Derek and Vafeiadis, Viktor},
+ title = {The power of parameterization in coinductive proof},
+ booktitle = {Proceedings of the 40th Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, POPL '13},
+ editor = {Roberto Giacobazzi and Radhia Cousot},
+ pages = {193--206},
+ month = jan,
+ year = {2013},
+ publisher = {ACM},
+}
+
+@InProceedings{BoveDybjerNorell:BriefAgda,
+ author = {Ana Bove and Peter Dybjer and Ulf Norell},
+ title = {A Brief Overview of {A}gda --- A Functional Language with Dependent Types},
+ booktitle = {Theorem Proving in Higher Order Logics, 22nd International Conference, TPHOLs 2009},
+ editor = {Stefan Berghofer and Tobias Nipkow and Christian Urban and Makarius Wenzel},
+ series = lncs,
+ volume = {5674},
+ publisher = {Springer},
+ month = aug,
+ year = {2009},
+ pages = {73-78},
+}
+
+@Article{Moore:Piton,
+ author = {J Strother Moore},
+ title = {A Mechanically Verified Language Implementation},
+ journal = {Journal of Automated Reasoning},
+ year = {1989},
+ volume = {5},
+ number = {4},
+ pages = {461-492},
+}
+
+@InProceedings{Leroy:ESOP2006,
+ author = {Xavier Leroy},
+ title = {Coinductive Big-Step Operational Semantics},
+ booktitle = {Programming Languages and Systems, 15th European Symposium on Programming, ESOP 2006},
+ pages = {54-68},
+ year = {2006},
+ editor = {Peter Sestoft},
+ volume = {3924},
+ series = lncs,
+ month = mar,
+ publisher = {Springer},
+}
+
+@InProceedings{Leino:ITP2013,
+ author = {K. Rustan M. Leino},
+ title = {Automating Theorem Proving with {SMT}},
+ booktitle = {Interactive Theorem Proving --- 4th International Conference, ITP 2013},
+ year = {2013},
+ editor = {Sandrine Blazy and Christine Paulin-Mohring and David Pichardie},
+ volume = {7998},
+ series = lncs,
+ pages = {2-16},
+ month = jul,
+ publisher = {Springer},
+}
+
+@TechReport{TR-version,
+ author = {K. Rustan M. Leino and Micha{\l} Moskal},
+ title = {Co-induction Simply: Automatic Co-inductive Proofs in a Program Verifier},
+ institution = {Microsoft Research},
+ year = {2013},
+ number = {MSR-TR-2013-49},
+ month = may,
+}
diff --git a/Docs/DafnyRef/poc.bib b/Docs/DafnyRef/poc.bib
new file mode 100644
index 00000000..a0f1ed79
--- /dev/null
+++ b/Docs/DafnyRef/poc.bib
@@ -0,0 +1,523 @@
+@string{lncs = "LNCS"}
+@string{lnai = "LNAI"}
+
+@InCollection{LeinoMoskal:UsableProgramVerification,
+ author = {K. Rustan M. Leino and Micha{\l} Moskal},
+ title = {Usable Auto-Active Verification},
+ booktitle = {Usable Verification workshop},
+ year = {2010},
+ editor = {Tom Ball and Lenore Zuck and N. Shankar},
+ publisher = {\url{http://fm.csl.sri.com/UV10/}},
+}
+ booktitle = {UV10 (Usable Verification) workshop},
+ month = nov,
+
+@Book{Coq:book,
+ author = {Yves Bertot and Pierre Cast{\'e}ran},
+ title = {Interactive Theorem Proving and Program Development --- {C}oq'{A}rt: The Calculus of Inductive Constructions},
+ publisher = {Springer},
+ year = {2004},
+}
+ series = {Texts in Theoretical Computer Science},
+
+@Manual{Isabelle:Guide,
+ title = {Programming and Proving in {I}sabelle/{HOL}},
+ author = {Tobias Nipkow},
+ organization = {\url{http://isabelle.informatik.tu-muenchen.de/}},
+ year = {2012},
+}
+ month = may,
+
+@InProceedings{Leino:Dafny:LPAR16,
+ author = {K. Rustan M. Leino},
+ title = {Dafny: An Automatic Program Verifier for Functional Correctness},
+ booktitle = {LPAR-16},
+ year = {2010},
+ volume = {6355},
+ series = lncs,
+ publisher = {Springer},
+ pages = {348-370},
+}
+ editor = {Edmund M. Clarke and Andrei Voronkov},
+ month = apr,
+
+@InProceedings{VCC:TPHOLs,
+ author = {Ernie Cohen and Markus Dahlweid and Mark
+ A. Hillebrand and Dirk Leinenbach and Micha{\l}
+ Moskal and Thomas Santen and Wolfram Schulte and
+ Stephan Tobies},
+ title = {{VCC}: A Practical System for Verifying Concurrent {C}},
+ booktitle = {TPHOLs 2009},
+ volume = {5674},
+ series = lncs,
+ publisher = {Springer},
+ year = {2009},
+ pages = {23-42},
+}
+ booktitle = {Theorem Proving in Higher Order Logics, 22nd International Conference, TPHOLs 2009},
+ editor = {Stefan Berghofer and Tobias Nipkow and Christian
+ Urban and Makarius Wenzel},
+ month = aug,
+
+@TechReport{VeriFast:TR,
+ author = {Bart Jacobs and Frank Piessens},
+ title = {The {VeriFast} program verifier},
+ institution = {Department of Computer Science, Katholieke Universiteit Leuven},
+ year = {2008},
+ number = {CW-520},
+}
+ month = aug,
+
+@InProceedings{VeriFast:ProgramsAsProofs,
+ author = {Bart Jacobs and Jan Smans and Frank Piessens},
+ title = {{VeriFast}: Imperative Programs as Proofs},
+ booktitle = {VS-Tools workshop at VSTTE 2010},
+ year = {2010},
+}
+ month = aug,
+
+@Article{IPL:vol53:3,
+ author = {Roland Backhouse},
+ title = {Special issue on The Calculational Method},
+ journal = {Information Processing Letters},
+ year = {1995},
+ volume = {53},
+ number = {3},
+ pages = {121-172},
+}
+ month = feb,
+
+@InProceedings{VonWright:ExtendingWindowInference,
+ author = {von Wright, Joakim},
+ title = {Extending Window Inference},
+ booktitle = {TPHOLs'98},
+ pages = {17-32},
+ year = {1998},
+ volume = {1479},
+ series = lncs,
+ publisher = {Springer},
+}
+ booktitle = {Theorem Proving in Higher Order Logics, 11th International Conference, TPHOLs'98},
+ editor = {Jim Grundy and Malcolm C. Newey},
+
+@PhdThesis{Wenzel:PhD,
+ author = {Markus Wenzel},
+ title = {{I}sabelle/{I}sar --- a versatile environment for human-readable formal proof documents},
+ school = {Institut f{\"u}r Informatik, Technische Universit{\"a}t M{\"u}nchen},
+ year = {2002},
+}
+
+@InProceedings{BauerWenzel:IsarExperience,
+ author = {Gertrud Bauer and Markus Wenzel},
+ title = {Calculational reasoning revisited: an {I}sabelle/{I}sar experience},
+ booktitle = {TPHOLs 2001},
+ pages = {75-90},
+ year = {2001},
+ volume = {2152},
+ series = lncs,
+ publisher = {Springer},
+}
+ booktitle = {Theorem Proving in Higher Order Logics, 14th International Conference, TPHOLs 2001},
+ editor = {Richard J. Boulton and Paul B. Jackson},
+ month = sep,
+
+@InCollection{KoenigLeino:MOD2011,
+ author = {Jason Koenig and K. Rustan M. Leino},
+ title = {Getting Started with {D}afny: A Guide},
+ booktitle = {Software Safety and Security: Tools for Analysis and Verification},
+ pages = {152-181},
+ publisher = {IOS Press},
+ year = {2012},
+}
+ volume = {33},
+ series = {NATO Science for Peace and Security Series D: Information and Communication Security},
+ editor = {Tobias Nipkow and Orna Grumberg and Benedikt Hauptmann},
+ note = {Summer School Marktoberdorf 2011 lecture notes},
+
+@InProceedings{Leino:induction,
+ author = {K. Rustan M. Leino},
+ title = {Automating Induction with an {SMT} Solver},
+ booktitle = {VMCAI 2012},
+ pages = {315-331},
+ year = {2012},
+ volume = {7148},
+ series = lncs,
+ publisher = {Springer},
+}
+ booktitle = {Verification, Model Checking, and Abstract Interpretation - 13th International Conference, VMCAI 2012},
+ editor = {Viktor Kuncak and Andrey Rybalchenko},
+ month = jan,
+
+@article{Hoare:AxiomaticBasis,
+ author = "C. A. R. Hoare",
+ title = "An axiomatic basis for computer programming",
+ journal = cacm,
+ volume = 12,
+ number = 10,
+ year = 1969,
+ pages = "576--580,583"
+}
+ month = oct,
+
+@Article{Simplify:tome,
+ author = "David Detlefs and Greg Nelson and James B. Saxe",
+ title = "Simplify: a theorem prover for program checking",
+ journal = JACM,
+ volume = 52,
+ number = 3,
+ year = 2005,
+ pages = "365-473",
+}
+ month = may,
+
+@techreport{Nelson:thesis,
+ author = "Charles Gregory Nelson",
+ title = "Techniques for Program Verification",
+ institution = "Xerox PARC",
+ year = 1981,
+ number = "CSL-81-10",
+ note = "PhD thesis, Stanford University"
+}
+ month = jun,
+
+@inproceedings{deMouraBjorner:Z3:overview,
+ author = "de Moura, Leonardo and Nikolaj Bj{\o}rner",
+ title = {{Z3}: An efficient {SMT} solver},
+ booktitle = {Tools and Algorithms for the Construction and
+ Analysis of Systems, 14th International Conference,
+ TACAS 2008},
+ series = lncs,
+ volume = 4963,
+ publisher = {Springer},
+ year = 2008,
+ pages = {337-340},
+}
+ editor = {C. R. Ramakrishnan and Jakob Rehof},
+ month = mar # "--" # apr,
+
+@InProceedings{LGLM:BVD,
+ author = {Le Goues, Claire and K. Rustan M. Leino and Micha{\l} Moskal},
+ title = {The {B}oogie {V}erification {D}ebugger (Tool Paper)},
+ booktitle = {Software Engineering and Formal Methods - 9th International Conference, SEFM 2011},
+ pages = {407-414},
+ year = {2011},
+ volume = {7041},
+ series = lncs,
+ publisher = {Springer},
+}
+ editor = {Gilles Barthe and Alberto Pardo and Gerardo Schneider},
+ month = nov,
+
+@InProceedings{HipSpec:WING,
+ author = {Koen Claessen and Moa Johansson and Dan Ros{\'e}n and Nicholas Smallbone},
+ title = {{HipSpec}: Automating Inductive Proofs of Program Properties},
+ booktitle = {Workshop on {A}utomated {T}heory e{X}ploration: {ATX} 2012},
+ year = {2012},
+}
+ month = jul,
+
+@InProceedings{HipSpec:CADE,
+ author = {Koen Claessen and Moa Johansson and Dan Ros{\'e}n and Nicholas Smallbone},
+ title = {Automating Inductive Proofs Using Theory Exploration},
+ booktitle = {CADE-24},
+ pages = {392-406},
+ year = {2013},
+ volume = {7898},
+ series = lncs,
+ publisher = {Springer},
+}
+ booktitle = {Automated Deduction --- CADE-24 --- 24th International Conference on Automated Deduction},
+ editor = {Maria Paola Bonacina},
+ month = jun,
+
+@article{ManoliosMoore:Calc,
+ author = {Panagiotis Manolios and J. Strother Moore},
+ title = {On the desirability of mechanizing calculational proofs},
+ journal = {Inf. Process. Lett.},
+ volume = {77},
+ number = {2-4},
+ year = {2001},
+ pages = {173-179},
+ ee = {http://dx.doi.org/10.1016/S0020-0190(00)00200-3},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+@article{Lifschitz:DS,
+ title={On Calculational Proofs},
+ author={Vladimir Lifschitz},
+ volume={113},
+ journal={Annals of Pure and Applied Logic},
+ pages={207-224},
+ url="http://www.cs.utexas.edu/users/ai-lab/pub-view.php?PubID=26805",
+ year={2002}
+}
+
+@article{BackGrundyWright:SCP,
+ author = {Ralph Back and Jim Grundy and Joakim von Wright},
+ title = {Structured Calculational Proof},
+ journal = {Formal Aspects of Computing},
+ year = {1997},
+ volume = {9},
+ number = {5--6},
+ pages = {469--483}
+}
+
+@article{Back:SD,
+ author = {Back, Ralph-Johan},
+ title = {Structured derivations: a unified proof style for teaching mathematics},
+ journal = {Formal Aspects of Computing},
+ issue_date = {September 2010},
+ volume = {22},
+ number = {5},
+ year = {2010},
+ issn = {0934-5043},
+ pages = {629--661},
+ numpages = {33},
+ url = {http://dx.doi.org/10.1007/s00165-009-0136-5},
+ doi = {10.1007/s00165-009-0136-5},
+ acmid = {1858559},
+ publisher = {Springer},
+}
+ month = sep,
+
+@article{Dijkstra:EWD1300,
+ author = {Edsger W. Dijkstra},
+ title = {{EWD1300}: The Notational Conventions {I} Adopted, and Why},
+ journal = {Formal Asp. Comput.},
+ volume = {14},
+ number = {2},
+ year = {2002},
+ pages = {99-107},
+ ee = {http://dx.doi.org/10.1007/s001650200030},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+@inproceedings{BCDJL05:Boogie,
+ author = {Michael Barnett and
+ Bor-Yuh Evan Chang and
+ Robert DeLine and
+ Bart Jacobs and
+ K. Rustan M. Leino},
+ title = {Boogie: A Modular Reusable Verifier for Object-Oriented
+ Programs},
+ booktitle = {FMCO 2005},
+ series = lncs,
+ volume = 4111,
+ publisher = "Springer",
+ year = {2006},
+ pages = {364-387},
+}
+ month = sep,
+
+@article{BVW:Mathpad,
+ author = {Roland Backhouse and
+ Richard Verhoeven and
+ Olaf Weber},
+ title = {Math{$\!\!\int\!\!$}pad: A System for On-Line Preparation of Mathematical
+ Documents},
+ journal = {Software --- Concepts and Tools},
+ volume = {18},
+ number = {2},
+ year = {1997},
+ pages = {80-},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+@inproceedings{VB:MathpadPVS,
+ author = {Richard Verhoeven and
+ Roland Backhouse},
+ title = {Interfacing Program Construction and Verification},
+ booktitle = {World Congress on Formal Methods},
+ year = {1999},
+ pages = {1128-1146},
+ ee = {http://dx.doi.org/10.1007/3-540-48118-4_10},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+@inproceedings{Corbineau:CoqDecl,
+ author = {Pierre Corbineau},
+ title = {A Declarative Language for the {Coq} Proof Assistant},
+ booktitle = {TYPES},
+ year = {2007},
+ pages = {69-84},
+ ee = {http://dx.doi.org/10.1007/978-3-540-68103-8_5},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+@inproceedings{Wiedijk:Sketches,
+ author = {Freek Wiedijk},
+ title = {Formal Proof Sketches},
+ booktitle = {TYPES},
+ year = {2003},
+ pages = {378-393},
+ ee = {http://dx.doi.org/10.1007/978-3-540-24849-1_24},
+ crossref = {DBLP:conf/types/2003},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+@book{DijkstraScholten:Book,
+ author = {Edsger W. Dijkstra and
+ Carel S. Scholten},
+ title = {Predicate calculus and program semantics},
+ publisher = {Springer},
+ series = {Texts and monographs in computer science},
+ year = {1990},
+ isbn = {978-3-540-96957-0},
+ pages = {I-X, 1-220},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+@inproceedings{Rudnicki:Mizar,
+ author = {Piotr Rudnicki},
+ title = {An Overview of the {MIZAR} Project},
+ booktitle = {University of Technology, Bastad},
+ year = {1992},
+ pages = {311--332},
+ publisher = {}
+}
+
+@inproceedings{ORS:PVS,
+ AUTHOR = {S. Owre and J. M. Rushby and N. Shankar},
+ TITLE = {{PVS:} {A} Prototype Verification System},
+ BOOKTITLE = {CADE-11},
+ YEAR = {1992},
+ SERIES = lnai,
+ VOLUME = {607},
+ PAGES = {748--752},
+ PUBLISHER = {Springer},
+ URL = {http://www.csl.sri.com/papers/cade92-pvs/}
+}
+ BOOKTITLE = {11th International Conference on Automated Deduction (CADE)},
+ EDITOR = {Deepak Kapur},
+
+@article{Robinson:Window,
+ author = {Peter J. Robinson and
+ John Staples},
+ title = {Formalizing a Hierarchical Structure of Practical Mathematical
+ Reasoning},
+ journal = {J. Log. Comput.},
+ volume = {3},
+ number = {1},
+ year = {1993},
+ pages = {47-61},
+ ee = {http://dx.doi.org/10.1093/logcom/3.1.47},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+@article{Grundy:WindowHOL,
+ author = {Jim Grundy},
+ title = {Transformational Hierarchical Reasoning},
+ journal = {Comput. J.},
+ volume = {39},
+ number = {4},
+ year = {1996},
+ pages = {291-302},
+ ee = {http://dx.doi.org/10.1093/comjnl/39.4.291},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+@inproceedings{BN:Sledgehammer,
+ author = {Sascha B{\"o}hme and
+ Tobias Nipkow},
+ title = {Sledgehammer: Judgement Day},
+ booktitle = {IJCAR},
+ year = {2010},
+ pages = {107-121},
+ ee = {http://dx.doi.org/10.1007/978-3-642-14203-1_9},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+@inproceedings{Armand:CoqSMT,
+ author = {Micha{\"e}l Armand and
+ Germain Faure and
+ Benjamin Gr{\'e}goire and
+ Chantal Keller and
+ Laurent Th{\'e}ry and
+ Benjamin Werner},
+ title = {A Modular Integration of {SAT}/{SMT} Solvers to {Coq} through
+ Proof Witnesses},
+ booktitle = {CPP},
+ year = {2011},
+ pages = {135-150},
+ ee = {http://dx.doi.org/10.1007/978-3-642-25379-9_12},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+@inproceedings{Besson:CoqSMTReflexive,
+ author = {Fr{\'e}d{\'e}ric Besson and
+ Pierre-Emmanuel Cornilleau and
+ David Pichardie},
+ title = {Modular SMT Proofs for Fast Reflexive Checking Inside Coq},
+ booktitle = {CPP},
+ year = {2011},
+ pages = {151-166},
+ ee = {http://dx.doi.org/10.1007/978-3-642-25379-9_13},
+ crossref = {DBLP:conf/cpp/2011},
+ bibsource = {DBLP, http://dblp.uni-trier.de}
+}
+
+@Book{ACL2:book,
+ author = {Matt Kaufmann and Panagiotis Manolios and J Strother Moore},
+ title = {Computer-Aided Reasoning: An Approach},
+ publisher = {Kluwer Academic Publishers},
+ year = {2000},
+}
+
+@inproceedings{boogie11why3,
+ author = {Fran\c{c}ois Bobot and Jean-Christophe Filli\^atre and Claude March\'e and Andrei Paskevich},
+ title = {{Why3}: Shepherd Your Herd of Provers},
+ booktitle = {BOOGIE 2011: Workshop on Intermediate Verification Languages},
+ year = 2011,
+ pages = {53--64},
+ url = {http://proval.lri.fr/publications/boogie11final.pdf},
+}
+ booktitle = {BOOGIE 2011: First International Workshop on Intermediate Verification Languages},
+ month = aug,
+
+@InProceedings{zeno,
+ author = {William Sonnex and Sophia Drossopoulou and Susan Eisenbach},
+ title = {Zeno: An Automated Prover for Properties of Recursive
+ Data Structures},
+ booktitle = {TACAS},
+ volume = {7214},
+ series = lncs,
+ year = {2012},
+ publisher = {Springer},
+ pages = {407-421},
+}
+ booktitle = {Tools and Algorithms for the Construction and Analysis of
+ Systems --- 18th International Conference, TACAS 2012},
+ editor = {Cormac Flanagan and Barbara K{\"o}nig},
+ month = mar # "--" # apr,
+
+@InProceedings{Chisholm:CalculationByComputer,
+ author = {P. Chisholm},
+ title = {Calculation by computer},
+ booktitle = {Third International Workshop on Software Engineering and its Applications},
+ address = {Toulouse, France},
+ year = {1990},
+ month = dec,
+ pages = {713-728},
+}
+
+@TechReport{VanDeSnepscheut:Proxac,
+ author = {van de Snepscheut, Jan L. A.},
+ title = {Proxac: an editor for program transformation},
+ institution = {Caltech},
+ year = {1993},
+ number = {CS-TR-93-33},
+}
+
+@InProceedings{VanGasterenBijlsma:CalcExtension,
+ author = {A. J. M. van Gasteren and A. Bijlsma},
+ title = {An extension of the program derivation format},
+ booktitle = {PROCOMET '98},
+ pages = {167-185},
+ year = {1998},
+ publisher = {IFIP Conference Proceedings},
+}
+ booktitle = {Programming Concepts and Methods, IFIP TC2/WG2.2,2.3 International Conference on
+ Programming Concepts and Methods (PROCOMET '98)},
+ editor = {David Gries and Willem P. de Roever},
+ month = jun,
+
diff --git a/Docs/DafnyRef/references.bib b/Docs/DafnyRef/references.bib
new file mode 100644
index 00000000..880d8446
--- /dev/null
+++ b/Docs/DafnyRef/references.bib
@@ -0,0 +1,1446 @@
+@InCollection{Leino:Dafny:MOD2008,
+ author = {K. Rustan M. Leino},
+ title = {Specification and verification of object-oriented software},
+ booktitle = {Engineering Methods and Tools for Software Safety and Security},
+ pages = {231-266},
+ publisher = {IOS Press},
+ year = {2009},
+ editor = {Manfred Broy and Wassiou Sitou and Tony Hoare},
+ volume = {22},
+ series = {NATO Science for Peace and Security Series D: Information and Communication Security},
+ note = {Summer School Marktoberdorf 2008 lecture notes},
+}
+
+@InCollection{LeinoSchulte:MOD2006,
+ author = {K. Rustan M. Leino and Wolfram Schulte},
+ title = {A verifying compiler for a multi-threaded object-oriented language},
+ booktitle = {Software Safety and Security},
+ pages = {351-416},
+ publisher = {IOS Press},
+ year = {2007},
+ editor = {Manfred Broy and Johannes Gr{\"u}nbauer and Tony Hoare},
+ volume = {9},
+ series = {NATO Science for Peace and Security Series D: Information and Communication Security},
+ note = {Summer School Marktoberdorf 2006 lecture notes},
+}
+
+@techreport{ESC:rr,
+ author = "David L. Detlefs and K. Rustan M. Leino and Greg Nelson
+ and James B. Saxe",
+ title = "Extended static checking",
+ institution = "Compaq Systems Research Center",
+ month = dec,
+ year = 1998,
+ type = "Research Report",
+ number = 159
+}
+
+@Article{Simplify:tome,
+ author = "David Detlefs and Greg Nelson and James B. Saxe",
+ title = "Simplify: a theorem prover for program checking",
+ journal = JACM,
+ volume = 52,
+ number = 3,
+ month = may,
+ year = 2005,
+ pages = "365-473",
+}
+
+@InProceedings{Doomed:FM2009,
+ author = {Jochen Hoenicke and K. Rustan M. Leino and Andreas
+ Podelski and Martin Sch{\"a}f and Thomas Wies},
+ title = {It's Doomed; We Can Prove It},
+ booktitle = {FM 2009: Formal Methods, Second World Congress},
+ editor = {Ana Cavalcanti and Dennis Dams},
+ volume = 5850,
+ series = lncs,
+ publisher = {Springer},
+ month = nov,
+ year = 2009,
+ pages = {338-353},
+}
+
+@InProceedings{Regis-Gianas:Pottier:MPC2008,
+ author = {Yann R{\'e}gis-Gianas and Fran{\c{c}}ois Pottier},
+ title = {A {Hoare} Logic for Call-by-Value Functional Programs},
+ booktitle = {Mathematics of Program Construction, 9th International Conference},
+ editor = {Philippe Audebaud and Christine Paulin-Mohring},
+ volume = {5133},
+ series = lncs,
+ publisher = {Springer},
+ month = jul,
+ year = {2008},
+ pages = {305-335},
+}
+
+@InProceedings{ZeeKuncakRinard:PLDI2008,
+ author = {Karen Zee and Viktor Kuncak and Martin C. Rinard},
+ title = {Full functional verification of linked data structures},
+ booktitle = {Proceedings of the ACM SIGPLAN 2008 Conference on
+ Programming Language Design and Implementation},
+ editor = {Rajiv Gupta and Saman P. Amarasinghe},
+ year = {2008},
+ month = jun,
+ publisher = {ACM},
+ pages = {349-361},
+}
+
+@InProceedings{VCC:TPHOLs,
+ author = {Ernie Cohen and Markus Dahlweid and Mark
+ A. Hillebrand and Dirk Leinenbach and Micha{\l}
+ Moskal and Thomas Santen and Wolfram Schulte and
+ Stephan Tobies},
+ title = {{VCC}: A Practical System for Verifying Concurrent {C}},
+ booktitle = {Theorem Proving in Higher Order Logics, 22nd
+ International Conference, TPHOLs 2009},
+ editor = {Stefan Berghofer and Tobias Nipkow and Christian
+ Urban and Makarius Wenzel},
+ volume = {5674},
+ series = lncs,
+ publisher = {Springer},
+ year = {2009},
+ month = aug,
+ pages = {23-42},
+}
+
+@InProceedings{seL4:SOSP2009,
+ author = {Gerwin Klein and Kevin Elphinstone and Gernot Heiser
+ and June Andronick and David Cock and Philip Derrin
+ and Dhammika Elkaduwe and Kai Engelhardt and Rafal
+ Kolanski and Michael Norrish and Thomas Sewell and
+ Harvey Tuch and Simon Winwood},
+ title = {{seL4}: formal verification of an {OS} kernel},
+ booktitle = {Proceedings of the 22nd ACM Symposium on Operating
+ Systems Principles 2009, SOSP 2009},
+ editor = {Jeanna Neefe Matthews and Thomas E. Anderson},
+ publisher = {ACM},
+ month = oct,
+ year = {2009},
+ pages = {207-220},
+}
+
+@book{Meyer:OOP,
+ author = "Bertrand Meyer",
+ title = "Object-oriented Software Construction",
+ publisher = "Prentice-Hall International",
+ series = "Series in Computer Science",
+ year = 1988
+}
+
+@InProceedings{SpecSharp:Overview,
+ author = {Mike Barnett and K. Rustan M. Leino and Wolfram Schulte},
+ title = {The {Spec\#} Programming System: An Overview},
+ booktitle = {{CASSIS 2004}, Construction and Analysis of Safe,
+ Secure and Interoperable Smart devices},
+ editor = "Gilles Barthe and Lilian Burdy and Marieke Huisman and
+ Jean-Louis Lanet and Traian Muntean",
+ series = lncs,
+ volume = 3362,
+ publisher = "Springer",
+ year = 2005,
+ pages = "49-69"
+}
+
+@InProceedings{Kassios:FM2006,
+ author = "Ioannis T. Kassios",
+ title = "Dynamic Frames: Support for Framing, Dependencies and Sharing Without Restrictions",
+ booktitle = "FM 2006: Formal Methods, 14th International Symposium on Formal Methods",
+ editor = "Jayadev Misra and Tobias Nipkow and Emil Sekerinski",
+ series = lncs,
+ volume = 4085,
+ publisher = "Springer",
+ month = aug,
+ year = 2006,
+ pages = "268-283",
+}
+
+@InProceedings{Boogie:Architecture,
+ author = "Mike Barnett and Bor-Yuh Evan Chang and Robert DeLine and
+ Bart Jacobs and K. Rustan M. Leino",
+ title = "{B}oogie: A Modular Reusable Verifier for Object-Oriented Programs",
+ booktitle = "Formal Methods for Components and Objects: 4th
+ International Symposium, FMCO 2005",
+ editor = "de Boer, Frank S. and Marcello M. Bonsangue and
+ Susanne Graf and de Roever, Willem-Paul",
+ series = lncs,
+ volume = 4111,
+ publisher = "Springer",
+ month = sep,
+ year = 2006,
+ pages = "364-387"
+}
+
+@Misc{Leino:Boogie2-RefMan,
+ author = {K. Rustan M. Leino},
+ title = {This is {B}oogie 2},
+ howpublished = {Manuscript KRML 178},
+ year = 2008,
+ note = "Available at \url{http://research.microsoft.com/~leino/papers.html}",
+}
+
+@inproceedings{deMouraBjorner:Z3:overview,
+ author = "de Moura, Leonardo and Nikolaj Bj{\o}rner",
+ title = {{Z3}: An efficient {SMT} solver},
+ booktitle = {Tools and Algorithms for the Construction and
+ Analysis of Systems, 14th International Conference,
+ TACAS 2008},
+ editor = {C. R. Ramakrishnan and Jakob Rehof},
+ series = lncs,
+ volume = 4963,
+ publisher = {Springer},
+ month = mar # "--" # apr,
+ year = 2008,
+ pages = {337-340},
+}
+
+@InProceedings{Gonthier:CAV2006,
+ author = {Georges Gonthier},
+ title = {Verifying the safety of a practical concurrent
+ garbage collector},
+ booktitle = {Computer Aided Verification, 8th International Conference, CAV '96},
+ editor = {Rajeev Alur and Thomas A. Henzinger},
+ volume = {1102},
+ series = lncs,
+ publisher = {Springer},
+ month = jul # "--" # aug,
+ year = {1996},
+ pages = {462-465},
+}
+
+@Article{CLIncStack,
+ author = {William R. Bevier and Hunt, Jr., Warren A. and
+ J Strother Moore and William D. Young},
+ title = {Special issue on system verification},
+ journal = {Journal of Automated Reasoning},
+ volume = {5},
+ number = {4},
+ month = dec,
+ year = {1989},
+ pages = {409-530},
+}
+
+@InProceedings{ParkinsonBierman:POPL2005,
+ author = {Matthew J. Parkinson and Gavin M. Bierman},
+ title = {Separation logic and abstraction},
+ booktitle = {Proceedings of the 32nd ACM SIGPLAN-SIGACT Symposium
+ on Principles of Programming Languages, POPL 2005},
+ publisher = {ACM},
+ month = jan,
+ year = {2005},
+ pages = {247-258},
+}
+
+@InProceedings{Weide:VSTTE2008,
+ author = {Bruce W. Weide and Murali Sitaraman and Heather
+ K. Harton and Bruce Adcock and Paolo Bucci and
+ Derek Bronish and Wayne D. Heym and Jason
+ Kirschenbaum and David Frazier},
+ title = {Incremental Benchmarks for Software Verification Tools and Techniques},
+ booktitle = {Verified Software: Theories, Tools, Experiments,
+ Second International Conference, VSTTE 2008},
+ editor = {Natarajan Shankar and Jim Woodcock},
+ volume = {5295},
+ series = lncs,
+ publisher = {Springer},
+ month = oct,
+ year = {2008},
+ pages = {84-98},
+}
+
+@Article{SchorrWaite:CACM1967,
+ author = {H. Schorr and W. M. Waite},
+ title = {An Efficient Machine-Independent Procedure for
+ Garbage Collection in Various List Structures},
+ journal = cacm,
+ volume = {10},
+ number = {8},
+ month = aug,
+ year = {1967},
+ pages = {501-506},
+}
+
+@phdthesis{Leino:thesis,
+ author = "K. Rustan M. Leino",
+ title = "Toward Reliable Modular Programs",
+ school = {California Institute of Technology},
+ year = 1995,
+ note = "Technical Report Caltech-CS-TR-95-03."
+}
+
+@inproceedings{Boyland:SAS2003,
+ author = {John Boyland},
+ title = {Checking Interference with Fractional Permissions},
+ booktitle = "Static Analysis, 10th International Symposium, SAS 2003",
+ editor = {Radhia Cousot},
+ series = lncs,
+ volume = 2694,
+ publisher = "Springer",
+ year = 2003,
+ pages = {55-72}
+}
+
+@InProceedings{Reynolds:SepLogic,
+ author = {John C. Reynolds},
+ title = {Separation Logic: A Logic for Shared Mutable Data Structures},
+ booktitle = {17th IEEE Symposium on Logic in Computer Science (LICS 2002)},
+ publisher = {IEEE Computer Society},
+ year = {2002},
+ month = jul,
+ pages = {55-74},
+}
+
+@InProceedings{Clarke-Drossopoulou02,
+ author = {Dave Clarke and Sophia Drossopoulou},
+ title = {Ownership, encapsulation and the disjointness of
+ type and effect},
+ booktitle = {Proceedings of the 2002 ACM SIGPLAN Conference on
+ Object-Oriented Programming Systems, Languages and
+ Applications, OOPSLA 2002},
+ publisher = {ACM},
+ Month = nov,
+ Year = 2002,
+ pages = {292--310},
+}
+
+@InProceedings{FAP:OOPSLA1998,
+ author = {Dave Clarke and John Potter and James Noble},
+ title = {Ownership Types for Flexible Alias Protection},
+ booktitle = {Proceedings of the 1998 ACM SIGPLAN Conference on
+ Object-Oriented Programming Systems, Languages \&
+ Applications (OOPSLA '98)},
+ publisher = {ACM},
+ month = oct,
+ year = {1998},
+ pages = {48-64},
+}
+
+@Article{LeinoNelson:tome,
+ author = "K. Rustan M. Leino and Greg Nelson",
+ title = "Data abstraction and information hiding",
+ journal = toplas,
+ month = sep,
+ year = 2002,
+ volume = 24,
+ number = 5,
+ pages = "491-553"
+}
+
+@PhdThesis{Darvas:thesis,
+ author = {{\'A}d{\'a}m P{\'e}ter Darvas},
+ title = {Reasoning About Data Abstraction in Contract Languages},
+ school = {ETH Zurich},
+ year = {2009},
+ note = {Diss. ETH No. 18622},
+}
+
+@InProceedings{SmansEtAl:VeriCool,
+ author = {Jan Smans and Bart Jacobs and Frank Piessens and Wolfram Schulte},
+ title = {Automatic Verifier for {J}ava-Like Programs Based on Dynamic Frames},
+ booktitle = {Fundamental Approaches to Software Engineering, 11th
+ International Conference, FASE 2008},
+ editor = {Jos{\'e} Luiz Fiadeiro and Paola Inverardi},
+ volume = {4961},
+ series = lncs,
+ publisher = {Springer},
+ month = mar # "--" # apr,
+ year = {2008},
+ pages = {261-275},
+}
+
+@inproceedings{Why:Platform,
+ author = {Jean-Christophe Filli{\^a}tre and Claude March{\'e}},
+ title = {The {Why}/{Krakatoa}/{Caduceus} Platform for Deductive Program Verification},
+ booktitle = {Computer Aided Verification, 19th International Conference, CAV 2007},
+ editor = {Werner Damm and Holger Hermanns},
+ volume = {4590},
+ series = lncs,
+ publisher = {Springer},
+ month = jul,
+ year = {2007},
+ pages = {173--177}
+}
+
+@InProceedings{BarrettTinelli:CVC3,
+ author = {Clark Barrett and Cesare Tinelli},
+ title = {{CVC3}},
+ booktitle = {Computer Aided Verification, 19th International Conference, CAV 2007},
+ editor = {Werner Damm and Holger Hermanns},
+ volume = {4590},
+ series = lncs,
+ publisher = {Springer},
+ month = jul,
+ year = {2007},
+ pages = {298-302},
+}
+
+@InProceedings{HubertMarche:SchorrWaite,
+ author = {Thierry Hubert and Claude March{\'e}},
+ title = {A case study of {C} source code verification: the
+ {S}chorr-{W}aite algorithm},
+ booktitle = {Third IEEE International Conference on Software
+ Engineering and Formal Methods (SEFM 2005)},
+ editor = {Bernhard K. Aichernig and Bernhard Beckert},
+ publisher = {IEEE Computer Society },
+ month = sep,
+ year = {2005},
+ pages = {190-199},
+}
+
+@Article{BroyPepper:SchorrWaite,
+ author = {Manfred Broy and Peter Pepper},
+ title = {Combining Algebraic and Algorithmic Reasoning: An
+ Approach to the {S}chorr-{W}aite Algorithm},
+ journal = toplas,
+ volume = {4},
+ number = {3},
+ month = jul,
+ year = {1982},
+ pages = {362-381},
+}
+
+@Article{MehtaNipkow:SchorrWaite,
+ author = {Farhad Mehta and Tobias Nipkow},
+ title = {Proving pointer programs in higher-order logic},
+ journal = {Information and Computation},
+ year = {2005},
+ volume = {199},
+ number = {1--2},
+ pages = {200-227},
+ month = may # "--" # jun,
+}
+
+@InProceedings{Abrial:SchorrWaite,
+ author = {Jean-Raymond Abrial},
+ title = {Event Based Sequential Program Development:
+ Application to Constructing a Pointer Program},
+ booktitle = {FME 2003: Formal Methods, International Symposium of
+ Formal Methods Europe},
+ editor = {Keijiro Araki and Stefania Gnesi and Dino Mandrioli},
+ volume = {2805},
+ series = lncs,
+ publisher = {Springer},
+ month = sep,
+ year = {2003},
+ pages = {51-74},
+}
+
+@InCollection{Bubel:SchorrWaite,
+ author = {Richard Bubel},
+ title = {The Schorr-Waite-Algorithm},
+ booktitle = {Verification of Object-Oriented Software: The {KeY} Approach},
+ crossref = {KeY:book},
+ chapter = {15},
+}
+
+@InProceedings{BanerjeeEtAl:RegionLogic,
+ author = {Anindya Banerjee and David A. Naumann and Stan Rosenberg},
+ title = {Regional Logic for Local Reasoning about Global Invariants},
+ booktitle = {ECOOP 2008 --- Object-Oriented Programming, 22nd European Conference},
+ editor = {Jan Vitek},
+ series = lncs,
+ volume = 5142,
+ publisher = {Springer},
+ month = jul,
+ year = 2008,
+ pages = {387-411},
+}
+
+@Book{Abrial:BBook,
+ author = "J.-R. Abrial",
+ title = "The {B}-Book: Assigning Programs to Meanings",
+ publisher = "Cambridge University Press",
+ year = 1996
+}
+
+@Book{Abrial:EventB:book,
+ author = {Jean-Raymond Abrial},
+ title = {Modeling in {Event-B}: System and Software Engineering},
+ publisher = {Cambridge University Press},
+ year = {2010},
+}
+
+@Article{MisraCook:Orc,
+ author = {Jayadev Misra and William R. Cook},
+ title = {Computation Orchestration: A Basis for Wide-Area Computing},
+ journal = {Software and Systems Modeling},
+ year = {2007},
+ volume = 6,
+ number = 1,
+ pages = {83-110},
+ month = mar,
+}
+
+@Book{Jackson:Alloy:book,
+ author = {Daniel Jackson},
+ title = {Software Abstractions: Logic, Language, and Analysis},
+ publisher = {MIT Press},
+ year = {2006},
+}
+
+@InProceedings{JacksonEtAl:Formula,
+ author = {Ethan K. Jackson and Dirk Seifert and Markus
+ Dahlweid and Thomas Santen and Nikolaj Bj{\o}rner
+ and Wolfram Schulte},
+ title = {Specifying and Composing Non-functional Requirements
+ in Model-Based Development},
+ booktitle = {Proceedings of the 8th International Conference on
+ Software Composition},
+ pages = {72-89},
+ year = {2009},
+ editor = {Alexandre Bergel and Johan Fabry},
+ series = lncs,
+ volume = {5634},
+ month = jul,
+ publisher = {Springer},
+}
+
+@InProceedings{HarelEtAl:PlayInPlayOut,
+ author = {David Harel and Hillel Kugler and Rami Marelly and
+ Amir Pnueli},
+ title = {Smart {P}lay-out of Behavioral Requirements},
+ booktitle = {Formal Methods in Computer-Aided Design, 4th
+ International Conference, FMCAD 2002},
+ pages = {378-398},
+ year = {2002},
+ editor = {Mark Aagaard and John W. O'Leary},
+ volume = {2517},
+ series = lncs,
+ month = nov,
+ publisher = {Springer},
+}
+
+@Article{Smith:KIDS-overview,
+ author = "Douglas R. Smith",
+ title = "{KIDS}: A Semi-Automatic Program Development System",
+ journal = {IEEE Transactions on Software Engineering },
+ volume = 16,
+ number = 9,
+ month = sep,
+ year = 1990,
+ pages = "1024-1043",
+}
+
+@article{Hoare:DataRepresentations,
+ author = "C. A. R. Hoare",
+ title = "Proof of correctness of data representations",
+ journal = acta,
+ volume = 1,
+ number = 4,
+ year = 1972,
+ pages = "271-281"
+}
+
+@InProceedings{Abrial:FM-in-practice,
+ author = {Jean-Raymond Abrial},
+ title = {Formal methods in industry: achievements, problems, future},
+ booktitle = {28th International Conference on Software Engineering (ICSE 2006)},
+ editor = {Leon J. Osterweil and H. Dieter Rombach and Mary Lou Soffa},
+ month = may,
+ year = {2006},
+ publisher = {ACM},
+ pages = {761-768},
+}
+
+@InProceedings{MartinEtAl:AsynchMIPS,
+ author = {Alain J. Martin and Andrew Lines and Rajit Manohar
+ and Mika Nystr{\"o}m and Paul I. P{\'e}nzes and
+ Robert Southworth and Uri Cummings},
+ title = {The Design of an Asynchronous MIPS R3000 Microprocessor},
+ booktitle = {17th Conference on Advanced Research in VLSI (ARVLSI '97}},
+ month = sep,
+ year = {1997},
+ publisher = {IEEE Computer Society},
+ pages = {164-181},
+}
+
+@InProceedings{BallEtAll:ScalableChecking,
+ author = {Thomas Ball and Brian Hackett and Shuvendu K. Lahiri
+ and Shaz Qadeer and Julien Vanegue},
+ title = {Towards Scalable Modular Checking of User-Defined Properties},
+ booktitle = {Verified Software: Theories, Tools, Experiments,
+ (VSTTE 2010)},
+ editor = {Gary T. Leavens and Peter O'Hearn and Sriram K. Rajamani},
+ volume = {6217},
+ series = lncs,
+ publisher = {Springer},
+ month = aug,
+ year = {2010},
+ pages = {1-24},
+}
+
+@InProceedings{RegisGianasPottier:FunctionalHoare,
+ author = {Yann R{\'e}gis-Gianas and Fran{\,c}ois Pottier},
+ title = {A {H}oare Logic for Call-by-Value Functional Programs},
+ booktitle = {Mathematics of Program Construction, 9th International Conference, MPC 2008},
+ pages = {305-335},
+ year = {2008},
+ editor = {Philippe Audebaud and Christine Paulin-Mohring},
+ volume = {5133},
+ series = lncs,
+ month = jul,
+ publisher = {Springer},
+}
+
+@InProceedings{VeanesEtAl:SpecExplorer,
+ author = {Margus Veanes and Colin Campbell and Wolfgang
+ Grieskamp and Wolfram Schulte and Nikolai Tillmann
+ and Lev Nachmanson},
+ title = {Model-Based Testing of Object-Oriented Reactive
+ Systems with {Spec} {Explorer}},
+ booktitle = {Formal Methods and Testing},
+ pages = {39-76},
+ year = {2008},
+ editor = {Robert M. Hierons and Jonathan P. Bowen and Mark Harman},
+ volume = {4949},
+ series = lncs,
+ publisher = {Springer},
+}
+
+@book{Dijkstra:Discipline,
+ author = "Edsger W. Dijkstra",
+ title = "A Discipline of Programming",
+ publisher = "Prentice Hall",
+ address = "Englewood Cliffs, NJ",
+ year = 1976
+}
+
+@InProceedings{LeinoMueller:ESOP2009,
+ author = {K. Rustan M. Leino and Peter M{\"u}ller},
+ title = {A Basis for Verifying Multi-threaded Programs},
+ booktitle = {Programming Languages and Systems, 18th European
+ Symposium on Programming, ESOP 2009},
+ editor = {Giuseppe Castagna},
+ volume = {5502},
+ series = lncs,
+ publisher = {Springer},
+ month = mar,
+ year = 2009,
+ pages = {378-393},
+}
+
+@InProceedings{LeinoRuemmer:Boogie2,
+ author = {K. Rustan M. Leino and Philipp R{\"u}mmer},
+ title = {A Polymorphic Intermediate Verification Language:
+ Design and Logical Encoding},
+ booktitle = {Tools and Algorithms for the Construction and
+ Analysis of Systems, 16th International Conference,
+ TACAS 2010},
+ editor = {Javier Esparza and Rupak Majumdar},
+ series = lncs,
+ volume = 6015,
+ publisher = {Springer},
+ month = mar,
+ year = 2010,
+ pages = {312-327},
+}
+
+@book{LiskovGuttag:book,
+ author = "Barbara Liskov and John Guttag",
+ title = "Abstraction and Specification in Program Development",
+ publisher = "MIT Press",
+ series = "MIT Electrical Engineering and Computer Science Series",
+ year = 1986
+}
+
+@TechReport{DahlEtAl:Simula67,
+ author = {Ole-Johan Dahl and Bj{\o}rn Myhrhaug and Kristen Nygaard},
+ title = {Common Base Language},
+ institution = {Norwegian Computing Center},
+ type = {Publication},
+ number = {S-22},
+ month = oct,
+ year = 1970,
+}
+
+@inproceedings{LeinoMueller:ModelFields,
+ author = {K. Rustan M. Leino and
+ Peter M{\"u}ller},
+ title = {A Verification Methodology for Model Fields},
+ booktitle = "Programming Languages and Systems, 15th European Symposium on Programming, ESOP 2006",
+ editor = "Peter Sestoft",
+ series = lncs,
+ volume = 3924,
+ publisher = "Springer",
+ month = mar,
+ year = 2006,
+ pages = {115-130},
+}
+
+@InProceedings{CarterEtAl:UsingPerfectDeveloper,
+ author = {Gareth Carter and Rosemary Monahan and Joseph M. Morris},
+ title = {Software Refinement with {P}erfect {D}eveloper},
+ booktitle = {Third IEEE International Conference on Software
+ Engineering and Formal Methods (SEFM 2005)},
+ pages = {363-373},
+ editor = {Bernhard K. Aichernig and Bernhard Beckert},
+ month = sep,
+ year = {2005},
+ publisher = {IEEE Computer Society},
+}
+
+@InProceedings{Abrial:SchorrWaite,
+ author = {Jean-Raymond Abrial},
+ title = {Event Based Sequential Program Development:
+ Application to Constructing a Pointer Program},
+ booktitle = {FME 2003: Formal Methods, International Symposium of
+ Formal Methods Europe},
+ editor = {Keijiro Araki and Stefania Gnesi and Dino Mandrioli},
+ volume = {2805},
+ series = lncs,
+ publisher = {Springer},
+ month = sep,
+ year = {2003},
+ pages = {51-74},
+}
+
+@article{Barnett-etal04,
+ author = {Mike Barnett and Robert DeLine and Manuel F{\"a}hndrich and
+ K. Rustan M. Leino and Wolfram Schulte},
+ title = {Verification of Object-Oriented Programs with Invariants},
+ journal = {Journal of Object Technology},
+ volume = 3,
+ number = 6,
+ year = 2004,
+ pages = {27-56},
+}
+
+@InProceedings{SmansEtAl:ImplicitDynamicFrames,
+ author = {Jan Smans and Bart Jacobs and Frank Piessens},
+ title = {Implicit Dynamic Frames: Combining Dynamic Frames
+ and Separation Logic},
+ booktitle = {ECOOP 2009 --- Object-Oriented Programming, 23rd
+ European Conference},
+ editor = {Sophia Drossopoulou},
+ volume = {5653},
+ series = lncs,
+ publisher = {Springer},
+ month = jul,
+ year = {2009},
+ pages = {148-172},
+}
+
+@inproceedings{GriesPrins:Encapsulation,
+ author = "David Gries and Jan Prins",
+ title = "A New Notion of Encapsulation",
+ booktitle = "Proceedings of the {ACM} {SIGPLAN} 85
+ Symposium on Language Issues in Programming Environments",
+ publisher = "ACM",
+ series = "SIGPLAN Notices 20",
+ number = 7,
+ month = jul,
+ year = 1985,
+ pages = "131-139"
+}
+
+@InProceedings{YangHawblitzel:Verve,
+ author = {Jean Yang and Chris Hawblitzel},
+ title = {Safe to the last instruction: automated verification of a type-safe operating system},
+ booktitle = {Proceedings of the 2010 ACM SIGPLAN Conference on
+ Programming Language Design and Implementation, PLDI
+ 2010},
+ editor = {Benjamin G. Zorn and Alexander Aiken},
+ month = jun,
+ year = {2010},
+ publisher = {ACM},
+ pages = {99-110},
+}
+
+@Book{BoyerMoore:book,
+ author = {Robert S. Boyer and J Strother Moore},
+ title = {A Computational Logic},
+ publisher = {Academic Press},
+ series = {ACM Monograph Series},
+ year = {1979},
+}
+
+@article{HoareWirth:Pascal,
+ author = "C. A. R. Hoare and N. Wirth",
+ title = "An axiomatic definition of the programming language {PASCAL}",
+ journal = acta,
+ volume = 2,
+ number = 4,
+ year = 1973,
+ pages = "335-355"
+}
+
+@article{Hoare:AxiomaticBasis,
+ author = "C. A. R. Hoare",
+ title = "An axiomatic basis for computer programming",
+ journal = cacm,
+ volume = 12,
+ number = 10,
+ year = 1969,
+ month = oct,
+ pages = "576--580,583"
+}
+
+@InProceedings{LeinoMoskal:vacid0-notYetConfirmed,
+ author = {K. Rustan M. Leino and Micha{\l} Moskal},
+ title = {{VACID-0}: {V}erification of {A}mple {C}orrectness
+ of {I}nvariants of {D}ata-structures, Edition 0},
+ booktitle = {VS-Tools & Experiments},
+ year = 2010,
+ editor = {Rajeev Joshi and Tiziana Margaria and Peter
+ M{\"u}ller and David Naumann and Hongseok Yang},
+ series = {VSTTE 2010 Workshop Proceedings},
+ publisher = {ETH Zurich Technical Report 676},
+ month = aug,
+}
+
+@InCollection{Chalice:tutorial,
+ author = {K. Rustan M. Leino and Peter M{\"u}ller and Jan Smans},
+ title = {Verification of Concurrent Programs with {C}halice},
+ booktitle = {Foundations of Security Analysis and Design {V}: {FOSAD} 2007/2008/2009 Tutorial Lectures},
+ editor = {Alessandro Aldini and Gilles Barthe and Roberto Gorrieri},
+ volume = {5705},
+ series = lncs,
+ publisher = {Springer},
+ year = {2009},
+ pages = {195-222}
+}
+
+@inproceedings{LeinoMuellerSmans10,
+ author = {K. Rustan M. Leino and Peter M{\"u}ller and Jan Smans},
+ title = {Deadlock-Free Channels and Locks},
+ booktitle = {Programming Languages and Systems, 19th European Symposium on Programming, ESOP 2010},
+ editor = {Andrew D. Gordon},
+ volume = {6012},
+ series = lncs,
+ publisher = {Springer},
+ month = mar,
+ year = {2010},
+ pages = {407-426}
+}
+
+@Book{BundyEtAl:Rippling,
+ author = {Alan Bundy and David Basin and Dieter Hutter and Andrew Ireland},
+ title = {Rippling: Meta-level Guidance for Mathematical Reasoning},
+ publisher = {Cambridge University Press},
+ volume = {56},
+ series = {Cambridge Tracts in Theoretical Computer Science},
+ year = {2005},
+}
+
+@book{Gries:Science,
+ author = "David Gries",
+ title = "The Science of Programming",
+ publisher = "Springer-Verlag",
+ series = "Texts and Monographs in Computer Science",
+ year = 1981
+}
+
+@Book{DijkstraFeijen:Book,
+ author = "Edsger W. Dijkstra and W. H. J. Feijen",
+ title = "A Method of Programming",
+ publisher = "Addison-Wesley",
+ month = jul,
+ year = 1988,
+}
+
+@book{Kaldewaij:Programming,
+ author = "Anne Kaldewaij",
+ title = "Programming: The Derivation of Algorithms",
+ publisher = "Prentice-Hall International",
+ year = 1990,
+ series = "Series in Computer Science",
+}
+
+@InProceedings{LeinoMonahan:VSTTE2010,
+ author = {K. Rustan M. Leino and Rosemary Monahan},
+ title = {Dafny Meets the Verification Benchmarks Challenge},
+ booktitle = {Verified Software: Theories, Tools, Experiments,
+ Third International Conference, VSTTE 2010},
+ pages = {112-126},
+ year = {2010},
+ editor = {Gary T. Leavens and Peter W. O'Hearn and Sriram K. Rajamani},
+ volume = {6217},
+ series = lncs,
+ month = aug,
+ publisher = {Springer},
+}
+
+@InProceedings{VSComp2010:report,
+ author = {Vladimir Klebanov and Peter M{\"u}ller and Natarajan Shankar and
+ Gary T. Leavens and Valentin W{\"u}stholz and Eyad Alkassar and
+ Rob Arthan and Derek Bronish and Rod Chapman and Ernie Cohen and
+ Mark Hillebrand and Bart Jacobs and K. Rustan M. Leino and
+ Rosemary Monahan and Frank Piessens and Nadia Polikarpova and
+ Tom Ridge and Jan Smans and Stephan Tobies and Thomas Tuerk and
+ Mattias Ulbrich and Benjamin Wei{\ss}},
+ title = {The 1st Verified Software Competition: Experience Report},
+ booktitle = {FM 2011: Formal Methods --- 17th International
+ Symposium on Formal Methods},
+ pages = {154-168},
+ year = {2011},
+ editor = {Michael Butler and Wolfram Schulte},
+ volume = {6664},
+ series = lncs,
+ month = jun,
+ publisher = {Springer},
+}
+
+@InProceedings{Leino:Dafny:LPAR16,
+ author = {K. Rustan M. Leino},
+ title = {Dafny: An Automatic Program Verifier for Functional Correctness},
+ booktitle = {LPAR-16},
+ year = {2010},
+ volume = {6355},
+ series = lncs,
+ publisher = {Springer},
+ month = apr,
+ editor = {Edmund M. Clarke and Andrei Voronkov},
+ pages = {348-370},
+}
+
+@book{BackVonWright:Book,
+ author = "Ralph-Johan Back and von Wright, Joakim",
+ title = "Refinement Calculus: A Systematic Introduction",
+ series = "Graduate Texts in Computer Science",
+ publisher = "Springer-Verlag",
+ year = 1998
+}
+
+@Article{BalzerCheathamGreen:1990s,
+ author = {Robert Balzer and {Cheatham, Jr.}, Thomas E. and Cordell Green},
+ title = {Software Technology in the 1990's: Using a New Paradigm},
+ journal = {IEEE Computer},
+ year = {1983},
+ volume = {16},
+ number = {11},
+ pages = {39-45 },
+ month = nov,
+}
+
+@InProceedings{Zloof:QBE,
+ author = {Mosh{\'e} M. Zloof},
+ title = {Query by Example},
+ booktitle = {American Federation of Information Processing
+ Societies: 1975 National Computer Conference},
+ pages = {431-438},
+ year = {1975},
+ month = may,
+ publisher = {AFIPS Press },
+}
+
+@InProceedings{HarrisGulwani:PLDI2011,
+ author = {William R. Harris and Sumit Gulwani},
+ title = {Spreadsheet table transformations from examples},
+ booktitle = {Proceedings of the 32nd ACM SIGPLAN Conference on
+ Programming Language Design and Implementation, PLDI
+ 2011},
+ pages = {317-328},
+ year = {2011},
+ editor = {Mary W. Hall and David A. Padua},
+ month = jun,
+ publisher = {ACM},
+}
+
+@Article{Smith:KIDS-overview,
+ author = "Douglas R. Smith",
+ title = "{KIDS}: A Semi-Automatic Program Development System",
+ journal = {IEEE Transactions on Software Engineering },
+ volume = 16,
+ number = 9,
+ month = sep,
+ year = 1990,
+ pages = "1024-1043",
+}
+
+@Article{RodinToolset,
+ author = {Jean-Raymond Abrial and Michael Butler and Stefan
+ Hallerstede and Thai Son Hoang and Farhad Mehta and
+ Laurent Voisin},
+ title = {Rodin: An Open Toolset for Modelling and Reasoning in {Event-B}},
+ journal = {International Journal on Software Tools for Technology Transfer},
+ year = {2010},
+ month = apr,
+}
+
+@Article{Summers:LISP-from-examples,
+ author = {Phillip D. Summers},
+ title = {A Methodology for {LISP} Program Construction from Examples},
+ journal = jacm,
+ year = {1977},
+ volume = {24},
+ number = {1},
+ pages = {161-175},
+ month = jan,
+}
+
+@InProceedings{Pex:overview,
+ author = {Nikolai Tillmann and de Halleux, Jonathan},
+ title = {Pex---White Box Test Generation for {.NET}},
+ booktitle = {Tests and Proofs, Second International Conference, TAP 2008},
+ pages = {134-153},
+ year = {2008},
+ editor = {Bernhard Beckert and Reiner H{\"a}hnle},
+ series = lncs,
+ volume = {4966},
+ month = apr,
+ publisher = {Springer},
+}
+
+@InProceedings{GodefroidKlarlundSen:DART,
+ author = {Patrice Godefroid and Nils Klarlund and Koushik Sen},
+ title = {{DART}: directed automated random testing},
+ booktitle = {Proceedings of the ACM SIGPLAN 2005 Conference on
+ Programming Language Design and Implementation},
+ pages = {213-223},
+ year = {2005},
+ editor = {Vivek Sarkar and Mary W. Hall},
+ month = jun,
+ publisher = {ACM},
+}
+
+@PhdThesis{Monahan:thesis,
+ author = {Rosemary Monahan},
+ title = {Data Refinement in Object-Oriented Verification},
+ school = {Dublin City University},
+ year = {2010},
+}
+
+@InProceedings{Denali:pldi2002,
+ author = {Rajeev Joshi and Greg Nelson and Keith H. Randall},
+ title = {Denali: A Goal-directed Superoptimizer},
+ booktitle = {Proceedings of the 2002 ACM SIGPLAN Conference on
+ Programming Language Design and Implementation
+ (PLDI)},
+ pages = {304-314},
+ year = {2002},
+ month = jun,
+ publisher = {ACM},
+}
+@Book{SETL,
+ author = {J. T. Schwartz and R. B. K. Dewar and E. Dubinsky and E. Schonberg},
+ title = {Programming with Sets: An Introduction to {SETL}},
+ series = {Texts and Monographs in Computer Science},
+ publisher = {Springer},
+ year = {1986},
+}
+
+@InProceedings{KuncakEtAl:PLDI2010,
+ author = {Viktor Kuncak and Mika{\"e}l Mayer and Ruzica Piskac
+ and Philippe Suter},
+ title = {Complete functional synthesis},
+ booktitle = {Proceedings of the 2010 ACM SIGPLAN Conference on
+ Programming Language Design and Implementation, PLDI
+ 2010},
+ pages = {316-329},
+ year = {2010},
+ editor = {Benjamin G. Zorn and Alexander Aiken},
+ month = jun,
+ publisher = {ACM},
+}
+
+@Article{JML:ToolSuite:STTT,
+ author = {Lilian Burdy and Yoonsik Cheon and David R. Cok and
+ Michael D. Ernst and Joeseph R. Kiniry and Gary T. Leavens and
+ K. Rustan M. Leino and Erik Poll},
+ title = {An overview of {JML} tools and applications},
+ journal = {International Journal on Software Tools
+ for Technology Transfer},
+ volume = 7,
+ number = 3,
+ publisher = {Springer},
+ month = jun,
+ year = 2005,
+ pages = {212-232},
+}
+
+@InProceedings{Green:ProblemSolving,
+ author = {Cordell Green},
+ title = {Application of Theorem Proving to Problem Solving},
+ booktitle = {Proceedings of the 1st International Joint Conference on Artificial Intelligence},
+ editor = {Donald E. Walker and Lewis M. Norton},
+ pages = {219-240},
+ year = {1969},
+ month = may,
+ publisher = {William Kaufmann},
+}
+
+@Article{MannaWaldinger:CACM1971,
+ author = {Zohar Manna and Richard J. Waldinger},
+ title = {Towards automatic program synthesis},
+ journal = cacm,
+ year = {1971},
+ volume = {14},
+ number = {3},
+ pages = {151-165},
+ month = mar,
+}
+
+@Article{RichWaters:ProgAppren,
+ author = {Charles Rich and Richard C. Waters},
+ title = {The {P}rogrammer's {A}pprentice: A Research Overview},
+ journal = {IEEE Computer},
+ year = {1988},
+ volume = {21},
+ number = {11},
+ pages = {10-25},
+ month = nov,
+}
+
+@InProceedings{Green:PSI,
+ author = {Cordell Green},
+ title = {The Design of the {PSI} Program Synthesis System},
+ booktitle = {Proceedings of the 2nd International Conference on Software Engineering},
+ pages = {4-18},
+ year = {1976},
+ month = oct,
+ publisher = {IEEE Computer Society},
+}
+
+@Article{SpecSharp:Retrospective:CACM,
+ author = {Mike Barnett and Manuel F{\"a}hndrich and
+ K. Rustan M. Leino and Peter M{\"u}ller and
+ Wolfram Schulte and Herman Venter},
+ title = {Specification and Verification: The {Spec\#} Experience},
+ journal = cacm,
+ volume = {54},
+ number = {6},
+ pages = {81-91},
+ month = jun,
+ year = 2011,
+}
+
+@article{Filipovic:SepLogicRefinement,
+ author = {Ivana Filipovi{\'c} and Peter O'Hearn and
+ Noah Torp-Smith and Hongseok Yang},
+ title = {Blaming the client: on data refinement in the presence of pointers},
+ journal = {Formal Aspects of Computing},
+ volume = {22},
+ number = {5},
+ month = sep,
+ year = {2010},
+ pages = {547-583},
+}
+
+@inproceedings{Grandy:JavaRefinement,
+ author = {Grandy, Holger and Stenzel, Kurt and Reif, Wolfgang},
+ title = {A refinement method for {J}ava programs},
+ booktitle = {Formal Methods for Open Object-Based Distributed Systems, 9th IFIP WG 6.1 International Conference, FMOODS 2007},
+ editor = {Marcello M. Bonsangue and Einar Broch Johnsen},
+ series = lncs,
+ number = {4468},
+ month = jun,
+ year = {2007},
+ publisher = {Springer},
+ pages = {221--235},
+}
+
+@InCollection{KoenigLeino:MOD2011,
+ author = {Jason Koenig and K. Rustan M. Leino},
+ title = {Getting Started with {D}afny: A Guide},
+ booktitle = {Software Safety and Security: Tools for Analysis and Verification},
+ pages = {152-181},
+ publisher = {IOS Press},
+ year = {2012},
+ editor = {Tobias Nipkow and Orna Grumberg and Benedikt Hauptmann},
+ volume = {33},
+ series = {NATO Science for Peace and Security Series D: Information and Communication Security},
+ note = {Summer School Marktoberdorf 2011 lecture notes},
+}
+
+@InProceedings{VonWright:ExtendingWindowInference,
+ author = {von Wright, Joakim},
+ title = {Extending Window Inference},
+ booktitle = {Theorem Proving in Higher Order Logics, 11th International Conference, TPHOLs'98},
+ pages = {17-32},
+ year = {1998},
+ editor = {Jim Grundy and Malcolm C. Newey},
+ volume = {1479},
+ series = lncs,
+ publisher = {Springer},
+}
+
+@InProceedings{BauerWenzel:IsarExperience,
+ author = {Gertrud Bauer and Markus Wenzel},
+ title = {Calculational reasoning revisited: an {I}sabelle/{I}sar experience},
+ booktitle = {Theorem Proving in Higher Order Logics, 14th International Conference, TPHOLs 2001},
+ pages = {75-90},
+ year = {2001},
+ editor = {Richard J. Boulton and Paul B. Jackson},
+ volume = {2152},
+ series = lncs,
+ month = sep,
+ publisher = {Springer},
+}
+
+@InProceedings{Leino:induction,
+ author = {K. Rustan M. Leino},
+ title = {Automating Induction with an {SMT} Solver},
+ booktitle = {Verification, Model Checking, and Abstract Interpretation --- 13th International Conference, VMCAI 2012},
+ pages = {315-331},
+ year = {2012},
+ editor = {Viktor Kuncak and Andrey Rybalchenko},
+ volume = {7148},
+ series = lncs,
+ month = jan,
+ publisher = {Springer},
+}
+
+@InProceedings{LGLM:BVD,
+ author = {Le Goues, Claire and K. Rustan M. Leino and Micha{\l} Moskal},
+ title = {The {B}oogie {V}erification {D}ebugger (Tool Paper)},
+ booktitle = {Software Engineering and Formal Methods --- 9th International Conference, SEFM 2011},
+ pages = {407-414},
+ year = {2011},
+ editor = {Gilles Barthe and Alberto Pardo and Gerardo Schneider},
+ volume = {7041},
+ series = lncs,
+ month = nov,
+ publisher = {Springer},
+}
+
+@InProceedings{Filliatre:2lines,
+ author = {Jean-Christophe Filli{\^a}tre},
+ title = {Verifying two lines of {C} with {Why3}: an exercise in
+ program verification},
+ booktitle = {Verified Software: Theories, Tools, Experiments ---
+ 4th International Conference, VSTTE 2012},
+ pages = {83-97},
+ year = {2012},
+ editor = {Rajeev Joshi and Peter M{\"u}ller and Andreas Podelski},
+ volume = {7152},
+ series = lncs,
+ month = jan,
+ publisher = {Springer},
+}
+
+@InCollection{LeinoMoskal:UsableProgramVerification,
+ author = {K. Rustan M. Leino and Micha{\l} Moskal},
+ title = {Usable Auto-Active Verification},
+ booktitle = {UV10 (Usable Verification) workshop},
+ year = {2010},
+ editor = {Tom Ball and Lenore Zuck and N. Shankar},
+ month = nov,
+ publisher = {\url{http://fm.csl.sri.com/UV10/}},
+}
+
+@InProceedings{LeinoMonahan:Comprehensions,
+ author = {K. Rustan M. Leino and Rosemary Monahan},
+ title = {Reasoning about Comprehensions with First-Order {SMT} Solvers},
+ booktitle = {Proceedings of the 2009 ACM Symposium on Applied Computing (SAC)},
+ editor = {Sung Y. Shin and Sascha Ossowski},
+ publisher = {ACM},
+ month = mar,
+ year = 2009,
+ pages = {615-622},
+}
+
+@TechReport{VeriFast:TR,
+ author = {Bart Jacobs and Frank Piessens},
+ title = {The {VeriFast} program verifier},
+ institution = {Department of Computer Science, Katholieke Universiteit Leuven},
+ year = {2008},
+ number = {CW-520},
+ month = aug,
+}
+
+@book{DijkstraScholten:book,
+ author = "Edsger W. Dijkstra and Carel S. Scholten",
+ title = "Predicate Calculus and Program Semantics",
+ publisher = "Springer-Verlag",
+ series = "Texts and Monographs in Computer Science",
+ year = 1990
+}
+
+@Book{KeY:book,
+ author = {Bernhard Beckert and Reiner H{\"a}hnle and Peter H. Schmitt},
+ title = {Verification of Object-Oriented Software: The {KeY} Approach},
+ volume = 4334,
+ series = lnai,
+ publisher = {Springer},
+ year = 2007,
+}
+
+@Book{Coq:book,
+ author = {Yves Bertot and Pierre Cast{\'e}ran},
+ title = {Interactive Theorem Proving and Program Development --- {C}oq'{A}rt: The Calculus of Inductive Constructions},
+ publisher = {Springer},
+ year = {2004},
+ series = {Texts in Theoretical Computer Science},
+}
+
+@Book{ACL2:book,
+ author = {Matt Kaufmann and Panagiotis Manolios and J Strother Moore},
+ title = {Computer-Aided Reasoning: An Approach},
+ publisher = {Kluwer Academic Publishers},
+ year = {2000},
+}
+
+@InProceedings{Coq:Coinduction,
+ author = {Eduardo Gim{\'e}nez},
+ title = {An Application of Co-inductive Types in {Coq}: Verification of the Alternating Bit Protocol},
+ booktitle = {Types for Proofs and Programs, International Workshop TYPES'95},
+ pages = {135-152},
+ year = {1996},
+ editor = {Stefano Berardi and Mario Coppo},
+ volume = 1158,
+ series = lncs,
+ publisher = {Springer},
+}
+
+@InCollection{JacobsRutten:IntroductionCoalgebra,
+ author = {Bart Jacobs and Jan Rutten},
+ title = {An Introduction to (Co)Algebra and (Co)Induction},
+ booktitle = {Advanced Topics in Bisimulation and Coinduction},
+ editor = {Davide Sangiorgi and Jan Rutten},
+ series = {Cambridge Tracts in Theoretical Computer Science},
+ number = {52},
+ publisher = {Cambridge University Press},
+ month = oct,
+ year = {2011},
+ pages = {38-99},
+}
+
+@InProceedings{SonnexEtAl:Zeno,
+ author = {William Sonnex and Sophia Drossopoulou and Susan Eisenbach},
+ title = {Zeno: An Automated Prover for Properties of Recursive
+ Data Structures},
+ booktitle = {Tools and Algorithms for the Construction and Analysis of
+ Systems --- 18th International Conference, TACAS 2012},
+ editor = {Cormac Flanagan and Barbara K{\"o}nig},
+ volume = {7214},
+ series = lncs,
+ year = {2012},
+ month = mar # "--" # apr,
+ publisher = {Springer},
+ pages = {407-421},
+}
+
+@InProceedings{JohanssonEtAl:IPT2010,
+ author = {Moa Johansson and Lucas Dixon and Alan Bundy},
+ title = {Case-Analysis for {R}ippling and Inductive Proof},
+ booktitle = {Interactive Theorem Proving, First International Conference, ITP 2010},
+ editor = {Matt Kaufmann and Lawrence C. Paulson},
+ volume = {6172},
+ series = lncs,
+ publisher = {Springer},
+ month = jul,
+ year = {2010},
+ pages = {291-306},
+}
+
+@Article{HatcliffEtAl:BISL,
+ author = {John Hatcliff and Gary T. Leavens and
+ K. Rustan M. Leino and Peter M{\"u}ller and Matthew Parkinson},
+ title = {Behavioral interface specification languages},
+ journal = {ACM Computing Surveys},
+ volume = {44},
+ number = {3},
+ note = {Article 16},
+ month = jun,
+ year = {2012},
+}
+
+@InProceedings{BoehmeNipkow:Sledgehammer,
+ author = {Sascha B{\"o}hme and Tobias Nipkow},
+ title = {Sledgehammer: {J}udgement {D}ay},
+ booktitle = {Automated Reasoning, 5th International Joint Conference, IJCAR 2010},
+ editor = {J{\"u}rgen Giesl and Reiner H{\"a}hnle},
+ year = {2010},
+ pages = {107-121},
+ volume = {6173},
+ series = lncs,
+ month = jul,
+ publisher = {Springer},
+}
+
+@InProceedings{Dafny:LASER2011,
+ author = {Luke Herbert and K. Rustan M. Leino and Jose Quaresma},
+ title = {Using {Dafny}, an Automatic Program Verifier},
+ booktitle = {Tools for Practical Software Verification, {LASER}, International Summer School 2011},
+ editor = {Bertrand Meyer and Martin Nordio},
+ volume = {7682},
+ series = lncs,
+ year = {2012},
+ pages = {156-181},
+ publisher = {Springer},
+}
+
+@Article{Leroy:CompCert:CACM,
+ author = {Xavier Leroy},
+ title = {Formal verification of a realistic compiler},
+ journal = cacm,
+ volume = {52},
+ number = {7},
+ year = {2009},
+ pages = {107-115},
+}
+
+@InProceedings{Leino:ITP2013,
+ author = {K. Rustan M. Leino},
+ title = {Automating Theorem Proving with {SMT}},
+ booktitle = {Interactive Theorem Proving --- 4th International Conference, ITP 2013},
+ year = {2013},
+ editor = {Sandrine Blazy and Christine Paulin-Mohring and David Pichardie},
+ volume = {7998},
+ series = lncs,
+ pages = {2-16},
+ month = jul,
+ publisher = {Springer},
+}
+
+@techreport{Nelson:thesis,
+ author = "Charles Gregory Nelson",
+ title = "Techniques for Program Verification",
+ institution = "Xerox PARC",
+ month = jun,
+ year = 1981,
+ number = "CSL-81-10",
+ note = "The author's PhD thesis"
+}
+
+@InProceedings{LernerMillsteinChambers:VerifiedOptimizations,
+ author = {Sorin Lerner and Todd Millstein and Craig Chambers},
+ title = {Automatically proving the correctness of compiler optimizations},
+ booktitle = {Proceedings of the ACM SIGPLAN 2003 Conference on
+ Programming Language Design and Implementation 2003},
+ year = {2003},
+ editor = {Ron Cytron and Rajiv Gupta},
+ pages = {220-231},
+ month = jun,
+ publisher = {ACM},
+}
+
+@InProceedings{BoyerHunt:ACL2,
+ author = {Robert S. Boyer and Hunt, Jr., Warren A.},
+ title = {Function Memoization and Unique Object Representation for {ACL2} Functions},
+ booktitle = {Proceedings of the Sixth International Workshop on
+ the ACL2 Theorem Prover and its Applications, ACL2 2006},
+ editor = {Panagiotis Manolios and Matthew Wilding},
+ month = aug,
+ year = {2006},
+ pages = {81--89},
+ publisher = {ACM},
+}
+
+@inproceedings{LeinoWuestholz:DafnyIDE,
+ author = {K. Rustan M. Leino and
+ Valentin W{\"{u}}stholz},
+ title = {The {D}afny Integrated Development Environment},
+ booktitle = {Proceedings 1st Workshop on Formal Integrated Development Environment,
+ {F-IDE} 2014},
+ month = apr,
+ year = {2014},
+ pages = {3--15},
+ editor = {Catherine Dubois and
+ Dimitra Giannakopoulou and
+ Dominique M{\'{e}}ry},
+ series = {{EPTCS}},
+ volume = {149},
+}
+
+@inproceedings{BarnettLeino:Weakest,
+ author = {Mike Barnett and K. Rustan M. Leino},
+ title = {Weakest-precondition of unstructured programs},
+ booktitle = {Proceedings of the 2005 ACM SIGPLAN-SIGSOFT Workshop on
+ Program Analysis For Software Tools and Engineering,
+ PASTE'05},
+ editor = {Michael D. Ernst and Thomas P. Jensen},
+ month = sep,
+ year = {2005},
+ pages = {82-87},
+ publisher = {ACM},
+}
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 00000000..e62332db
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,57 @@
+Building on Linux
+=================
+
+Tested on a fresh Linux Mint 17.2. Note that we now have official releases for
+Linux, so these instructions mostly apply to people interested in looking at
+Dafny's sources.
+
+0. Dependencies
+
+ apt install mono-devel g++
+
+1. Create an empty base directory
+
+ mkdir BASE-DIRECTORY
+ cd BASE-DIRECTORY
+
+2. Download and build Boogie:
+
+ git clone https://github.com/boogie-org/boogie
+ cd boogie
+ mozroots --import --sync
+ wget https://nuget.org/nuget.exe
+ mono ./nuget.exe restore Source/Boogie.sln
+ xbuild Source/Boogie.sln
+ cd ..
+
+3. Download and build Dafny:
+
+ hg clone https://hg.codeplex.com/dafny
+ cd dafny/Source/
+ xbuild Dafny.sln
+
+4. Download and unpack z3 (Dafny looks for `z3` in Binaries/z3/bin/)
+
+ cd BASE-DIRECTORY
+ wget https://github.com/Z3Prover/z3/releases/download/z3-4.4.0/z3-4.4.0-x64-ubuntu-14.04.zip
+ unzip z3-4.4.0-x64-ubuntu-14.04.zip
+ mv z3-4.4.0-x64-ubuntu-14.04 dafny/Binaries/z3
+
+5. (Optional) If you plan to use Boogie directly, copy (or symlink) the z3 binary so that Boogie, too, can find it:
+
+ cd BASE-DIRECTORY
+ rm -f boogie/Binaries/z3.exe
+ cp dafny/Binaries/z3/bin/z3 boogie/Binaries/z3.exe
+
+6. Run Dafny using the `dafny` shell script in the Binaries directory (it calls mono as appropriate)
+
+Editing in Emacs
+================
+
+The README at https://github.com/boogie-org/boogie-friends has plenty of
+information on how to set-up Emacs to work with Dafny. In short, it boils down
+to running [M-x package-install RET boogie-friends RET] and adding the following
+to your .emacs:
+ (setq flycheck-dafny-executable "BASE-DIRECTORY/dafny/Binaries/dafny")
+
+Do look at the README, though! It's full of useful tips.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..327f6778
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Microsoft Public License (MS-PL)
+
+This license governs use of the accompanying software. If you use the software, you
+accept this license. If you do not accept the license, do not use the software.
+
+1. Definitions
+The terms "reproduce," "reproduction," "derivative works," and "distribution" have the
+same meaning here as under U.S. copyright law.
+A "contribution" is the original software, or any additions or changes to the software.
+A "contributor" is any person that distributes its contribution under this license.
+"Licensed patents" are a contributor's patent claims that read directly on its contribution.
+
+2. Grant of Rights
+(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
+(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
+
+3. Conditions and Limitations
+(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
+(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
+(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
+(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
+(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
diff --git a/Source/Dafny.sln b/Source/Dafny.sln
index 40e71952..3e71a2a4 100644
--- a/Source/Dafny.sln
+++ b/Source/Dafny.sln
@@ -5,6 +5,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyDriver", "DafnyDriver\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyPipeline", "Dafny\DafnyPipeline.csproj", "{FE44674A-1633-4917-99F4-57635E6FA740}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyServer", "DafnyServer\DafnyServer.csproj", "{AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}"
+ ProjectSection(ProjectDependencies) = postProject
+ {FE44674A-1633-4917-99F4-57635E6FA740} = {FE44674A-1633-4917-99F4-57635E6FA740}
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Checked|.NET = Checked|.NET
@@ -28,8 +33,8 @@ Global
{63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|.NET.Build.0 = Debug|Any CPU
{63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|Mixed Platforms.ActiveCfg = Checked|Any CPU
- {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|Mixed Platforms.Build.0 = Checked|Any CPU
+ {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Release|.NET.ActiveCfg = Release|Any CPU
{63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Release|Any CPU.Build.0 = Release|Any CPU
@@ -45,13 +50,28 @@ Global
{FE44674A-1633-4917-99F4-57635E6FA740}.Debug|.NET.Build.0 = Debug|Any CPU
{FE44674A-1633-4917-99F4-57635E6FA740}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FE44674A-1633-4917-99F4-57635E6FA740}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FE44674A-1633-4917-99F4-57635E6FA740}.Debug|Mixed Platforms.ActiveCfg = Checked|Any CPU
- {FE44674A-1633-4917-99F4-57635E6FA740}.Debug|Mixed Platforms.Build.0 = Checked|Any CPU
+ {FE44674A-1633-4917-99F4-57635E6FA740}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {FE44674A-1633-4917-99F4-57635E6FA740}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{FE44674A-1633-4917-99F4-57635E6FA740}.Release|.NET.ActiveCfg = Release|Any CPU
{FE44674A-1633-4917-99F4-57635E6FA740}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE44674A-1633-4917-99F4-57635E6FA740}.Release|Any CPU.Build.0 = Release|Any CPU
{FE44674A-1633-4917-99F4-57635E6FA740}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{FE44674A-1633-4917-99F4-57635E6FA740}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Checked|.NET.ActiveCfg = Release|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Checked|Any CPU.ActiveCfg = Release|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Checked|Any CPU.Build.0 = Release|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Checked|Mixed Platforms.ActiveCfg = Checked|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Checked|Mixed Platforms.Build.0 = Checked|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Debug|.NET.ActiveCfg = Debug|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Release|.NET.ActiveCfg = Release|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Release|Mixed Platforms.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Source/Dafny/BigIntegerParser.cs b/Source/Dafny/BigIntegerParser.cs
new file mode 100644
index 00000000..cd2cf314
--- /dev/null
+++ b/Source/Dafny/BigIntegerParser.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Numerics;
+using System.Globalization;
+
+namespace Microsoft.Dafny {
+ internal static class BigIntegerParser {
+ /// <summary>
+ /// Mono does not support the BigInteger.TryParse method. In practice,
+ /// we seldom actually need to parse huge integers, so it makes sense
+ /// to support most real-life cases by simply trying to parse using
+ /// Int64, and only falling back if needed.
+ /// </summary>
+ internal static BigInteger Parse(string str, NumberStyles style) {
+ UInt64 parsed;
+ if (UInt64.TryParse(str, style, NumberFormatInfo.CurrentInfo, out parsed)) {
+ return new BigInteger(parsed);
+ } else {
+ // Throws on Mono 3.2.8
+ return BigInteger.Parse(str, style);
+ }
+ }
+
+ internal static BigInteger Parse(string str) {
+ return BigIntegerParser.Parse(str, NumberStyles.Integer);
+ }
+ }
+} \ No newline at end of file
diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs
index 9f83bf09..6d5e2961 100644
--- a/Source/Dafny/Cloner.cs
+++ b/Source/Dafny/Cloner.cs
@@ -17,12 +17,18 @@ namespace Microsoft.Dafny
if (m is DefaultModuleDecl) {
nw = new DefaultModuleDecl();
} else {
- nw = new ModuleDefinition(Tok(m.tok), name, m.IsAbstract, m.IsFacade, m.RefinementBaseName, m.Module, CloneAttributes(m.Attributes), true);
+ nw = new ModuleDefinition(Tok(m.tok), name, m.IsAbstract, m.IsFacade, m.IsExclusiveRefinement, m.RefinementBaseName, m.Module, CloneAttributes(m.Attributes), true);
}
foreach (var d in m.TopLevelDecls) {
nw.TopLevelDecls.Add(CloneDeclaration(d, nw));
}
- nw.RefinementBase = m.RefinementBase;
+ if (null != m.RefinementBase) {
+ nw.RefinementBase = m.RefinementBase;
+ }
+ if (null != m.RefinementBaseSig) {
+ nw.RefinementBaseSig = m.RefinementBaseSig;
+ }
+ nw.ClonedFrom = m;
nw.Height = m.Height;
return nw;
}
@@ -33,17 +39,17 @@ namespace Microsoft.Dafny
if (d is OpaqueTypeDecl) {
var dd = (OpaqueTypeDecl)d;
- return new OpaqueTypeDecl(Tok(dd.tok), dd.Name, m, dd.EqualitySupport, dd.TypeArgs.ConvertAll(CloneTypeParam), CloneAttributes(dd.Attributes));
+ return new OpaqueTypeDecl(Tok(dd.tok), dd.Name, m, dd.EqualitySupport, dd.TypeArgs.ConvertAll(CloneTypeParam), CloneAttributes(dd.Attributes), d);
} else if (d is TypeSynonymDecl) {
var dd = (TypeSynonymDecl)d;
var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
- return new TypeSynonymDecl(Tok(dd.tok), dd.Name, tps, m, CloneType(dd.Rhs), CloneAttributes(dd.Attributes));
+ return new TypeSynonymDecl(Tok(dd.tok), dd.Name, tps, m, CloneType(dd.Rhs), CloneAttributes(dd.Attributes), dd);
} else if (d is NewtypeDecl) {
var dd = (NewtypeDecl)d;
if (dd.Var == null) {
- return new NewtypeDecl(Tok(dd.tok), dd.Name, m, CloneType(dd.BaseType), CloneAttributes(dd.Attributes));
+ return new NewtypeDecl(Tok(dd.tok), dd.Name, m, CloneType(dd.BaseType), CloneAttributes(dd.Attributes), dd);
} else {
- return new NewtypeDecl(Tok(dd.tok), dd.Name, m, CloneBoundVar(dd.Var), CloneExpr(dd.Constraint), CloneAttributes(dd.Attributes));
+ return new NewtypeDecl(Tok(dd.tok), dd.Name, m, CloneBoundVar(dd.Var), CloneExpr(dd.Constraint), CloneAttributes(dd.Attributes), dd);
}
} else if (d is TupleTypeDecl) {
var dd = (TupleTypeDecl)d;
@@ -52,13 +58,13 @@ namespace Microsoft.Dafny
var dd = (IndDatatypeDecl)d;
var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
var ctors = dd.Ctors.ConvertAll(CloneCtor);
- var dt = new IndDatatypeDecl(Tok(dd.tok), dd.Name, m, tps, ctors, CloneAttributes(dd.Attributes));
+ var dt = new IndDatatypeDecl(Tok(dd.tok), dd.Name, m, tps, ctors, CloneAttributes(dd.Attributes), dd);
return dt;
} else if (d is CoDatatypeDecl) {
var dd = (CoDatatypeDecl)d;
var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
var ctors = dd.Ctors.ConvertAll(CloneCtor);
- var dt = new CoDatatypeDecl(Tok(dd.tok), dd.Name, m, tps, ctors, CloneAttributes(dd.Attributes));
+ var dt = new CoDatatypeDecl(Tok(dd.tok), dd.Name, m, tps, ctors, CloneAttributes(dd.Attributes), dd);
return dt;
} else if (d is IteratorDecl) {
var dd = (IteratorDecl)d;
@@ -94,7 +100,7 @@ namespace Microsoft.Dafny
var dd = (TraitDecl)d;
var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
var mm = dd.Members.ConvertAll(CloneMember);
- var cl = new TraitDecl(Tok(dd.tok), dd.Name, m, tps, mm, CloneAttributes(dd.Attributes));
+ var cl = new TraitDecl(Tok(dd.tok), dd.Name, m, tps, mm, CloneAttributes(dd.Attributes), dd);
return cl;
}
}
@@ -103,9 +109,9 @@ namespace Microsoft.Dafny
var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
var mm = dd.Members.ConvertAll(CloneMember);
if (d is DefaultClassDecl) {
- return new DefaultClassDecl(m, mm);
+ return new DefaultClassDecl(m, mm, ((DefaultClassDecl)d));
} else {
- return new ClassDecl(Tok(dd.tok), dd.Name, m, tps, mm, CloneAttributes(dd.Attributes), dd.TraitsTyp.ConvertAll(CloneType));
+ return new ClassDecl(Tok(dd.tok), dd.Name, m, tps, mm, CloneAttributes(dd.Attributes), dd.TraitsTyp.ConvertAll(CloneType), dd);
}
} else if (d is ModuleDecl) {
if (d is LiteralModuleDecl) {
@@ -123,6 +129,11 @@ namespace Microsoft.Dafny
abs.Signature = a.Signature;
abs.OriginalSignature = a.OriginalSignature;
return abs;
+ } else if (d is ModuleExportDecl) {
+ var a = (ModuleExportDecl)d;
+ var export = new ModuleExportDecl(a.tok, m, a.IsDefault, a.Exports, a.Extends);
+ export.Signature = a.Signature;
+ return export;
} else {
Contract.Assert(false); // unexpected declaration
return null; // to please compiler
@@ -134,11 +145,11 @@ namespace Microsoft.Dafny
}
public DatatypeCtor CloneCtor(DatatypeCtor ct) {
- return new DatatypeCtor(Tok(ct.tok), ct.Name, ct.Formals.ConvertAll(CloneFormal), CloneAttributes(ct.Attributes));
+ return new DatatypeCtor(Tok(ct.tok), ct.Name, ct.Formals.ConvertAll(CloneFormal), CloneAttributes(ct.Attributes), ct);
}
public TypeParameter CloneTypeParam(TypeParameter tp) {
- return new TypeParameter(Tok(tp.tok), tp.Name, tp.EqualitySupport);
+ return new TypeParameter(Tok(tp.tok), tp.Name, tp.EqualitySupport, tp);
}
public MemberDecl CloneMember(MemberDecl member) {
@@ -160,7 +171,7 @@ namespace Microsoft.Dafny
return t;
} else if (t is SetType) {
var tt = (SetType)t;
- return new SetType(CloneType(tt.Arg));
+ return new SetType(tt.Finite, CloneType(tt.Arg));
} else if (t is SeqType) {
var tt = (SeqType)t;
return new SeqType(CloneType(tt.Arg));
@@ -187,7 +198,7 @@ namespace Microsoft.Dafny
return new InferredTypeProxy();
} else if (t is OperationTypeProxy) {
var p = (OperationTypeProxy)t;
- return new OperationTypeProxy(p.AllowInts, p.AllowReals, p.AllowChar, p.AllowSeq, p.AllowSetVarieties);
+ return new OperationTypeProxy(p.AllowInts, p.AllowReals, p.AllowChar, p.AllowSeq, p.AllowSetVarieties, p.AllowISet);
} else if (t is ParamTypeProxy) {
return new ParamTypeProxy(CloneTypeParam(((ParamTypeProxy)t).orig));
} else {
@@ -225,6 +236,9 @@ namespace Microsoft.Dafny
public Attributes CloneAttributes(Attributes attrs) {
if (attrs == null) {
return null;
+ } else if (attrs.Name.StartsWith("_")) {
+ // skip this attribute, since it would have been produced during resolution
+ return CloneAttributes(attrs.Prev);
} else {
return new Attributes(attrs.Name, attrs.Args.ConvertAll(CloneExpr), CloneAttributes(attrs.Prev));
}
@@ -243,7 +257,7 @@ namespace Microsoft.Dafny
var e = (LiteralExpr)expr;
if (e is StaticReceiverExpr) {
var ee = (StaticReceiverExpr)e;
- return new StaticReceiverExpr(e.tok, CloneType(ee.UnresolvedType));
+ return new StaticReceiverExpr(e.tok, CloneType(ee.UnresolvedType), ee.IsImplicit);
} else if (e.Value == null) {
return new LiteralExpr(Tok(e.tok));
} else if (e.Value is bool) {
@@ -277,7 +291,7 @@ namespace Microsoft.Dafny
} else if (expr is DisplayExpression) {
DisplayExpression e = (DisplayExpression)expr;
if (expr is SetDisplayExpr) {
- return new SetDisplayExpr(Tok(e.tok), e.Elements.ConvertAll(CloneExpr));
+ return new SetDisplayExpr(Tok(e.tok), ((SetDisplayExpr)expr).Finite, e.Elements.ConvertAll(CloneExpr));
} else if (expr is MultiSetDisplayExpr) {
return new MultiSetDisplayExpr(Tok(e.tok), e.Elements.ConvertAll(CloneExpr));
} else {
@@ -294,15 +308,13 @@ namespace Microsoft.Dafny
return new MapDisplayExpr(Tok(expr.tok), e.Finite, pp);
} else if (expr is NameSegment) {
- var e = (NameSegment)expr;
- return new NameSegment(Tok(e.tok), e.Name, e.OptTypeArguments == null ? null : e.OptTypeArguments.ConvertAll(CloneType));
+ return CloneNameSegment(expr);
} else if (expr is ExprDotName) {
var e = (ExprDotName)expr;
return new ExprDotName(Tok(e.tok), CloneExpr(e.Lhs), e.SuffixName, e.OptTypeArguments == null ? null : e.OptTypeArguments.ConvertAll(CloneType));
} else if (expr is ApplySuffix) {
- var e = (ApplySuffix)expr;
- return new ApplySuffix(Tok(e.tok), CloneExpr(e.Lhs), e.Args.ConvertAll(CloneExpr));
-
+ var e = (ApplySuffix) expr;
+ return CloneApplySuffix(e);
} else if (expr is MemberSelectExpr) {
var e = (MemberSelectExpr)expr;
return new MemberSelectExpr(Tok(e.tok), CloneExpr(e.Obj), e.MemberName);
@@ -319,6 +331,10 @@ namespace Microsoft.Dafny
var e = (SeqUpdateExpr)expr;
return new SeqUpdateExpr(Tok(e.tok), CloneExpr(e.Seq), CloneExpr(e.Index), CloneExpr(e.Value));
+ } else if (expr is DatatypeUpdateExpr) {
+ var e = (DatatypeUpdateExpr)expr;
+ return new DatatypeUpdateExpr(Tok(e.tok), CloneExpr(e.Root), e.Updates.ConvertAll(t => Tuple.Create(Tok(t.Item1), t.Item2, CloneExpr(t.Item3))));
+
} else if (expr is FunctionCallExpr) {
var e = (FunctionCallExpr)expr;
return new FunctionCallExpr(Tok(e.tok), e.Name, CloneExpr(e.Receiver), e.OpenParen == null ? null : Tok(e.OpenParen), e.Args.ConvertAll(CloneExpr));
@@ -385,7 +401,8 @@ namespace Microsoft.Dafny
return new LambdaExpr(tk, l.OneShot, bvs, range, l.Reads.ConvertAll(CloneFrameExpr), term);
} else {
Contract.Assert(e is SetComprehension);
- return new SetComprehension(tk, bvs, range, term, CloneAttributes(e.Attributes));
+ var tt = (SetComprehension)e;
+ return new SetComprehension(tk, tt.Finite, bvs, range, term, CloneAttributes(e.Attributes));
}
} else if (expr is WildcardExpr) {
@@ -411,7 +428,7 @@ namespace Microsoft.Dafny
} else if (expr is MatchExpr) {
var e = (MatchExpr)expr;
return new MatchExpr(Tok(e.tok), CloneExpr(e.Source),
- e.Cases.ConvertAll(c => new MatchCaseExpr(Tok(c.tok), c.Id, c.Arguments.ConvertAll(CloneBoundVar), CloneExpr(c.Body))), e.UsesOptionalBraces);
+ e.Cases.ConvertAll(CloneMatchCaseExpr), e.UsesOptionalBraces);
} else if (expr is NegationExpression) {
var e = (NegationExpression)expr;
@@ -422,6 +439,22 @@ namespace Microsoft.Dafny
}
}
+ public MatchCaseExpr CloneMatchCaseExpr(MatchCaseExpr c) {
+ Contract.Requires(c != null);
+ if (c.Arguments != null) {
+ Contract.Assert(c.CasePatterns == null);
+ return new MatchCaseExpr(Tok(c.tok), c.Id, c.Arguments.ConvertAll(CloneBoundVar), CloneExpr(c.Body));
+ } else {
+ Contract.Assert(c.Arguments == null);
+ Contract.Assert(c.CasePatterns != null);
+ return new MatchCaseExpr(Tok(c.tok), c.Id, c.CasePatterns.ConvertAll(CloneCasePattern), CloneExpr(c.Body));
+ }
+ }
+
+ public virtual Expression CloneApplySuffix(ApplySuffix e) {
+ return new ApplySuffix(Tok(e.tok), CloneExpr(e.Lhs), e.Args.ConvertAll(CloneExpr));
+ }
+
public virtual CasePattern CloneCasePattern(CasePattern pat) {
Contract.Requires(pat != null);
if (pat.Var != null) {
@@ -433,6 +466,11 @@ namespace Microsoft.Dafny
}
}
+ public virtual NameSegment CloneNameSegment(Expression expr) {
+ var e = (NameSegment)expr;
+ return new NameSegment(Tok(e.tok), e.Name, e.OptTypeArguments == null ? null : e.OptTypeArguments.ConvertAll(CloneType));
+ }
+
public virtual AssignmentRhs CloneRHS(AssignmentRhs rhs) {
AssignmentRhs c;
if (rhs is ExprRhs) {
@@ -505,7 +543,7 @@ namespace Microsoft.Dafny
} else if (stmt is IfStmt) {
var s = (IfStmt)stmt;
- r = new IfStmt(Tok(s.Tok), Tok(s.EndTok), CloneExpr(s.Guard), CloneBlockStmt(s.Thn), CloneStmt(s.Els));
+ r = new IfStmt(Tok(s.Tok), Tok(s.EndTok), s.IsExistentialGuard, CloneExpr(s.Guard), CloneBlockStmt(s.Thn), CloneStmt(s.Els));
} else if (stmt is AlternativeStmt) {
var s = (AlternativeStmt)stmt;
@@ -522,15 +560,25 @@ namespace Microsoft.Dafny
} else if (stmt is ForallStmt) {
var s = (ForallStmt)stmt;
r = new ForallStmt(Tok(s.Tok), Tok(s.EndTok), s.BoundVars.ConvertAll(CloneBoundVar), null, CloneExpr(s.Range), s.Ens.ConvertAll(CloneMayBeFreeExpr), CloneStmt(s.Body));
-
+ if (s.ForallExpressions != null) {
+ ((ForallStmt)r).ForallExpressions = s.ForallExpressions.ConvertAll(CloneExpr);
+ }
} else if (stmt is CalcStmt) {
- var s = (CalcStmt)stmt;
- r = new CalcStmt(Tok(s.Tok), Tok(s.EndTok), CloneCalcOp(s.Op), s.Lines.ConvertAll(CloneExpr), s.Hints.ConvertAll(CloneBlockStmt), s.StepOps.ConvertAll(CloneCalcOp), CloneCalcOp(s.ResultOp));
+ var s = (CalcStmt)stmt;
+ // calc statements have the unusual property that the last line is duplicated. If that is the case (which
+ // we expect it to be here), we share the clone of that line as well.
+ var lineCount = s.Lines.Count;
+ var lines = new List<Expression>(lineCount);
+ for (int i = 0; i < lineCount; i++) {
+ lines.Add(i == lineCount - 1 && 2 <= lineCount && s.Lines[i] == s.Lines[i - 1] ? lines[i - 1] : CloneExpr(s.Lines[i]));
+ }
+ Contract.Assert(lines.Count == lineCount);
+ r = new CalcStmt(Tok(s.Tok), Tok(s.EndTok), CloneCalcOp(s.Op), lines, s.Hints.ConvertAll(CloneBlockStmt), s.StepOps.ConvertAll(CloneCalcOp), CloneCalcOp(s.ResultOp), CloneAttributes(s.Attributes));
} else if (stmt is MatchStmt) {
var s = (MatchStmt)stmt;
r = new MatchStmt(Tok(s.Tok), Tok(s.EndTok), CloneExpr(s.Source),
- s.Cases.ConvertAll(c => new MatchCaseStmt(Tok(c.tok), c.Id, c.Arguments.ConvertAll(CloneBoundVar), c.Body.ConvertAll(CloneStmt))), s.UsesOptionalBraces);
+ s.Cases.ConvertAll(CloneMatchCaseStmt), s.UsesOptionalBraces);
} else if (stmt is AssignSuchThatStmt) {
var s = (AssignSuchThatStmt)stmt;
@@ -545,6 +593,10 @@ namespace Microsoft.Dafny
var lhss = s.Locals.ConvertAll(c => new LocalVariable(Tok(c.Tok), Tok(c.EndTok), c.Name, CloneType(c.OptionalType), c.IsGhost));
r = new VarDeclStmt(Tok(s.Tok), Tok(s.EndTok), lhss, (ConcreteUpdateStatement)CloneStmt(s.Update));
+ } else if (stmt is LetStmt) {
+ var s = (LetStmt) stmt;
+ r = new LetStmt(Tok(s.Tok), Tok(s.EndTok), s.LHSs.ConvertAll(CloneCasePattern), s.RHSs.ConvertAll(CloneExpr));
+
} else if (stmt is ModifyStmt) {
var s = (ModifyStmt)stmt;
var mod = CloneSpecFrameExpr(s.Mod);
@@ -562,6 +614,18 @@ namespace Microsoft.Dafny
return r;
}
+ public MatchCaseStmt CloneMatchCaseStmt(MatchCaseStmt c) {
+ Contract.Requires(c != null);
+ if (c.Arguments != null) {
+ Contract.Assert(c.CasePatterns == null);
+ return new MatchCaseStmt(Tok(c.tok), c.Id, c.Arguments.ConvertAll(CloneBoundVar), c.Body.ConvertAll(CloneStmt));
+ } else {
+ Contract.Assert(c.Arguments == null);
+ Contract.Assert(c.CasePatterns != null);
+ return new MatchCaseStmt(Tok(c.tok), c.Id, c.CasePatterns.ConvertAll(CloneCasePattern), c.Body.ConvertAll(CloneStmt));
+ }
+ }
+
public CalcStmt.CalcOp CloneCalcOp(CalcStmt.CalcOp op) {
if (op is CalcStmt.BinaryCalcOp) {
return new CalcStmt.BinaryCalcOp(((CalcStmt.BinaryCalcOp) op).Op);
@@ -585,7 +649,7 @@ namespace Microsoft.Dafny
}
public GuardedAlternative CloneGuardedAlternative(GuardedAlternative alt) {
- return new GuardedAlternative(Tok(alt.Tok), CloneExpr(alt.Guard), alt.Body.ConvertAll(CloneStmt));
+ return new GuardedAlternative(Tok(alt.Tok), alt.IsExistentialGuard, CloneExpr(alt.Guard), alt.Body.ConvertAll(CloneStmt));
}
public Function CloneFunction(Function f, string newName = null) {
@@ -603,16 +667,16 @@ namespace Microsoft.Dafny
if (f is Predicate) {
return new Predicate(Tok(f.tok), newName, f.HasStaticKeyword, f.IsProtected, f.IsGhost, tps, formals,
- req, reads, ens, decreases, body, Predicate.BodyOriginKind.OriginalOrInherited, CloneAttributes(f.Attributes), null);
+ req, reads, ens, decreases, body, Predicate.BodyOriginKind.OriginalOrInherited, CloneAttributes(f.Attributes), null, f);
} else if (f is InductivePredicate) {
return new InductivePredicate(Tok(f.tok), newName, f.HasStaticKeyword, f.IsProtected, tps, formals,
- req, reads, ens, body, CloneAttributes(f.Attributes), null);
+ req, reads, ens, body, CloneAttributes(f.Attributes), null, f);
} else if (f is CoPredicate) {
return new CoPredicate(Tok(f.tok), newName, f.HasStaticKeyword, f.IsProtected, tps, formals,
- req, reads, ens, body, CloneAttributes(f.Attributes), null);
+ req, reads, ens, body, CloneAttributes(f.Attributes), null, f);
} else {
return new Function(Tok(f.tok), newName, f.HasStaticKeyword, f.IsProtected, f.IsGhost, tps, formals, CloneType(f.ResultType),
- req, reads, ens, decreases, body, CloneAttributes(f.Attributes), null);
+ req, reads, ens, decreases, body, CloneAttributes(f.Attributes), null, f);
}
}
@@ -630,19 +694,19 @@ namespace Microsoft.Dafny
var body = CloneBlockStmt(m.Body);
if (m is Constructor) {
return new Constructor(Tok(m.tok), m.Name, tps, ins,
- req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null);
+ req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null, m);
} else if (m is InductiveLemma) {
return new InductiveLemma(Tok(m.tok), m.Name, m.HasStaticKeyword, tps, ins, m.Outs.ConvertAll(CloneFormal),
- req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null);
+ req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null, m);
} else if (m is CoLemma) {
return new CoLemma(Tok(m.tok), m.Name, m.HasStaticKeyword, tps, ins, m.Outs.ConvertAll(CloneFormal),
- req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null);
+ req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null, m);
} else if (m is Lemma) {
return new Lemma(Tok(m.tok), m.Name, m.HasStaticKeyword, tps, ins, m.Outs.ConvertAll(CloneFormal),
- req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null);
+ req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null, m);
} else {
return new Method(Tok(m.tok), m.Name, m.HasStaticKeyword, m.IsGhost, tps, ins, m.Outs.ConvertAll(CloneFormal),
- req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null);
+ req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null, m);
}
}
public virtual IToken Tok(IToken tok) {
@@ -668,21 +732,27 @@ namespace Microsoft.Dafny
abstract class FixpointCloner : Cloner
{
protected readonly Expression k;
- readonly Resolver resolver;
- readonly string suffix;
- protected FixpointCloner(Expression k, Resolver resolver)
+ protected readonly ErrorReporter reporter;
+ protected readonly string suffix;
+ protected FixpointCloner(Expression k, ErrorReporter reporter)
{
Contract.Requires(k != null);
- Contract.Requires(resolver != null);
+ Contract.Requires(reporter != null);
this.k = k;
- this.resolver = resolver;
+ this.reporter = reporter;
this.suffix = string.Format("#[{0}]", Printer.ExprToString(k));
}
- protected void ReportAdditionalInformation(IToken tok, string s)
- {
- Contract.Requires(tok != null);
- Contract.Requires(s != null);
- resolver.ReportAdditionalInformation(tok, s + suffix, s.Length);
+ protected Expression CloneCallAndAddK(FunctionCallExpr e) {
+ Contract.Requires(e != null);
+ var receiver = CloneExpr(e.Receiver);
+ var args = new List<Expression>();
+ args.Add(k);
+ foreach (var arg in e.Args) {
+ args.Add(CloneExpr(arg));
+ }
+ var fexp = new FunctionCallExpr(Tok(e.tok), e.Name + "#", receiver, e.OpenParen, args);
+ reporter.Info(MessageSource.Cloner, e.tok, e.Name + suffix);
+ return fexp;
}
}
@@ -693,17 +763,18 @@ namespace Microsoft.Dafny
/// precondition (resp. postcondition) of the inductive lemma's (resp. colemma's) corresponding prefix lemma.
/// It is assumed that the source expression has been resolved. Note, the "k" given to the constructor
/// is not cloned with each use; it is simply used as is.
+ /// The resulting expression needs to be resolved by the caller.
/// </summary>
class FixpointLemmaSpecificationSubstituter : FixpointCloner
{
readonly bool isCoContext;
readonly ISet<Expression> friendlyCalls;
- public FixpointLemmaSpecificationSubstituter(ISet<Expression> friendlyCalls, Expression k, Resolver resolver, bool isCoContext)
- : base(k, resolver)
+ public FixpointLemmaSpecificationSubstituter(ISet<Expression> friendlyCalls, Expression k, ErrorReporter reporter, bool isCoContext)
+ : base(k, reporter)
{
Contract.Requires(friendlyCalls != null);
Contract.Requires(k != null);
- Contract.Requires(resolver != null);
+ Contract.Requires(reporter != null);
this.isCoContext = isCoContext;
this.friendlyCalls = friendlyCalls;
}
@@ -716,15 +787,7 @@ namespace Microsoft.Dafny
} else if (expr is FunctionCallExpr) {
var e = (FunctionCallExpr)expr;
if (friendlyCalls.Contains(e)) {
- var receiver = CloneExpr(e.Receiver);
- var args = new List<Expression>();
- args.Add(k);
- foreach (var arg in e.Args) {
- args.Add(CloneExpr(arg));
- }
- var fexp = new FunctionCallExpr(Tok(e.tok), e.Name + "#", receiver, e.OpenParen, args);
- ReportAdditionalInformation(e.tok, e.Name);
- return fexp;
+ return CloneCallAndAddK(e);
}
} else if (expr is BinaryExpr && isCoContext) {
var e = (BinaryExpr)expr;
@@ -734,7 +797,7 @@ namespace Microsoft.Dafny
var B = CloneExpr(e.E1);
var teq = new TernaryExpr(Tok(e.tok), op, k, A, B);
var opString = op == TernaryExpr.Opcode.PrefixEqOp ? "==" : "!=";
- ReportAdditionalInformation(e.tok, opString);
+ reporter.Info(MessageSource.Cloner, e.tok, opString + suffix);
return teq;
}
}
@@ -763,19 +826,77 @@ namespace Microsoft.Dafny
}
/// <summary>
- /// The task of the FixpointLemmaBodyCloner is to fill in the implicit _k-1 arguments in recursive inductive/co-lemma calls.
+ /// The task of the FixpointLemmaBodyCloner is to fill in the implicit _k-1 arguments in recursive inductive/co-lemma calls
+ /// and in calls to the focal predicates.
/// The source statement and the given "k" are assumed to have been resolved.
/// </summary>
class FixpointLemmaBodyCloner : FixpointCloner
{
readonly FixpointLemma context;
- public FixpointLemmaBodyCloner(FixpointLemma context, Expression k, Resolver resolver)
- : base(k, resolver)
+ readonly ISet<FixpointPredicate> focalPredicates;
+ public FixpointLemmaBodyCloner(FixpointLemma context, Expression k, ISet<FixpointPredicate> focalPredicates, ErrorReporter reporter)
+ : base(k, reporter)
{
Contract.Requires(context != null);
Contract.Requires(k != null);
- Contract.Requires(resolver != null);
+ Contract.Requires(reporter != null);
this.context = context;
+ this.focalPredicates = focalPredicates;
+ }
+ public override Expression CloneExpr(Expression expr) {
+ if (DafnyOptions.O.RewriteFocalPredicates) {
+ if (expr is FunctionCallExpr) {
+ var e = (FunctionCallExpr)expr;
+#if DEBUG_PRINT
+ if (e.Function.Name.EndsWith("#") && Contract.Exists(focalPredicates, p => e.Function.Name == p.Name + "#")) {
+ Console.WriteLine("{0}({1},{2}): DEBUG: Possible opportunity to rely on new rewrite: {3}", e.tok.filename, e.tok.line, e.tok.col, Printer.ExprToString(e));
+ }
+#endif
+ // Note, we don't actually ever get here, because all calls will have been parsed as ApplySuffix.
+ // However, if something changes in the future (for example, some rewrite that changing an ApplySuffix
+ // to its resolved FunctionCallExpr), then we do want this code, so with the hope of preventing
+ // some error in the future, this case is included. (Of course, it is currently completely untested!)
+ var f = e.Function as FixpointPredicate;
+ if (f != null && focalPredicates.Contains(f)) {
+#if DEBUG_PRINT
+ var r = CloneCallAndAddK(e);
+ Console.WriteLine("{0}({1},{2}): DEBUG: Rewrote extreme predicate into prefix predicate: {3}", e.tok.filename, e.tok.line, e.tok.col, Printer.ExprToString(r));
+ return r;
+#else
+ return CloneCallAndAddK(e);
+#endif
+ }
+ } else if (expr is ApplySuffix) {
+ var apply = (ApplySuffix)expr;
+ if (!apply.WasResolved()) {
+ // Since we're assuming the enclosing statement to have been resolved, this ApplySuffix must
+ // be part of an ExprRhs that actually designates a method call. Such an ApplySuffix does
+ // not get listed as being resolved, but its components (like its .Lhs) are resolved.
+ var mse = (MemberSelectExpr)apply.Lhs.Resolved;
+ Contract.Assume(mse.Member is Method);
+ } else {
+ var fce = apply.Resolved as FunctionCallExpr;
+ if (fce != null) {
+#if DEBUG_PRINT
+ if (fce.Function.Name.EndsWith("#") && Contract.Exists(focalPredicates, p => fce.Function.Name == p.Name + "#")) {
+ Console.WriteLine("{0}({1},{2}): DEBUG: Possible opportunity to rely on new rewrite: {3}", fce.tok.filename, fce.tok.line, fce.tok.col, Printer.ExprToString(fce));
+ }
+#endif
+ var f = fce.Function as FixpointPredicate;
+ if (f != null && focalPredicates.Contains(f)) {
+#if DEBUG_PRINT
+ var r = CloneCallAndAddK(fce);
+ Console.WriteLine("{0}({1},{2}): DEBUG: Rewrote extreme predicate into prefix predicate: {3}", fce.tok.filename, fce.tok.line, fce.tok.col, Printer.ExprToString(r));
+ return r;
+#else
+ return CloneCallAndAddK(fce);
+#endif
+ }
+ }
+ }
+ }
+ }
+ return base.CloneExpr(expr);
}
public override AssignmentRhs CloneRHS(AssignmentRhs rhs) {
var r = rhs as ExprRhs;
@@ -799,7 +920,7 @@ namespace Microsoft.Dafny
apply.Args.ForEach(arg => args.Add(CloneExpr(arg)));
var applyClone = new ApplySuffix(Tok(apply.tok), lhsClone, args);
var c = new ExprRhs(applyClone);
- ReportAdditionalInformation(apply.tok, mse.Member.Name);
+ reporter.Info(MessageSource.Cloner, apply.Lhs.tok, mse.Member.Name + suffix);
return c;
}
}
diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs
index 570ac30d..66765b34 100644
--- a/Source/Dafny/Compiler.cs
+++ b/Source/Dafny/Compiler.cs
@@ -14,18 +14,11 @@ using System.Text;
namespace Microsoft.Dafny {
public class Compiler {
- public Compiler(TextWriter wr) {
- Contract.Requires(wr != null);
- this.wr = wr;
+ public Compiler() {
+
}
- [ContractInvariantMethod]
- void ObjectInvariant()
- {
- Contract.Invariant(wr!=null);
- }
-
- TextWriter wr;
+ StringBuilder copyInstrWriter = new StringBuilder(); // a buffer that stores copy instructions generated by letExpr that uses out param.
Method enclosingMethod; // non-null when a method body is being translated
FreshIdGenerator idGenerator = new FreshIdGenerator();
@@ -50,7 +43,7 @@ namespace Microsoft.Dafny {
public int ErrorCount;
public TextWriter ErrorWriter = Console.Out;
- void Error(string msg, params object[] args) {
+ void Error(string msg, TextWriter wr, params object[] args) {
Contract.Requires(msg != null);
Contract.Requires(args != null);
@@ -60,7 +53,7 @@ namespace Microsoft.Dafny {
ErrorCount++;
}
- void ReadRuntimeSystem() {
+ void ReadRuntimeSystem(TextWriter wr) {
string codebase = cce.NonNull( System.IO.Path.GetDirectoryName(cce.NonNull(System.Reflection.Assembly.GetExecutingAssembly().Location)));
string path = System.IO.Path.Combine(codebase, "DafnyRuntime.cs");
using (TextReader rd = new StreamReader(new FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read)))
@@ -75,16 +68,16 @@ namespace Microsoft.Dafny {
}
readonly int IndentAmount = 2;
- void Indent(int ind) {
+ void Indent(int ind, TextWriter wr) {
Contract.Requires(0 <= ind);
string spaces = " ";
for (; spaces.Length < ind; ind -= spaces.Length) {
wr.Write(spaces);
}
- wr.Write(spaces.Substring(0, ind));
+ wr.Write(spaces.Substring(0, ind));
}
- public void Compile(Program program) {
+ public void Compile(Program program, TextWriter wr) {
Contract.Requires(program != null);
wr.WriteLine("// Dafny program {0} compiled into C#", program.Name);
wr.WriteLine("// To recompile, use 'csc' with: /r:System.Numerics.dll");
@@ -92,8 +85,8 @@ namespace Microsoft.Dafny {
wr.WriteLine("// You might also want to include compiler switches like:");
wr.WriteLine("// /debug /nowarn:0164 /nowarn:0219");
wr.WriteLine();
- ReadRuntimeSystem();
- CompileBuiltIns(program.BuiltIns);
+ ReadRuntimeSystem(wr);
+ CompileBuiltIns(program.BuiltIns, wr);
foreach (ModuleDefinition m in program.CompileModules) {
if (m.IsAbstract) {
@@ -102,7 +95,11 @@ namespace Microsoft.Dafny {
}
int indent = 0;
if (!m.IsDefaultModule) {
- wr.WriteLine("namespace @{0} {{", m.CompileName);
+ var m_prime = m;
+ while (DafnyOptions.O.IronDafny && m_prime.ClonedFrom != null) {
+ m_prime = m.ClonedFrom;
+ }
+ wr.WriteLine("namespace @{0} {{", m_prime.CompileName);
indent += IndentAmount;
}
foreach (TopLevelDecl d in m.TopLevelDecls) {
@@ -113,21 +110,33 @@ namespace Microsoft.Dafny {
wr.WriteLine();
if (d is OpaqueTypeDecl) {
var at = (OpaqueTypeDecl)d;
- Error("Opaque type ('{0}') cannot be compiled", at.FullName);
+ Error("Opaque type ('{0}') cannot be compiled", wr, at.FullName);
} else if (d is TypeSynonymDecl) {
// do nothing, just bypass type synonyms in the compiler
} else if (d is NewtypeDecl) {
- // do nothing, just bypass newtypes in the compiler
+ var nt = (NewtypeDecl)d;
+ Indent(indent, wr);
+ wr.WriteLine("public class @{0} {{", nt.CompileName);
+ if (nt.NativeType != null) {
+ Indent(indent + IndentAmount, wr);
+ wr.WriteLine("public static System.Collections.Generic.IEnumerable<{0}> IntegerRange(BigInteger lo, BigInteger hi) {{", nt.NativeType.Name);
+ Indent(indent + 2 * IndentAmount, wr);
+ wr.WriteLine("for (var j = lo; j < hi; j++) {{ yield return ({0})j; }}", nt.NativeType.Name);
+ Indent(indent + IndentAmount, wr);
+ wr.WriteLine("}");
+ }
+ Indent(indent, wr);
+ wr.WriteLine("}");
} else if (d is DatatypeDecl) {
var dt = (DatatypeDecl)d;
- Indent(indent);
+ Indent(indent, wr);
wr.Write("public abstract class Base_{0}", dt.CompileName);
if (dt.TypeArgs.Count != 0) {
wr.Write("<{0}>", TypeParameters(dt.TypeArgs));
}
wr.WriteLine(" { }");
- CompileDatatypeConstructors(dt, indent);
- CompileDatatypeStruct(dt, indent);
+ CompileDatatypeConstructors(dt, indent, wr);
+ CompileDatatypeStruct(dt, indent, wr);
} else if (d is IteratorDecl) {
var iter = (IteratorDecl)d;
// An iterator is compiled as follows:
@@ -153,7 +162,7 @@ namespace Microsoft.Dafny {
// }
// }
- Indent(indent);
+ Indent(indent, wr);
wr.Write("public class @{0}", iter.CompileName);
if (iter.TypeArgs.Count != 0) {
wr.Write("<{0}>", TypeParameters(iter.TypeArgs));
@@ -165,58 +174,58 @@ namespace Microsoft.Dafny {
foreach (var member in iter.Members) {
var f = member as Field;
if (f != null && !f.IsGhost) {
- Indent(ind);
- wr.WriteLine("public {0} @{1} = {2};", TypeName(f.Type), f.CompileName, DefaultValue(f.Type));
+ Indent(ind, wr);
+ wr.WriteLine("public {0} @{1} = {2};", TypeName(f.Type, wr), f.CompileName, DefaultValue(f.Type, wr));
} else if (member is Constructor) {
Contract.Assert(ct == null); // we're expecting just one constructor
ct = (Constructor)member;
}
}
Contract.Assert(ct != null); // we do expect a constructor
- Indent(ind); wr.WriteLine("System.Collections.Generic.IEnumerator<object> __iter;");
+ Indent(ind, wr); wr.WriteLine("System.Collections.Generic.IEnumerator<object> __iter;");
// here's the initializer method
- Indent(ind); wr.Write("public void @{0}(", ct.CompileName);
+ Indent(ind, wr); wr.Write("public void @{0}(", ct.CompileName);
string sep = "";
foreach (var p in ct.Ins) {
if (!p.IsGhost) {
// here we rely on the parameters and the corresponding fields having the same names
- wr.Write("{0}{1} @{2}", sep, TypeName(p.Type), p.CompileName);
+ wr.Write("{0}{1} @{2}", sep, TypeName(p.Type, wr), p.CompileName);
sep = ", ";
}
}
wr.WriteLine(") {");
foreach (var p in ct.Ins) {
if (!p.IsGhost) {
- Indent(ind + IndentAmount);
+ Indent(ind + IndentAmount, wr);
wr.WriteLine("this.@{0} = @{0};", p.CompileName);
}
}
- Indent(ind + IndentAmount); wr.WriteLine("__iter = TheIterator();");
- Indent(ind); wr.WriteLine("}");
+ Indent(ind + IndentAmount, wr); wr.WriteLine("__iter = TheIterator();");
+ Indent(ind, wr); wr.WriteLine("}");
// here are the enumerator methods
- Indent(ind); wr.WriteLine("public void MoveNext(out bool more) { more = __iter.MoveNext(); }");
- Indent(ind); wr.WriteLine("private System.Collections.Generic.IEnumerator<object> TheIterator() {");
+ Indent(ind, wr); wr.WriteLine("public void MoveNext(out bool more) { more = __iter.MoveNext(); }");
+ Indent(ind, wr); wr.WriteLine("private System.Collections.Generic.IEnumerator<object> TheIterator() {");
if (iter.Body == null) {
- Error("Iterator {0} has no body", iter.FullName);
+ Error("Iterator {0} has no body", wr, iter.FullName);
} else {
- TrStmt(iter.Body, ind + IndentAmount);
+ wr.Write(TrStmt(iter.Body, ind + IndentAmount).ToString());
}
- Indent(ind + IndentAmount); wr.WriteLine("yield break;");
- Indent(ind); wr.WriteLine("}");
+ Indent(ind + IndentAmount, wr); wr.WriteLine("yield break;");
+ Indent(ind, wr); wr.WriteLine("}");
// end of the class
- Indent(indent); wr.WriteLine("}");
+ Indent(indent, wr); wr.WriteLine("}");
}
else if (d is TraitDecl)
{
//writing the trait
var trait = (TraitDecl)d;
- Indent(indent);
+ Indent(indent, wr);
wr.Write("public interface @{0}", trait.CompileName);
wr.WriteLine(" {");
- CompileClassMembers(trait, false, indent + IndentAmount);
- Indent(indent); wr.WriteLine("}");
+ CompileClassMembers(trait, false, indent + IndentAmount, wr);
+ Indent(indent, wr); wr.WriteLine("}");
//writing the _Companion class
List<MemberDecl> members = new List<MemberDecl>();
@@ -236,27 +245,27 @@ namespace Microsoft.Dafny {
}
}
}
- Indent(indent);
+ Indent(indent, wr);
wr.Write("public class @_Companion_{0}", trait.CompileName);
wr.WriteLine(" {");
- CompileClassMembers(trait, true, indent + IndentAmount);
- Indent(indent); wr.WriteLine("}");
+ CompileClassMembers(trait, true, indent + IndentAmount, wr);
+ Indent(indent, wr); wr.WriteLine("}");
}
else if (d is ClassDecl) {
var cl = (ClassDecl)d;
- Indent(indent);
- wr.Write("public class @{0}", cl.CompileName);
+ Indent(indent, wr);
+ wr.Write("public partial class @{0}", cl.CompileName);
if (cl.TypeArgs.Count != 0) {
wr.Write("<{0}>", TypeParameters(cl.TypeArgs));
}
string sep = " : ";
foreach (var trait in cl.TraitsTyp) {
- wr.Write("{0}{1}", sep, TypeName(trait));
+ wr.Write("{0}{1}", sep, TypeName(trait, wr));
sep = ", ";
}
wr.WriteLine(" {");
- CompileClassMembers(cl, false, indent+IndentAmount);
- Indent(indent); wr.WriteLine("}");
+ CompileClassMembers(cl, false, indent+IndentAmount, wr);
+ Indent(indent, wr); wr.WriteLine("}");
} else if (d is ModuleDecl) {
// nop
} else { Contract.Assert(false); }
@@ -267,15 +276,15 @@ namespace Microsoft.Dafny {
}
}
- void CompileBuiltIns(BuiltIns builtIns) {
+ void CompileBuiltIns(BuiltIns builtIns, TextWriter wr) {
wr.WriteLine("namespace Dafny {");
- Indent(IndentAmount);
+ Indent(IndentAmount, wr);
wr.WriteLine("public partial class Helpers {");
foreach (var decl in builtIns.SystemModule.TopLevelDecls) {
if (decl is ArrayClassDecl) {
int dims = ((ArrayClassDecl)decl).Dims;
// public static T[,] InitNewArray2<T>(BigInteger size0, BigInteger size1) {
- Indent(3 * IndentAmount);
+ Indent(3 * IndentAmount, wr);
wr.Write("public static T[");
RepeatWrite(wr, dims, "", ",");
wr.Write("] InitNewArray{0}<T>(", dims);
@@ -283,52 +292,52 @@ namespace Microsoft.Dafny {
wr.WriteLine(") {");
// int s0 = (int)size0;
for (int i = 0; i < dims; i++) {
- Indent(4 * IndentAmount);
+ Indent(4 * IndentAmount, wr);
wr.WriteLine("int s{0} = (int)size{0};", i);
}
// T[,] a = new T[s0, s1];
- Indent(4 * IndentAmount);
+ Indent(4 * IndentAmount, wr);
wr.Write("T[");
RepeatWrite(wr, dims, "", ",");
wr.Write("] a = new T[");
RepeatWrite(wr, dims, "s{0}", ",");
wr.WriteLine("];");
// BigInteger[,] b = a as BigInteger[,];
- Indent(4 * IndentAmount);
+ Indent(4 * IndentAmount, wr);
wr.Write("BigInteger[");
RepeatWrite(wr, dims, "", ",");
wr.Write("] b = a as BigInteger[");
RepeatWrite(wr, dims, "", ",");
wr.WriteLine("];");
// if (b != null) {
- Indent(4 * IndentAmount);
+ Indent(4 * IndentAmount, wr);
wr.WriteLine("if (b != null) {");
// BigInteger z = new BigInteger(0);
- Indent(5 * IndentAmount);
+ Indent(5 * IndentAmount, wr);
wr.WriteLine("BigInteger z = new BigInteger(0);");
// for (int i0 = 0; i0 < s0; i0++)
// for (int i1 = 0; i1 < s1; i1++)
for (int i = 0; i < dims; i++) {
- Indent((5+i) * IndentAmount);
+ Indent((5+i) * IndentAmount, wr);
wr.WriteLine("for (int i{0} = 0; i{0} < s{0}; i{0}++)", i);
}
// b[i0,i1] = z;
- Indent((5+dims) * IndentAmount);
+ Indent((5+dims) * IndentAmount, wr);
wr.Write("b[");
RepeatWrite(wr, dims, "i{0}", ",");
wr.WriteLine("] = z;");
// }
- Indent(4 * IndentAmount);
+ Indent(4 * IndentAmount, wr);
wr.WriteLine("}");
// return a;
- Indent(4 * IndentAmount);
+ Indent(4 * IndentAmount, wr);
wr.WriteLine("return a;");
// }
- Indent(3 * IndentAmount);
+ Indent(3 * IndentAmount, wr);
wr.WriteLine("}"); // end of method
}
}
- Indent(IndentAmount);
+ Indent(IndentAmount, wr);
wr.WriteLine("}"); // end of class Helpers
wr.WriteLine("}"); // end of namespace
}
@@ -343,7 +352,7 @@ namespace Microsoft.Dafny {
}
}
- void CompileDatatypeConstructors(DatatypeDecl dt, int indent)
+ void CompileDatatypeConstructors(DatatypeDecl dt, int indent, TextWriter wr)
{
Contract.Requires(dt != null);
@@ -356,20 +365,20 @@ namespace Microsoft.Dafny {
// public Dt__Lazy(Computer c) { this.c = c; }
// public Base_Dt<T> Get() { return c(); }
// }
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("public class {0}__Lazy{1} : Base_{0}{1} {{", dt.CompileName, typeParams);
int ind = indent + IndentAmount;
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("public delegate Base_{0}{1} Computer();", dt.CompileName, typeParams);
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("public delegate Computer ComputerComputer();");
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("Computer c;");
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("public {0}__Lazy(Computer c) {{ this.c = c; }}", dt.CompileName);
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("public Base_{0}{1} Get() {{ return c(); }}", dt.CompileName, typeParams);
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("}");
}
@@ -391,7 +400,7 @@ namespace Microsoft.Dafny {
// // ...
// }
// }
- Indent(indent);
+ Indent(indent, wr);
wr.Write("public class {0}", DtCtorDeclarationName(ctor, dt.TypeArgs));
wr.WriteLine(" : Base_{0}{1} {{", dt.CompileName, typeParams);
int ind = indent + IndentAmount;
@@ -399,32 +408,32 @@ namespace Microsoft.Dafny {
int i = 0;
foreach (Formal arg in ctor.Formals) {
if (!arg.IsGhost) {
- Indent(ind);
- wr.WriteLine("public readonly {0} @{1};", TypeName(arg.Type), FormalName(arg, i));
+ Indent(ind, wr);
+ wr.WriteLine("public readonly {0} @{1};", TypeName(arg.Type, wr), FormalName(arg, i));
i++;
}
}
- Indent(ind);
+ Indent(ind, wr);
wr.Write("public {0}(", DtCtorDeclartionName(ctor));
- WriteFormals("", ctor.Formals);
+ WriteFormals("", ctor.Formals, wr);
wr.WriteLine(") {");
i = 0;
foreach (Formal arg in ctor.Formals) {
if (!arg.IsGhost) {
- Indent(ind + IndentAmount);
+ Indent(ind + IndentAmount, wr);
wr.WriteLine("this.@{0} = @{0};", FormalName(arg, i));
i++;
}
}
- Indent(ind); wr.WriteLine("}");
+ Indent(ind, wr); wr.WriteLine("}");
// Equals method
- Indent(ind); wr.WriteLine("public override bool Equals(object other) {");
- Indent(ind + IndentAmount);
+ Indent(ind, wr); wr.WriteLine("public override bool Equals(object other) {");
+ Indent(ind + IndentAmount, wr);
wr.Write("var oth = other as {0}", DtCtorName(ctor, dt.TypeArgs));
wr.WriteLine(";");
- Indent(ind + IndentAmount);
+ Indent(ind + IndentAmount, wr);
wr.Write("return oth != null");
i = 0;
foreach (Formal arg in ctor.Formals) {
@@ -439,56 +448,76 @@ namespace Microsoft.Dafny {
}
}
wr.WriteLine(";");
- Indent(ind); wr.WriteLine("}");
-
- // GetHashCode method
- Indent(ind); wr.WriteLine("public override int GetHashCode() {");
- Indent(ind + IndentAmount); wr.Write("return " + constructorIndex);
+ Indent(ind, wr); wr.WriteLine("}");
+ // GetHashCode method (Uses the djb2 algorithm)
+ Indent(ind, wr); wr.WriteLine("public override int GetHashCode() {");
+ Indent(ind + IndentAmount, wr); wr.WriteLine("ulong hash = 5381;");
+ Indent(ind + IndentAmount, wr); wr.WriteLine("hash = ((hash << 5) + hash) + {0};", constructorIndex);
i = 0;
foreach (Formal arg in ctor.Formals) {
if (!arg.IsGhost) {
string nm = FormalName(arg, i);
- wr.Write(" ^ this.@{0}.GetHashCode()", nm);
+ Indent(ind + IndentAmount, wr); wr.WriteLine("hash = ((hash << 5) + hash) + ((ulong)this.@{0}.GetHashCode());", nm);
i++;
}
}
- wr.WriteLine(";");
- Indent(ind); wr.WriteLine("}");
+ Indent(ind + IndentAmount, wr); wr.WriteLine("return (int) hash;");
+ Indent(ind, wr); wr.WriteLine("}");
if (dt is IndDatatypeDecl) {
- Indent(ind); wr.WriteLine("public override string ToString() {");
+ Indent(ind, wr); wr.WriteLine("public override string ToString() {");
string nm;
if (dt is TupleTypeDecl) {
nm = "";
} else {
nm = (dt.Module.IsDefaultModule ? "" : dt.Module.CompileName + ".") + dt.CompileName + "." + ctor.CompileName;
}
- Indent(ind + IndentAmount); wr.WriteLine("string s = \"{0}\";", nm);
+ var tempVar = GenVarName("s", ctor.Formals);
+ Indent(ind + IndentAmount, wr); wr.WriteLine("string {0} = \"{1}\";", tempVar, nm);
if (ctor.Formals.Count != 0) {
- Indent(ind + IndentAmount); wr.WriteLine("s += \"(\";");
+ Indent(ind + IndentAmount, wr); wr.WriteLine("{0} += \"(\";", tempVar);
i = 0;
foreach (var arg in ctor.Formals) {
if (!arg.IsGhost) {
if (i != 0) {
- Indent(ind + IndentAmount); wr.WriteLine("s += \", \";");
+ Indent(ind + IndentAmount, wr); wr.WriteLine("{0} += \", \";", tempVar);
}
- Indent(ind + IndentAmount); wr.WriteLine("s += @{0}.ToString();", FormalName(arg, i));
+ Indent(ind + IndentAmount, wr); wr.WriteLine("{0} += @{1}.ToString();", tempVar,FormalName(arg, i));
i++;
}
}
- Indent(ind + IndentAmount); wr.WriteLine("s += \")\";");
+ Indent(ind + IndentAmount, wr); wr.WriteLine("{0} += \")\";", tempVar);
}
- Indent(ind + IndentAmount); wr.WriteLine("return s;");
- Indent(ind); wr.WriteLine("}");
+ Indent(ind + IndentAmount, wr); wr.WriteLine("return {0};", tempVar);
+ Indent(ind, wr); wr.WriteLine("}");
}
- Indent(indent); wr.WriteLine("}");
+ Indent(indent, wr); wr.WriteLine("}");
}
constructorIndex++;
}
- void CompileDatatypeStruct(DatatypeDecl dt, int indent) {
+ // create a varName that is not a duplicate of formals' name
+ string GenVarName(string root, List<Formal> formals) {
+ bool finished = false;
+ while (!finished) {
+ finished = true;
+ int i = 0;
+ foreach (var arg in formals) {
+ if (!arg.IsGhost) {
+ if (root.Equals(FormalName(arg, i))) {
+ root += root;
+ finished = false;
+ }
+ i++;
+ }
+ }
+ }
+ return root;
+ }
+
+ void CompileDatatypeStruct(DatatypeDecl dt, int indent, TextWriter wr) {
Contract.Requires(dt != null);
// public struct Dt<T> : IDatatype{
@@ -532,46 +561,46 @@ namespace Microsoft.Dafny {
DtT += DtT_TypeArgs;
}
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("public struct @{0} {{", DtT);
int ind = indent + IndentAmount;
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("Base_{0} _d;", DtT);
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("public Base_{0} _D {{", DtT);
- Indent(ind + IndentAmount);
+ Indent(ind + IndentAmount, wr);
wr.WriteLine("get {");
- Indent(ind + 2 * IndentAmount);
+ Indent(ind + 2 * IndentAmount, wr);
wr.WriteLine("if (_d == null) {");
- Indent(ind + 3 * IndentAmount);
+ Indent(ind + 3 * IndentAmount, wr);
wr.WriteLine("_d = Default;");
if (dt is CoDatatypeDecl) {
string typeParams = dt.TypeArgs.Count == 0 ? "" : string.Format("<{0}>", TypeParameters(dt.TypeArgs));
- Indent(ind + 2 * IndentAmount);
+ Indent(ind + 2 * IndentAmount, wr);
wr.WriteLine("}} else if (_d is {0}__Lazy{1}) {{", dt.CompileName, typeParams);
- Indent(ind + 3 * IndentAmount);
+ Indent(ind + 3 * IndentAmount, wr);
wr.WriteLine("_d = (({0}__Lazy{1})_d).Get();", dt.CompileName, typeParams);
}
- Indent(ind + 2 * IndentAmount); wr.WriteLine("}");
- Indent(ind + 2 * IndentAmount); wr.WriteLine("return _d;");
- Indent(ind + IndentAmount); wr.WriteLine("}");
- Indent(ind); wr.WriteLine("}");
+ Indent(ind + 2 * IndentAmount, wr); wr.WriteLine("}");
+ Indent(ind + 2 * IndentAmount, wr); wr.WriteLine("return _d;");
+ Indent(ind + IndentAmount, wr); wr.WriteLine("}");
+ Indent(ind, wr); wr.WriteLine("}");
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("public @{0}(Base_{1} d) {{ this._d = d; }}", dt.CompileName, DtT);
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("static Base_{0} theDefault;", DtT);
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("public static Base_{0} Default {{", DtT);
- Indent(ind + IndentAmount);
+ Indent(ind + IndentAmount, wr);
wr.WriteLine("get {");
- Indent(ind + 2 * IndentAmount);
+ Indent(ind + 2 * IndentAmount, wr);
wr.WriteLine("if (theDefault == null) {");
- Indent(ind + 3 * IndentAmount);
+ Indent(ind + 3 * IndentAmount, wr);
wr.Write("theDefault = ");
DatatypeCtor defaultCtor;
@@ -585,55 +614,55 @@ namespace Microsoft.Dafny {
string sep = "";
foreach (Formal f in defaultCtor.Formals) {
if (!f.IsGhost) {
- wr.Write("{0}{1}", sep, DefaultValue(f.Type));
+ wr.Write("{0}{1}", sep, DefaultValue(f.Type, wr));
sep = ", ";
}
}
wr.Write(")");
wr.WriteLine(";");
- Indent(ind + 2 * IndentAmount);
+ Indent(ind + 2 * IndentAmount, wr);
wr.WriteLine("}");
- Indent(ind + 2 * IndentAmount);
+ Indent(ind + 2 * IndentAmount, wr);
wr.WriteLine("return theDefault;");
- Indent(ind + IndentAmount); wr.WriteLine("}");
+ Indent(ind + IndentAmount, wr); wr.WriteLine("}");
- Indent(ind); wr.WriteLine("}");
+ Indent(ind, wr); wr.WriteLine("}");
- Indent(ind); wr.WriteLine("public override bool Equals(object other) {");
- Indent(ind + IndentAmount);
+ Indent(ind, wr); wr.WriteLine("public override bool Equals(object other) {");
+ Indent(ind + IndentAmount, wr);
wr.WriteLine("return other is @{0} && _D.Equals(((@{0})other)._D);", DtT);
- Indent(ind); wr.WriteLine("}");
+ Indent(ind, wr); wr.WriteLine("}");
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("public override int GetHashCode() { return _D.GetHashCode(); }");
if (dt is IndDatatypeDecl) {
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("public override string ToString() { return _D.ToString(); }");
}
// query properties
foreach (var ctor in dt.Ctors) {
// public bool is_Ctor0 { get { return _D is Dt_Ctor0; } }
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("public bool is_{0} {{ get {{ return _D is {1}_{0}{2}; }} }}", ctor.CompileName, dt.CompileName, DtT_TypeArgs);
}
if (dt.HasFinitePossibleValues) {
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("public static System.Collections.Generic.IEnumerable<@{0}> AllSingletonConstructors {{", DtT);
- Indent(ind + IndentAmount);
+ Indent(ind + IndentAmount, wr);
wr.WriteLine("get {");
foreach (var ctr in dt.Ctors) {
if (ctr.Formals.Count == 0) {
- Indent(ind + IndentAmount + IndentAmount);
+ Indent(ind + IndentAmount + IndentAmount, wr);
wr.WriteLine("yield return new @{0}(new {2}_{1}());", DtT, ctr.CompileName, dt.CompileName);
}
}
- Indent(ind + IndentAmount + IndentAmount);
+ Indent(ind + IndentAmount + IndentAmount, wr);
wr.WriteLine("yield break;");
- Indent(ind + IndentAmount);
+ Indent(ind + IndentAmount, wr);
wr.WriteLine("}");
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("}");
}
@@ -642,17 +671,17 @@ namespace Microsoft.Dafny {
foreach (var arg in ctor.Formals) {
if (!arg.IsGhost && arg.HasName) {
// public T0 @Dtor0 { get { return ((DT_Ctor)_D).@Dtor0; } }
- Indent(ind);
- wr.WriteLine("public {0} dtor_{1} {{ get {{ return (({2}_{3}{4})_D).@{1}; }} }}", TypeName(arg.Type), arg.CompileName, dt.CompileName, ctor.CompileName, DtT_TypeArgs);
+ Indent(ind, wr);
+ wr.WriteLine("public {0} dtor_{1} {{ get {{ return (({2}_{3}{4})_D).@{1}; }} }}", TypeName(arg.Type, wr), arg.CompileName, dt.CompileName, ctor.CompileName, DtT_TypeArgs);
}
}
}
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("}");
}
- int WriteFormals(string sep, List<Formal/*!*/>/*!*/ formals)
+ int WriteFormals(string sep, List<Formal/*!*/>/*!*/ formals, TextWriter wr)
{
Contract.Requires(sep != null);
Contract.Requires(cce.NonNullElements(formals));
@@ -660,7 +689,7 @@ namespace Microsoft.Dafny {
foreach (Formal arg in formals) {
if (!arg.IsGhost) {
string name = FormalName(arg, i);
- wr.Write("{0}{1}{2} @{3}", sep, arg.InParam ? "" : "out ", TypeName(arg.Type), name);
+ wr.Write("{0}{1}{2} @{3}", sep, arg.InParam ? "" : "out ", TypeName(arg.Type, wr), name);
sep = ", ";
i++;
}
@@ -676,7 +705,11 @@ namespace Microsoft.Dafny {
}
string DtName(DatatypeDecl decl) {
- return decl.Module.IsDefaultModule ? decl.CompileName : decl.FullCompileName;
+ var d = (TopLevelDecl)decl;
+ while (DafnyOptions.O.IronDafny && d.ClonedFrom != null) {
+ d = (TopLevelDecl)d.ClonedFrom;
+ }
+ return d.Module.IsDefaultModule ? d.CompileName : d.FullCompileName;
}
string DtCtorName(DatatypeCtor ctor) {
Contract.Requires(ctor != null);
@@ -712,51 +745,80 @@ namespace Microsoft.Dafny {
return s;
}
- string DtCtorName(DatatypeCtor ctor, List<Type> typeArgs) {
+ string DtCtorName(DatatypeCtor ctor, List<Type> typeArgs, TextWriter wr) {
Contract.Requires(ctor != null);
Contract.Ensures(Contract.Result<string>() != null);
var s = DtCtorName(ctor);
if (typeArgs != null && typeArgs.Count != 0) {
- s += "<" + TypeNames(typeArgs) + ">";
+ s += "<" + TypeNames(typeArgs, wr) + ">";
}
return s;
}
public bool HasMain(Program program) {
+ Method mainMethod = null;
+ bool hasMain = false;
foreach (var module in program.Modules) {
+ if (module.IsAbstract) {
+ // the purpose of an abstract module is to skip compilation
+ continue;
+ }
foreach (var decl in module.TopLevelDecls) {
var c = decl as ClassDecl;
if (c != null) {
foreach (var member in c.Members) {
var m = member as Method;
if (m != null && IsMain(m)) {
- return true;
+ if (mainMethod == null) {
+ mainMethod = m;
+ hasMain = true;
+ } else {
+ // more than one main in the program
+ ErrorWriter.WriteLine("More than one method is declared as \"main\"");
+ ErrorCount++;
+ hasMain = false;
+ }
}
}
}
}
}
- return false;
+ return hasMain;
}
public static bool IsMain(Method m) {
// In order to be a legal Main() method, the following must be true:
- // The method takes no parameters
+ // The method takes no parameters
// The method is not a ghost method
// The method has no requires clause
// The method has no modifies clause
// If the method is an instance (that is, non-static) method in a class, then the enclosing class must not declare any constructor
- if (!m.IsGhost && m.Name == "Main" && m.TypeArgs.Count == 0 && m.Ins.Count == 0 && m.Outs.Count == 0 && m.Req.Count == 0
+ // Or if a method is annotated with {:main} and the above restrictions apply, except it is allowed to take ghost arguments,
+ // and it is allowed to have preconditions and modifies. This lets the programmer add some explicit assumptions about the outside world,
+ // modeled, for example, via ghost parameters.
+ if (!m.IsGhost && m.Name == "Main" && m.TypeArgs.Count == 0 && m.Ins.Count == 0 && m.Outs.Count == 0 && m.Req.Count == 0
&& m.Mod.Expressions.Count == 0 && (m.IsStatic || (((ClassDecl)m.EnclosingClass) == null) || !((ClassDecl)m.EnclosingClass).HasConstructor)) {
return true;
- }
- else {
+ } else if (Attributes.Contains(m.Attributes, "main") && !m.IsGhost && m.TypeArgs.Count == 0 && m.Outs.Count == 0
+ && (m.IsStatic || (((ClassDecl)m.EnclosingClass) == null) || !((ClassDecl)m.EnclosingClass).HasConstructor)) {
+ if (m.Ins.Count == 0) {
+ return true;
+ } else {
+ bool isGhost = true;
+ foreach (var arg in m.Ins) {
+ if (!arg.IsGhost) {
+ isGhost = false;
+ }
+ }
+ return isGhost;
+ }
+ } else {
return false;
}
}
- void CompileClassMembers(ClassDecl c, bool forCompanionClass, int indent) {
+ void CompileClassMembers(ClassDecl c, bool forCompanionClass, int indent, TextWriter wr) {
Contract.Requires(c != null);
Contract.Requires(!forCompanionClass || c is TraitDecl);
Contract.Requires(0 <= indent);
@@ -765,9 +827,9 @@ namespace Microsoft.Dafny {
if (member is Field) {
var f = (Field)member;
// every field is inherited
- Indent(indent);
- wr.WriteLine("public {0} @_{1};", TypeName(f.Type), f.CompileName);
- wr.Write("public {0} @{1}", TypeName(f.Type), f.CompileName);
+ Indent(indent, wr);
+ wr.WriteLine("public {0} @_{1};", TypeName(f.Type, wr), f.CompileName);
+ wr.Write("public {0} @{1}", TypeName(f.Type, wr), f.CompileName);
wr.WriteLine(" {");
wr.WriteLine(" get { ");
wr.Write("return this.@_{0};", f.CompileName);
@@ -779,11 +841,11 @@ namespace Microsoft.Dafny {
} else if (member is Function) {
var f = (Function)member;
Contract.Assert(f.Body != null);
- CompileFunction(indent, f);
+ CompileFunction(indent, f, wr);
} else if (member is Method) {
var method = (Method)member;
Contract.Assert(method.Body != null);
- CompileMethod(c, indent, method);
+ CompileMethod(c, indent, method, wr);
} else {
Contract.Assert(false); // unexpected member
}
@@ -794,12 +856,12 @@ namespace Microsoft.Dafny {
if (f.IsGhost || forCompanionClass) {
// emit nothing
} else if (c is TraitDecl) {
- Indent(indent);
- wr.Write("{0} @{1}", TypeName(f.Type), f.CompileName);
+ Indent(indent, wr);
+ wr.Write("{0} @{1}", TypeName(f.Type, wr), f.CompileName);
wr.WriteLine(" { get; set; }");
} else {
- Indent(indent);
- wr.WriteLine("public {0} @{1} = {2};", TypeName(f.Type), f.CompileName, DefaultValue(f.Type));
+ Indent(indent, wr);
+ wr.WriteLine("public {0} @{1} = {2};", TypeName(f.Type, wr), f.CompileName, DefaultValue(f.Type, wr));
}
} else if (member is Function) {
var f = (Function)member;
@@ -808,29 +870,29 @@ namespace Microsoft.Dafny {
if (forCompanionClass || Attributes.Contains(f.Attributes, "axiom")) {
// suppress error message (in the case of "forCompanionClass", the non-forCompanionClass call will produce the error message)
} else {
- Error("Function {0} has no body", f.FullName);
+ Error("Function {0} has no body", wr, f.FullName);
}
} else if (f.IsGhost) {
// nothing to compile, but we do check for assumes
if (f.Body == null) {
Contract.Assert(c is TraitDecl && !f.IsStatic);
} else {
- var v = new CheckHasNoAssumes_Visitor(this);
+ var v = new CheckHasNoAssumes_Visitor(this, wr);
v.Visit(f.Body);
}
} else if (c is TraitDecl && !forCompanionClass) {
// include it, unless it's static
if (!f.IsStatic) {
- Indent(indent);
- wr.Write("{0} @{1}", TypeName(f.ResultType), f.CompileName);
+ Indent(indent, wr);
+ wr.Write("{0} @{1}", TypeName(f.ResultType, wr), f.CompileName);
wr.Write("(");
- WriteFormals("", f.Formals);
+ WriteFormals("", f.Formals, wr);
wr.WriteLine(");");
}
} else if (forCompanionClass && !f.IsStatic) {
// companion classes only has static members
} else {
- CompileFunction(indent, f);
+ CompileFunction(indent, f, wr);
}
} else if (member is Method) {
var m = (Method)member;
@@ -839,30 +901,30 @@ namespace Microsoft.Dafny {
if (forCompanionClass || Attributes.Contains(m.Attributes, "axiom")) {
// suppress error message (in the case of "forCompanionClass", the non-forCompanionClass call will produce the error message)
} else {
- Error("Method {0} has no body", m.FullName);
+ Error("Method {0} has no body", wr, m.FullName);
}
} else if (m.IsGhost) {
// nothing to compile, but we do check for assumes
if (m.Body == null) {
Contract.Assert(c is TraitDecl && !m.IsStatic);
} else {
- var v = new CheckHasNoAssumes_Visitor(this);
+ var v = new CheckHasNoAssumes_Visitor(this, wr);
v.Visit(m.Body);
}
} else if (c is TraitDecl && !forCompanionClass) {
// include it, unless it's static
if (!m.IsStatic) {
- Indent(indent);
+ Indent(indent, wr);
wr.Write("void @{0}", m.CompileName);
wr.Write("(");
- int nIns = WriteFormals("", m.Ins);
- WriteFormals(nIns == 0 ? "" : ", ", m.Outs);
+ int nIns = WriteFormals("", m.Ins, wr);
+ WriteFormals(nIns == 0 ? "" : ", ", m.Outs, wr);
wr.WriteLine(");");
}
} else if (forCompanionClass && !m.IsStatic) {
// companion classes only has static members
} else {
- CompileMethod(c, indent, m);
+ CompileMethod(c, indent, m, wr);
}
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member
@@ -870,85 +932,218 @@ namespace Microsoft.Dafny {
}
}
- private void CompileFunction(int indent, Function f) {
- Indent(indent);
- wr.Write("public {0}{1} @{2}", f.IsStatic ? "static " : "", TypeName(f.ResultType), f.CompileName);
+ private void CompileFunction(int indent, Function f, TextWriter wr) {
+ Indent(indent, wr);
+ wr.Write("public {0}{1} @{2}", f.IsStatic ? "static " : "", TypeName(f.ResultType, wr), f.CompileName);
if (f.TypeArgs.Count != 0) {
wr.Write("<{0}>", TypeParameters(f.TypeArgs));
}
wr.Write("(");
- WriteFormals("", f.Formals);
+ WriteFormals("", f.Formals, wr);
wr.WriteLine(") {");
- CompileReturnBody(f.Body, indent + IndentAmount);
- Indent(indent); wr.WriteLine("}");
+ CompileReturnBody(f.Body, indent + IndentAmount, wr);
+ Indent(indent, wr); wr.WriteLine("}");
}
- private void CompileMethod(ClassDecl c, int indent, Method m) {
- Indent(indent);
+ private void CompileMethod(ClassDecl c, int indent, Method m, TextWriter wr) {
+ Indent(indent, wr);
wr.Write("public {0}void @{1}", m.IsStatic ? "static " : "", m.CompileName);
if (m.TypeArgs.Count != 0) {
wr.Write("<{0}>", TypeParameters(m.TypeArgs));
}
wr.Write("(");
- int nIns = WriteFormals("", m.Ins);
- WriteFormals(nIns == 0 ? "" : ", ", m.Outs);
+ int nIns = WriteFormals("", m.Ins, wr);
+ WriteFormals(nIns == 0 ? "" : ", ", m.Outs, wr);
wr.WriteLine(")");
- Indent(indent); wr.WriteLine("{");
+ Indent(indent, wr); wr.WriteLine("{");
foreach (Formal p in m.Outs) {
if (!p.IsGhost) {
- Indent(indent + IndentAmount);
- wr.WriteLine("@{0} = {1};", p.CompileName, DefaultValue(p.Type));
+ Indent(indent + IndentAmount, wr);
+ wr.WriteLine("@{0} = {1};", p.CompileName, DefaultValue(p.Type, wr));
}
}
if (m.Body == null) {
- Error("Method {0} has no body", m.FullName);
+ Error("Method {0} has no body", wr, m.FullName);
} else {
if (m.IsTailRecursive) {
- Indent(indent); wr.WriteLine("TAIL_CALL_START: ;");
if (!m.IsStatic) {
- Indent(indent + IndentAmount); wr.WriteLine("var _this = this;");
+ Indent(indent + IndentAmount, wr); wr.WriteLine("var _this = this;");
}
+ Indent(indent, wr); wr.WriteLine("TAIL_CALL_START: ;");
}
Contract.Assert(enclosingMethod == null);
enclosingMethod = m;
- TrStmtList(m.Body.Body, indent);
+ TrStmtList(m.Body.Body, indent, wr);
Contract.Assert(enclosingMethod == m);
enclosingMethod = null;
}
- Indent(indent); wr.WriteLine("}");
+ Indent(indent, wr); wr.WriteLine("}");
// allow the Main method to be an instance method
- if (!m.IsStatic && IsMain(m)) {
- Indent(indent);
+ if (IsMain(m) && (!m.IsStatic || m.CompileName != "Main")) {
+ Indent(indent, wr);
wr.WriteLine("public static void Main(string[] args) {");
- Contract.Assert(m.EnclosingClass == c);
- Indent(indent + IndentAmount);
- wr.Write("@{0} b = new @{0}", c.CompileName);
- if (c.TypeArgs.Count != 0) {
- // instantiate every parameter, it doesn't particularly matter how
- wr.Write("<");
- string sep = "";
- for (int i = 0; i < c.TypeArgs.Count; i++) {
- wr.Write("{0}int", sep);
- sep = ", ";
+ if (!m.IsStatic) {
+ Contract.Assert(m.EnclosingClass == c);
+ Indent(indent + IndentAmount, wr);
+ wr.Write("@{0} b = new @{0}", c.CompileName);
+ if (c.TypeArgs.Count != 0) {
+ // instantiate every parameter, it doesn't particularly matter how
+ wr.Write("<");
+ string sep = "";
+ for (int i = 0; i < c.TypeArgs.Count; i++) {
+ wr.Write("{0}int", sep);
+ sep = ", ";
+ }
+ wr.Write(">");
}
- wr.Write(">");
+ wr.WriteLine("();");
+ Indent(indent + IndentAmount, wr); wr.WriteLine("b.@{0}();", m.CompileName);
+ } else {
+ Indent(indent + IndentAmount, wr); wr.WriteLine("@{0}();", m.CompileName);
}
- wr.WriteLine("();");
- Indent(indent + IndentAmount); wr.WriteLine("b.@Main();");
- Indent(indent); wr.WriteLine("}");
+ Indent(indent, wr); wr.WriteLine("}");
}
}
- void CompileReturnBody(Expression body, int indent) {
- Contract.Requires(0 <= indent);
- body = body.Resolved;
- Indent(indent);
+ void TrCasePatternOpt(CasePattern pat, Expression rhs, string rhs_string, int indent, TextWriter wr, bool inLetExprBody) {
+ Contract.Requires(pat != null);
+ Contract.Requires(pat.Var != null || rhs != null);
+ if (pat.Var != null) {
+ // The trivial Dafny "pattern" expression
+ // var x := G
+ // is translated into C# as:
+ // var x := G;
+ var bv = pat.Var;
+ if (!bv.IsGhost) {
+ Indent(indent, wr);
+ wr.Write("{0} {1} = ", TypeName(bv.Type, wr), "@" + bv.CompileName);
+ if (rhs != null) {
+ TrExpr(rhs, wr, inLetExprBody);
+ } else {
+ wr.Write(rhs_string);
+ }
+ wr.Write(";\n");
+ }
+ } else if (pat.Arguments != null) {
+ // The Dafny "pattern" expression
+ // var Pattern(x,y) := G
+ // is translated into C# as:
+ // var tmp := G;
+ // var x := dtorX(tmp);
+ // var y := dtorY(tmp);
+ var ctor = pat.Ctor;
+ Contract.Assert(ctor != null); // follows from successful resolution
+ Contract.Assert(pat.Arguments.Count == ctor.Formals.Count); // follows from successful resolution
+
+ // Create the temporary variable to hold G
+ var tmp_name = idGenerator.FreshId("_let_tmp_rhs");
+ Indent(indent, wr);
+ wr.Write("{0} {1} = ", TypeName(rhs.Type, wr), tmp_name);
+ TrExpr(rhs, wr, inLetExprBody);
+ wr.WriteLine(";");
+
+ var k = 0; // number of non-ghost formals processed
+ for (int i = 0; i < pat.Arguments.Count; i++) {
+ var arg = pat.Arguments[i];
+ var formal = ctor.Formals[i];
+ if (formal.IsGhost) {
+ // nothing to compile, but do a sanity check
+ Contract.Assert(!Contract.Exists(arg.Vars, bv => !bv.IsGhost));
+ } else {
+ TrCasePatternOpt(arg, null, string.Format("(({0})({1})._D).@{2}", DtCtorName(ctor, ((DatatypeValue)pat.Expr).InferredTypeArgs, wr), tmp_name, FormalName(formal, k)), indent, wr, inLetExprBody);
+ k++;
+ }
+ }
+ }
+ }
+
+ void ReturnExpr(Expression expr, int indent, TextWriter wr, bool inLetExprBody) {
+ Indent(indent, wr);
wr.Write("return ");
- TrExpr(body);
+ TrExpr(expr, wr, inLetExprBody);
wr.WriteLine(";");
}
+ void TrExprOpt(Expression expr, int indent, TextWriter wr, bool inLetExprBody) {
+ Contract.Requires(expr != null);
+ if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ if (e.Exact) {
+ for (int i = 0; i < e.LHSs.Count; i++) {
+ var lhs = e.LHSs[i];
+ if (Contract.Exists(lhs.Vars, bv => !bv.IsGhost)) {
+ TrCasePatternOpt(lhs, e.RHSs[i], null, indent, wr, inLetExprBody);
+ }
+ }
+ TrExprOpt(e.Body, indent, wr, inLetExprBody);
+ } else {
+ // We haven't optimized the other cases, so fallback to normal compilation
+ ReturnExpr(e, indent, wr, inLetExprBody);
+ }
+ } else if (expr is ITEExpr) {
+ ITEExpr e = (ITEExpr)expr;
+ Indent(indent, wr);
+ wr.Write("if (");
+ TrExpr(e.Test, wr, inLetExprBody);
+ wr.Write(") {\n");
+ TrExprOpt(e.Thn, indent + IndentAmount, wr, inLetExprBody);
+ Indent(indent, wr);
+ wr.WriteLine("} else {");
+ TrExprOpt(e.Els, indent + IndentAmount, wr, inLetExprBody);
+ Indent(indent, wr);
+ wr.WriteLine("}");
+ } else if (expr is MatchExpr) {
+ var e = (MatchExpr)expr;
+ // var _source = E;
+ // if (source.is_Ctor0) {
+ // FormalType f0 = ((Dt_Ctor0)source._D).a0;
+ // ...
+ // return Body0;
+ // } else if (...) {
+ // ...
+ // } else if (true) {
+ // ...
+ // }
+ string source = idGenerator.FreshId("_source");
+ Indent(indent, wr);
+ wr.Write("{0} {1} = ", TypeName(e.Source.Type, wr), source);
+ TrExpr(e.Source, wr, inLetExprBody);
+ wr.WriteLine(";");
+
+ if (e.Cases.Count == 0) {
+ // the verifier would have proved we never get here; still, we need some code that will compile
+ wr.Write("throw new System.Exception();");
+ } else {
+ int i = 0;
+ var sourceType = (UserDefinedType)e.Source.Type.NormalizeExpand();
+ foreach (MatchCaseExpr mc in e.Cases) {
+ //Indent(indent);
+ MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, e.Cases.Count, indent, wr);
+ TrExprOpt(mc.Body, indent + IndentAmount, wr, inLetExprBody);
+ i++;
+ }
+ Indent(indent, wr);
+ wr.WriteLine("}");
+ }
+ } else if (expr is StmtExpr) {
+ var e = (StmtExpr)expr;
+ TrExprOpt(e.E, indent, wr, inLetExprBody);
+ } else {
+ // We haven't optimized any other cases, so fallback to normal compilation
+ ReturnExpr(expr, indent, wr, inLetExprBody);
+ }
+ }
+
+ void CompileReturnBody(Expression body, int indent, TextWriter wr) {
+ Contract.Requires(0 <= indent);
+ body = body.Resolved;
+ //Indent(indent);
+ //wr.Write("return ");
+ TrExprOpt(body, indent, wr, false);
+ //wr.WriteLine(";");
+ }
+
// ----- Type ---------------------------------------------------------------------------------
readonly string DafnySetClass = "Dafny.Set";
@@ -964,168 +1159,207 @@ namespace Microsoft.Dafny {
return null;
}
- string TypeName_Companion(Type type) {
+ string TypeName_Companion(Type type, TextWriter wr) {
Contract.Requires(type != null);
var udt = type as UserDefinedType;
if (udt != null && udt.ResolvedClass is TraitDecl) {
string s = udt.FullCompanionCompileName;
if (udt.TypeArgs.Count != 0) {
if (udt.TypeArgs.Exists(argType => argType is ObjectType)) {
- Error("compilation does not support type 'object' as a type parameter; consider introducing a ghost");
+ Error("compilation does not support type 'object' as a type parameter; consider introducing a ghost", wr);
}
- s += "<" + TypeNames(udt.TypeArgs) + ">";
+ s += "<" + TypeNames(udt.TypeArgs, wr) + ">";
}
return s;
} else {
- return TypeName(type);
+ return TypeName(type, wr);
}
}
- string TypeName(Type type)
+ string TypeName(Type type, TextWriter wr)
{
Contract.Requires(type != null);
Contract.Ensures(Contract.Result<string>() != null);
- type = type.NormalizeExpand();
- if (type is TypeProxy) {
+ var xType = type.NormalizeExpand();
+ if (xType is TypeProxy) {
// unresolved proxy; just treat as ref, since no particular type information is apparently needed for this type
return "object";
}
- if (type is BoolType) {
+ if (xType is BoolType) {
return "bool";
- } else if (type is CharType) {
+ } else if (xType is CharType) {
return "char";
- } else if (type is IntType) {
+ } else if (xType is IntType) {
return "BigInteger";
- } else if (type is RealType) {
+ } else if (xType is RealType) {
return "Dafny.BigRational";
- } else if (type.AsNewtype != null) {
- NativeType nativeType = type.AsNewtype.NativeType;
+ } else if (xType.AsNewtype != null) {
+ NativeType nativeType = xType.AsNewtype.NativeType;
if (nativeType != null) {
return nativeType.Name;
}
- return TypeName(type.AsNewtype.BaseType);
- } else if (type is ObjectType) {
+ return TypeName(xType.AsNewtype.BaseType, wr);
+ } else if (xType is ObjectType) {
return "object";
- } else if (type.IsArrayType) {
- ArrayClassDecl at = type.AsArrayType;
+ } else if (xType.IsArrayType) {
+ ArrayClassDecl at = xType.AsArrayType;
Contract.Assert(at != null); // follows from type.IsArrayType
- Type elType = UserDefinedType.ArrayElementType(type);
- string name = TypeName(elType) + "[";
+ Type elType = UserDefinedType.ArrayElementType(xType);
+ string name = TypeName(elType, wr) + "[";
for (int i = 1; i < at.Dims; i++) {
name += ",";
}
return name + "]";
- } else if (type is UserDefinedType) {
- var udt = (UserDefinedType)type;
- return TypeName_UDT(udt.FullCompileName, udt.TypeArgs);
- } else if (type is SetType) {
- Type argType = ((SetType)type).Arg;
+ } else if (xType is UserDefinedType) {
+ var udt = (UserDefinedType)xType;
+ var s = udt.FullCompileName;
+ var rc = udt.ResolvedClass;
+ if (DafnyOptions.O.IronDafny &&
+ !(xType is ArrowType) &&
+ rc != null &&
+ rc.Module != null &&
+ !rc.Module.IsDefaultModule) {
+ while (rc.ClonedFrom != null || rc.ExclusiveRefinement != null) {
+ if (rc.ClonedFrom != null) {
+ rc = (TopLevelDecl)rc.ClonedFrom;
+ } else {
+ Contract.Assert(rc.ExclusiveRefinement != null);
+ rc = rc.ExclusiveRefinement;
+ }
+ }
+ s = rc.FullCompileName;
+ }
+ return TypeName_UDT(s, udt.TypeArgs, wr);
+ } else if (xType is SetType) {
+ Type argType = ((SetType)xType).Arg;
if (argType is ObjectType) {
- Error("compilation of set<object> is not supported; consider introducing a ghost");
+ Error("compilation of set<object> is not supported; consider introducing a ghost", wr);
}
- return DafnySetClass + "<" + TypeName(argType) + ">";
- } else if (type is SeqType) {
- Type argType = ((SeqType)type).Arg;
+ return DafnySetClass + "<" + TypeName(argType, wr) + ">";
+ } else if (xType is SeqType) {
+ Type argType = ((SeqType)xType).Arg;
if (argType is ObjectType) {
- Error("compilation of seq<object> is not supported; consider introducing a ghost");
+ Error("compilation of seq<object> is not supported; consider introducing a ghost", wr);
}
- return DafnySeqClass + "<" + TypeName(argType) + ">";
- } else if (type is MultiSetType) {
- Type argType = ((MultiSetType)type).Arg;
+ return DafnySeqClass + "<" + TypeName(argType, wr) + ">";
+ } else if (xType is MultiSetType) {
+ Type argType = ((MultiSetType)xType).Arg;
if (argType is ObjectType) {
- Error("compilation of seq<object> is not supported; consider introducing a ghost");
+ Error("compilation of seq<object> is not supported; consider introducing a ghost", wr);
}
- return DafnyMultiSetClass + "<" + TypeName(argType) + ">";
- } else if (type is MapType) {
- Type domType = ((MapType)type).Domain;
- Type ranType = ((MapType)type).Range;
+ return DafnyMultiSetClass + "<" + TypeName(argType, wr) + ">";
+ } else if (xType is MapType) {
+ Type domType = ((MapType)xType).Domain;
+ Type ranType = ((MapType)xType).Range;
if (domType is ObjectType || ranType is ObjectType) {
- Error("compilation of map<object, _> or map<_, object> is not supported; consider introducing a ghost");
+ Error("compilation of map<object, _> or map<_, object> is not supported; consider introducing a ghost", wr);
}
- return DafnyMapClass + "<" + TypeName(domType) + "," + TypeName(ranType) + ">";
+ return DafnyMapClass + "<" + TypeName(domType, wr) + "," + TypeName(ranType, wr) + ">";
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
}
}
- string TypeName_UDT(string fullCompileName, List<Type> typeArgs) {
+ string TypeName_UDT(string fullCompileName, List<Type> typeArgs, TextWriter wr) {
Contract.Requires(fullCompileName != null);
Contract.Requires(typeArgs != null);
string s = "@" + fullCompileName;
if (typeArgs.Count != 0) {
if (typeArgs.Exists(argType => argType is ObjectType)) {
- Error("compilation does not support type 'object' as a type parameter; consider introducing a ghost");
+ Error("compilation does not support type 'object' as a type parameter; consider introducing a ghost", wr);
}
- s += "<" + TypeNames(typeArgs) + ">";
+ s += "<" + TypeNames(typeArgs, wr) + ">";
}
return s;
}
- string/*!*/ TypeNames(List<Type/*!*/>/*!*/ types) {
+ string/*!*/ TypeNames(List<Type/*!*/>/*!*/ types, TextWriter wr) {
Contract.Requires(cce.NonNullElements(types));
Contract.Ensures(Contract.Result<string>() != null);
- return Util.Comma(types, TypeName);
+ string res = "";
+ string c = "";
+ foreach (var t in types) {
+ res += c + TypeName(t, wr);
+ c = ",";
+ }
+ return res;
}
string/*!*/ TypeParameters(List<TypeParameter/*!*/>/*!*/ targs) {
Contract.Requires(cce.NonNullElements(targs));
Contract.Ensures(Contract.Result<string>() != null);
- string s = "";
- string sep = "";
- foreach (TypeParameter tp in targs) {
- s += sep + "@" + tp.CompileName;
- sep = ",";
- }
- return s;
+ return Util.Comma(targs, tp => "@" + tp.CompileName);
}
- string DefaultValue(Type type)
+ string DefaultValue(Type type, TextWriter wr)
{
Contract.Requires(type != null);
Contract.Ensures(Contract.Result<string>() != null);
- type = type.NormalizeExpand();
- if (type is TypeProxy) {
+ var xType = type.NormalizeExpand();
+ if (xType is TypeProxy) {
// unresolved proxy; just treat as ref, since no particular type information is apparently needed for this type
return "null";
}
- if (type is BoolType) {
+ if (xType is BoolType) {
return "false";
- } else if (type is CharType) {
+ } else if (xType is CharType) {
return "'D'";
- } else if (type is IntType) {
+ } else if (xType is IntType) {
return "BigInteger.Zero";
- } else if (type is RealType) {
+ } else if (xType is RealType) {
return "Dafny.BigRational.ZERO";
- } else if (type.AsNewtype != null) {
- if (type.AsNewtype.NativeType != null) {
+ } else if (xType.AsNewtype != null) {
+ if (xType.AsNewtype.NativeType != null) {
return "0";
}
- return DefaultValue(type.AsNewtype.BaseType);
- } else if (type.IsRefType) {
- return string.Format("({0})null", TypeName(type));
- } else if (type.IsDatatype) {
- UserDefinedType udt = (UserDefinedType)type;
- string s = "@" + udt.FullCompileName;
+ return DefaultValue(xType.AsNewtype.BaseType, wr);
+ } else if (xType.IsRefType) {
+ return string.Format("({0})null", TypeName(xType, wr));
+ } else if (xType.IsDatatype) {
+ var udt = (UserDefinedType)xType;
+ var s = "@" + udt.FullCompileName;
+ var rc = udt.ResolvedClass;
+ if (DafnyOptions.O.IronDafny &&
+ !(xType is ArrowType) &&
+ rc != null &&
+ rc.Module != null &&
+ !rc.Module.IsDefaultModule) {
+ while (rc.ClonedFrom != null || rc.ExclusiveRefinement != null) {
+ if (rc.ClonedFrom != null) {
+ rc = (TopLevelDecl)rc.ClonedFrom;
+ } else {
+ Contract.Assert(rc.ExclusiveRefinement != null);
+ rc = rc.ExclusiveRefinement;
+ }
+ }
+ s = "@" + rc.FullCompileName;
+ }
if (udt.TypeArgs.Count != 0) {
- s += "<" + TypeNames(udt.TypeArgs) + ">";
+ s += "<" + TypeNames(udt.TypeArgs, wr) + ">";
}
return string.Format("new {0}()", s);
- } else if (type.IsTypeParameter) {
- var udt = (UserDefinedType)type;
- return "default(@" + udt.FullCompileName + ")";
- } else if (type is SetType) {
- return DafnySetClass + "<" + TypeName(((SetType)type).Arg) + ">.Empty";
- } else if (type is MultiSetType) {
- return DafnyMultiSetClass + "<" + TypeName(((MultiSetType)type).Arg) + ">.Empty";
- } else if (type is SeqType) {
- return DafnySeqClass + "<" + TypeName(((SeqType)type).Arg) + ">.Empty";
- } else if (type is MapType) {
- return TypeName(type)+".Empty";
- } else if (type is ArrowType) {
+ } else if (xType.IsTypeParameter) {
+ var udt = (UserDefinedType)xType;
+ string s = "default(@" + udt.FullCompileName;
+ if (udt.TypeArgs.Count != 0)
+ {
+ s += "<" + TypeNames(udt.TypeArgs, wr) + ">";
+ }
+ s += ")";
+ return s;
+ } else if (xType is SetType) {
+ return DafnySetClass + "<" + TypeName(((SetType)xType).Arg, wr) + ">.Empty";
+ } else if (xType is MultiSetType) {
+ return DafnyMultiSetClass + "<" + TypeName(((MultiSetType)xType).Arg, wr) + ">.Empty";
+ } else if (xType is SeqType) {
+ return DafnySeqClass + "<" + TypeName(((SeqType)xType).Arg, wr) + ">.Empty";
+ } else if (xType is MapType) {
+ return TypeName(xType, wr) + ".Empty";
+ } else if (xType is ArrowType) {
return "null";
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
@@ -1137,59 +1371,61 @@ namespace Microsoft.Dafny {
public class CheckHasNoAssumes_Visitor : BottomUpVisitor
{
readonly Compiler compiler;
- public CheckHasNoAssumes_Visitor(Compiler c) {
+ TextWriter wr;
+ public CheckHasNoAssumes_Visitor(Compiler c, TextWriter wr) {
Contract.Requires(c != null);
compiler = c;
+ this.wr = wr;
}
protected override void VisitOneStmt(Statement stmt) {
if (stmt is AssumeStmt) {
- compiler.Error("an assume statement cannot be compiled (line {0})", stmt.Tok.line);
+ compiler.Error("an assume statement cannot be compiled (line {0})", wr, stmt.Tok.line);
} else if (stmt is AssignSuchThatStmt) {
var s = (AssignSuchThatStmt)stmt;
if (s.AssumeToken != null) {
- compiler.Error("an assume statement cannot be compiled (line {0})", s.AssumeToken.line);
+ compiler.Error("an assume statement cannot be compiled (line {0})", wr, s.AssumeToken.line);
}
} else if (stmt is ForallStmt) {
var s = (ForallStmt)stmt;
if (s.Body == null) {
- compiler.Error("a forall statement without a body cannot be compiled (line {0})", stmt.Tok.line);
+ compiler.Error("a forall statement without a body cannot be compiled (line {0})", wr, stmt.Tok.line);
}
} else if (stmt is WhileStmt) {
var s = (WhileStmt)stmt;
if (s.Body == null) {
- compiler.Error("a while statement without a body cannot be compiled (line {0})", stmt.Tok.line);
+ compiler.Error("a while statement without a body cannot be compiled (line {0})", wr, stmt.Tok.line);
}
}
}
}
- void TrStmt(Statement stmt, int indent)
+ TextWriter TrStmt(Statement stmt, int indent)
{
Contract.Requires(stmt != null);
+ TextWriter wr = new StringWriter();
if (stmt.IsGhost) {
- var v = new CheckHasNoAssumes_Visitor(this);
+ var v = new CheckHasNoAssumes_Visitor(this, wr);
v.Visit(stmt);
- Indent(indent); wr.WriteLine("{ }");
- return;
+ Indent(indent, wr); wr.WriteLine("{ }");
+ return wr;
}
-
if (stmt is PrintStmt) {
PrintStmt s = (PrintStmt)stmt;
foreach (var arg in s.Args) {
- Indent(indent);
+ Indent(indent, wr);
wr.Write("System.Console.Write(");
- TrExpr(arg);
+ TrExpr(arg, wr, false);
wr.WriteLine(");");
}
} else if (stmt is BreakStmt) {
var s = (BreakStmt)stmt;
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("goto after_{0};", s.TargetStmt.Labels.Data.AssignUniqueId("after_", idGenerator));
} else if (stmt is ProduceStmt) {
var s = (ProduceStmt)stmt;
if (s.hiddenUpdate != null)
- TrStmt(s.hiddenUpdate, indent);
- Indent(indent);
+ wr.Write(TrStmt(s.hiddenUpdate, indent).ToString());
+ Indent(indent, wr);
if (s is YieldStmt) {
wr.WriteLine("yield return null;");
} else {
@@ -1199,7 +1435,7 @@ namespace Microsoft.Dafny {
var s = (UpdateStmt)stmt;
var resolved = s.ResolvedStatements;
if (resolved.Count == 1) {
- TrStmt(resolved[0], indent);
+ wr.Write(TrStmt(resolved[0], indent).ToString());
} else {
// multi-assignment
Contract.Assert(s.Lhss.Count == resolved.Count);
@@ -1211,17 +1447,17 @@ namespace Microsoft.Dafny {
var lhs = s.Lhss[i];
var rhs = s.Rhss[i];
if (!(rhs is HavocRhs)) {
- lvalues.Add(CreateLvalue(lhs, indent));
+ lvalues.Add(CreateLvalue(lhs, indent, wr));
string target = idGenerator.FreshId("_rhs");
rhss.Add(target);
- TrRhs("var " + target, null, rhs, indent);
+ TrRhs("var " + target, null, rhs, indent, wr);
}
}
}
Contract.Assert(lvalues.Count == rhss.Count);
for (int i = 0; i < lvalues.Count; i++) {
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("{0} = {1};", lvalues[i], rhss[i]);
}
}
@@ -1229,32 +1465,32 @@ namespace Microsoft.Dafny {
} else if (stmt is AssignStmt) {
AssignStmt s = (AssignStmt)stmt;
Contract.Assert(!(s.Lhs is SeqSelectExpr) || ((SeqSelectExpr)s.Lhs).SelectOne); // multi-element array assignments are not allowed
- TrRhs(null, s.Lhs, s.Rhs, indent);
+ TrRhs(null, s.Lhs, s.Rhs, indent, wr);
} else if (stmt is AssignSuchThatStmt) {
var s = (AssignSuchThatStmt)stmt;
if (s.AssumeToken != null) {
// Note, a non-ghost AssignSuchThatStmt may contain an assume
- Error("an assume statement cannot be compiled (line {0})", s.AssumeToken.line);
+ Error("an assume statement cannot be compiled (line {0})", wr, s.AssumeToken.line);
} else if (s.MissingBounds != null) {
foreach (var bv in s.MissingBounds) {
- Error("this assign-such-that statement is too advanced for the current compiler; Dafny's heuristics cannot find any bound for variable '{0}' (line {1})", bv.Name, s.Tok.line);
+ Error("this assign-such-that statement is too advanced for the current compiler; Dafny's heuristics cannot find any bound for variable '{0}' (line {1})", wr, bv.Name, s.Tok.line);
}
} else {
Contract.Assert(s.Bounds != null); // follows from s.MissingBounds == null
TrAssignSuchThat(indent,
s.Lhss.ConvertAll(lhs => ((IdentifierExpr)lhs.Resolved).Var), // the resolver allows only IdentifierExpr left-hand sides
- s.Expr, s.Bounds, s.Tok.line);
+ s.Expr, s.Bounds, s.Tok.line, wr, false);
}
} else if (stmt is CallStmt) {
CallStmt s = (CallStmt)stmt;
- TrCallStmt(s, null, indent);
+ wr.Write(TrCallStmt(s, null, indent).ToString());
} else if (stmt is BlockStmt) {
- Indent(indent); wr.WriteLine("{");
- TrStmtList(((BlockStmt)stmt).Body, indent);
- Indent(indent); wr.WriteLine("}");
+ Indent(indent, wr); wr.WriteLine("{");
+ TrStmtList(((BlockStmt)stmt).Body, indent, wr);
+ Indent(indent, wr); wr.WriteLine("}");
} else if (stmt is IfStmt) {
IfStmt s = (IfStmt)stmt;
@@ -1263,37 +1499,45 @@ namespace Microsoft.Dafny {
if (s.Els == null) {
// let's compile the "else" branch, since that involves no work
// (still, let's leave a marker in the source code to indicate that this is what we did)
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("if (!false) { }");
} else {
// let's compile the "then" branch
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("if (true)");
- TrStmt(s.Thn, indent);
+ wr.Write(TrStmt(s.Thn, indent).ToString());
}
} else {
- Indent(indent); wr.Write("if (");
- TrExpr(s.Guard);
+ Indent(indent, wr); wr.Write("if (");
+ TrExpr(s.IsExistentialGuard ? Translator.AlphaRename((ExistsExpr)s.Guard, "eg_d", new Translator(null)) : s.Guard, wr, false);
wr.WriteLine(")");
- TrStmt(s.Thn, indent);
+ // We'd like to do "TrStmt(s.Thn, indent)", except we want the scope of any existential variables to come inside the block
+ Indent(indent, wr); wr.WriteLine("{");
+ if (s.IsExistentialGuard) {
+ IntroduceAndAssignBoundVars(indent + IndentAmount, (ExistsExpr)s.Guard, wr);
+ }
+ TrStmtList(s.Thn.Body, indent, wr);
+ Indent(indent, wr); wr.WriteLine("}");
+
if (s.Els != null) {
- Indent(indent); wr.WriteLine("else");
- TrStmt(s.Els, indent);
+ Indent(indent, wr); wr.WriteLine("else");
+ wr.Write(TrStmt(s.Els, indent).ToString());
}
}
} else if (stmt is AlternativeStmt) {
var s = (AlternativeStmt)stmt;
- foreach (var alternative in s.Alternatives) {
- }
- Indent(indent);
+ Indent(indent, wr);
foreach (var alternative in s.Alternatives) {
wr.Write("if (");
- TrExpr(alternative.Guard);
+ TrExpr(alternative.IsExistentialGuard ? Translator.AlphaRename((ExistsExpr)alternative.Guard, "eg_d", new Translator(null)) : alternative.Guard, wr, false);
wr.WriteLine(") {");
- TrStmtList(alternative.Body, indent);
- Indent(indent);
+ if (alternative.IsExistentialGuard) {
+ IntroduceAndAssignBoundVars(indent + IndentAmount, (ExistsExpr)alternative.Guard, wr);
+ }
+ TrStmtList(alternative.Body, indent, wr);
+ Indent(indent, wr);
wr.Write("} else ");
}
wr.WriteLine("{ /*unreachable alternative*/ }");
@@ -1301,38 +1545,38 @@ namespace Microsoft.Dafny {
} else if (stmt is WhileStmt) {
WhileStmt s = (WhileStmt)stmt;
if (s.Body == null) {
- return;
+ return wr;
}
if (s.Guard == null) {
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("while (false) { }");
} else {
- Indent(indent);
+ Indent(indent, wr);
wr.Write("while (");
- TrExpr(s.Guard);
+ TrExpr(s.Guard, wr, false);
wr.WriteLine(")");
- TrStmt(s.Body, indent);
+ wr.Write(TrStmt(s.Body, indent).ToString());
}
} else if (stmt is AlternativeLoopStmt) {
var s = (AlternativeLoopStmt)stmt;
if (s.Alternatives.Count != 0) {
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("while (true) {");
int ind = indent + IndentAmount;
foreach (var alternative in s.Alternatives) {
}
- Indent(ind);
+ Indent(ind, wr);
foreach (var alternative in s.Alternatives) {
wr.Write("if (");
- TrExpr(alternative.Guard);
+ TrExpr(alternative.Guard, wr, false);
wr.WriteLine(") {");
- TrStmtList(alternative.Body, ind);
- Indent(ind);
+ TrStmtList(alternative.Body, ind, wr);
+ Indent(ind, wr);
wr.Write("} else ");
}
wr.WriteLine("{ break; }");
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("}");
}
@@ -1340,17 +1584,17 @@ namespace Microsoft.Dafny {
var s = (ForallStmt)stmt;
if (s.Kind != ForallStmt.ParBodyKind.Assign) {
// Call and Proof have no side effects, so they can simply be optimized away.
- return;
+ return wr;
} else if (s.BoundVars.Count == 0) {
// the bound variables just spell out a single point, so the forall statement is equivalent to one execution of the body
- TrStmt(s.Body, indent);
- return;
+ wr.Write(TrStmt(s.Body, indent).ToString());
+ return wr;
}
var s0 = (AssignStmt)s.S0;
if (s0.Rhs is HavocRhs) {
// The forall statement says to havoc a bunch of things. This can be efficiently compiled
// into doing nothing.
- return;
+ return wr;
}
var rhs = ((ExprRhs)s0.Rhs).Expr;
@@ -1394,29 +1638,29 @@ namespace Microsoft.Dafny {
if (s0.Lhs is MemberSelectExpr) {
var lhs = (MemberSelectExpr)s0.Lhs;
L = 2;
- tupleTypeArgs = TypeName(lhs.Obj.Type);
+ tupleTypeArgs = TypeName(lhs.Obj.Type, wr);
} else if (s0.Lhs is SeqSelectExpr) {
var lhs = (SeqSelectExpr)s0.Lhs;
L = 3;
// note, we might as well do the BigInteger-to-int cast for array indices here, before putting things into the Tuple rather than when they are extracted from the Tuple
- tupleTypeArgs = TypeName(lhs.Seq.Type) + ",int";
+ tupleTypeArgs = TypeName(lhs.Seq.Type, wr) + ",int";
} else {
var lhs = (MultiSelectExpr)s0.Lhs;
L = 2 + lhs.Indices.Count;
if (8 < L) {
- Error("compiler currently does not support assignments to more-than-6-dimensional arrays in forall statements");
- return;
+ Error("compiler currently does not support assignments to more-than-6-dimensional arrays in forall statements", wr);
+ return wr;
}
- tupleTypeArgs = TypeName(lhs.Array.Type);
+ tupleTypeArgs = TypeName(lhs.Array.Type, wr);
for (int i = 0; i < lhs.Indices.Count; i++) {
// note, we might as well do the BigInteger-to-int cast for array indices here, before putting things into the Tuple rather than when they are extracted from the Tuple
tupleTypeArgs += ",int";
}
}
- tupleTypeArgs += "," + TypeName(rhs.Type);
+ tupleTypeArgs += "," + TypeName(rhs.Type, wr);
// declare and construct "ingredients"
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("var {0} = new System.Collections.Generic.List<System.Tuple<{1}>>();", ingredients, tupleTypeArgs);
var n = s.BoundVars.Count;
@@ -1426,31 +1670,38 @@ namespace Microsoft.Dafny {
var bound = s.Bounds[i];
var bv = s.BoundVars[i];
if (bound is ComprehensionExpr.BoolBoundedPool) {
- Indent(ind);
+ Indent(ind, wr);
wr.Write("foreach (var @{0} in Dafny.Helpers.AllBooleans) {{ ", bv.CompileName);
+ } else if (bound is ComprehensionExpr.CharBoundedPool) {
+ Indent(ind, wr);
+ wr.Write("foreach (var @{0} in Dafny.Helpers.AllChars) {{ ", bv.CompileName);
} else if (bound is ComprehensionExpr.IntBoundedPool) {
var b = (ComprehensionExpr.IntBoundedPool)bound;
- Indent(ind);
- wr.Write("for (var @{0} = ", bv.CompileName);
- TrExpr(b.LowerBound);
- wr.Write("; @{0} < ", bv.CompileName);
- TrExpr(b.UpperBound);
- wr.Write("; @{0}++) {{ ", bv.CompileName);
+ Indent(ind, wr);
+ if (AsNativeType(bv.Type) != null) {
+ wr.Write("foreach (var @{0} in @{1}.IntegerRange(", bv.CompileName, bv.Type.AsNewtype.FullCompileName);
+ } else {
+ wr.Write("foreach (var @{0} in Dafny.Helpers.IntegerRange(", bv.CompileName);
+ }
+ TrExpr(b.LowerBound, wr, false);
+ wr.Write(", ");
+ TrExpr(b.UpperBound, wr, false);
+ wr.Write(")) { ");
} else if (bound is ComprehensionExpr.SetBoundedPool) {
var b = (ComprehensionExpr.SetBoundedPool)bound;
- Indent(ind);
+ Indent(ind, wr);
wr.Write("foreach (var @{0} in (", bv.CompileName);
- TrExpr(b.Set);
+ TrExpr(b.Set, wr, false);
wr.Write(").Elements) { ");
} else if (bound is ComprehensionExpr.SeqBoundedPool) {
var b = (ComprehensionExpr.SeqBoundedPool)bound;
- Indent(ind);
+ Indent(ind, wr);
wr.Write("foreach (var @{0} in (", bv.CompileName);
- TrExpr(b.Seq);
+ TrExpr(b.Seq, wr, false);
wr.Write(").UniqueElements) { ");
} else if (bound is ComprehensionExpr.DatatypeBoundedPool) {
var b = (ComprehensionExpr.DatatypeBoundedPool)bound;
- wr.Write("foreach (var @{0} in {1}.AllSingletonConstructors) {{", bv.CompileName, TypeName(bv.Type));
+ wr.Write("foreach (var @{0} in {1}.AllSingletonConstructors) {{", bv.CompileName, TypeName(bv.Type, wr));
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type
}
@@ -1460,55 +1711,55 @@ namespace Microsoft.Dafny {
// if (range) {
// ingredients.Add(new L-Tuple( LHS0(w,x,y,z), LHS1(w,x,y,z), ..., RHS(w,x,y,z) ));
// }
- Indent(indent + n * IndentAmount);
+ Indent(indent + n * IndentAmount, wr);
wr.Write("if (");
foreach (var bv in s.BoundVars) {
if (bv.Type.NormalizeExpand() is NatType) {
wr.Write("0 <= {0} && ", bv.CompileName);
}
}
- TrExpr(s.Range);
+ TrExpr(s.Range, wr, false);
wr.WriteLine(") {");
var indFinal = indent + (n + 1) * IndentAmount;
- Indent(indFinal);
+ Indent(indFinal, wr);
wr.Write("{0}.Add(new System.Tuple<{1}>(", ingredients, tupleTypeArgs);
if (s0.Lhs is MemberSelectExpr) {
var lhs = (MemberSelectExpr)s0.Lhs;
- TrExpr(lhs.Obj);
+ TrExpr(lhs.Obj, wr, false);
} else if (s0.Lhs is SeqSelectExpr) {
var lhs = (SeqSelectExpr)s0.Lhs;
- TrExpr(lhs.Seq);
+ TrExpr(lhs.Seq, wr, false);
wr.Write(", (int)(");
- TrExpr(lhs.E0);
+ TrExpr(lhs.E0, wr, false);
wr.Write(")");
} else {
var lhs = (MultiSelectExpr)s0.Lhs;
- TrExpr(lhs.Array);
+ TrExpr(lhs.Array, wr, false);
for (int i = 0; i < lhs.Indices.Count; i++) {
wr.Write(", (int)(");
- TrExpr(lhs.Indices[i]);
+ TrExpr(lhs.Indices[i], wr, false);
wr.Write(")");
}
}
wr.Write(", ");
- TrExpr(rhs);
+ TrExpr(rhs, wr, false);
wr.WriteLine("));");
- Indent(indent + n * IndentAmount);
+ Indent(indent + n * IndentAmount, wr);
wr.WriteLine("}");
for (int i = n; 0 <= --i; ) {
- Indent(indent + i * IndentAmount);
+ Indent(indent + i * IndentAmount, wr);
wr.WriteLine("}");
}
// foreach (L-Tuple l in ingredients) {
// LHS[ l0, l1, l2, ..., l(L-2) ] = l(L-1);
// }
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("foreach (var {0} in {1}) {{", tup, ingredients);
- Indent(indent + IndentAmount);
+ Indent(indent + IndentAmount, wr);
if (s0.Lhs is MemberSelectExpr) {
var lhs = (MemberSelectExpr)s0.Lhs;
wr.WriteLine("{0}.Item1.@{1} = {0}.Item2;", tup, lhs.MemberName);
@@ -1525,7 +1776,7 @@ namespace Microsoft.Dafny {
}
wr.WriteLine("] = {0}.Item{1};", tup, L);
}
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("}");
} else if (stmt is MatchStmt) {
@@ -1542,42 +1793,64 @@ namespace Microsoft.Dafny {
// }
if (s.Cases.Count != 0) {
string source = idGenerator.FreshId("_source");
- Indent(indent);
- wr.Write("{0} {1} = ", TypeName(cce.NonNull(s.Source.Type)), source);
- TrExpr(s.Source);
+ Indent(indent, wr);
+ wr.Write("{0} {1} = ", TypeName(cce.NonNull(s.Source.Type), wr), source);
+ TrExpr(s.Source, wr, false);
wr.WriteLine(";");
int i = 0;
var sourceType = (UserDefinedType)s.Source.Type.NormalizeExpand();
foreach (MatchCaseStmt mc in s.Cases) {
- MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, s.Cases.Count, indent);
- TrStmtList(mc.Body, indent);
+ MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, s.Cases.Count, indent, wr);
+ TrStmtList(mc.Body, indent, wr);
i++;
}
- Indent(indent); wr.WriteLine("}");
+ Indent(indent, wr); wr.WriteLine("}");
}
} else if (stmt is VarDeclStmt) {
var s = (VarDeclStmt)stmt;
foreach (var local in s.Locals) {
- TrLocalVar(local, true, indent);
+ TrLocalVar(local, true, indent, wr);
}
if (s.Update != null) {
- TrStmt(s.Update, indent);
+ wr.Write(TrStmt(s.Update, indent).ToString());
}
+ } else if (stmt is LetStmt) {
+ var s = (LetStmt)stmt;
+ for (int i = 0; i < s.LHSs.Count; i++) {
+ var lhs = s.LHSs[i];
+ if (Contract.Exists(lhs.Vars, bv => !bv.IsGhost)) {
+ TrCasePatternOpt(lhs, s.RHSs[i], null, indent, wr, false);
+ }
+ }
} else if (stmt is ModifyStmt) {
var s = (ModifyStmt)stmt;
if (s.Body != null) {
- TrStmt(s.Body, indent);
+ wr.Write(TrStmt(s.Body, indent).ToString());
}
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement
}
+
+ return wr;
}
- private void TrAssignSuchThat(int indent, List<IVariable> lhss, Expression constraint, List<ComprehensionExpr.BoundedPool> bounds, int debuginfoLine) {
+ private void IntroduceAndAssignBoundVars(int indent, ExistsExpr exists, TextWriter wr) {
+ Contract.Requires(0 <= indent);
+ Contract.Requires(exists != null);
+ Contract.Assume(exists.Bounds != null); // follows from successful resolution
+ Contract.Assert(exists.Range == null); // follows from invariant of class IfStmt
+ foreach (var bv in exists.BoundVars) {
+ TrLocalVar(bv, false, indent, wr);
+ }
+ var ivars = exists.BoundVars.ConvertAll(bv => (IVariable)bv);
+ TrAssignSuchThat(indent, ivars, exists.Term, exists.Bounds, exists.tok.line, wr, false);
+ }
+
+ private void TrAssignSuchThat(int indent, List<IVariable> lhss, Expression constraint, List<ComprehensionExpr.BoundedPool> bounds, int debuginfoLine, TextWriter wr, bool inLetExprBody) {
Contract.Requires(0 <= indent);
Contract.Requires(lhss != null);
Contract.Requires(constraint != null);
@@ -1619,7 +1892,7 @@ namespace Microsoft.Dafny {
int ind = indent;
bool needIterLimit = lhss.Count != 1 && bounds.Exists(bnd => !bnd.IsFinite);
if (needIterLimit) {
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("for (var {0} = new BigInteger(5); ; {0} *= 2) {{", iterLimit);
ind += IndentAmount;
}
@@ -1628,89 +1901,87 @@ namespace Microsoft.Dafny {
var bound = bounds[i];
var bv = lhss[i];
if (needIterLimit) {
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("var {0}_{1} = {0};", iterLimit, i);
}
var tmpVar = idGenerator.FreshId("_assign_such_that_");
- Indent(ind);
+ Indent(ind, wr);
if (bound is ComprehensionExpr.BoolBoundedPool) {
wr.WriteLine("foreach (var {0} in Dafny.Helpers.AllBooleans) {{ @{1} = {0};", tmpVar, bv.CompileName);
+ } else if (bound is ComprehensionExpr.CharBoundedPool) {
+ wr.WriteLine("foreach (var {0} in Dafny.Helpers.AllChars) {{ @{1} = {0};", tmpVar, bv.CompileName);
} else if (bound is ComprehensionExpr.IntBoundedPool) {
var b = (ComprehensionExpr.IntBoundedPool)bound;
- // (tmpVar is not used in this case)
- if (b.LowerBound != null) {
- wr.Write("@{0} = ", bv.CompileName);
- TrExpr(b.LowerBound);
- wr.WriteLine(";");
- Indent(ind);
- if (b.UpperBound != null) {
- wr.Write("for (; @{0} < ", bv.CompileName);
- TrExpr(b.UpperBound);
- wr.WriteLine("; @{0}++) {{ ", bv.CompileName);
- } else {
- wr.WriteLine("for (;; @{0}++) {{ ", bv.CompileName);
- }
+ if (AsNativeType(bv.Type) != null) {
+ wr.Write("foreach (var @{0} in @{1}.IntegerRange(", tmpVar, bv.Type.AsNewtype.FullCompileName);
} else {
- Contract.Assert(b.UpperBound != null);
- wr.Write("@{0} = ", bv.CompileName);
- TrExpr(b.UpperBound);
- wr.WriteLine(";");
- Indent(ind);
- wr.WriteLine("for (;; @{0}--) {{ ", bv.CompileName);
+ wr.Write("foreach (var @{0} in Dafny.Helpers.IntegerRange(", tmpVar);
+ }
+ if (b.LowerBound == null) {
+ wr.Write("null");
+ } else {
+ TrExpr(b.LowerBound, wr, inLetExprBody);
+ }
+ wr.Write(", ");
+ if (b.UpperBound == null) {
+ wr.Write("null");
+ } else {
+ TrExpr(b.UpperBound, wr, inLetExprBody);
}
+ wr.WriteLine(")) {{ @{1} = {0};", tmpVar, bv.CompileName);
} else if (bound is AssignSuchThatStmt.WiggleWaggleBound) {
wr.WriteLine("foreach (var {0} in Dafny.Helpers.AllIntegers) {{ @{1} = {0};", tmpVar, bv.CompileName);
} else if (bound is ComprehensionExpr.SetBoundedPool) {
var b = (ComprehensionExpr.SetBoundedPool)bound;
wr.Write("foreach (var {0} in (", tmpVar);
- TrExpr(b.Set);
+ TrExpr(b.Set, wr, inLetExprBody);
wr.WriteLine(").Elements) {{ @{0} = {1};", bv.CompileName, tmpVar);
} else if (bound is ComprehensionExpr.SubSetBoundedPool) {
var b = (ComprehensionExpr.SubSetBoundedPool)bound;
wr.Write("foreach (var {0} in (", tmpVar);
- TrExpr(b.UpperBound);
+ TrExpr(b.UpperBound, wr, inLetExprBody);
wr.WriteLine(").AllSubsets) {{ @{0} = {1};", bv.CompileName, tmpVar);
} else if (bound is ComprehensionExpr.MapBoundedPool) {
var b = (ComprehensionExpr.MapBoundedPool)bound;
wr.Write("foreach (var {0} in (", tmpVar);
- TrExpr(b.Map);
+ TrExpr(b.Map, wr, inLetExprBody);
wr.WriteLine(").Domain) {{ @{0} = {1};", bv.CompileName, tmpVar);
} else if (bound is ComprehensionExpr.SeqBoundedPool) {
var b = (ComprehensionExpr.SeqBoundedPool)bound;
wr.Write("foreach (var {0} in (", tmpVar);
- TrExpr(b.Seq);
+ TrExpr(b.Seq, wr, inLetExprBody);
wr.WriteLine(").Elements) {{ @{0} = {1};", bv.CompileName, tmpVar);
} else if (bound is ComprehensionExpr.DatatypeBoundedPool) {
var b = (ComprehensionExpr.DatatypeBoundedPool)bound;
- wr.WriteLine("foreach (var {0} in {1}.AllSingletonConstructors) {{ @{2} = {0};", tmpVar, TypeName(bv.Type), bv.CompileName);
+ wr.WriteLine("foreach (var {0} in {1}.AllSingletonConstructors) {{ @{2} = {0};", tmpVar, TypeName(bv.Type, wr), bv.CompileName);
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type
}
if (needIterLimit) {
- Indent(ind + IndentAmount);
+ Indent(ind + IndentAmount, wr);
wr.WriteLine("if ({0}_{1} == 0) {{ break; }} {0}_{1}--;", iterLimit, i);
}
}
- Indent(ind);
+ Indent(ind, wr);
wr.Write("if (");
- TrExpr(constraint);
+ TrExpr(constraint, wr, inLetExprBody);
wr.WriteLine(") {");
- Indent(ind + IndentAmount);
+ Indent(ind + IndentAmount, wr);
wr.WriteLine("goto {0};", doneLabel);
- Indent(ind);
+ Indent(ind, wr);
wr.WriteLine("}");
- Indent(indent);
+ Indent(indent, wr);
for (int i = 0; i < n; i++) {
wr.Write(i == 0 ? "}" : " }");
}
wr.WriteLine(needIterLimit ? " }" : "");
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("throw new System.Exception(\"assign-such-that search produced no value (line {0})\");", debuginfoLine);
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("{0}: ;", doneLabel);
}
- string CreateLvalue(Expression lhs, int indent) {
+ string CreateLvalue(Expression lhs, int indent, TextWriter wr) {
lhs = lhs.Resolved;
if (lhs is IdentifierExpr) {
var ll = (IdentifierExpr)lhs;
@@ -1718,9 +1989,9 @@ namespace Microsoft.Dafny {
} else if (lhs is MemberSelectExpr) {
var ll = (MemberSelectExpr)lhs;
string obj = idGenerator.FreshId("_obj");
- Indent(indent);
+ Indent(indent, wr);
wr.Write("var {0} = ", obj);
- TrExpr(ll.Obj);
+ TrExpr(ll.Obj, wr, false);
wr.WriteLine(";");
return string.Format("{0}.@{1}", obj, ll.Member.CompileName);
} else if (lhs is SeqSelectExpr) {
@@ -1728,31 +1999,31 @@ namespace Microsoft.Dafny {
var c = idGenerator.FreshNumericId("_arr+_index");
string arr = "_arr" + c;
string index = "_index" + c;
- Indent(indent);
+ Indent(indent, wr);
wr.Write("var {0} = ", arr);
- TrExpr(ll.Seq);
+ TrExpr(ll.Seq, wr, false);
wr.WriteLine(";");
- Indent(indent);
+ Indent(indent, wr);
wr.Write("var {0} = ", index);
- TrExpr(ll.E0);
+ TrExpr(ll.E0, wr, false);
wr.WriteLine(";");
return string.Format("{0}[(int){1}]", arr, index);
} else {
var ll = (MultiSelectExpr)lhs;
var c = idGenerator.FreshNumericId("_arr+_index");
string arr = "_arr" + c;
- Indent(indent);
+ Indent(indent, wr);
wr.Write("var {0} = ", arr);
- TrExpr(ll.Array);
+ TrExpr(ll.Array, wr, false);
wr.WriteLine(";");
string fullString = arr + "[";
string sep = "";
int i = 0;
foreach (var idx in ll.Indices) {
string index = "_index" + i + "_" + c;
- Indent(indent);
+ Indent(indent, wr);
wr.Write("var {0} = ", index);
- TrExpr(idx);
+ TrExpr(idx, wr, false);
wr.WriteLine(";");
fullString += sep + "(int)" + index;
sep = ", ";
@@ -1762,21 +2033,21 @@ namespace Microsoft.Dafny {
}
}
- void TrRhs(string target, Expression targetExpr, AssignmentRhs rhs, int indent) {
+ void TrRhs(string target, Expression targetExpr, AssignmentRhs rhs, int indent, TextWriter wr) {
Contract.Requires((target == null) != (targetExpr == null));
var tRhs = rhs as TypeRhs;
if (tRhs != null && tRhs.InitCall != null) {
string nw = idGenerator.FreshId("_nw");
- Indent(indent);
+ Indent(indent, wr);
wr.Write("var {0} = ", nw);
- TrAssignmentRhs(rhs); // in this case, this call will not require us to spill any let variables first
+ TrAssignmentRhs(rhs, wr); // in this case, this call will not require us to spill any let variables first
wr.WriteLine(";");
- TrCallStmt(tRhs.InitCall, nw, indent);
- Indent(indent);
+ wr.Write(TrCallStmt(tRhs.InitCall, nw, indent).ToString());
+ Indent(indent, wr);
if (target != null) {
wr.Write(target);
} else {
- TrExpr(targetExpr);
+ TrExpr(targetExpr, wr, false);
}
wr.WriteLine(" = {0};", nw);
} else if (rhs is HavocRhs) {
@@ -1787,22 +2058,23 @@ namespace Microsoft.Dafny {
foreach (Expression dim in tRhs.ArrayDimensions) {
}
}
- Indent(indent);
+ Indent(indent, wr);
if (target != null) {
wr.Write(target);
} else {
- TrExpr(targetExpr);
+ TrExpr(targetExpr, wr, false);
}
wr.Write(" = ");
- TrAssignmentRhs(rhs);
+ TrAssignmentRhs(rhs, wr);
wr.WriteLine(";");
}
}
- void TrCallStmt(CallStmt s, string receiverReplacement, int indent) {
+ TextWriter TrCallStmt(CallStmt s, string receiverReplacement, int indent) {
Contract.Requires(s != null);
Contract.Assert(s.Method != null); // follows from the fact that stmt has been successfully resolved
+ StringWriter wr = new StringWriter();
if (s.Method == enclosingMethod && enclosingMethod.IsTailRecursive) {
// compile call as tail-recursive
@@ -1819,9 +2091,9 @@ namespace Microsoft.Dafny {
string inTmp = idGenerator.FreshId("_in");
inTmps.Add(inTmp);
- Indent(indent);
+ Indent(indent, wr);
wr.Write("var {0} = ", inTmp);
- TrExpr(s.Receiver);
+ TrExpr(s.Receiver, wr, false);
wr.WriteLine(";");
}
for (int i = 0; i < s.Method.Ins.Count; i++) {
@@ -1829,29 +2101,29 @@ namespace Microsoft.Dafny {
if (!p.IsGhost) {
string inTmp = idGenerator.FreshId("_in");
inTmps.Add(inTmp);
- Indent(indent);
+ Indent(indent, wr);
wr.Write("var {0} = ", inTmp);
- TrExpr(s.Args[i]);
+ TrExpr(s.Args[i], wr, false);
wr.WriteLine(";");
}
}
// Now, assign to the formals
int n = 0;
if (!s.Method.IsStatic) {
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("_this = {0};", inTmps[n]);
n++;
}
foreach (var p in s.Method.Ins) {
if (!p.IsGhost) {
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("{0} = {1};", p.CompileName, inTmps[n]);
n++;
}
}
Contract.Assert(n == inTmps.Count);
// finally, the jump back to the head of the method
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("goto TAIL_CALL_START;");
} else {
@@ -1862,7 +2134,7 @@ namespace Microsoft.Dafny {
for (int i = 0; i < s.Method.Outs.Count; i++) {
Formal p = s.Method.Outs[i];
if (!p.IsGhost) {
- lvalues.Add(CreateLvalue(s.Lhs[i], indent));
+ lvalues.Add(CreateLvalue(s.Lhs[i], indent, wr));
}
}
var outTmps = new List<string>();
@@ -1871,8 +2143,8 @@ namespace Microsoft.Dafny {
if (!p.IsGhost) {
string target = idGenerator.FreshId("_out");
outTmps.Add(target);
- Indent(indent);
- wr.WriteLine("{0} {1};", TypeName(s.Lhs[i].Type), target);
+ Indent(indent, wr);
+ wr.WriteLine("{0} {1};", TypeName(s.Lhs[i].Type, wr), target);
}
}
Contract.Assert(lvalues.Count == outTmps.Count);
@@ -1883,14 +2155,14 @@ namespace Microsoft.Dafny {
}
}
if (receiverReplacement != null) {
- Indent(indent);
+ Indent(indent, wr);
wr.Write("@" + receiverReplacement);
} else if (s.Method.IsStatic) {
- Indent(indent);
- wr.Write(TypeName_Companion(s.Receiver.Type));
+ Indent(indent, wr);
+ wr.Write(TypeName_Companion(s.Receiver.Type, wr));
} else {
- Indent(indent);
- TrParenExpr(s.Receiver);
+ Indent(indent, wr);
+ TrParenExpr(s.Receiver, wr, false);
}
wr.Write(".@{0}(", s.Method.CompileName);
@@ -1899,7 +2171,7 @@ namespace Microsoft.Dafny {
Formal p = s.Method.Ins[i];
if (!p.IsGhost) {
wr.Write(sep);
- TrExpr(s.Args[i]);
+ TrExpr(s.Args[i], wr, false);
sep = ", ";
}
}
@@ -1912,44 +2184,45 @@ namespace Microsoft.Dafny {
// assign to the actual LHSs
for (int j = 0; j < lvalues.Count; j++) {
- Indent(indent);
+ Indent(indent, wr);
wr.WriteLine("{0} = {1};", lvalues[j], outTmps[j]);
}
}
+ return wr;
}
/// <summary>
/// Before calling TrAssignmentRhs(rhs), the caller must have spilled the let variables declared in "rhs".
/// </summary>
- void TrAssignmentRhs(AssignmentRhs rhs) {
+ void TrAssignmentRhs(AssignmentRhs rhs, TextWriter wr) {
Contract.Requires(rhs != null);
Contract.Requires(!(rhs is HavocRhs));
if (rhs is ExprRhs) {
ExprRhs e = (ExprRhs)rhs;
- TrExpr(e.Expr);
+ TrExpr(e.Expr, wr, false);
} else {
TypeRhs tp = (TypeRhs)rhs;
if (tp.ArrayDimensions == null) {
- wr.Write("new {0}()", TypeName(tp.EType));
+ wr.Write("new {0}()", TypeName(tp.EType, wr));
} else {
if (tp.EType.IsIntegerType || tp.EType.IsTypeParameter) {
// Because the default constructor for BigInteger does not generate a valid BigInteger, we have
// to excplicitly initialize the elements of an integer array. This is all done in a helper routine.
- wr.Write("Dafny.Helpers.InitNewArray{0}<{1}>", tp.ArrayDimensions.Count, TypeName(tp.EType));
+ wr.Write("Dafny.Helpers.InitNewArray{0}<{1}>", tp.ArrayDimensions.Count, TypeName(tp.EType, wr));
string prefix = "(";
foreach (Expression dim in tp.ArrayDimensions) {
wr.Write(prefix);
- TrParenExpr(dim);
+ TrParenExpr(dim, wr, false);
prefix = ", ";
}
wr.Write(")");
} else {
- wr.Write("new {0}", TypeName(tp.EType));
+ wr.Write("new {0}", TypeName(tp.EType, wr));
string prefix = "[";
foreach (Expression dim in tp.ArrayDimensions) {
wr.Write("{0}(int)", prefix);
- TrParenExpr(dim);
+ TrParenExpr(dim, wr, false);
prefix = ", ";
}
wr.Write("]");
@@ -1958,34 +2231,43 @@ namespace Microsoft.Dafny {
}
}
- void TrStmtList(List<Statement/*!*/>/*!*/ stmts, int indent) {Contract.Requires(cce.NonNullElements(stmts));
+ void TrStmtList(List<Statement/*!*/>/*!*/ stmts, int indent, TextWriter writer) {Contract.Requires(cce.NonNullElements(stmts));
foreach (Statement ss in stmts) {
- TrStmt(ss, indent + IndentAmount);
+ copyInstrWriter.Clear();
+ TextWriter wr = TrStmt(ss, indent + IndentAmount);
+ // write out any copy instructions that copies the out param
+ // used in letexpr to a local
+ string copyInstr = copyInstrWriter.ToString();
+ if (copyInstr != "") {
+ Indent(indent + IndentAmount, writer);
+ writer.Write(copyInstrWriter.ToString());
+ }
+ writer.Write(wr.ToString());
if (ss.Labels != null) {
- Indent(indent); // labels are not indented as much as the statements
- wr.WriteLine("after_{0}: ;", ss.Labels.Data.AssignUniqueId("after_", idGenerator));
+ Indent(indent, writer); // labels are not indented as much as the statements
+ writer.WriteLine("after_{0}: ;", ss.Labels.Data.AssignUniqueId("after_", idGenerator));
}
}
}
- void TrLocalVar(LocalVariable s, bool alwaysInitialize, int indent) {
- Contract.Requires(s != null);
- if (s.IsGhost) {
+ void TrLocalVar(IVariable v, bool alwaysInitialize, int indent, TextWriter wr) {
+ Contract.Requires(v != null);
+ if (v.IsGhost) {
// only emit non-ghosts (we get here only for local variables introduced implicitly by call statements)
return;
}
- Indent(indent);
- wr.Write("{0} @{1}", TypeName(s.Type), s.CompileName);
+ Indent(indent, wr);
+ wr.Write("{0} @{1}", TypeName(v.Type, wr), v.CompileName);
if (alwaysInitialize) {
// produce a default value
- wr.WriteLine(" = {0};", DefaultValue(s.Type));
+ wr.WriteLine(" = {0};", DefaultValue(v.Type, wr));
} else {
wr.WriteLine(";");
}
}
- void MatchCasePrelude(string source, UserDefinedType sourceType, DatatypeCtor ctor, List<BoundVar/*!*/>/*!*/ arguments, int caseIndex, int caseCount, int indent) {
+ void MatchCasePrelude(string source, UserDefinedType sourceType, DatatypeCtor ctor, List<BoundVar/*!*/>/*!*/ arguments, int caseIndex, int caseCount, int indent, TextWriter wr) {
Contract.Requires(source != null);
Contract.Requires(sourceType != null);
Contract.Requires(ctor != null);
@@ -1994,7 +2276,7 @@ namespace Microsoft.Dafny {
// if (source.is_Ctor0) {
// FormalType f0 = ((Dt_Ctor0)source._D).a0;
// ...
- Indent(indent);
+ Indent(indent, wr);
wr.Write("{0}if (", caseIndex == 0 ? "" : "} else ");
if (caseIndex == caseCount - 1) {
wr.Write("true");
@@ -2009,9 +2291,9 @@ namespace Microsoft.Dafny {
if (!arg.IsGhost) {
BoundVar bv = arguments[m];
// FormalType f0 = ((Dt_Ctor0)source._D).a0;
- Indent(indent + IndentAmount);
+ Indent(indent + IndentAmount, wr);
wr.WriteLine("{0} @{1} = (({2}){3}._D).@{4};",
- TypeName(bv.Type), bv.CompileName, DtCtorName(ctor, sourceType.TypeArgs), source, FormalName(arg, k));
+ TypeName(bv.Type, wr), bv.CompileName, DtCtorName(ctor, sourceType.TypeArgs, wr), source, FormalName(arg, k));
k++;
}
}
@@ -2022,51 +2304,51 @@ namespace Microsoft.Dafny {
/// <summary>
/// Before calling TrParenExpr(expr), the caller must have spilled the let variables declared in "expr".
/// </summary>
- void TrParenExpr(string prefix, Expression expr) {
+ void TrParenExpr(string prefix, Expression expr, TextWriter wr, bool inLetExprBody) {
Contract.Requires(prefix != null);
Contract.Requires(expr != null);
wr.Write(prefix);
- TrParenExpr(expr);
+ TrParenExpr(expr, wr, inLetExprBody);
}
/// <summary>
/// Before calling TrParenExpr(expr), the caller must have spilled the let variables declared in "expr".
/// </summary>
- void TrParenExpr(Expression expr) {
+ void TrParenExpr(Expression expr, TextWriter wr, bool inLetExprBody) {
Contract.Requires(expr != null);
wr.Write("(");
- TrExpr(expr);
+ TrExpr(expr, wr, inLetExprBody);
wr.Write(")");
}
/// <summary>
/// Before calling TrExprList(exprs), the caller must have spilled the let variables declared in expressions in "exprs".
/// </summary>
- void TrExprList(List<Expression/*!*/>/*!*/ exprs) {
+ void TrExprList(List<Expression/*!*/>/*!*/ exprs, TextWriter wr, bool inLetExprBody) {
Contract.Requires(cce.NonNullElements(exprs));
wr.Write("(");
string sep = "";
foreach (Expression e in exprs) {
wr.Write(sep);
- TrExpr(e);
+ TrExpr(e, wr, inLetExprBody);
sep = ", ";
}
wr.Write(")");
}
- void TrExprPairList(List<ExpressionPair/*!*/>/*!*/ exprs) {
+ void TrExprPairList(List<ExpressionPair/*!*/>/*!*/ exprs, TextWriter wr, bool inLetExprBody) {
Contract.Requires(cce.NonNullElements(exprs));
wr.Write("(");
string sep = "";
foreach (ExpressionPair p in exprs) {
wr.Write(sep);
wr.Write("new Dafny.Pair<");
- wr.Write(TypeName(p.A.Type));
+ wr.Write(TypeName(p.A.Type, wr));
wr.Write(",");
- wr.Write(TypeName(p.B.Type));
+ wr.Write(TypeName(p.B.Type, wr));
wr.Write(">(");
- TrExpr(p.A);
+ TrExpr(p.A, wr, inLetExprBody);
wr.Write(",");
- TrExpr(p.B);
+ TrExpr(p.B, wr, inLetExprBody);
wr.Write(")");
sep = ", ";
}
@@ -2076,13 +2358,15 @@ namespace Microsoft.Dafny {
/// <summary>
/// Before calling TrExpr(expr), the caller must have spilled the let variables declared in "expr".
/// </summary>
- void TrExpr(Expression expr)
+ void TrExpr(Expression expr, TextWriter wr, bool inLetExprBody)
{
Contract.Requires(expr != null);
if (expr is LiteralExpr) {
LiteralExpr e = (LiteralExpr)expr;
- if (e.Value == null) {
- wr.Write("({0})null", TypeName(e.Type));
+ if (e is StaticReceiverExpr) {
+ wr.Write(TypeName(e.Type, wr));
+ } else if (e.Value == null) {
+ wr.Write("({0})null", TypeName(e.Type, wr));
} else if (e.Value is bool) {
wr.Write((bool)e.Value ? "true" : "false");
} else if (e is CharLiteralExpr) {
@@ -2123,41 +2407,49 @@ namespace Microsoft.Dafny {
} else if (expr is IdentifierExpr) {
var e = (IdentifierExpr)expr;
- wr.Write("@" + e.Var.CompileName);
-
+ if (e.Var is Formal && inLetExprBody && !((Formal)e.Var).InParam) {
+ // out param in letExpr body, need to copy it to a temp since
+ // letExpr body is translated to an anonymous function that doesn't
+ // allow out parameters
+ var name = string.Format("_pat_let_tv{0}", GetUniqueAstNumber(e));
+ wr.Write("@" + name);
+ copyInstrWriter.Append("var @" + name + "= @" + e.Var.CompileName + ";\n");
+ } else {
+ wr.Write("@" + e.Var.CompileName);
+ }
} else if (expr is SetDisplayExpr) {
var e = (SetDisplayExpr)expr;
var elType = e.Type.AsSetType.Arg;
- wr.Write("{0}<{1}>.FromElements", DafnySetClass, TypeName(elType));
- TrExprList(e.Elements);
+ wr.Write("{0}<{1}>.FromElements", DafnySetClass, TypeName(elType, wr));
+ TrExprList(e.Elements, wr, inLetExprBody);
} else if (expr is MultiSetDisplayExpr) {
var e = (MultiSetDisplayExpr)expr;
var elType = e.Type.AsMultiSetType.Arg;
- wr.Write("{0}<{1}>.FromElements", DafnyMultiSetClass, TypeName(elType));
- TrExprList(e.Elements);
+ wr.Write("{0}<{1}>.FromElements", DafnyMultiSetClass, TypeName(elType, wr));
+ TrExprList(e.Elements, wr, inLetExprBody);
} else if (expr is SeqDisplayExpr) {
var e = (SeqDisplayExpr)expr;
var elType = e.Type.AsSeqType.Arg;
- wr.Write("{0}<{1}>.FromElements", DafnySeqClass, TypeName(elType));
- TrExprList(e.Elements);
+ wr.Write("{0}<{1}>.FromElements", DafnySeqClass, TypeName(elType, wr));
+ TrExprList(e.Elements, wr, inLetExprBody);
} else if (expr is MapDisplayExpr) {
MapDisplayExpr e = (MapDisplayExpr)expr;
- wr.Write("{0}.FromElements", TypeName(e.Type));
- TrExprPairList(e.Elements);
+ wr.Write("{0}.FromElements", TypeName(e.Type, wr));
+ TrExprPairList(e.Elements, wr, inLetExprBody);
} else if (expr is MemberSelectExpr) {
MemberSelectExpr e = (MemberSelectExpr)expr;
SpecialField sf = e.Member as SpecialField;
if (sf != null) {
wr.Write(sf.PreString);
- TrParenExpr(e.Obj);
+ TrParenExpr(e.Obj, wr, inLetExprBody);
wr.Write(".@{0}", sf.CompiledName);
wr.Write(sf.PostString);
} else {
- TrParenExpr(e.Obj);
+ TrExpr(e.Obj, wr, inLetExprBody);
wr.Write(".@{0}", e.Member.CompileName);
}
@@ -2167,50 +2459,50 @@ namespace Microsoft.Dafny {
if (e.Seq.Type.IsArrayType) {
if (e.SelectOne) {
Contract.Assert(e.E0 != null && e.E1 == null);
- TrParenExpr(e.Seq);
+ TrParenExpr(e.Seq, wr, inLetExprBody);
wr.Write("[(int)");
- TrParenExpr(e.E0);
+ TrParenExpr(e.E0, wr, inLetExprBody);
wr.Write("]");
} else {
- TrParenExpr("Dafny.Helpers.SeqFromArray", e.Seq);
+ TrParenExpr("Dafny.Helpers.SeqFromArray", e.Seq, wr, inLetExprBody);
if (e.E1 != null) {
- TrParenExpr(".Take", e.E1);
+ TrParenExpr(".Take", e.E1, wr, inLetExprBody);
}
if (e.E0 != null) {
- TrParenExpr(".Drop", e.E0);
+ TrParenExpr(".Drop", e.E0, wr, inLetExprBody);
}
}
} else if (e.SelectOne) {
Contract.Assert(e.E0 != null && e.E1 == null);
- TrParenExpr(e.Seq);
- TrParenExpr(".Select", e.E0);
+ TrParenExpr(e.Seq, wr, inLetExprBody);
+ TrParenExpr(".Select", e.E0, wr, inLetExprBody);
} else {
- TrParenExpr(e.Seq);
+ TrParenExpr(e.Seq, wr, inLetExprBody);
if (e.E1 != null) {
- TrParenExpr(".Take", e.E1);
+ TrParenExpr(".Take", e.E1, wr, inLetExprBody);
}
if (e.E0 != null) {
- TrParenExpr(".Drop", e.E0);
+ TrParenExpr(".Drop", e.E0, wr, inLetExprBody);
}
}
} else if (expr is MultiSetFormingExpr) {
var e = (MultiSetFormingExpr)expr;
- wr.Write("{0}<{1}>", DafnyMultiSetClass, TypeName(e.E.Type.AsCollectionType.Arg));
+ wr.Write("{0}<{1}>", DafnyMultiSetClass, TypeName(e.E.Type.AsCollectionType.Arg, wr));
var eeType = e.E.Type.NormalizeExpand();
if (eeType is SeqType) {
- TrParenExpr(".FromSeq", e.E);
+ TrParenExpr(".FromSeq", e.E, wr, inLetExprBody);
} else if (eeType is SetType) {
- TrParenExpr(".FromSet", e.E);
+ TrParenExpr(".FromSet", e.E, wr, inLetExprBody);
} else {
Contract.Assert(false); throw new cce.UnreachableException();
}
} else if (expr is MultiSelectExpr) {
MultiSelectExpr e = (MultiSelectExpr)expr;
- TrParenExpr(e.Array);
+ TrParenExpr(e.Array, wr, inLetExprBody);
string prefix = "[";
foreach (Expression idx in e.Indices) {
wr.Write("{0}(int)", prefix);
- TrParenExpr(idx);
+ TrParenExpr(idx, wr, inLetExprBody);
prefix = ", ";
}
wr.Write("]");
@@ -2219,47 +2511,46 @@ namespace Microsoft.Dafny {
SeqUpdateExpr e = (SeqUpdateExpr)expr;
if (e.ResolvedUpdateExpr != null)
{
- TrExpr(e.ResolvedUpdateExpr);
+ TrExpr(e.ResolvedUpdateExpr, wr, inLetExprBody);
}
else
{
- TrParenExpr(e.Seq);
+ TrParenExpr(e.Seq, wr, inLetExprBody);
wr.Write(".Update(");
- TrExpr(e.Index);
+ TrExpr(e.Index, wr, inLetExprBody);
wr.Write(", ");
- TrExpr(e.Value);
+ TrExpr(e.Value, wr, inLetExprBody);
wr.Write(")");
}
} else if (expr is FunctionCallExpr) {
FunctionCallExpr e = (FunctionCallExpr)expr;
- CompileFunctionCallExpr(e, wr, TrExpr);
+ CompileFunctionCallExpr(e, wr, wr, inLetExprBody, TrExpr);
} else if (expr is ApplyExpr) {
var e = expr as ApplyExpr;
wr.Write("Dafny.Helpers.Id<");
- wr.Write(TypeName(e.Function.Type));
+ wr.Write(TypeName(e.Function.Type, wr));
wr.Write(">(");
- TrExpr(e.Function);
+ TrExpr(e.Function, wr, inLetExprBody);
wr.Write(")");
- TrExprList(e.Args);
+ TrExprList(e.Args, wr, inLetExprBody);
} else if (expr is DatatypeValue) {
DatatypeValue dtv = (DatatypeValue)expr;
Contract.Assert(dtv.Ctor != null); // since dtv has been successfully resolved
- var typeParams = dtv.InferredTypeArgs.Count == 0 ? "" : string.Format("<{0}>", TypeNames(dtv.InferredTypeArgs));
-
- wr.Write("new {0}{1}(", DtName(dtv.Ctor.EnclosingDatatype), typeParams);
+ var typeParams = dtv.InferredTypeArgs.Count == 0 ? "" : string.Format("<{0}>", TypeNames(dtv.InferredTypeArgs, wr));
+ wr.Write("new @{0}{1}(", DtName(dtv.Ctor.EnclosingDatatype), typeParams);
if (!dtv.IsCoCall) {
// For an ordinary constructor (that is, one that does not guard any co-recursive calls), generate:
// new Dt_Cons<T>( args )
- wr.Write("new {0}(", DtCtorName(dtv.Ctor, dtv.InferredTypeArgs));
+ wr.Write("new {0}(", DtCtorName(dtv.Ctor, dtv.InferredTypeArgs, wr));
string sep = "";
for (int i = 0; i < dtv.Arguments.Count; i++) {
Formal formal = dtv.Ctor.Formals[i];
if (!formal.IsGhost) {
wr.Write(sep);
- TrExpr(dtv.Arguments[i]);
+ TrExpr(dtv.Arguments[i], wr, inLetExprBody);
sep = ", ";
}
}
@@ -2288,17 +2579,17 @@ namespace Microsoft.Dafny {
arg = varName;
wr.Write("var {0} = ", varName);
- TrExpr(actual);
+ TrExpr(actual, wr, inLetExprBody);
wr.Write("; ");
} else {
var sw = new StringWriter();
- CompileFunctionCallExpr(fce, sw, (exp) => {
+ CompileFunctionCallExpr(fce, sw, wr, inLetExprBody, (exp, wrr, inLetExpr) => {
string varName = idGenerator.FreshId("_ac");
sw.Write(varName);
- wr.Write("var {0} = ", varName);
- TrExpr(exp);
- wr.Write("; ");
+ wrr.Write("var {0} = ", varName);
+ TrExpr(exp, wrr, inLetExpr);
+ wrr.Write("; ");
});
arg = sw.ToString();
@@ -2310,7 +2601,7 @@ namespace Microsoft.Dafny {
wr.Write("return () => { return ");
- wr.Write("new {0}({1}", DtCtorName(dtv.Ctor, dtv.InferredTypeArgs), args);
+ wr.Write("new {0}({1}", DtCtorName(dtv.Ctor, dtv.InferredTypeArgs, wr), args);
wr.Write("); }; })())");
}
wr.Write(")");
@@ -2323,11 +2614,11 @@ namespace Microsoft.Dafny {
switch (e.Op) {
case UnaryOpExpr.Opcode.Not:
wr.Write("!");
- TrParenExpr(e.E);
+ TrParenExpr(e.E, wr, inLetExprBody);
break;
case UnaryOpExpr.Opcode.Cardinality:
wr.Write("new BigInteger(");
- TrParenExpr(e.E);
+ TrParenExpr(e.E, wr, inLetExprBody);
wr.Write(".Length)");
break;
default:
@@ -2345,7 +2636,7 @@ namespace Microsoft.Dafny {
if (AsNativeType(e.E.Type) != null) {
wr.Write("new BigInteger");
}
- TrParenExpr(e.E);
+ TrParenExpr(e.E, wr, inLetExprBody);
};
Action toIntCast = () => {
Contract.Assert(toInt);
@@ -2361,7 +2652,7 @@ namespace Microsoft.Dafny {
} else if (!fromInt && toInt) {
// real -> int
toIntCast();
- TrParenExpr(e.E);
+ TrParenExpr(e.E, wr, inLetExprBody);
wr.Write(".ToBigInteger()");
} else if (AsNativeType(e.ToType) != null) {
toIntCast();
@@ -2373,14 +2664,14 @@ namespace Microsoft.Dafny {
wr.Write("(" + (BigInteger)lit.Value + AsNativeType(e.ToType).Suffix + ")");
} else if ((u != null && u.Op == UnaryOpExpr.Opcode.Cardinality) || (m != null && m.MemberName == "Length" && m.Obj.Type.IsArrayType)) {
// Optimize .Length to avoid intermediate BigInteger
- TrParenExpr((u != null) ? u.E : m.Obj);
+ TrParenExpr((u != null) ? u.E : m.Obj, wr, inLetExprBody);
if (AsNativeType(e.ToType).UpperBound <= new BigInteger(0x80000000U)) {
wr.Write(".Length");
} else {
wr.Write(".LongLength");
}
} else {
- TrParenExpr(e.E);
+ TrParenExpr(e.E, wr, inLetExprBody);
}
} else if (e.ToType.IsIntegerType && AsNativeType(e.E.Type) != null) {
fromIntAsBigInteger();
@@ -2388,7 +2679,7 @@ namespace Microsoft.Dafny {
Contract.Assert(fromInt == toInt);
Contract.Assert(AsNativeType(e.ToType) == null);
Contract.Assert(AsNativeType(e.E.Type) == null);
- TrParenExpr(e.E);
+ TrParenExpr(e.E, wr, inLetExprBody);
}
} else if (expr is BinaryExpr) {
@@ -2408,27 +2699,27 @@ namespace Microsoft.Dafny {
opString = "&&"; break;
case BinaryExpr.ResolvedOpcode.EqCommon: {
- if (e.E0.Type.IsDatatype || e.E0.Type.IsTypeParameter || e.E0.Type.SupportsEquality) {
- callString = "Equals";
- } else if (e.E0.Type.IsRefType) {
+ if (e.E0.Type.IsRefType) {
// Dafny's type rules are slightly different C#, so we may need a cast here.
// For example, Dafny allows x==y if x:array<T> and y:array<int> and T is some
// type parameter.
opString = "== (object)";
+ } else if (e.E0.Type.IsDatatype || e.E0.Type.IsTypeParameter || e.E0.Type.SupportsEquality) {
+ callString = "Equals";
} else {
opString = "==";
}
break;
}
case BinaryExpr.ResolvedOpcode.NeqCommon: {
- if (e.E0.Type.IsDatatype || e.E0.Type.IsTypeParameter || e.E0.Type.SupportsEquality) {
- preOpString = "!";
- callString = "Equals";
- } else if (e.E0.Type.IsRefType) {
+ if (e.E0.Type.IsRefType) {
// Dafny's type rules are slightly different C#, so we may need a cast here.
// For example, Dafny allows x==y if x:array<T> and y:array<int> and T is some
// type parameter.
opString = "!= (object)";
+ } else if (e.E0.Type.IsDatatype || e.E0.Type.IsTypeParameter || e.E0.Type.SupportsEquality) {
+ preOpString = "!";
+ callString = "Equals";
} else {
opString = "!=";
}
@@ -2457,9 +2748,9 @@ namespace Microsoft.Dafny {
if (expr.Type.IsIntegerType || (AsNativeType(expr.Type) != null && AsNativeType(expr.Type).LowerBound < BigInteger.Zero)) {
string suffix = AsNativeType(expr.Type) != null ? ("_" + AsNativeType(expr.Type).Name) : "";
wr.Write("Dafny.Helpers.EuclideanDivision" + suffix + "(");
- TrParenExpr(e.E0);
+ TrParenExpr(e.E0, wr, inLetExprBody);
wr.Write(", ");
- TrExpr(e.E1);
+ TrExpr(e.E1, wr, inLetExprBody);
wr.Write(")");
} else {
opString = "/"; // for reals
@@ -2469,9 +2760,9 @@ namespace Microsoft.Dafny {
if (expr.Type.IsIntegerType || (AsNativeType(expr.Type) != null && AsNativeType(expr.Type).LowerBound < BigInteger.Zero)) {
string suffix = AsNativeType(expr.Type) != null ? ("_" + AsNativeType(expr.Type).Name) : "";
wr.Write("Dafny.Helpers.EuclideanModulus" + suffix + "(");
- TrParenExpr(e.E0);
+ TrParenExpr(e.E0, wr, inLetExprBody);
wr.Write(", ");
- TrExpr(e.E1);
+ TrExpr(e.E1, wr, inLetExprBody);
wr.Write(")");
} else {
opString = "%"; // for reals
@@ -2506,18 +2797,18 @@ namespace Microsoft.Dafny {
case BinaryExpr.ResolvedOpcode.InSet:
case BinaryExpr.ResolvedOpcode.InMultiSet:
case BinaryExpr.ResolvedOpcode.InMap:
- TrParenExpr(e.E1);
+ TrParenExpr(e.E1, wr, inLetExprBody);
wr.Write(".Contains(");
- TrExpr(e.E0);
+ TrExpr(e.E0, wr, inLetExprBody);
wr.Write(")");
break;
case BinaryExpr.ResolvedOpcode.NotInSet:
case BinaryExpr.ResolvedOpcode.NotInMultiSet:
case BinaryExpr.ResolvedOpcode.NotInMap:
wr.Write("!");
- TrParenExpr(e.E1);
+ TrParenExpr(e.E1, wr, inLetExprBody);
wr.Write(".Contains(");
- TrExpr(e.E0);
+ TrExpr(e.E0, wr, inLetExprBody);
wr.Write(")");
break;
case BinaryExpr.ResolvedOpcode.Union:
@@ -2537,16 +2828,16 @@ namespace Microsoft.Dafny {
case BinaryExpr.ResolvedOpcode.Concat:
callString = "Concat"; break;
case BinaryExpr.ResolvedOpcode.InSeq:
- TrParenExpr(e.E1);
+ TrParenExpr(e.E1, wr, inLetExprBody);
wr.Write(".Contains(");
- TrExpr(e.E0);
+ TrExpr(e.E0, wr, inLetExprBody);
wr.Write(")");
break;
case BinaryExpr.ResolvedOpcode.NotInSeq:
wr.Write("!");
- TrParenExpr(e.E1);
+ TrParenExpr(e.E1, wr, inLetExprBody);
wr.Write(".Contains(");
- TrExpr(e.E0);
+ TrExpr(e.E0, wr, inLetExprBody);
wr.Write(")");
break;
@@ -2560,17 +2851,17 @@ namespace Microsoft.Dafny {
wr.Write("(" + nativeType.Name + ")(");
}
wr.Write(preOpString);
- TrParenExpr(e.E0);
+ TrParenExpr(e.E0, wr, inLetExprBody);
wr.Write(" {0} ", opString);
- TrParenExpr(e.E1);
+ TrParenExpr(e.E1, wr, inLetExprBody);
if (needsCast) {
wr.Write(")");
}
} else if (callString != null) {
wr.Write(preOpString);
- TrParenExpr(e.E0);
+ TrParenExpr(e.E0, wr, inLetExprBody);
wr.Write(".@{0}(", callString);
- TrExpr(e.E1);
+ TrExpr(e.E1, wr, inLetExprBody);
wr.Write(")");
}
@@ -2593,17 +2884,18 @@ namespace Microsoft.Dafny {
if (Contract.Exists(lhs.Vars, bv => !bv.IsGhost)) {
var rhsName = string.Format("_pat_let{0}_{1}", GetUniqueAstNumber(e), i);
wr.Write("Dafny.Helpers.Let<");
- wr.Write(TypeName(e.RHSs[i].Type) + "," + TypeName(e.Body.Type));
+ wr.Write(TypeName(e.RHSs[i].Type, wr) + "," + TypeName(e.Body.Type, wr));
wr.Write(">(");
- TrExpr(e.RHSs[i]);
+ TrExpr(e.RHSs[i], wr, inLetExprBody);
wr.Write(", " + rhsName + " => ");
neededCloseParens++;
- var c = TrCasePattern(lhs, rhsName, e.Body.Type);
+ var c = TrCasePattern(lhs, rhsName, e.Body.Type, wr);
Contract.Assert(c != 0); // we already checked that there's at least one non-ghost
neededCloseParens += c;
}
}
- TrExpr(e.Body);
+
+ TrExpr(e.Body, wr, true);
for (int i = 0; i < neededCloseParens; i++) {
wr.Write(")");
}
@@ -2612,7 +2904,7 @@ namespace Microsoft.Dafny {
// ghost var x,y :| Constraint; E
// is compiled just like E is, because the resolver has already checked that x,y (or other ghost variables, for that matter) don't
// occur in E (moreover, the verifier has checked that values for x,y satisfying Constraint exist).
- TrExpr(e.Body);
+ TrExpr(e.Body, wr, inLetExprBody);
} else {
// The Dafny "let" expression
// var x,y :| Constraint; E
@@ -2627,17 +2919,17 @@ namespace Microsoft.Dafny {
Contract.Assert(e.RHSs.Count == 1); // checked by resolution
if (e.Constraint_MissingBounds != null) {
foreach (var bv in e.Constraint_MissingBounds) {
- Error("this let-such-that expression is too advanced for the current compiler; Dafny's heuristics cannot find any bound for variable '{0}' (line {1})", bv.Name, e.tok.line);
+ Error("this let-such-that expression is too advanced for the current compiler; Dafny's heuristics cannot find any bound for variable '{0}' (line {1})", wr, bv.Name, e.tok.line);
}
} else {
- wr.Write("Dafny.Helpers.Let<int," + TypeName(e.Body.Type) + ">(0, _let_dummy_" + GetUniqueAstNumber(e) + " => {");
+ wr.Write("Dafny.Helpers.Let<int," + TypeName(e.Body.Type, wr) + ">(0, _let_dummy_" + GetUniqueAstNumber(e) + " => {");
foreach (var bv in e.BoundVars) {
- wr.Write("{0} @{1}", TypeName(bv.Type), bv.CompileName);
- wr.WriteLine(" = {0};", DefaultValue(bv.Type));
+ wr.Write("{0} @{1}", TypeName(bv.Type, wr), bv.CompileName);
+ wr.WriteLine(" = {0};", DefaultValue(bv.Type, wr));
}
- TrAssignSuchThat(0, new List<IVariable>(e.BoundVars).ConvertAll(bv => (IVariable)bv), e.RHSs[0], e.Constraint_Bounds, e.tok.line);
+ TrAssignSuchThat(0, new List<IVariable>(e.BoundVars).ConvertAll(bv => (IVariable)bv), e.RHSs[0], e.Constraint_Bounds, e.tok.line, wr, inLetExprBody);
wr.Write(" return ");
- TrExpr(e.Body);
+ TrExpr(e.Body, wr, true);
wr.Write("; })");
}
}
@@ -2657,7 +2949,7 @@ namespace Microsoft.Dafny {
// }(src)
string source = idGenerator.FreshId("_source");
- wr.Write("new Dafny.Helpers.Function<{0}, {1}>(delegate ({0} {2}) {{ ", TypeName(e.Source.Type), TypeName(e.Type), source);
+ wr.Write("new Dafny.Helpers.Function<{0}, {1}>(delegate ({0} {2}) {{ ", TypeName(e.Source.Type, wr), TypeName(e.Type, wr), source);
if (e.Cases.Count == 0) {
// the verifier would have proved we never get here; still, we need some code that will compile
@@ -2666,9 +2958,9 @@ namespace Microsoft.Dafny {
int i = 0;
var sourceType = (UserDefinedType)e.Source.Type.NormalizeExpand();
foreach (MatchCaseExpr mc in e.Cases) {
- MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, e.Cases.Count, 0);
+ MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, e.Cases.Count, 0, wr);
wr.Write("return ");
- TrExpr(mc.Body);
+ TrExpr(mc.Body, wr, inLetExprBody);
wr.Write("; ");
i++;
}
@@ -2676,11 +2968,14 @@ namespace Microsoft.Dafny {
}
// We end with applying the source expression to the delegate we just built
wr.Write("})(");
- TrExpr(e.Source);
+ TrExpr(e.Source, wr, inLetExprBody);
wr.Write(")");
} else if (expr is QuantifierExpr) {
var e = (QuantifierExpr)expr;
+
+ // Compilation does not check whether a quantifier was split.
+
Contract.Assert(e.Bounds != null); // for non-ghost quantifiers, the resolver would have insisted on finding bounds
var n = e.BoundVars.Count;
Contract.Assert(e.Bounds.Count == n);
@@ -2690,27 +2985,29 @@ namespace Microsoft.Dafny {
// emit: Dafny.Helpers.QuantX(boundsInformation, isForall, bv => body)
if (bound is ComprehensionExpr.BoolBoundedPool) {
wr.Write("Dafny.Helpers.QuantBool(");
+ } else if (bound is ComprehensionExpr.CharBoundedPool) {
+ wr.Write("Dafny.Helpers.QuantChar(");
} else if (bound is ComprehensionExpr.IntBoundedPool) {
var b = (ComprehensionExpr.IntBoundedPool)bound;
wr.Write("Dafny.Helpers.QuantInt(");
- TrExpr(b.LowerBound);
+ TrExpr(b.LowerBound, wr, inLetExprBody);
wr.Write(", ");
- TrExpr(b.UpperBound);
+ TrExpr(b.UpperBound, wr, inLetExprBody);
wr.Write(", ");
} else if (bound is ComprehensionExpr.SetBoundedPool) {
var b = (ComprehensionExpr.SetBoundedPool)bound;
wr.Write("Dafny.Helpers.QuantSet(");
- TrExpr(b.Set);
+ TrExpr(b.Set, wr, inLetExprBody);
wr.Write(", ");
} else if (bound is ComprehensionExpr.MapBoundedPool) {
var b = (ComprehensionExpr.MapBoundedPool)bound;
wr.Write("Dafny.Helpers.QuantMap(");
- TrExpr(b.Map);
+ TrExpr(b.Map, wr, inLetExprBody);
wr.Write(", ");
} else if (bound is ComprehensionExpr.SeqBoundedPool) {
var b = (ComprehensionExpr.SeqBoundedPool)bound;
wr.Write("Dafny.Helpers.QuantSeq(");
- TrExpr(b.Seq);
+ TrExpr(b.Seq, wr, inLetExprBody);
wr.Write(", ");
} else if (bound is ComprehensionExpr.DatatypeBoundedPool) {
var b = (ComprehensionExpr.DatatypeBoundedPool)bound;
@@ -2723,7 +3020,7 @@ namespace Microsoft.Dafny {
wr.Write("{0}, ", expr is ForallExpr ? "true" : "false");
wr.Write("@{0} => ", bv.CompileName);
}
- TrExpr(e.LogicalBody());
+ TrExpr(e.LogicalBody(true), wr, inLetExprBody);
for (int i = 0; i < n; i++) {
wr.Write(")");
}
@@ -2747,9 +3044,10 @@ namespace Microsoft.Dafny {
// return Dafny.Set<G>.FromCollection(_coll);
// })()
Contract.Assert(e.Bounds != null); // the resolver would have insisted on finding bounds
- var typeName = TypeName(e.Type.AsSetType.Arg);
+ var typeName = TypeName(e.Type.AsSetType.Arg, wr);
+ var collection_name = idGenerator.FreshId("_coll");
wr.Write("((Dafny.Helpers.ComprehensionDelegate<{0}>)delegate() {{ ", typeName);
- wr.Write("var _coll = new System.Collections.Generic.List<{0}>(); ", typeName);
+ wr.Write("var {0} = new System.Collections.Generic.List<{1}>(); ", collection_name, typeName);
var n = e.BoundVars.Count;
Contract.Assert(e.Bounds.Count == n);
for (int i = 0; i < n; i++) {
@@ -2757,44 +3055,51 @@ namespace Microsoft.Dafny {
var bv = e.BoundVars[i];
if (bound is ComprehensionExpr.BoolBoundedPool) {
wr.Write("foreach (var @{0} in Dafny.Helpers.AllBooleans) {{ ", bv.CompileName);
+ } else if (bound is ComprehensionExpr.CharBoundedPool) {
+ wr.Write("foreach (var @{0} in Dafny.Helpers.AllChars) {{ ", bv.CompileName);
} else if (bound is ComprehensionExpr.IntBoundedPool) {
var b = (ComprehensionExpr.IntBoundedPool)bound;
- wr.Write("for (var @{0} = ", bv.CompileName);
- TrExpr(b.LowerBound);
- wr.Write("; @{0} < ", bv.CompileName);
- TrExpr(b.UpperBound);
- wr.Write("; @{0}++) {{ ", bv.CompileName);
+ if (AsNativeType(bv.Type) != null) {
+ wr.Write("foreach (var @{0} in @{1}.IntegerRange(", bv.CompileName, bv.Type.AsNewtype.FullCompileName);
+ } else {
+ wr.Write("foreach (var @{0} in Dafny.Helpers.IntegerRange(", bv.CompileName);
+ }
+ TrExpr(b.LowerBound, wr, inLetExprBody);
+ wr.Write(", ");
+ TrExpr(b.UpperBound, wr, inLetExprBody);
+ wr.Write(")) { ");
} else if (bound is ComprehensionExpr.SetBoundedPool) {
var b = (ComprehensionExpr.SetBoundedPool)bound;
wr.Write("foreach (var @{0} in (", bv.CompileName);
- TrExpr(b.Set);
+ TrExpr(b.Set, wr, inLetExprBody);
wr.Write(").Elements) { ");
} else if (bound is ComprehensionExpr.MapBoundedPool) {
var b = (ComprehensionExpr.MapBoundedPool)bound;
wr.Write("foreach (var @{0} in (", bv.CompileName);
- TrExpr(b.Map);
+ TrExpr(b.Map, wr, inLetExprBody);
wr.Write(").Domain) { ");
} else if (bound is ComprehensionExpr.SeqBoundedPool) {
var b = (ComprehensionExpr.SeqBoundedPool)bound;
wr.Write("foreach (var @{0} in (", bv.CompileName);
- TrExpr(b.Seq);
+ TrExpr(b.Seq, wr, inLetExprBody);
wr.Write(").Elements) { ");
} else if (bound is ComprehensionExpr.DatatypeBoundedPool) {
var b = (ComprehensionExpr.DatatypeBoundedPool)bound;
- wr.Write("foreach (var @{0} in {1}.AllSingletonConstructors) {{", bv.CompileName, TypeName(bv.Type));
+ wr.Write("foreach (var @{0} in {1}.AllSingletonConstructors) {{", bv.CompileName, TypeName(bv.Type, wr));
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type
}
}
wr.Write("if (");
- TrExpr(e.Range);
- wr.Write(") { _coll.Add(");
- TrExpr(e.Term);
+ TrExpr(e.Range, wr, inLetExprBody);
+ wr.Write(") {");
+ wr.Write("{0}.Add(", collection_name);
+ TrExpr(e.Term, wr, inLetExprBody);
wr.Write("); }");
for (int i = 0; i < n; i++) {
wr.Write("}");
}
- wr.Write("return Dafny.Set<{0}>.FromCollection(_coll); ", typeName);
+ wr.Write("return Dafny.Set<{0}>.FromCollection({1}); ", typeName, collection_name);
wr.Write("})()");
} else if (expr is MapComprehension) {
@@ -2816,58 +3121,63 @@ namespace Microsoft.Dafny {
// return Dafny.Map<U, V>.FromElements(_coll);
// })()
Contract.Assert(e.Bounds != null); // the resolver would have insisted on finding bounds
- var domtypeName = TypeName(e.Type.AsMapType.Domain);
- var rantypeName = TypeName(e.Type.AsMapType.Range);
+ var domtypeName = TypeName(e.Type.AsMapType.Domain, wr);
+ var rantypeName = TypeName(e.Type.AsMapType.Range, wr);
+ var collection_name = idGenerator.FreshId("_coll");
wr.Write("((Dafny.Helpers.MapComprehensionDelegate<{0},{1}>)delegate() {{ ", domtypeName, rantypeName);
- wr.Write("var _coll = new System.Collections.Generic.List<Dafny.Pair<{0},{1}>>(); ", domtypeName, rantypeName);
+ wr.Write("var {0} = new System.Collections.Generic.List<Dafny.Pair<{1},{2}>>(); ", collection_name, domtypeName, rantypeName);
var n = e.BoundVars.Count;
Contract.Assert(e.Bounds.Count == n && n == 1);
var bound = e.Bounds[0];
var bv = e.BoundVars[0];
if (bound is ComprehensionExpr.BoolBoundedPool) {
wr.Write("foreach (var @{0} in Dafny.Helpers.AllBooleans) {{ ", bv.CompileName);
+ } else if (bound is ComprehensionExpr.CharBoundedPool) {
+ wr.Write("foreach (var @{0} in Dafny.Helpers.AllChars) {{ ", bv.CompileName);
} else if (bound is ComprehensionExpr.IntBoundedPool) {
var b = (ComprehensionExpr.IntBoundedPool)bound;
- wr.Write("for (var @{0} = ", bv.CompileName);
- TrExpr(b.LowerBound);
- wr.Write("; @{0} < ", bv.CompileName);
- TrExpr(b.UpperBound);
- wr.Write("; @{0}++) {{ ", bv.CompileName);
+ if (AsNativeType(bv.Type) != null) {
+ wr.Write("foreach (var @{0} in @{1}.IntegerRange(", bv.CompileName, bv.Type.AsNewtype.FullCompileName);
+ } else {
+ wr.Write("foreach (var @{0} in Dafny.Helpers.IntegerRange(", bv.CompileName);
+ }
+ TrExpr(b.LowerBound, wr, inLetExprBody);
+ wr.Write(", ");
+ TrExpr(b.UpperBound, wr, inLetExprBody);
+ wr.Write(")) { ");
} else if (bound is ComprehensionExpr.SetBoundedPool) {
var b = (ComprehensionExpr.SetBoundedPool)bound;
wr.Write("foreach (var @{0} in (", bv.CompileName);
- TrExpr(b.Set);
+ TrExpr(b.Set, wr, inLetExprBody);
wr.Write(").Elements) { ");
} else if (bound is ComprehensionExpr.MapBoundedPool) {
var b = (ComprehensionExpr.MapBoundedPool)bound;
wr.Write("foreach (var @{0} in (", bv.CompileName);
- TrExpr(b.Map);
+ TrExpr(b.Map, wr, inLetExprBody);
wr.Write(").Domain) { ");
} else if (bound is ComprehensionExpr.SeqBoundedPool) {
var b = (ComprehensionExpr.SeqBoundedPool)bound;
wr.Write("foreach (var @{0} in (", bv.CompileName);
- TrExpr(b.Seq);
+ TrExpr(b.Seq, wr, inLetExprBody);
wr.Write(").Elements) { ");
} else {
+ // TODO: handle ComprehensionExpr.SubSetBoundedPool
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type
}
wr.Write("if (");
- TrExpr(e.Range);
+ TrExpr(e.Range, wr, inLetExprBody);
wr.Write(") { ");
- wr.Write("_coll.Add(new Dafny.Pair<{0},{1}>(@{2},", domtypeName, rantypeName, bv.CompileName);
- TrExpr(e.Term);
+ wr.Write("{0}.Add(new Dafny.Pair<{1},{2}>(@{3},", collection_name, domtypeName, rantypeName, bv.CompileName);
+ TrExpr(e.Term, wr, inLetExprBody);
wr.Write(")); }");
wr.Write("}");
- wr.Write("return Dafny.Map<{0},{1}>.FromCollection(_coll); ", domtypeName, rantypeName);
+ wr.Write("return Dafny.Map<{0},{1}>.FromCollection({2}); ", domtypeName, rantypeName, collection_name);
wr.Write("})()");
} else if (expr is LambdaExpr) {
LambdaExpr e = (LambdaExpr)expr;
- ISet<IVariable> fvs = new HashSet<IVariable>();
- bool dontCare = false;
- Type dontCareT = null;
- Translator.ComputeFreeVariables(expr, fvs, ref dontCare, ref dontCare, ref dontCareT, false);
+ var fvs = Translator.ComputeFreeVariables(expr);
var sm = new Dictionary<IVariable, Expression>();
var bvars = new List<BoundVar>();
@@ -2887,46 +3197,46 @@ namespace Microsoft.Dafny {
var su = new Translator.Substituter(null, sm, new Dictionary<TypeParameter, Type>(), null);
- BetaRedex(bvars, fexprs, expr.Type, () => {
+ BetaRedex(bvars, fexprs, expr.Type, wr, inLetExprBody, () => {
wr.Write("(");
wr.Write(Util.Comma(e.BoundVars, bv => "@" + bv.CompileName));
wr.Write(") => ");
- TrExpr(su.Substitute(e.Body));
+ TrExpr(su.Substitute(e.Body), wr, inLetExprBody);
});
} else if (expr is StmtExpr) {
var e = (StmtExpr)expr;
- TrExpr(e.E);
+ TrExpr(e.E, wr, inLetExprBody);
} else if (expr is ITEExpr) {
ITEExpr e = (ITEExpr)expr;
wr.Write("(");
- TrExpr(e.Test);
+ TrExpr(e.Test, wr, inLetExprBody);
wr.Write(") ? (");
- TrExpr(e.Thn);
+ TrExpr(e.Thn, wr, inLetExprBody);
wr.Write(") : (");
- TrExpr(e.Els);
+ TrExpr(e.Els, wr, inLetExprBody);
wr.Write(")");
} else if (expr is ConcreteSyntaxExpression) {
var e = (ConcreteSyntaxExpression)expr;
- TrExpr(e.ResolvedExpression);
+ TrExpr(e.ResolvedExpression, wr, inLetExprBody);
} else if (expr is NamedExpr) {
- TrExpr(((NamedExpr)expr).Body);
+ TrExpr(((NamedExpr)expr).Body, wr, inLetExprBody);
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
}
}
- int TrCasePattern(CasePattern pat, string rhsString, Type bodyType) {
+ int TrCasePattern(CasePattern pat, string rhsString, Type bodyType, TextWriter wr) {
Contract.Requires(pat != null);
Contract.Requires(rhsString != null);
int c = 0;
if (pat.Var != null) {
var bv = pat.Var;
if (!bv.IsGhost) {
- wr.Write("Dafny.Helpers.Let<" + TypeName(bv.Type) + "," + TypeName(bodyType) + ">");
+ wr.Write("Dafny.Helpers.Let<" + TypeName(bv.Type, wr) + "," + TypeName(bodyType, wr) + ">");
wr.Write("(" + rhsString + ", @" + bv.CompileName + " => ");
c++;
}
@@ -2942,7 +3252,7 @@ namespace Microsoft.Dafny {
// nothing to compile, but do a sanity check
Contract.Assert(!Contract.Exists(arg.Vars, bv => !bv.IsGhost));
} else {
- c += TrCasePattern(arg, string.Format("(({0})({1})._D).@{2}", DtCtorName(ctor, ((DatatypeValue)pat.Expr).InferredTypeArgs), rhsString, FormalName(formal, k)), bodyType);
+ c += TrCasePattern(arg, string.Format("(({0})({1})._D).@{2}", DtCtorName(ctor, ((DatatypeValue)pat.Expr).InferredTypeArgs, wr), rhsString, FormalName(formal, k)), bodyType, wr);
k++;
}
}
@@ -2950,36 +3260,40 @@ namespace Microsoft.Dafny {
return c;
}
- delegate void FCE_Arg_Translator(Expression e);
+ delegate void FCE_Arg_Translator(Expression e, TextWriter wr, bool inLetExpr=false);
- void CompileFunctionCallExpr(FunctionCallExpr e, TextWriter twr, FCE_Arg_Translator tr) {
+ void CompileFunctionCallExpr(FunctionCallExpr e, TextWriter twr, TextWriter wr, bool inLetExprBody, FCE_Arg_Translator tr) {
Function f = cce.NonNull(e.Function);
if (f.IsStatic) {
- twr.Write(TypeName_Companion(e.Receiver.Type));
+ twr.Write(TypeName_Companion(e.Receiver.Type, wr));
} else {
twr.Write("(");
- tr(e.Receiver);
+ tr(e.Receiver, wr, inLetExprBody);
twr.Write(")");
}
twr.Write(".@{0}", f.CompileName);
+ if (f.TypeArgs.Count != 0) {
+ List<Type> typeArgs = f.TypeArgs.ConvertAll(ta => e.TypeArgumentSubstitutions[ta]);
+ twr.Write("<" + TypeNames(typeArgs, wr) + ">");
+ }
twr.Write("(");
string sep = "";
for (int i = 0; i < e.Args.Count; i++) {
if (!e.Function.Formals[i].IsGhost) {
twr.Write(sep);
- tr(e.Args[i]);
+ tr(e.Args[i], wr);
sep = ", ";
}
}
twr.Write(")");
}
- void BetaRedex(List<BoundVar> bvars, List<Expression> exprs, Type bodyType, Action makeBody) {
+ void BetaRedex(List<BoundVar> bvars, List<Expression> exprs, Type bodyType, TextWriter wr, bool inLetExprBody, Action makeBody) {
Contract.Requires(bvars != null);
Contract.Requires(exprs != null);
Contract.Requires(bvars.Count == exprs.Count);
wr.Write("Dafny.Helpers.Id<");
- wr.Write(TypeName_UDT(ArrowType.Arrow_FullCompileName, Util.Snoc(bvars.ConvertAll(bv => bv.Type), bodyType)));
+ wr.Write(TypeName_UDT(ArrowType.Arrow_FullCompileName, Util.Snoc(bvars.ConvertAll(bv => bv.Type), bodyType), wr));
wr.Write(">((");
wr.Write(Util.Comma(bvars, bv => "@" + bv.CompileName));
wr.Write(") => ");
@@ -2987,7 +3301,7 @@ namespace Microsoft.Dafny {
makeBody();
wr.Write(")");
- TrExprList(exprs);
+ TrExprList(exprs, wr, inLetExprBody);
}
}
diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg
index c03f5ce0..af7082a4 100644
--- a/Source/Dafny/Dafny.atg
+++ b/Source/Dafny/Dafny.atg
@@ -16,17 +16,120 @@ COMPILER Dafny
/*--------------------------------------------------------------------------*/
readonly Expression/*!*/ dummyExpr;
readonly AssignmentRhs/*!*/ dummyRhs;
-readonly FrameExpression/*!*/ dummyFrameExpr;
+readonly FrameExpression/*!*/ dummyFrameExpr;
readonly Statement/*!*/ dummyStmt;
readonly ModuleDecl theModule;
readonly BuiltIns theBuiltIns;
readonly bool theVerifyThisFile;
int anonymousIds = 0;
-struct MemberModifiers {
+/// <summary>
+/// Holds the modifiers given for a declaration
+///
+/// Not all modifiers are applicable to all kinds of declarations.
+/// Errors are given when a modify does not apply.
+/// We also record the tokens for the specified modifiers so that
+/// they can be used in error messages.
+/// </summary>
+struct DeclModifierData {
+ public bool IsAbstract;
+ public IToken AbstractToken;
public bool IsGhost;
+ public IToken GhostToken;
public bool IsStatic;
+ public IToken StaticToken;
public bool IsProtected;
+ public IToken ProtectedToken;
+ public bool IsExtern;
+ public IToken ExternToken;
+ public StringLiteralExpr ExternName;
+
+}
+
+// Check that token has not been set, then set it.
+public void CheckAndSetToken(ref IToken token)
+{
+ if (token != null) {
+ SemErr(t, "Duplicate declaration modifier: " + t.val);
+ }
+ token = t;
+}
+
+/// <summary>
+// A flags type used to tell what declaration modifiers are allowed for a declaration.
+/// </summary>
+[Flags]
+enum AllowedDeclModifiers {
+ None = 0,
+ Abstract = 1,
+ Ghost = 2,
+
+ // Means ghost not allowed because already implicitly ghost.
+ AlreadyGhost = 4,
+ Static = 8,
+ Protected = 16,
+ Extern = 32
+};
+
+/// <summary>
+/// Check the declaration modifiers against those that are allowed.
+///
+/// The 'allowed' parameter specifies which declaratio modifiers are allowed.
+/// The 'declCaption' parameter should be a string describing the kind of declaration.
+/// It is used in error messages.
+/// Any declaration modifiers that are present but not allowed are cleared.
+///</summary>
+void CheckDeclModifiers(DeclModifierData dmod, string declCaption, AllowedDeclModifiers allowed)
+{
+ if (dmod.IsAbstract && ((allowed & AllowedDeclModifiers.Abstract) == 0)) {
+ SemErr(dmod.AbstractToken, declCaption + " cannot be declared 'abstract'.");
+ dmod.IsAbstract = false;
+ }
+ if (dmod.IsGhost) {
+ if ((allowed & AllowedDeclModifiers.AlreadyGhost) != 0) {
+ SemErr(dmod.GhostToken, declCaption + " cannot be declared ghost (they are 'ghost' by default).");
+ dmod.IsGhost = false;
+ } else if ((allowed & AllowedDeclModifiers.Ghost) == 0) {
+ SemErr(dmod.GhostToken, declCaption + " cannot be declared 'ghost'.");
+ dmod.IsGhost = false;
+ }
+ }
+ if (dmod.IsStatic && ((allowed & AllowedDeclModifiers.Static) == 0)) {
+ SemErr(dmod.StaticToken, declCaption + " cannot be declared 'static'.");
+ dmod.IsStatic = false;
+ }
+ if (dmod.IsProtected && ((allowed & AllowedDeclModifiers.Protected) == 0)) {
+ SemErr(dmod.ProtectedToken, declCaption + " cannot be declared 'protected'.");
+ dmod.IsProtected = false;
+ }
+ if (dmod.IsExtern && ((allowed & AllowedDeclModifiers.Extern) == 0)) {
+ SemErr(dmod.ExternToken, declCaption + " cannot be declared 'extern'.");
+ dmod.IsExtern = false;
+ }
+}
+
+/// <summary>
+/// Encode an 'extern' declaration modifier as an {:extern name} attribute.
+///
+/// We also include an {:axiom} attribute since the specification of an
+/// external entity is assumed to hold, but only for methods or functions.
+///</summary>
+static void EncodeExternAsAttribute(DeclModifierData dmod, ref Attributes attrs, IToken/*!*/ id, bool needAxiom) {
+ if (dmod.IsExtern) {
+ StringLiteralExpr name = dmod.ExternName;
+ if (name == null) {
+ bool isVerbatimString = false;
+ name = new StringLiteralExpr(id, id.val, isVerbatimString);
+ }
+ var args = new List<Expression>();
+ args.Add(name);
+ attrs = new Attributes("extern", args, attrs);
+
+ // Also 'extern' implies 'axiom' for methods or functions.
+ if (needAxiom) {
+ attrs = new Attributes("axiom", new List<Expression>(), attrs);
+ }
+ }
}
///<summary>
@@ -41,11 +144,11 @@ public static int Parse (string/*!*/ filename, ModuleDecl module, BuiltIns built
string s;
if (filename == "stdin.dfy") {
s = Microsoft.Boogie.ParserHelper.Fill(System.Console.In, new List<string>());
- return Parse(s, filename, module, builtIns, errors, verifyThisFile);
+ return Parse(s, filename, filename, module, builtIns, errors, verifyThisFile);
} else {
using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) {
s = Microsoft.Boogie.ParserHelper.Fill(reader, new List<string>());
- return Parse(s, DafnyOptions.Clo.UseBaseNameForFileName ? Path.GetFileName(filename) : filename, module, builtIns, errors, verifyThisFile);
+ return Parse(s, filename, DafnyOptions.Clo.UseBaseNameForFileName ? Path.GetFileName(filename) : filename, module, builtIns, errors, verifyThisFile);
}
}
}
@@ -55,12 +158,12 @@ public static int Parse (string/*!*/ filename, ModuleDecl module, BuiltIns built
/// Returns the number of parsing errors encountered.
/// Note: first initialize the Scanner.
///</summary>
-public static int Parse (string/*!*/ s, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, bool verifyThisFile=true) {
+public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, ErrorReporter reporter, bool verifyThisFile=true) {
Contract.Requires(s != null);
Contract.Requires(filename != null);
Contract.Requires(module != null);
- Errors errors = new Errors();
- return Parse(s, filename, module, builtIns, errors, verifyThisFile);
+ Errors errors = new Errors(reporter);
+ return Parse(s, fullFilename, filename, module, builtIns, errors, verifyThisFile);
}
///<summary>
/// Parses top-level things (modules, classes, datatypes, class members)
@@ -68,18 +171,18 @@ public static int Parse (string/*!*/ s, string/*!*/ filename, ModuleDecl module,
/// Returns the number of parsing errors encountered.
/// Note: first initialize the Scanner with the given Errors sink.
///</summary>
-public static int Parse (string/*!*/ s, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns,
- Errors/*!*/ errors, bool verifyThisFile=true) {
+public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, ModuleDecl module,
+ BuiltIns builtIns, Errors/*!*/ errors, bool verifyThisFile=true) {
Contract.Requires(s != null);
Contract.Requires(filename != null);
Contract.Requires(module != null);
Contract.Requires(errors != null);
byte[]/*!*/ buffer = cce.NonNull( UTF8Encoding.Default.GetBytes(s));
MemoryStream ms = new MemoryStream(buffer,false);
- Scanner scanner = new Scanner(ms, errors, filename);
+ Scanner scanner = new Scanner(ms, errors, fullFilename, filename);
Parser parser = new Parser(scanner, errors, module, builtIns, verifyThisFile);
parser.Parse();
- return parser.errors.count;
+ return parser.errors.ErrorCount;
}
public Parser(Scanner/*!*/ scanner, Errors/*!*/ errors, ModuleDecl module, BuiltIns builtIns, bool verifyThisFile=true)
: this(scanner, errors) // the real work
@@ -104,6 +207,25 @@ bool IsAlternative() {
return la.kind == _lbrace && x.kind == _case;
}
+// an existential guard starts with an identifier and is then followed by
+// * a colon (if the first identifier is given an explicit type),
+// * a comma (if there's a list a bound variables and the first one is not given an explicit type),
+// * a start-attribute (if there's one bound variable and it is not given an explicit type and there are attributes), or
+// * a bored smiley (if there's one bound variable and it is not given an explicit type).
+bool IsExistentialGuard() {
+ scanner.ResetPeek();
+ if (la.kind == _ident) {
+ Token x = scanner.Peek();
+ if (x.kind == _colon || x.kind == _comma || x.kind == _boredSmiley) {
+ return true;
+ } else if (x.kind == _lbrace) {
+ x = scanner.Peek();
+ return x.kind == _colon;
+ }
+ }
+ return false;
+}
+
bool IsLoopSpec() {
return la.kind == _invariant | la.kind == _decreases | la.kind == _modifies;
}
@@ -175,6 +297,9 @@ bool IsMapDisplay() {
bool IsIMapDisplay() {
return la.kind == _imap && scanner.Peek().kind == _lbracket;
}
+bool IsISetDisplay() {
+ return la.kind == _iset && scanner.Peek().kind == _lbrace;
+}
bool IsSuffix() {
return la.kind == _dot || la.kind == _lbracket || la.kind == _openparen;
@@ -296,6 +421,9 @@ bool IsGenericInstantiation() {
return false;
}
}
+/* Returns true if the next thing is of the form:
+ * "<" Type { "," Type } ">"
+ */
bool IsTypeList(ref IToken pt) {
if (pt.kind != _openAngleBracket) {
return false;
@@ -303,6 +431,10 @@ bool IsTypeList(ref IToken pt) {
pt = scanner.Peek();
return IsTypeSequence(ref pt, _closeAngleBracket);
}
+/* Returns true if the next thing is of the form:
+ * Type { "," Type }
+ * followed by an endBracketKind.
+ */
bool IsTypeSequence(ref IToken pt, int endBracketKind) {
while (true) {
if (!IsType(ref pt)) {
@@ -334,12 +466,13 @@ bool IsType(ref IToken pt) {
return true;
case _arrayToken:
case _set:
+ case _iset:
case _multiset:
case _seq:
case _map:
case _imap:
pt = scanner.Peek();
- return IsTypeList(ref pt);
+ return pt.kind != _openAngleBracket || IsTypeList(ref pt);
case _ident:
while (true) {
// invariant: next token is an ident
@@ -358,12 +491,24 @@ bool IsType(ref IToken pt) {
}
case _openparen:
pt = scanner.Peek();
+ if (pt.kind == _closeparen) {
+ // end of type list
+ pt = scanner.Peek();
+ return true;
+ }
return IsTypeSequence(ref pt, _closeparen);
default:
return false;
}
}
+
+bool IsDefaultImport() {
+ scanner.ResetPeek();
+ Token x = scanner.Peek(); // lookahead token again
+ return la.val == "default" && x.val != "export";
+}
+
/*--------------------------------------------------------------------------*/
CHARACTERS
letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".
@@ -416,6 +561,7 @@ TOKENS
object = "object".
string = "string".
set = "set".
+ iset = "iset".
multiset = "multiset".
seq = "seq".
map = "map".
@@ -439,6 +585,7 @@ TOKENS
comma = ','.
verticalbar = '|'.
doublecolon = "::".
+ boredSmiley = ":|".
bullet = '\u2022'.
dot = '.'.
semi = ';'.
@@ -479,17 +626,14 @@ IGNORE cr + lf + tab
/*------------------------------------------------------------------------*/
PRODUCTIONS
Dafny
-= (. ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; TopLevelDecl td; IteratorDecl iter;
- List<MemberDecl/*!*/> membersDefaultClass = new List<MemberDecl/*!*/>();
- ModuleDecl submodule;
+= (. List<MemberDecl/*!*/> membersDefaultClass = new List<MemberDecl/*!*/>();
// to support multiple files, create a default module only if theModule is null
DefaultModuleDecl defaultModule = (DefaultModuleDecl)((LiteralModuleDecl)theModule).ModuleDef;
// theModule should be a DefaultModuleDecl (actually, the singular DefaultModuleDecl)
- TraitDecl/*!*/ trait;
Contract.Assert(defaultModule != null);
.)
{ "include" stringToken (. {
- string parsedFile = t.filename;
+ string parsedFile = scanner.FullFilename;
bool isVerbatimString;
string includedFile = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString);
includedFile = Util.RemoveEscaping(includedFile, isVerbatimString);
@@ -503,15 +647,7 @@ Dafny
}
.)
}
- { SubModuleDecl<defaultModule, out submodule> (. defaultModule.TopLevelDecls.Add(submodule); .)
- | ClassDecl<defaultModule, out c> (. defaultModule.TopLevelDecls.Add(c); .)
- | DatatypeDecl<defaultModule, out dt> (. defaultModule.TopLevelDecls.Add(dt); .)
- | NewtypeDecl<defaultModule, out td> (. defaultModule.TopLevelDecls.Add(td); .)
- | OtherTypeDecl<defaultModule, out td> (. defaultModule.TopLevelDecls.Add(td); .)
- | IteratorDecl<defaultModule, out iter> (. defaultModule.TopLevelDecls.Add(iter); .)
- | TraitDecl<defaultModule, out trait> (. defaultModule.TopLevelDecls.Add(trait); .)
- | ClassMemberDecl<membersDefaultClass, false, !DafnyOptions.O.AllowGlobals>
- }
+ { TopDecl<defaultModule, membersDefaultClass, /* isTopLevel */ true, /* isAbstract */ false> }
(. // find the default class in the default module, then append membersDefaultClass to its member list
DefaultClassDecl defaultClass = null;
foreach (TopLevelDecl topleveldecl in defaultModule.TopLevelDecls) {
@@ -527,44 +663,78 @@ Dafny
} .)
EOF
.
-SubModuleDecl<ModuleDefinition parent, out ModuleDecl submodule>
-= (. ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; TopLevelDecl td; IteratorDecl iter;
- Attributes attrs = null; IToken/*!*/ id;
- TraitDecl/*!*/ trait;
- List<MemberDecl/*!*/> namedModuleDefaultClassMembers = new List<MemberDecl>();;
+
+DeclModifier<ref DeclModifierData dmod>
+= ( "abstract" (. dmod.IsAbstract = true; CheckAndSetToken(ref dmod.AbstractToken); .)
+ | "ghost" (. dmod.IsGhost = true; CheckAndSetToken(ref dmod.GhostToken); .)
+ | "static" (. dmod.IsStatic = true; CheckAndSetToken(ref dmod.StaticToken); .)
+ | "protected" (. dmod.IsProtected = true; CheckAndSetToken(ref dmod.ProtectedToken); .)
+ | "extern" (. dmod.IsExtern = true; CheckAndSetToken(ref dmod.ExternToken); .)
+ [ stringToken (. bool isVerbatimString;
+ string s = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString);
+ dmod.ExternName = new StringLiteralExpr(t, s, isVerbatimString);
+ .)
+ ]
+ )
+ .
+
+TopDecl<. ModuleDefinition module, List<MemberDecl/*!*/> membersDefaultClass, bool isTopLevel, bool isAbstract .>
+= (. DeclModifierData dmod = new DeclModifierData(); ModuleDecl submodule;
+ ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; TopLevelDecl td; IteratorDecl iter;
+ TraitDecl/*!*/ trait;
+ .)
+ { DeclModifier<ref dmod> }
+ ( SubModuleDecl<dmod, module, out submodule> (. module.TopLevelDecls.Add(submodule); .)
+ | ClassDecl<dmod, module, out c> (. module.TopLevelDecls.Add(c); .)
+ | DatatypeDecl<dmod, module, out dt> (. module.TopLevelDecls.Add(dt); .)
+ | NewtypeDecl<dmod, module, out td> (. module.TopLevelDecls.Add(td); .)
+ | OtherTypeDecl<dmod, module, out td> (. module.TopLevelDecls.Add(td); .)
+ | IteratorDecl<dmod, module, out iter> (. module.TopLevelDecls.Add(iter); .)
+ | TraitDecl<dmod, module, out trait> (. module.TopLevelDecls.Add(trait); .)
+ | ClassMemberDecl<dmod, membersDefaultClass, false, !DafnyOptions.O.AllowGlobals,
+ !isTopLevel && DafnyOptions.O.IronDafny && isAbstract>
+ ) .
+
+SubModuleDecl<DeclModifierData dmod, ModuleDefinition parent, out ModuleDecl submodule>
+= (. Attributes attrs = null; IToken/*!*/ id;
+ List<MemberDecl/*!*/> namedModuleDefaultClassMembers = new List<MemberDecl>();;
List<IToken> idRefined = null, idPath = null, idAssignment = null;
ModuleDefinition module;
- ModuleDecl sm;
submodule = null; // appease compiler
- bool isAbstract = false;
+ bool isAbstract = dmod.IsAbstract;
+ bool isExclusively = false;
bool opened = false;
+ CheckDeclModifiers(dmod, "Modules", AllowedDeclModifiers.Abstract | AllowedDeclModifiers.Extern);
.)
- ( [ "abstract" (. isAbstract = true; .) ]
- "module"
+ ( "module"
{ Attribute<ref attrs> }
NoUSIdent<out id>
+ (. EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ false); .)
- [ "refines" QualifiedModuleName<out idRefined> ] (. module = new ModuleDefinition(id, id.val, isAbstract, false, idRefined == null ? null : idRefined, parent, attrs, false); .)
+ [ "exclusively" "refines" QualifiedModuleName<out idRefined> (. isExclusively = true; .)
+ | "refines" QualifiedModuleName<out idRefined> (. isExclusively = false; .) ]
+ (. module = new ModuleDefinition(id, id.val, isAbstract, false, isExclusively, idRefined == null ? null : idRefined, parent, attrs, false, this); .)
"{" (. module.BodyStartTok = t; .)
- { SubModuleDecl<module, out sm> (. module.TopLevelDecls.Add(sm); .)
- | ClassDecl<module, out c> (. module.TopLevelDecls.Add(c); .)
- | TraitDecl<module, out trait> (. module.TopLevelDecls.Add(trait); .)
- | DatatypeDecl<module, out dt> (. module.TopLevelDecls.Add(dt); .)
- | NewtypeDecl<module, out td> (. module.TopLevelDecls.Add(td); .)
- | OtherTypeDecl<module, out td> (. module.TopLevelDecls.Add(td); .)
- | IteratorDecl<module, out iter> (. module.TopLevelDecls.Add(iter); .)
- | ClassMemberDecl<namedModuleDefaultClassMembers, false, !DafnyOptions.O.AllowGlobals>
- }
- "}" (. module.BodyEndTok = t;
+ { TopDecl<module, namedModuleDefaultClassMembers, /* isTopLevel */ false, isAbstract>}
+ "}" (. module.BodyEndTok = t;
module.TopLevelDecls.Add(new DefaultClassDecl(module, namedModuleDefaultClassMembers));
submodule = new LiteralModuleDecl(module, parent); .)
|
"import" ["opened" (.opened = true;.)]
NoUSIdent<out id>
+ (. EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ false); .)
[ "=" QualifiedModuleName<out idPath>
(. submodule = new AliasModuleDecl(idPath, id, parent, opened); .)
- | "as" QualifiedModuleName<out idPath> ["default" QualifiedModuleName<out idAssignment> ]
+ | "as" QualifiedModuleName<out idPath> [IF(IsDefaultImport()) "default" QualifiedModuleName<out idAssignment> ]
+ (. submodule = new ModuleFacadeDecl(idPath, id, parent, idAssignment, opened);
+ errors.Warning(t, "\"import A as B\" has been deprecated; in the new syntax, it is \"import A:B\"");
+ .)
+ | ":" QualifiedModuleName<out idPath>
(. submodule = new ModuleFacadeDecl(idPath, id, parent, idAssignment, opened); .)
+ | "." QualifiedModuleName<out idPath>
+ (. idPath.Insert(0, id);
+ submodule = new AliasModuleDecl(idPath, id, parent, opened);
+ .)
]
[ SYNC ";"
// This semi-colon used to be required, but it seems silly to have it.
@@ -578,6 +748,33 @@ SubModuleDecl<ModuleDefinition parent, out ModuleDecl submodule>
submodule = new AliasModuleDecl(idPath, id, parent, opened);
}
.)
+ | (.
+ bool isDefault = false;
+ bool includeBody;
+ IToken exportId;
+ List<ExportSignature> exports = new List<ExportSignature>();;
+ List<string> extends = new List<string>();
+ .)
+ ["default" (. isDefault = true; .) ]
+ "export"
+ NoUSIdent<out exportId>
+ ["extends"
+ NoUSIdent<out id>(. extends.Add(id.val); .)
+ {"," NoUSIdent<out id> (. extends.Add(id.val); .) }
+ ]
+ "{"
+ NoUSIdent<out id> (. includeBody = false; .)
+ ['+' (. includeBody = true; .)]
+ (. exports.Add(new ExportSignature(id, includeBody)); .)
+ { ","
+ NoUSIdent<out id> (. includeBody = false; .)
+ ['+' (. includeBody = true; .)]
+ (. exports.Add(new ExportSignature(id, includeBody)); .)
+ }
+ "}"
+ (.
+ submodule = new ModuleExportDecl(exportId, parent, isDefault, exports, extends);
+ .)
)
.
@@ -589,7 +786,8 @@ QualifiedModuleName<.out List<IToken> ids.>
}
.
-ClassDecl<ModuleDefinition/*!*/ module, out ClassDecl/*!*/ c>
+
+ClassDecl<DeclModifierData dmodClass, ModuleDefinition/*!*/ module, out ClassDecl/*!*/ c>
= (. Contract.Requires(module != null);
Contract.Ensures(Contract.ValueAtReturn(out c) != null);
IToken/*!*/ id;
@@ -599,18 +797,23 @@ ClassDecl<ModuleDefinition/*!*/ module, out ClassDecl/*!*/ c>
List<TypeParameter/*!*/> typeArgs = new List<TypeParameter/*!*/>();
List<MemberDecl/*!*/> members = new List<MemberDecl/*!*/>();
IToken bodyStart;
+ CheckDeclModifiers(dmodClass, "Classes", AllowedDeclModifiers.Extern);
+ DeclModifierData dmod;
.)
SYNC
"class"
{ Attribute<ref attrs> }
NoUSIdent<out id>
+ (. EncodeExternAsAttribute(dmodClass, ref attrs, id, /* needAxiom */ false); .)
[ GenericParameters<typeArgs> ]
["extends"
Type<out trait> (. traits.Add(trait); .)
{"," Type<out trait> (. traits.Add(trait); .) }
]
"{" (. bodyStart = t; .)
- { ClassMemberDecl<members, true, false>
+ { (. dmod = new DeclModifierData(); .)
+ { DeclModifier<ref dmod> }
+ ClassMemberDecl<dmod, members, true, false, false>
}
"}"
(. c = new ClassDecl(id, id.val, module, typeArgs, members, attrs, traits);
@@ -619,23 +822,27 @@ ClassDecl<ModuleDefinition/*!*/ module, out ClassDecl/*!*/ c>
.)
.
- TraitDecl<ModuleDefinition/*!*/ module, out TraitDecl/*!*/ trait>
- = (. Contract.Requires(module != null);
+TraitDecl<DeclModifierData dmodIn, ModuleDefinition/*!*/ module, out TraitDecl/*!*/ trait>
+ = (. Contract.Requires(module != null);
Contract.Ensures(Contract.ValueAtReturn(out trait) != null);
+ CheckDeclModifiers(dmodIn, "Traits", AllowedDeclModifiers.None);
IToken/*!*/ id;
Attributes attrs = null;
List<TypeParameter/*!*/> typeArgs = new List<TypeParameter/*!*/>(); //traits should not support type parameters at the moment
List<MemberDecl/*!*/> members = new List<MemberDecl/*!*/>();
IToken bodyStart;
+ DeclModifierData dmod;
.)
SYNC
"trait"
{ Attribute<ref attrs> }
NoUSIdent<out id>
[ GenericParameters<typeArgs> ]
- "{" (. bodyStart = t; .)
- { ClassMemberDecl<members, true, false>
- }
+ "{" (. bodyStart = t; .)
+ { (. dmod = new DeclModifierData(); .)
+ { DeclModifier<ref dmod> }
+ ClassMemberDecl<dmod, members, true, false, false>
+ }
"}"
(. trait = new TraitDecl(id, id.val, module, typeArgs, members, attrs);
trait.BodyStartTok = bodyStart;
@@ -643,44 +850,33 @@ ClassDecl<ModuleDefinition/*!*/ module, out ClassDecl/*!*/ c>
.)
.
-ClassMemberDecl<.List<MemberDecl> mm, bool allowConstructors, bool moduleLevelDecl.>
+ClassMemberDecl<. DeclModifierData dmod, List<MemberDecl> mm, bool allowConstructors, bool moduleLevelDecl, bool isWithinAbstractModule.>
= (. Contract.Requires(cce.NonNullElements(mm));
Method/*!*/ m;
Function/*!*/ f;
- MemberModifiers mmod = new MemberModifiers();
- IToken staticToken = null, protectedToken = null;
.)
- { "ghost" (. mmod.IsGhost = true; .)
- | "static" (. mmod.IsStatic = true; staticToken = t; .)
- | "protected" (. mmod.IsProtected = true; protectedToken = t; .)
- }
( (. if (moduleLevelDecl) {
SemErr(la, "fields are not allowed to be declared at the module level; instead, wrap the field in a 'class' declaration");
- mmod.IsStatic = false;
- mmod.IsProtected = false;
+ dmod.IsStatic = false;
}
.)
- FieldDecl<mmod, mm>
+ FieldDecl<dmod, mm>
| IF(IsFunctionDecl())
- (. if (moduleLevelDecl && staticToken != null) {
- errors.Warning(staticToken, "module-level functions are always non-instance, so the 'static' keyword is not allowed here");
- mmod.IsStatic = false;
+ (. if (moduleLevelDecl && dmod.StaticToken != null) {
+ errors.Warning(dmod.StaticToken, "module-level functions are always non-instance, so the 'static' keyword is not allowed here");
+ dmod.IsStatic = false;
}
.)
- FunctionDecl<mmod, out f> (. mm.Add(f); .)
- | (. if (moduleLevelDecl && staticToken != null) {
- errors.Warning(staticToken, "module-level methods are always non-instance, so the 'static' keyword is not allowed here");
- mmod.IsStatic = false;
- }
- if (protectedToken != null) {
- SemErr(protectedToken, "only functions, not methods, can be declared 'protected'");
- mmod.IsProtected = false;
+ FunctionDecl<dmod, isWithinAbstractModule, out f> (. mm.Add(f); .)
+ | (. if (moduleLevelDecl && dmod.StaticToken != null) {
+ errors.Warning(dmod.StaticToken, "module-level methods are always non-instance, so the 'static' keyword is not allowed here");
+ dmod.IsStatic = false;
}
.)
- MethodDecl<mmod, allowConstructors, out m> (. mm.Add(m); .)
+ MethodDecl<dmod, allowConstructors, isWithinAbstractModule, out m> (. mm.Add(m); .)
)
.
-DatatypeDecl<ModuleDefinition/*!*/ module, out DatatypeDecl/*!*/ dt>
+DatatypeDecl<DeclModifierData dmod, ModuleDefinition/*!*/ module, out DatatypeDecl/*!*/ dt>
= (. Contract.Requires(module != null);
Contract.Ensures(Contract.ValueAtReturn(out dt)!=null);
IToken/*!*/ id;
@@ -689,6 +885,7 @@ DatatypeDecl<ModuleDefinition/*!*/ module, out DatatypeDecl/*!*/ dt>
List<DatatypeCtor/*!*/> ctors = new List<DatatypeCtor/*!*/>();
IToken bodyStart = Token.NoToken; // dummy assignment
bool co = false;
+ CheckDeclModifiers(dmod, "Datatypes or codatatypes", AllowedDeclModifiers.None);
.)
SYNC
( "datatype"
@@ -726,27 +923,27 @@ DatatypeMemberDecl<.List<DatatypeCtor/*!*/>/*!*/ ctors.>
[ FormalsOptionalIds<formals> ]
(. ctors.Add(new DatatypeCtor(id, id.val, formals, attrs)); .)
.
-FieldDecl<.MemberModifiers mmod, List<MemberDecl/*!*/>/*!*/ mm.>
+FieldDecl<.DeclModifierData dmod, List<MemberDecl/*!*/>/*!*/ mm.>
= (. Contract.Requires(cce.NonNullElements(mm));
Attributes attrs = null;
IToken/*!*/ id; Type/*!*/ ty;
+ CheckDeclModifiers(dmod, "Fields", AllowedDeclModifiers.Ghost);
.)
SYNC
"var"
- (. if (mmod.IsStatic) { SemErr(t, "fields cannot be declared 'static'"); }
- .)
{ Attribute<ref attrs> }
- FIdentType<out id, out ty> (. mm.Add(new Field(id, id.val, mmod.IsGhost, ty, attrs)); .)
- { "," FIdentType<out id, out ty> (. mm.Add(new Field(id, id.val, mmod.IsGhost, ty, attrs)); .)
+ FIdentType<out id, out ty> (. mm.Add(new Field(id, id.val, dmod.IsGhost, ty, attrs)); .)
+ { "," FIdentType<out id, out ty> (. mm.Add(new Field(id, id.val, dmod.IsGhost, ty, attrs)); .)
}
OldSemi
.
-NewtypeDecl<ModuleDefinition module, out TopLevelDecl td>
+NewtypeDecl<DeclModifierData dmod, ModuleDefinition module, out TopLevelDecl td>
= (. IToken id, bvId;
Attributes attrs = null;
td = null;
Type baseType = null;
Expression wh;
+ CheckDeclModifiers(dmod, "Newtypes", AllowedDeclModifiers.None);
.)
"newtype"
{ Attribute<ref attrs> }
@@ -754,19 +951,20 @@ NewtypeDecl<ModuleDefinition module, out TopLevelDecl td>
"="
( IF(IsIdentColonOrBar())
NoUSIdent<out bvId>
- [ ":" Type<out baseType> ] (. if (baseType == null) { baseType = new OperationTypeProxy(true, true, false, false, false); } .)
+ [ ":" Type<out baseType> ] (. if (baseType == null) { baseType = new OperationTypeProxy(true, true, false, false, false, false); } .)
"|"
Expression<out wh, false, true> (. td = new NewtypeDecl(theVerifyThisFile ? id : new IncludeToken(id), id.val, module, new BoundVar(bvId, bvId.val, baseType), wh, attrs); .)
| Type<out baseType> (. td = new NewtypeDecl(theVerifyThisFile ? id : new IncludeToken(id), id.val, module, baseType, attrs); .)
)
.
-OtherTypeDecl<ModuleDefinition module, out TopLevelDecl td>
+OtherTypeDecl<DeclModifierData dmod, ModuleDefinition module, out TopLevelDecl td>
= (. IToken id;
Attributes attrs = null;
var eqSupport = TypeParameter.EqualitySupportValue.Unspecified;
var typeArgs = new List<TypeParameter>();
td = null;
Type ty;
+ CheckDeclModifiers(dmod, "Type aliases", AllowedDeclModifiers.None);
.)
"type"
{ Attribute<ref attrs> }
@@ -862,7 +1060,7 @@ TypeIdentOptional<out IToken/*!*/ id, out string/*!*/ identName, out Type/*!*/ t
.)
.
/*------------------------------------------------------------------------*/
-IteratorDecl<ModuleDefinition module, out IteratorDecl/*!*/ iter>
+IteratorDecl<DeclModifierData dmod, ModuleDefinition module, out IteratorDecl/*!*/ iter>
= (. Contract.Ensures(Contract.ValueAtReturn(out iter) != null);
IToken/*!*/ id;
Attributes attrs = null;
@@ -884,6 +1082,7 @@ IteratorDecl<ModuleDefinition module, out IteratorDecl/*!*/ iter>
IToken signatureEllipsis = null;
IToken bodyStart = Token.NoToken;
IToken bodyEnd = Token.NoToken;
+ CheckDeclModifiers(dmod, "Iterators", AllowedDeclModifiers.None);
.)
SYNC
"iterator"
@@ -929,7 +1128,7 @@ GenericParameters<.List<TypeParameter/*!*/>/*!*/ typeArgs.>
">"
.
/*------------------------------------------------------------------------*/
-MethodDecl<MemberModifiers mmod, bool allowConstructor, out Method/*!*/ m>
+MethodDecl<DeclModifierData dmod, bool allowConstructor, bool isWithinAbstractModule, out Method/*!*/ m>
= (. Contract.Ensures(Contract.ValueAtReturn(out m) !=null);
IToken/*!*/ id = Token.NoToken;
bool hasName = false; IToken keywordToken;
@@ -951,43 +1150,36 @@ MethodDecl<MemberModifiers mmod, bool allowConstructor, out Method/*!*/ m>
IToken signatureEllipsis = null;
IToken bodyStart = Token.NoToken;
IToken bodyEnd = Token.NoToken;
+ AllowedDeclModifiers allowed = AllowedDeclModifiers.None;
+ string caption = "";
.)
SYNC
- ( "method"
- | "lemma" (. isLemma = true; .)
- | "colemma" (. isCoLemma = true; .)
- | "comethod" (. isCoLemma = true;
+ ( "method" (. caption = "Methods";
+ allowed = AllowedDeclModifiers.Ghost | AllowedDeclModifiers.Static
+ | AllowedDeclModifiers.Extern; .)
+ | "lemma" (. isLemma = true; caption = "Lemmas";
+ allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static
+ | AllowedDeclModifiers.Protected; .)
+ | "colemma" (. isCoLemma = true; caption = "Colemmas";
+ allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static
+ | AllowedDeclModifiers.Protected; .)
+ | "comethod" (. isCoLemma = true; caption = "Comethods";
+ allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static
+ | AllowedDeclModifiers.Protected;
errors.Warning(t, "the 'comethod' keyword has been deprecated; it has been renamed to 'colemma'");
.)
- | "inductive" "lemma" (. isIndLemma = true; .)
+ | "inductive" "lemma" (. isIndLemma = true; caption = "Inductive lemmas";
+ allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static;.)
| "constructor" (. if (allowConstructor) {
isConstructor = true;
} else {
SemErr(t, "constructors are allowed only in classes");
- }
+ }
+ caption = "Constructors";
+ allowed = AllowedDeclModifiers.None;
.)
- ) (. keywordToken = t; .)
- (. if (isLemma) {
- if (mmod.IsGhost) {
- SemErr(t, "lemmas cannot be declared 'ghost' (they are automatically 'ghost')");
- }
- } else if (isConstructor) {
- if (mmod.IsGhost) {
- SemErr(t, "constructors cannot be declared 'ghost'");
- }
- if (mmod.IsStatic) {
- SemErr(t, "constructors cannot be declared 'static'");
- }
- } else if (isIndLemma) {
- if (mmod.IsGhost) {
- SemErr(t, "inductive lemmas cannot be declared 'ghost' (they are automatically 'ghost')");
- }
- } else if (isCoLemma) {
- if (mmod.IsGhost) {
- SemErr(t, "colemmas cannot be declared 'ghost' (they are automatically 'ghost')");
- }
- }
- .)
+ ) (. keywordToken = t;
+ CheckDeclModifiers(dmod, caption, allowed); .)
{ Attribute<ref attrs> }
[ NoUSIdent<out id> (. hasName = true; .)
]
@@ -997,12 +1189,13 @@ MethodDecl<MemberModifiers mmod, bool allowConstructor, out Method/*!*/ m>
SemErr(la, "a method must be given a name (expecting identifier)");
}
}
+ EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ true);
.)
(
[ GenericParameters<typeArgs> ]
- Formals<true, !mmod.IsGhost, ins>
+ Formals<true, !dmod.IsGhost, ins>
[ "returns" (. if (isConstructor) { SemErr(t, "constructors cannot have out-parameters"); } .)
- Formals<false, !mmod.IsGhost, outs>
+ Formals<false, !dmod.IsGhost, outs>
]
| "..." (. signatureEllipsis = t; .)
)
@@ -1010,7 +1203,7 @@ MethodDecl<MemberModifiers mmod, bool allowConstructor, out Method/*!*/ m>
[ BlockStmt<out body, out bodyStart, out bodyEnd>
]
(.
- if (DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported") && !Attributes.Contains(attrs, "decl") && theVerifyThisFile) {
+ if (!isWithinAbstractModule && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported") && !Attributes.Contains(attrs, "decl") && theVerifyThisFile) {
SemErr(t, "a method with an ensures clause must have a body, unless given the :axiom attribute");
}
@@ -1019,16 +1212,16 @@ MethodDecl<MemberModifiers mmod, bool allowConstructor, out Method/*!*/ m>
m = new Constructor(tok, hasName ? id.val : "_ctor", typeArgs, ins,
req, new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, attrs, signatureEllipsis);
} else if (isIndLemma) {
- m = new InductiveLemma(tok, id.val, mmod.IsStatic, typeArgs, ins, outs,
+ m = new InductiveLemma(tok, id.val, dmod.IsStatic, typeArgs, ins, outs,
req, new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, attrs, signatureEllipsis);
} else if (isCoLemma) {
- m = new CoLemma(tok, id.val, mmod.IsStatic, typeArgs, ins, outs,
+ m = new CoLemma(tok, id.val, dmod.IsStatic, typeArgs, ins, outs,
req, new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, attrs, signatureEllipsis);
} else if (isLemma) {
- m = new Lemma(tok, id.val, mmod.IsStatic, typeArgs, ins, outs,
+ m = new Lemma(tok, id.val, dmod.IsStatic, typeArgs, ins, outs,
req, new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, attrs, signatureEllipsis);
} else {
- m = new Method(tok, id.val, mmod.IsStatic, mmod.IsGhost, typeArgs, ins, outs,
+ m = new Method(tok, id.val, dmod.IsStatic, dmod.IsGhost, typeArgs, ins, outs,
req, new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, attrs, signatureEllipsis);
}
m.BodyStartTok = bodyStart;
@@ -1139,7 +1332,13 @@ TypeAndToken<out IToken tok, out Type ty>
[ GenericInstantiation<gt> ] (. if (gt.Count > 1) {
SemErr("set type expects only one type argument");
}
- ty = new SetType(gt.Count == 1 ? gt[0] : null);
+ ty = new SetType(true, gt.Count == 1 ? gt[0] : null);
+ .)
+ | "iset" (. tok = t; gt = new List<Type>(); .)
+ [ GenericInstantiation<gt> ] (. if (gt.Count > 1) {
+ SemErr("set type expects only one type argument");
+ }
+ ty = new SetType(false, gt.Count == 1 ? gt[0] : null);
.)
| "multiset" (. tok = t; gt = new List<Type>(); .)
[ GenericInstantiation<gt> ] (. if (gt.Count > 1) {
@@ -1227,7 +1426,7 @@ GenericInstantiation<.List<Type/*!*/>/*!*/ gt.>
">"
.
/*------------------------------------------------------------------------*/
-FunctionDecl<MemberModifiers mmod, out Function/*!*/ f>
+FunctionDecl<DeclModifierData dmod, bool isWithinAbstractModule, out Function/*!*/ f>
= (. Contract.Ensures(Contract.ValueAtReturn(out f)!=null);
Attributes attrs = null;
IToken/*!*/ id = Token.NoToken; // to please compiler
@@ -1250,7 +1449,13 @@ FunctionDecl<MemberModifiers mmod, out Function/*!*/ f>
( "function"
[ "method" (. isFunctionMethod = true; .)
]
- (. if (mmod.IsGhost) { SemErr(t, "functions cannot be declared 'ghost' (they are ghost by default)"); }
+ (. AllowedDeclModifiers allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected;
+ string caption = "Functions";
+ if (isFunctionMethod) {
+ allowed |= AllowedDeclModifiers.Extern;
+ caption = "Function methods";
+ }
+ CheckDeclModifiers(dmod, caption, allowed);
.)
{ Attribute<ref attrs> }
NoUSIdent<out id>
@@ -1266,7 +1471,13 @@ FunctionDecl<MemberModifiers mmod, out Function/*!*/ f>
| "predicate" (. isPredicate = true; .)
[ "method" (. isFunctionMethod = true; .)
]
- (. if (mmod.IsGhost) { SemErr(t, "predicates cannot be declared 'ghost' (they are ghost by default)"); }
+ (. AllowedDeclModifiers allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected;
+ string caption = "Predicates";
+ if (isFunctionMethod) {
+ allowed |= AllowedDeclModifiers.Extern;
+ caption = "Predicate methods";
+ }
+ CheckDeclModifiers(dmod, caption, allowed);
.)
{ Attribute<ref attrs> }
NoUSIdent<out id>
@@ -1281,7 +1492,8 @@ FunctionDecl<MemberModifiers mmod, out Function/*!*/ f>
/* ----- inductive predicate ----- */
| "inductive" "predicate" (. isIndPredicate = true; .)
- (. if (mmod.IsGhost) { SemErr(t, "inductive predicates cannot be declared 'ghost' (they are ghost by default)"); }
+ (. CheckDeclModifiers(dmod, "Inductive predicates",
+ AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected);
.)
{ Attribute<ref attrs> }
NoUSIdent<out id>
@@ -1295,7 +1507,8 @@ FunctionDecl<MemberModifiers mmod, out Function/*!*/ f>
/* ----- copredicate ----- */
| "copredicate" (. isCoPredicate = true; .)
- (. if (mmod.IsGhost) { SemErr(t, "copredicates cannot be declared 'ghost' (they are ghost by default)"); }
+ (. CheckDeclModifiers(dmod, "Copredicates",
+ AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected);
.)
{ Attribute<ref attrs> }
NoUSIdent<out id>
@@ -1312,22 +1525,23 @@ FunctionDecl<MemberModifiers mmod, out Function/*!*/ f>
{ FunctionSpec<reqs, reads, ens, decreases> }
[ FunctionBody<out body, out bodyStart, out bodyEnd>
]
- (. if (DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported")) {
+ (. if (!isWithinAbstractModule && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 &&
+ !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported")) {
SemErr(t, "a function with an ensures clause must have a body, unless given the :axiom attribute");
}
-
+ EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ true);
IToken tok = theVerifyThisFile ? id : new IncludeToken(id);
if (isPredicate) {
- f = new Predicate(tok, id.val, mmod.IsStatic, mmod.IsProtected, !isFunctionMethod, typeArgs, formals,
+ f = new Predicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, !isFunctionMethod, typeArgs, formals,
reqs, reads, ens, new Specification<Expression>(decreases, null), body, Predicate.BodyOriginKind.OriginalOrInherited, attrs, signatureEllipsis);
} else if (isIndPredicate) {
- f = new InductivePredicate(tok, id.val, mmod.IsStatic, mmod.IsProtected, typeArgs, formals,
+ f = new InductivePredicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, typeArgs, formals,
reqs, reads, ens, body, attrs, signatureEllipsis);
} else if (isCoPredicate) {
- f = new CoPredicate(tok, id.val, mmod.IsStatic, mmod.IsProtected, typeArgs, formals,
+ f = new CoPredicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, typeArgs, formals,
reqs, reads, ens, body, attrs, signatureEllipsis);
} else {
- f = new Function(tok, id.val, mmod.IsStatic, mmod.IsProtected, !isFunctionMethod, typeArgs, formals, returnType,
+ f = new Function(tok, id.val, dmod.IsStatic, dmod.IsProtected, !isFunctionMethod, typeArgs, formals, returnType,
reqs, reads, ens, new Specification<Expression>(decreases, null), body, attrs, signatureEllipsis);
}
f.BodyStartTok = bodyStart;
@@ -1582,50 +1796,78 @@ VarDeclStatement<.out Statement/*!*/ s.>
Expression suchThat = null;
Attributes attrs = null;
IToken endTok;
+ s = dummyStmt;
.)
[ "ghost" (. isGhost = true; x = t; .)
]
"var" (. if (!isGhost) { x = t; } .)
- { Attribute<ref attrs> }
- LocalIdentTypeOptional<out d, isGhost> (. lhss.Add(d); d.Attributes = attrs; attrs = null; .)
- { ","
- { Attribute<ref attrs> }
- LocalIdentTypeOptional<out d, isGhost> (. lhss.Add(d); d.Attributes = attrs; attrs = null; .)
- }
- [ ":=" (. assignTok = t; .)
- Rhs<out r> (. rhss.Add(r); .)
- { "," Rhs<out r> (. rhss.Add(r); .)
+ ( { Attribute<ref attrs> }
+ LocalIdentTypeOptional<out d, isGhost> (. lhss.Add(d); d.Attributes = attrs; attrs = null; .)
+ { ","
+ { Attribute<ref attrs> }
+ LocalIdentTypeOptional<out d, isGhost> (. lhss.Add(d); d.Attributes = attrs; attrs = null; .)
}
- | { Attribute<ref attrs> }
- ":|" (. assignTok = t; .)
- [ IF(la.kind == _assume) /* an Expression can also begin with an "assume", so this says to resolve it to pick up any "assume" here */
- "assume" (. suchThatAssume = t; .)
+ [ ":=" (. assignTok = t; .)
+ Rhs<out r> (. rhss.Add(r); .)
+ { "," Rhs<out r> (. rhss.Add(r); .)
+ }
+ | { Attribute<ref attrs> }
+ ":|" (. assignTok = t; .)
+ [ IF(la.kind == _assume) /* an Expression can also begin with an "assume", so this says to resolve it to pick up any "assume" here */
+ "assume" (. suchThatAssume = t; .)
+ ]
+ Expression<out suchThat, false, true>
]
- Expression<out suchThat, false, true>
- ]
- SYNC ";" (. endTok = t; .)
- (. ConcreteUpdateStatement update;
- if (suchThat != null) {
- var ies = new List<Expression>();
- foreach (var lhs in lhss) {
- ies.Add(new IdentifierExpr(lhs.Tok, lhs.Name));
- }
- update = new AssignSuchThatStmt(assignTok, endTok, ies, suchThat, suchThatAssume, attrs);
- } else if (rhss.Count == 0) {
- update = null;
- } else {
- var ies = new List<Expression>();
- foreach (var lhs in lhss) {
- ies.Add(new AutoGhostIdentifierExpr(lhs.Tok, lhs.Name));
+ SYNC ";" (. endTok = t; .)
+ (. ConcreteUpdateStatement update;
+ if (suchThat != null) {
+ var ies = new List<Expression>();
+ foreach (var lhs in lhss) {
+ ies.Add(new IdentifierExpr(lhs.Tok, lhs.Name));
+ }
+ update = new AssignSuchThatStmt(assignTok, endTok, ies, suchThat, suchThatAssume, attrs);
+ } else if (rhss.Count == 0) {
+ update = null;
+ } else {
+ var ies = new List<Expression>();
+ foreach (var lhs in lhss) {
+ ies.Add(new AutoGhostIdentifierExpr(lhs.Tok, lhs.Name));
+ }
+ update = new UpdateStmt(assignTok, endTok, ies, rhss);
}
- update = new UpdateStmt(assignTok, endTok, ies, rhss);
- }
- s = new VarDeclStmt(x, endTok, lhss, update);
- .)
+ s = new VarDeclStmt(x, endTok, lhss, update);
+ .)
+ | "(" (. var letLHSs = new List<CasePattern>();
+ var letRHSs = new List<Expression>();
+ List<CasePattern> arguments = new List<CasePattern>();
+ CasePattern pat;
+ Expression e = dummyExpr;
+ IToken id = t;
+ .)
+ [ CasePattern<out pat> (. arguments.Add(pat); .)
+ { "," CasePattern<out pat> (. arguments.Add(pat); .)
+ }
+ ]
+ ")" (. // Parse parenthesis without an identifier as a built in tuple type.
+ theBuiltIns.TupleType(id, arguments.Count, true); // make sure the tuple type exists
+ string ctor = BuiltIns.TupleTypeCtorName; //use the TupleTypeCtors
+ pat = new CasePattern(id, ctor, arguments);
+ if (isGhost) { pat.Vars.Iter(bv => bv.IsGhost = true); }
+ letLHSs.Add(pat);
+ .)
+ ( ":="
+ | { Attribute<ref attrs> }
+ ":|" (. SemErr(pat.tok, "LHS of assign-such-that expression must be variables, not general patterns"); .)
+ )
+ Expression<out e, false, true> (. letRHSs.Add(e); .)
+
+ ";"
+ (. s = new LetStmt(e.tok, e.tok, letLHSs, letRHSs); .)
+ )
.
IfStmt<out Statement/*!*/ ifStmt>
= (. Contract.Ensures(Contract.ValueAtReturn(out ifStmt) != null); IToken/*!*/ x;
- Expression guard = null; IToken guardEllipsis = null;
+ Expression guard = null; IToken guardEllipsis = null; bool isExistentialGuard = false;
BlockStmt/*!*/ thn;
BlockStmt/*!*/ bs;
Statement/*!*/ s;
@@ -1637,11 +1879,13 @@ IfStmt<out Statement/*!*/ ifStmt>
"if" (. x = t; .)
(
IF(IsAlternative())
- AlternativeBlock<out alternatives, out endTok>
+ AlternativeBlock<true, out alternatives, out endTok>
(. ifStmt = new AlternativeStmt(x, endTok, alternatives); .)
|
- ( Guard<out guard>
- | "..." (. guardEllipsis = t; .)
+ ( IF(IsExistentialGuard())
+ ExistentialGuard<out guard, true> (. isExistentialGuard = true; .)
+ | Guard<out guard>
+ | "..." (. guardEllipsis = t; .)
)
BlockStmt<out thn, out bodyStart, out bodyEnd> (. endTok = thn.EndTok; .)
[ "else"
@@ -1650,26 +1894,29 @@ IfStmt<out Statement/*!*/ ifStmt>
)
]
(. if (guardEllipsis != null) {
- ifStmt = new SkeletonStatement(new IfStmt(x, endTok, guard, thn, els), guardEllipsis, null);
+ ifStmt = new SkeletonStatement(new IfStmt(x, endTok, isExistentialGuard, guard, thn, els), guardEllipsis, null);
} else {
- ifStmt = new IfStmt(x, endTok, guard, thn, els);
+ ifStmt = new IfStmt(x, endTok, isExistentialGuard, guard, thn, els);
}
.)
)
.
-AlternativeBlock<.out List<GuardedAlternative> alternatives, out IToken endTok.>
+AlternativeBlock<.bool allowExistentialGuards, out List<GuardedAlternative> alternatives, out IToken endTok.>
= (. alternatives = new List<GuardedAlternative>();
IToken x;
- Expression e;
+ Expression e; bool isExistentialGuard;
List<Statement> body;
.)
"{"
- { "case" (. x = t; .)
- Expression<out e, true, false> // NB: don't allow lambda here
+ { "case" (. x = t; isExistentialGuard = false; e = dummyExpr; .)
+ ( IF(allowExistentialGuards && IsExistentialGuard())
+ ExistentialGuard<out e, false > (. isExistentialGuard = true; .) // NB: don't allow lambda here
+ | Expression<out e, true, false> // NB: don't allow lambda here
+ )
"=>"
(. body = new List<Statement>(); .)
{ Stmt<body> }
- (. alternatives.Add(new GuardedAlternative(x, e, body)); .)
+ (. alternatives.Add(new GuardedAlternative(x, isExistentialGuard, e, body)); .)
}
"}" (. endTok = t; .)
.
@@ -1693,7 +1940,7 @@ WhileStmt<out Statement stmt>
(
IF(IsLoopSpec() || IsAlternative())
{ LoopSpec<invariants, decreases, ref mod, ref decAttrs, ref modAttrs> }
- AlternativeBlock<out alternatives, out endTok>
+ AlternativeBlock<false, out alternatives, out endTok>
(. stmt = new AlternativeLoopStmt(x, endTok, invariants, new Specification<Expression>(decreases, decAttrs), new Specification<FrameExpression>(mod, modAttrs), alternatives); .)
|
( Guard<out guard> (. Contract.Assume(guard == null || cce.Owner.None(guard)); .)
@@ -1773,6 +2020,21 @@ Guard<out Expression e> /* null represents demonic-choice */
| Expression<out ee, true, true> (. e = ee; .)
)
.
+ExistentialGuard<out Expression e, bool allowLambda>
+= (. var bvars = new List<BoundVar>();
+ BoundVar bv; IToken x;
+ Attributes attrs = null;
+ Expression body;
+ .)
+ IdentTypeOptional<out bv> (. bvars.Add(bv); x = bv.tok; .)
+ { ","
+ IdentTypeOptional<out bv> (. bvars.Add(bv); .)
+ }
+ { Attribute<ref attrs> }
+ ":|"
+ Expression<out body, true, allowLambda>
+ (. e = new ExistsExpr(x, bvars, null, body, attrs); .)
+ .
MatchStmt<out Statement/*!*/ s>
= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null);
Token x; Expression/*!*/ e; MatchCaseStmt/*!*/ c;
@@ -1794,17 +2056,25 @@ MatchStmt<out Statement/*!*/ s>
CaseStatement<out MatchCaseStmt/*!*/ c>
= (. Contract.Ensures(Contract.ValueAtReturn(out c) != null);
IToken/*!*/ x, id;
- List<BoundVar/*!*/> arguments = new List<BoundVar/*!*/>();
- BoundVar/*!*/ bv;
+ List<CasePattern/*!*/> arguments = new List<CasePattern/*!*/>();
+ CasePattern/*!*/ pat;
List<Statement/*!*/> body = new List<Statement/*!*/>();
+ string/*!*/ name = "";
.)
"case" (. x = t; .)
- Ident<out id>
- [ "("
- IdentTypeOptional<out bv> (. arguments.Add(bv); .)
- { "," IdentTypeOptional<out bv> (. arguments.Add(bv); .)
- }
- ")" ]
+ ( Ident<out id> (. name = id.val; .)
+ [ "("
+ [ CasePattern<out pat> (. arguments.Add(pat); .)
+ { "," CasePattern<out pat> (. arguments.Add(pat); .)
+ }
+ ]
+ ")" ]
+ | "("
+ CasePattern<out pat> (. arguments.Add(pat); .)
+ { "," CasePattern<out pat> (. arguments.Add(pat); .)
+ }
+ ")"
+ )
"=>"
SYNC /* this SYNC and the one inside the loop below are used to avoid problems with the IsNotEndOfCase test. The SYNC will
* skip until the next symbol that can legally occur here, which is either the beginning of a Stmt or whatever is allowed
@@ -1814,7 +2084,7 @@ CaseStatement<out MatchCaseStmt/*!*/ c>
Stmt<body>
SYNC /* see comment about SYNC above */
}
- (. c = new MatchCaseStmt(x, id.val, arguments, body); .)
+ (. c = new MatchCaseStmt(x, name, arguments, body); .)
.
/*------------------------------------------------------------------------*/
AssertStmt<out Statement/*!*/ s>
@@ -1947,6 +2217,7 @@ ModifyStmt<out Statement s>
CalcStmt<out Statement s>
= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null);
Token x;
+ Attributes attrs = null;
CalcStmt.CalcOp op, calcOp = Microsoft.Dafny.CalcStmt.DefaultOp, resOp = Microsoft.Dafny.CalcStmt.DefaultOp;
var lines = new List<Expression>();
var hints = new List<BlockStmt>();
@@ -1958,6 +2229,7 @@ CalcStmt<out Statement s>
IToken danglingOperator = null;
.)
"calc" (. x = t; .)
+ { IF(IsAttribute()) Attribute<ref attrs> }
[ CalcOp<out opTok, out calcOp> (. maybeOp = calcOp.ResultOp(calcOp); // guard against non-transitive calcOp (like !=)
if (maybeOp == null) {
SemErr(opTok, "the main operator of a calculation must be transitive");
@@ -2006,7 +2278,7 @@ CalcStmt<out Statement s>
// Repeat the last line to create a dummy line for the dangling hint
lines.Add(lines[lines.Count - 1]);
}
- s = new CalcStmt(x, t, calcOp, lines, hints, stepOps, resOp);
+ s = new CalcStmt(x, t, calcOp, lines, hints, stepOps, resOp, attrs);
.)
.
CalcOp<out IToken x, out CalcStmt.CalcOp/*!*/ op>
@@ -2100,10 +2372,13 @@ ImpliesExpliesExpression<out Expression e0, bool allowSemi, bool allowLambda>
( ImpliesOp (. x = t; .)
ImpliesExpression<out e1, allowSemi, allowLambda> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Imp, e0, e1); .)
| ExpliesOp (. x = t; .)
- LogicalExpression<out e1, allowSemi, allowLambda> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Exp, e0, e1); .)
+ LogicalExpression<out e1, allowSemi, allowLambda> (. // The order of operands is reversed so that it can be turned into implication during resolution
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Exp, e1, e0); .)
{ IF(IsExpliesOp()) /* read a reverse implication as far as possible */
ExpliesOp (. x = t; .)
- LogicalExpression<out e1, allowSemi, allowLambda> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Exp, e0, e1); .)
+ LogicalExpression<out e1, allowSemi, allowLambda> (. //The order of operands is reversed so that it can be turned into implication during resolution
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Exp, e1, e0);
+ .)
}
)
]
@@ -2317,10 +2592,13 @@ UnaryExpression<out Expression e, bool allowSemi, bool allowLambda>
"imap" (. x = t; .)
MapDisplayExpr<x, false, out e>
{ IF(IsSuffix()) Suffix<ref e> }
+ | IF(IsISetDisplay()) /* this alternative must be checked before going into EndlessExpression, where there is another "iset" */
+ "iset" (. x = t; .)
+ ISetDisplayExpr<x, false, out e>
+ { IF(IsSuffix()) Suffix<ref e> }
| IF(IsLambda(allowLambda))
LambdaExpression<out e, allowSemi> /* this is an endless expression */
| EndlessExpression<out e, allowSemi, allowLambda>
-
| NameSegment<out e>
{ IF(IsSuffix()) Suffix<ref e> }
| DisplayExpr<out e>
@@ -2425,13 +2703,22 @@ ParensExpression<out Expression e, bool allowSemi, bool allowLambda>
}
.)
.
+ISetDisplayExpr<IToken/*!*/ setToken, bool finite, out Expression e>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null);
+ List<Expression> elements = new List<Expression/*!*/>();;
+ e = dummyExpr;
+ .)
+ "{"
+ [ Expressions<elements> ] (. e = new SetDisplayExpr(setToken, finite, elements);.)
+ "}"
+ .
DisplayExpr<out Expression e>
= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null);
IToken x; List<Expression> elements;
e = dummyExpr;
.)
( "{" (. x = t; elements = new List<Expression/*!*/>(); .)
- [ Expressions<elements> ] (. e = new SetDisplayExpr(x, elements);.)
+ [ Expressions<elements> ] (. e = new SetDisplayExpr(x, true, elements);.)
"}"
| "[" (. x = t; elements = new List<Expression/*!*/>(); .)
[ Expressions<elements> ] (. e = new SeqDisplayExpr(x, elements); .)
@@ -2496,7 +2783,10 @@ EndlessExpression<out Expression e, bool allowSemi, bool allowLambda>
"else" Expression<out e1, allowSemi, allowLambda> (. e = new ITEExpr(x, e, e0, e1); .)
| MatchExpression<out e, allowSemi, allowLambda>
| QuantifierGuts<out e, allowSemi, allowLambda>
- | SetComprehensionExpr<out e, allowSemi, allowLambda>
+ | "set" (. x = t; .)
+ SetComprehensionExpr<x, true, out e, allowSemi, allowLambda>
+ | "iset" (. x = t; .)
+ SetComprehensionExpr<x, false, out e, allowSemi, allowLambda>
| StmtInExpr<out s>
Expression<out e, allowSemi, allowLambda> (. e = new StmtExpr(s.Tok, s, e); .)
| LetExpr<out e, allowSemi, allowLambda>
@@ -2584,19 +2874,27 @@ MatchExpression<out Expression e, bool allowSemi, bool allowLambda>
.
CaseExpression<out MatchCaseExpr c, bool allowSemi, bool allowLambda>
= (. Contract.Ensures(Contract.ValueAtReturn(out c) != null); IToken/*!*/ x, id;
- List<BoundVar/*!*/> arguments = new List<BoundVar/*!*/>();
- BoundVar/*!*/ bv;
+ List<CasePattern/*!*/> arguments = new List<CasePattern/*!*/>();
+ CasePattern/*!*/ pat;
Expression/*!*/ body;
+ string/*!*/ name = "";
.)
"case" (. x = t; .)
- Ident<out id>
- [ "("
- IdentTypeOptional<out bv> (. arguments.Add(bv); .)
- { "," IdentTypeOptional<out bv> (. arguments.Add(bv); .)
- }
- ")" ]
+ ( Ident<out id> (. name = id.val; .)
+ [ "("
+ [ CasePattern<out pat> (. arguments.Add(pat); .)
+ { "," CasePattern<out pat> (. arguments.Add(pat); .)
+ }
+ ]
+ ")" ]
+ | "("
+ CasePattern<out pat> (. arguments.Add(pat); .)
+ { "," CasePattern<out pat> (. arguments.Add(pat); .)
+ }
+ ")"
+ )
"=>"
- Expression<out body, allowSemi, allowLambda> (. c = new MatchCaseExpr(x, id.val, arguments, body); .)
+ Expression<out body, allowSemi, allowLambda> (. c = new MatchCaseExpr(x, name, arguments, body); .)
.
CasePattern<out CasePattern pat>
= (. IToken id; List<CasePattern> arguments;
@@ -2611,7 +2909,18 @@ CasePattern<out CasePattern pat>
}
]
")" (. pat = new CasePattern(id, id.val, arguments); .)
-
+ | "(" (. id = t;
+ arguments = new List<CasePattern>();
+ .)
+ [ CasePattern<out pat> (. arguments.Add(pat); .)
+ { "," CasePattern<out pat> (. arguments.Add(pat); .)
+ }
+ ]
+ ")" (. // Parse parenthesis without an identifier as a built in tuple type.
+ theBuiltIns.TupleType(id, arguments.Count, true); // make sure the tuple type exists
+ string ctor = BuiltIns.TupleTypeCtorName; //use the TupleTypeCtors
+ pat = new CasePattern(id, ctor, arguments);
+ .)
| IdentTypeOptional<out bv> (. // This could be a BoundVar of a parameter-less constructor and we may not know until resolution.
// Nevertheless, we do put the "bv" into the CasePattern here (even though it will get thrown out
// later if resolution finds the CasePattern to denote a parameter-less constructor), because this
@@ -2683,8 +2992,17 @@ Suffix<ref Expression e>
Expression e0 = null; Expression e1 = null; Expression ee; bool anyDots = false;
List<Expression> multipleLengths = null; bool takeRest = false; // takeRest is relevant only if multipleLengths is non-null
List<Expression> multipleIndices = null;
+ List<Tuple<IToken, string, Expression>> updates;
+ Expression v;
.)
- ( DotSuffix<out id, out x> (. if (x != null) {
+ ( "."
+ ( "(" (. x = t; updates = new List<Tuple<IToken, string, Expression>>(); .)
+ MemberBindingUpdate<out id, out v> (. updates.Add(Tuple.Create(id, id.val, v)); .)
+ { "," MemberBindingUpdate<out id, out v> (. updates.Add(Tuple.Create(id, id.val, v)); .)
+ }
+ ")"
+ (. e = new DatatypeUpdateExpr(x, e, updates); .)
+ | DotSuffix<out id, out x> (. if (x != null) {
// process id as a Suffix in its own right
e = new ExprDotName(id, e, id.val, null);
id = x; // move to the next Suffix
@@ -2693,17 +3011,18 @@ Suffix<ref Expression e>
.)
- ( IF(IsGenericInstantiation())
- (. typeArgs = new List<Type>(); .)
- GenericInstantiation<typeArgs>
- | HashCall<id, out openParen, out typeArgs, out args>
- | /* empty */
+ ( IF(IsGenericInstantiation())
+ (. typeArgs = new List<Type>(); .)
+ GenericInstantiation<typeArgs>
+ | HashCall<id, out openParen, out typeArgs, out args>
+ | /* empty */
+ )
+ (. e = new ExprDotName(id, e, id.val, typeArgs);
+ if (openParen != null) {
+ e = new ApplySuffix(openParen, e, args);
+ }
+ .)
)
- (. e = new ExprDotName(id, e, id.val, typeArgs);
- if (openParen != null) {
- e = new ApplySuffix(openParen, e, args);
- }
- .)
| "[" (. x = t; .)
( Expression<out ee, true, true> (. e0 = ee; .)
( ".." (. anyDots = true; .)
@@ -2817,16 +3136,14 @@ QuantifierDomain<.out List<BoundVar> bvars, out Attributes attrs, out Expression
]
.
-SetComprehensionExpr<out Expression q, bool allowSemi, bool allowLambda>
+SetComprehensionExpr<IToken setToken, bool finite, out Expression q, bool allowSemi, bool allowLambda>
= (. Contract.Ensures(Contract.ValueAtReturn(out q) != null);
- IToken x = Token.NoToken;
BoundVar bv;
List<BoundVar/*!*/> bvars = new List<BoundVar>();
Expression range;
Expression body = null;
Attributes attrs = null;
.)
- "set" (. x = t; .)
IdentTypeOptional<out bv> (. bvars.Add(bv); .)
{ ","
IdentTypeOptional<out bv> (. bvars.Add(bv); .)
@@ -2838,7 +3155,7 @@ SetComprehensionExpr<out Expression q, bool allowSemi, bool allowLambda>
Expression<out body, allowSemi, allowLambda>
]
(. if (body == null && bvars.Count != 1) { SemErr(t, "a set comprehension with more than one bound variable must have a term expression"); }
- q = new SetComprehension(x, bvars, range, body, attrs);
+ q = new SetComprehension(setToken, finite, bvars, range, body, attrs);
.)
.
Expressions<.List<Expression> args.>
@@ -2849,10 +3166,10 @@ Expressions<.List<Expression> args.>
.
/*------------------------------------------------------------------------*/
Attribute<ref Attributes attrs>
-= (. string name;
+= (. IToken x; string name;
var args = new List<Expression>();
.)
- "{" ":" ident (. name = t.val; .)
+ "{" ":" NoUSIdent<out x> (. name = x.val; .)
[ Expressions<args> ]
"}"
(. attrs = new Attributes(name, args, attrs); .)
@@ -2863,10 +3180,10 @@ Ident<out IToken/*!*/ x>
ident (. x = t; .)
.
// Identifier or sequence of digits
-// Parse one of the following:
-// . ident
-// . digits
-// . digits . digits
+// Parse one of the following, which are supposed to follow a ".":
+// ident
+// digits
+// digits . digits
// In the first two cases, x returns as the token for the ident/digits and y returns as null.
// In the third case, x and y return as the tokens for the first and second digits.
// This parser production solves a problem where the scanner might parse a real number instead
@@ -2876,7 +3193,6 @@ DotSuffix<out IToken x, out IToken y>
x = Token.NoToken;
y = null;
.)
- "."
( ident (. x = t; .)
| digits (. x = t; .)
| decimaldigits (. x = t;
@@ -2902,6 +3218,15 @@ DotSuffix<out IToken x, out IToken y>
| "reads" (. x = t; .)
)
.
+MemberBindingUpdate<out IToken id, out Expression e>
+= (. id = Token.NoToken; e = dummyExpr; .)
+ ( ident (. id = t; .)
+ | digits (. id = t; .)
+ )
+ ":="
+ Expression<out e, true, true>
+ .
+
// Identifier, disallowing leading underscores
NoUSIdent<out IToken/*!*/ x>
= (. Contract.Ensures(Contract.ValueAtReturn(out x) != null); .)
@@ -2933,7 +3258,7 @@ Nat<out BigInteger n>
( digits
(. S = Util.RemoveUnderscores(t.val);
try {
- n = BigInteger.Parse(S);
+ n = BigIntegerParser.Parse(S);
} catch (System.FormatException) {
SemErr("incorrectly formatted number");
n = BigInteger.Zero;
@@ -2943,7 +3268,7 @@ Nat<out BigInteger n>
(. S = Util.RemoveUnderscores(t.val.Substring(2));
try {
// note: leading 0 required when parsing positive hex numbers
- n = BigInteger.Parse("0" + S, System.Globalization.NumberStyles.HexNumber);
+ n = BigIntegerParser.Parse("0" + S, System.Globalization.NumberStyles.HexNumber);
} catch (System.FormatException) {
SemErr("incorrectly formatted number");
n = BigInteger.Zero;
diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs
index 99dfecd6..ab1a411a 100644
--- a/Source/Dafny/DafnyAst.cs
+++ b/Source/Dafny/DafnyAst.cs
@@ -10,6 +10,7 @@ using System.Diagnostics.Contracts;
using System.Numerics;
using System.Linq;
using Microsoft.Boogie;
+using System.Diagnostics;
namespace Microsoft.Dafny {
public class Program {
@@ -26,20 +27,22 @@ namespace Microsoft.Dafny {
public List<ModuleDefinition> CompileModules; // filled in during resolution.
// Contains the definitions to be used for compilation.
- List<AdditionalInformation> _additionalInformation = new List<AdditionalInformation>();
- public List<AdditionalInformation> AdditionalInformation { get { return _additionalInformation; } }
public readonly ModuleDecl DefaultModule;
public readonly ModuleDefinition DefaultModuleDef;
public readonly BuiltIns BuiltIns;
public readonly List<TranslationTask> TranslationTasks;
- public Program(string name, [Captured] ModuleDecl module, [Captured] BuiltIns builtIns) {
+ public readonly ErrorReporter reporter;
+
+ public Program(string name, [Captured] ModuleDecl module, [Captured] BuiltIns builtIns, ErrorReporter reporter) {
Contract.Requires(name != null);
Contract.Requires(module != null);
Contract.Requires(module is LiteralModuleDecl);
+ Contract.Requires(reporter != null);
FullName = name;
DefaultModule = module;
DefaultModuleDef = (DefaultModuleDecl)((LiteralModuleDecl)module).ModuleDef;
BuiltIns = builtIns;
+ this.reporter = reporter;
Modules = new List<ModuleDefinition>();
CompileModules = new List<ModuleDefinition>();
TranslationTasks = new List<TranslationTask>();
@@ -86,7 +89,7 @@ namespace Microsoft.Dafny {
public class BuiltIns
{
- public readonly ModuleDefinition SystemModule = new ModuleDefinition(Token.NoToken, "_System", false, false, null, null, null, true);
+ public readonly ModuleDefinition SystemModule = new ModuleDefinition(Token.NoToken, "_System", false, false, /*isExclusiveRefinement:*/ false, null, null, null, true);
readonly Dictionary<int, ClassDecl> arrayTypeDecls = new Dictionary<int, ClassDecl>();
readonly Dictionary<int, ArrowTypeDecl> arrowTypeDecls = new Dictionary<int, ArrowTypeDecl>();
readonly Dictionary<int, TupleTypeDecl> tupleTypeDecls = new Dictionary<int, TupleTypeDecl>();
@@ -165,7 +168,7 @@ namespace Microsoft.Dafny {
var argExprs = args.ConvertAll(a =>
(Expression)new IdentifierExpr(tok, a.Name) { Var = a, Type = a.Type });
var readsIS = new FunctionCallExpr(tok, "reads", new ImplicitThisExpr(tok), tok, argExprs) {
- Type = new SetType(new ObjectType()),
+ Type = new SetType(true, new ObjectType()),
};
var readsFrame = new List<FrameExpression> { new FrameExpression(tok, readsIS, null) };
var req = new Function(tok, "requires", false, false, true,
@@ -174,13 +177,13 @@ namespace Microsoft.Dafny {
new Specification<Expression>(new List<Expression>(), null),
null, null, null);
var reads = new Function(tok, "reads", false, false, true,
- new List<TypeParameter>(), args, new SetType(new ObjectType()),
+ new List<TypeParameter>(), args, new SetType(true, new ObjectType()),
new List<Expression>(), readsFrame, new List<Expression>(),
new Specification<Expression>(new List<Expression>(), null),
null, null, null);
readsIS.Function = reads; // just so we can really claim the member declarations are resolved
readsIS.TypeArgumentSubstitutions = Util.Dict(tps, tys); // ditto
- var arrowDecl = new ArrowTypeDecl(tps, req, reads, SystemModule, DontCompile());
+ var arrowDecl = new ArrowTypeDecl(tps, req, reads, SystemModule, DontCompile(), null);
arrowTypeDecls.Add(arity, arrowDecl);
SystemModule.TopLevelDecls.Add(arrowDecl);
}
@@ -232,21 +235,12 @@ namespace Microsoft.Dafny {
}
public static IEnumerable<Expression> SubExpressions(Attributes attrs) {
- for (; attrs != null; attrs = attrs.Prev) {
- foreach (var arg in attrs.Args) {
- yield return arg;
- }
- }
+ return attrs.AsEnumerable().SelectMany(aa => attrs.Args);
}
public static bool Contains(Attributes attrs, string nm) {
Contract.Requires(nm != null);
- for (; attrs != null; attrs = attrs.Prev) {
- if (attrs.Name == nm) {
- return true;
- }
- }
- return false;
+ return attrs.AsEnumerable().Any(aa => aa.Name == nm);
}
/// <summary>
@@ -258,10 +252,10 @@ namespace Microsoft.Dafny {
[Pure]
public static bool ContainsBool(Attributes attrs, string nm, ref bool value) {
Contract.Requires(nm != null);
- for (; attrs != null; attrs = attrs.Prev) {
- if (attrs.Name == nm) {
- if (attrs.Args.Count == 1) {
- var arg = attrs.Args[0] as LiteralExpr;
+ foreach (var attr in attrs.AsEnumerable()) {
+ if (attr.Name == nm) {
+ if (attr.Args.Count == 1) {
+ var arg = attr.Args[0] as LiteralExpr;
if (arg != null && arg.Value is bool) {
value = (bool)arg.Value;
}
@@ -306,12 +300,28 @@ namespace Microsoft.Dafny {
/// </summary>
public static List<Expression> FindExpressions(Attributes attrs, string nm) {
Contract.Requires(nm != null);
+ foreach (var attr in attrs.AsEnumerable()) {
+ if (attr.Name == nm) {
+ return attr.Args;
+ }
+ }
+ return null;
+ }
+
+
+ /// <summary>
+ /// Same as FindExpressions, but returns all matches
+ /// </summary>
+ public static List<List<Expression>> FindAllExpressions(Attributes attrs, string nm) {
+ Contract.Requires(nm != null);
+ List<List<Expression>> ret = null;
for (; attrs != null; attrs = attrs.Prev) {
if (attrs.Name == nm) {
- return attrs.Args;
+ ret = ret ?? new List<List<Expression>>(); // Avoid allocating the list in the common case where we don't find nm
+ ret.Add(attrs.Args);
}
}
- return null;
+ return ret;
}
/// <summary>
@@ -322,13 +332,13 @@ namespace Microsoft.Dafny {
/// - if "allowed" contains Int and Args contains one BigInteger literal, return true and set value to the BigInteger literal. Otherwise,
/// - if "allowed" contains String and Args contains one string literal, return true and set value to the string literal. Otherwise,
/// - if "allowed" contains Expression and Args contains one element, return true and set value to the one element (of type Expression). Otherwise,
- /// - return false, leave value unmodified, and call errorReporter with an error string.
+ /// - return false, leave value unmodified, and call reporter with an error string.
/// </summary>
public enum MatchingValueOption { Empty, Bool, Int, String, Expression }
- public static bool ContainsMatchingValue(Attributes attrs, string nm, ref object value, IEnumerable<MatchingValueOption> allowed, Action<string> errorReporter) {
+ public static bool ContainsMatchingValue(Attributes attrs, string nm, ref object value, IEnumerable<MatchingValueOption> allowed, Action<string> reporter) {
Contract.Requires(nm != null);
Contract.Requires(allowed != null);
- Contract.Requires(errorReporter != null);
+ Contract.Requires(reporter != null);
List<Expression> args = FindExpressions(attrs, nm);
if (args == null) {
return false;
@@ -336,7 +346,7 @@ namespace Microsoft.Dafny {
if (allowed.Contains(MatchingValueOption.Empty)) {
return true;
} else {
- errorReporter("Attribute " + nm + " requires one argument");
+ reporter("Attribute " + nm + " requires one argument");
return false;
}
} else if (args.Count == 1) {
@@ -356,16 +366,25 @@ namespace Microsoft.Dafny {
value = arg;
return true;
} else {
- errorReporter("Attribute " + nm + " expects an argument in one of the following categories: " + String.Join(", ", allowed));
+ reporter("Attribute " + nm + " expects an argument in one of the following categories: " + String.Join(", ", allowed));
return false;
}
} else {
- errorReporter("Attribute " + nm + " cannot have more than one argument");
+ reporter("Attribute " + nm + " cannot have more than one argument");
return false;
}
}
}
+ internal static class AttributesExtensions {
+ public static IEnumerable<Attributes> AsEnumerable(this Attributes attr) {
+ while (attr != null) {
+ yield return attr;
+ attr = attr.Prev;
+ }
+ }
+ }
+
// ------------------------------------------------------------------------------------------------------
public abstract class Type {
@@ -412,16 +431,33 @@ namespace Microsoft.Dafny {
var pt = type as TypeProxy;
if (pt != null && pt.T != null) {
type = pt.T;
- } else {
+ continue;
+ }
var syn = type.AsTypeSynonym;
if (syn != null) {
var udt = (UserDefinedType)type; // correctness of cast follows from the AsTypeSynonym != null test.
// Instantiate with the actual type arguments
type = syn.RhsWithArgument(udt.TypeArgs);
+ continue;
+ }
+ if (DafnyOptions.O.IronDafny && type is UserDefinedType) {
+ var rc = ((UserDefinedType)type).ResolvedClass;
+ if (rc != null) {
+ while (rc.ClonedFrom != null || rc.ExclusiveRefinement != null) {
+ if (rc.ClonedFrom != null) {
+ rc = (TopLevelDecl)rc.ClonedFrom;
} else {
- return type;
+ Contract.Assert(rc.ExclusiveRefinement != null);
+ rc = rc.ExclusiveRefinement;
+ }
+ }
+ }
+ if (rc is TypeSynonymDecl) {
+ type = ((TypeSynonymDecl)rc).Rhs;
+ continue;
}
}
+ return type;
}
}
@@ -490,6 +526,27 @@ namespace Microsoft.Dafny {
}
}
+ public bool HasFinitePossibleValues {
+ get {
+ if (IsBoolType || IsCharType || IsRefType) {
+ return true;
+ }
+ var st = AsSetType;
+ if (st != null && st.Arg.HasFinitePossibleValues) {
+ return true;
+ }
+ var mt = AsMapType;
+ if (mt != null && mt.Domain.HasFinitePossibleValues) {
+ return true;
+ }
+ var dt = AsDatatype;
+ if (dt != null && dt.HasFinitePossibleValues) {
+ return true;
+ }
+ return false;
+ }
+ }
+
public CollectionType AsCollectionType { get { return NormalizeExpand() as CollectionType; } }
public SetType AsSetType { get { return NormalizeExpand() as SetType; } }
public MultiSetType AsMultiSetType { get { return NormalizeExpand() as MultiSetType; } }
@@ -535,6 +592,12 @@ namespace Microsoft.Dafny {
return t != null && !t.Finite;
}
}
+ public bool IsISetType {
+ get {
+ var t = NormalizeExpand() as SetType;
+ return t != null && !t.Finite;
+ }
+ }
public NewtypeDecl AsNewtype {
get {
var udt = NormalizeExpand() as UserDefinedType;
@@ -633,7 +696,7 @@ namespace Microsoft.Dafny {
/// </summary>
public bool IsOrdered {
get {
- return !IsTypeParameter && !IsCoDatatype && !IsArrowType && !IsIMapType;
+ return !IsTypeParameter && !IsCoDatatype && !IsArrowType && !IsIMapType && !IsISetType;
}
}
@@ -859,17 +922,25 @@ namespace Microsoft.Dafny {
}
public class SetType : CollectionType {
- public SetType(Type arg) : base(arg) {
+ private bool finite;
+
+ public bool Finite {
+ get { return finite; }
+ set { finite = value; }
}
- public override string CollectionTypeName { get { return "set"; } }
+
+ public SetType(bool finite, Type arg) : base(arg) {
+ this.finite = finite;
+ }
+ public override string CollectionTypeName { get { return finite ? "set" : "iset"; } }
[Pure]
public override bool Equals(Type that) {
var t = that.NormalizeExpand() as SetType;
- return t != null && Arg.Equals(t.Arg);
+ return t != null && Finite == t.Finite && Arg.Equals(t.Arg);
}
public override bool PossiblyEquals_W(Type that) {
var t = that as SetType;
- return t != null && Arg.PossiblyEquals(t.Arg);
+ return t != null && Finite == t.Finite && Arg.PossiblyEquals(t.Arg);
}
}
@@ -988,7 +1059,7 @@ namespace Microsoft.Dafny {
public virtual string FullCompileName {
get {
if (ResolvedClass != null && !ResolvedClass.Module.IsDefaultModule) {
- return ResolvedClass.Module.CompileName + ".@" + CompileName;
+ return ResolvedClass.Module.CompileName + ".@" + ResolvedClass.CompileName;
} else {
return CompileName;
}
@@ -997,7 +1068,11 @@ namespace Microsoft.Dafny {
public string FullCompanionCompileName {
get {
Contract.Requires(ResolvedClass is TraitDecl);
- var s = ResolvedClass.Module.IsDefaultModule ? "" : ResolvedClass.Module.CompileName + ".";
+ var m = ResolvedClass.Module;
+ while (DafnyOptions.O.IronDafny && m.ClonedFrom != null) {
+ m = m.ClonedFrom;
+ }
+ var s = m.IsDefaultModule ? "" : m.CompileName + ".";
return s + "@_Companion_" + CompileName;
}
}
@@ -1312,20 +1387,22 @@ namespace Microsoft.Dafny {
/// <summary>
/// This proxy stands for:
- /// set(Arg) or multiset(Arg) or seq(Arg) or map(Arg, anyRange) or imap(Arg, anyRange)
+ /// set(Arg) or iset(Arg) or multiset(Arg) or seq(Arg) or map(Arg, anyRange) or imap(Arg, anyRange)
/// </summary>
public class CollectionTypeProxy : RestrictedTypeProxy {
public readonly Type Arg;
public readonly bool AllowIMap;
+ public readonly bool AllowISet;
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(Arg != null);
}
- public CollectionTypeProxy(Type arg, bool allowIMap) {
+ public CollectionTypeProxy(Type arg, bool allowIMap, bool allowISet) {
Contract.Requires(arg != null);
Arg = arg;
AllowIMap = allowIMap;
+ AllowISet = allowISet;
}
public override int OrderID {
get {
@@ -1338,6 +1415,7 @@ namespace Microsoft.Dafny {
/// This proxy can stand for any numeric type.
/// In addition, if AllowSeq, then it can stand for a seq.
/// In addition, if AllowSetVarieties, it can stand for a set or multiset.
+ /// In addition, if AllowISet, then it can stand for a iset
/// </summary>
public class OperationTypeProxy : RestrictedTypeProxy {
public readonly bool AllowInts;
@@ -1345,13 +1423,14 @@ namespace Microsoft.Dafny {
public readonly bool AllowChar;
public readonly bool AllowSeq;
public readonly bool AllowSetVarieties;
+ public readonly bool AllowISet;
public bool JustInts {
- get { return AllowInts && !AllowReals && !AllowChar && !AllowSeq && !AllowSetVarieties; }
+ get { return AllowInts && !AllowReals && !AllowChar && !AllowSeq && !AllowSetVarieties && !AllowISet; }
}
public bool JustReals {
- get { return !AllowInts && AllowReals && !AllowChar && !AllowSeq && !AllowSetVarieties; }
+ get { return !AllowInts && AllowReals && !AllowChar && !AllowSeq && !AllowSetVarieties && !AllowISet; }
}
- public OperationTypeProxy(bool allowInts, bool allowReals, bool allowChar, bool allowSeq, bool allowSetVarieties) {
+ public OperationTypeProxy(bool allowInts, bool allowReals, bool allowChar, bool allowSeq, bool allowSetVarieties, bool allowISet) {
Contract.Requires(allowInts || allowReals || allowChar || allowSeq || allowSetVarieties); // don't allow unsatisfiable constraint
Contract.Requires(!(!allowInts && !allowReals && allowChar && !allowSeq && !allowSetVarieties)); // to constrain to just char, don't use a proxy
AllowInts = allowInts;
@@ -1359,6 +1438,7 @@ namespace Microsoft.Dafny {
AllowChar = allowChar;
AllowSeq = allowSeq;
AllowSetVarieties = allowSetVarieties;
+ AllowISet = allowISet;
}
public override int OrderID {
get {
@@ -1451,22 +1531,41 @@ namespace Microsoft.Dafny {
IToken INamedRegion.BodyEndTok { get { return BodyEndTok; } }
string INamedRegion.Name { get { return Name; } }
string compileName;
+ private readonly Declaration clonedFrom;
+
public virtual string CompileName {
get {
if (compileName == null) {
- compileName = NonglobalVariable.CompilerizeName(Name);
+ object externValue = "";
+ string errorMessage = "";
+ bool isExternal = Attributes.ContainsMatchingValue(this.Attributes, "extern", ref externValue,
+ new Attributes.MatchingValueOption[] { Attributes.MatchingValueOption.String },
+ err => errorMessage = err);
+ if (isExternal) {
+ compileName = (string)externValue;
+ }
+ else {
+ compileName = NonglobalVariable.CompilerizeName(Name);
+ }
}
return compileName;
}
}
public Attributes Attributes; // readonly, except during class merging in the refinement transformations
- public Declaration(IToken tok, string name, Attributes attributes) {
+ public Declaration(IToken tok, string name, Attributes attributes, Declaration clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
this.tok = tok;
this.Name = name;
this.Attributes = attributes;
+ this.clonedFrom = clonedFrom;
+ }
+
+ public Declaration ClonedFrom {
+ get {
+ return this.clonedFrom;
+ }
}
[Pure]
@@ -1478,12 +1577,10 @@ namespace Microsoft.Dafny {
internal FreshIdGenerator IdGenerator = new FreshIdGenerator();
}
- public class OpaqueType_AsParameter : TypeParameter
- {
+ public class OpaqueType_AsParameter : TypeParameter {
public readonly List<TypeParameter> TypeArgs;
public OpaqueType_AsParameter(IToken tok, string name, EqualitySupportValue equalitySupport, List<TypeParameter> typeArgs)
- : base(tok, name, equalitySupport)
- {
+ : base(tok, name, equalitySupport) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(typeArgs != null);
@@ -1507,11 +1604,6 @@ namespace Microsoft.Dafny {
set {
Contract.Requires(Parent == null); // set it only once
Contract.Requires(value != null);
- // BUGBUG: The following line is a workaround to tell the verifier that 'value' is not of an Immutable type.
- // A proper solution would be to be able to express that in the program (in a specification or attribute) or
- // to be able to declare 'parent' as [PeerOrImmutable].
- Contract.Requires(value is TopLevelDecl || value is Function || value is Method || value is DatatypeCtor || value is QuantifierExpr);
- //modifies parent;
parent = value;
}
}
@@ -1531,8 +1623,8 @@ namespace Microsoft.Dafny {
}
public int PositionalIndex; // which type parameter this is (ie. in C<S, T, U>, S is 0, T is 1 and U is 2).
- public TypeParameter(IToken tok, string name, EqualitySupportValue equalitySupport = EqualitySupportValue.Unspecified)
- : base(tok, name, null) {
+ public TypeParameter(IToken tok, string name, EqualitySupportValue equalitySupport = EqualitySupportValue.Unspecified, Declaration clonedFrom = null)
+ : base(tok, name, null, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
EqualitySupport = equalitySupport;
@@ -1570,6 +1662,8 @@ namespace Microsoft.Dafny {
public class LiteralModuleDecl : ModuleDecl
{
public readonly ModuleDefinition ModuleDef;
+ public ModuleSignature DefaultExport; // the default export of the module. fill in by the resolver.
+
public LiteralModuleDecl(ModuleDefinition module, ModuleDefinition parent)
: base(module.tok, module.Name, parent, false) {
ModuleDef = module;
@@ -1588,6 +1682,7 @@ namespace Microsoft.Dafny {
}
public override object Dereference() { return Signature.ModuleDef; }
}
+
// Represents "module name as path [ = compilePath];", where name is a identifier and path is a possibly qualified name.
public class ModuleFacadeDecl : ModuleDecl
{
@@ -1606,8 +1701,41 @@ namespace Microsoft.Dafny {
public override object Dereference() { return this; }
}
- public class ModuleSignature {
+ // Represents the exports of a module.
+ public class ModuleExportDecl : ModuleDecl
+ {
+ public bool IsDefault;
+ public List<ExportSignature> Exports; // list of TopLevelDecl that are included in the export
+ public List<string> Extends; // list of exports that are extended
+ public readonly List<ModuleExportDecl> ExtendDecls = new List<ModuleExportDecl>(); // fill in by the resolver
+
+ public ModuleExportDecl(IToken tok, ModuleDefinition parent, bool isDefault,
+ List<ExportSignature> exports, List<string> extends)
+ : base(tok, tok.val, parent, false) {
+ IsDefault = isDefault;
+ Exports = exports;
+ Extends = extends;
+ }
+
+ public override object Dereference() { return this; }
+ }
+ public class ExportSignature
+ {
+ public bool IncludeBody;
+ public IToken Id;
+ public string Name;
+ public Declaration Decl; // fill in by the resolver
+
+ public ExportSignature(IToken id, bool includeBody) {
+ Id = id;
+ Name = id.val;
+ IncludeBody = includeBody;
+ }
+ }
+
+ public class ModuleSignature {
+ private ModuleDefinition exclusiveRefinement = null;
public readonly Dictionary<string, TopLevelDecl> TopLevels = new Dictionary<string, TopLevelDecl>();
public readonly Dictionary<string, Tuple<DatatypeCtor, bool>> Ctors = new Dictionary<string, Tuple<DatatypeCtor, bool>>();
public readonly Dictionary<string, MemberDecl> StaticMembers = new Dictionary<string, MemberDecl>();
@@ -1615,7 +1743,7 @@ namespace Microsoft.Dafny {
// it is abstract). Otherwise, it points to that definition.
public ModuleSignature CompileSignature = null; // This is the version of the signature that should be used at compile time.
public ModuleSignature Refines = null;
- public bool IsGhost = false;
+ public bool IsAbstract = false;
public ModuleSignature() {}
public bool FindSubmodule(string name, out ModuleSignature pp) {
@@ -1628,6 +1756,25 @@ namespace Microsoft.Dafny {
return false;
}
}
+
+ public ModuleDefinition ExclusiveRefinement {
+ get {
+ if (null == exclusiveRefinement) {
+ return ModuleDef == null ? null : ModuleDef.ExclusiveRefinement;
+ } else {
+ return exclusiveRefinement;
+ }
+ }
+
+ set {
+ if (null == ExclusiveRefinement) {
+ exclusiveRefinement = null;
+ } else {
+ throw new InvalidOperationException("An exclusive refinement relationship cannot be amended.");
+ }
+ }
+ }
+
}
public class ModuleDefinition : INamedRegion
@@ -1643,22 +1790,79 @@ namespace Microsoft.Dafny {
public readonly Attributes Attributes;
public readonly List<IToken> RefinementBaseName; // null if no refinement base
public ModuleDecl RefinementBaseRoot; // filled in early during resolution, corresponds to RefinementBaseName[0]
- public ModuleDefinition RefinementBase; // filled in during resolution (null if no refinement base)
+
public List<Include> Includes;
public readonly List<TopLevelDecl> TopLevelDecls = new List<TopLevelDecl>(); // filled in by the parser; readonly after that
public readonly Graph<ICallable> CallGraph = new Graph<ICallable>(); // filled in during resolution
public int Height; // height in the topological sorting of modules; filled in during resolution
public readonly bool IsAbstract;
+ public readonly bool IsExclusiveRefinement;
public readonly bool IsFacade; // True iff this module represents a module facade (that is, an abstract interface)
private readonly bool IsBuiltinName; // true if this is something like _System that shouldn't have it's name mangled.
+
+ private ModuleDefinition exclusiveRefinement;
+
+ public ModuleDefinition ExclusiveRefinement {
+ get { return exclusiveRefinement; }
+ set {
+ if (null == exclusiveRefinement) {
+ if (!value.IsExclusiveRefinement) {
+ throw new ArgumentException(
+ string.Format("Exclusive refinement of {0} with 'new' module {0} is disallowed.",
+ Name,
+ value.Name));
+ }
+ // todo: validate state of `value`.
+ exclusiveRefinement = value;
+ } else {
+ throw new InvalidOperationException(string.Format("Exclusive refinement of {0} has already been established {1}; cannot reestabilish as {2}.", Name, exclusiveRefinement.Name, value.Name));
+ }
+ }
+ }
+
+ public int ExclusiveRefinementCount { get; set; }
+
+ private ModuleSignature refinementBaseSig; // module signature of the refinementBase.
+ public ModuleSignature RefinementBaseSig {
+ get {
+ return refinementBaseSig;
+ }
+
+ set {
+ // the refinementBase member may only be changed once.
+ if (null != refinementBaseSig) {
+ throw new InvalidOperationException(string.Format("This module ({0}) already has a refinement base ({1}).", Name, refinementBase.Name));
+ }
+ refinementBaseSig = value;
+ }
+ }
+
+ private ModuleDefinition refinementBase; // filled in during resolution via RefinementBase property (null if no refinement base yet or at all).
+
+ public ModuleDefinition RefinementBase {
+ get {
+ return refinementBase;
+ }
+
+ set {
+ // the refinementBase member may only be changed once.
+ if (null != refinementBase) {
+ throw new InvalidOperationException(string.Format("This module ({0}) already has a refinement base ({1}).", Name, refinementBase.Name));
+ }
+ refinementBase = value;
+ }
+ }
+
+ public ModuleDefinition ClonedFrom { get; set; }
+
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(cce.NonNullElements(TopLevelDecls));
Contract.Invariant(CallGraph != null);
}
- public ModuleDefinition(IToken tok, string name, bool isAbstract, bool isFacade, List<IToken> refinementBase, ModuleDefinition parent, Attributes attributes, bool isBuiltinName)
+ public ModuleDefinition(IToken tok, string name, bool isAbstract, bool isFacade, bool isExclusiveRefinement, List<IToken> refinementBase, ModuleDefinition parent, Attributes attributes, bool isBuiltinName, Parser parser = null)
{
Contract.Requires(tok != null);
Contract.Requires(name != null);
@@ -1669,10 +1873,19 @@ namespace Microsoft.Dafny {
RefinementBaseName = refinementBase;
IsAbstract = isAbstract;
IsFacade = isFacade;
+ IsExclusiveRefinement = isExclusiveRefinement;
RefinementBaseRoot = null;
- RefinementBase = null;
+ this.refinementBase = null;
Includes = new List<Include>();
IsBuiltinName = isBuiltinName;
+
+ if (isExclusiveRefinement && !DafnyOptions.O.IronDafny) {
+ parser.errors.SynErr(
+ tok.filename,
+ tok.line,
+ tok.col,
+ "The exclusively keyword is experimental and only available when IronDafny features are enabled (/ironDafny).");
+ }
}
public virtual bool IsDefaultModule {
get {
@@ -1683,15 +1896,34 @@ namespace Microsoft.Dafny {
public string CompileName {
get {
if (compileName == null) {
- if (IsBuiltinName)
- compileName = Name;
- else
- compileName = "_" + Height.ToString() + "_" + NonglobalVariable.CompilerizeName(Name);
+ object externValue = "";
+ string errorMessage = "";
+ bool isExternal = Attributes.ContainsMatchingValue(this.Attributes, "extern", ref externValue,
+ new Attributes.MatchingValueOption[] { Attributes.MatchingValueOption.String },
+ err => errorMessage = err);
+ if (isExternal) {
+ compileName = (string)externValue;
+ } else {
+ if (IsBuiltinName)
+ compileName = Name;
+ else
+ compileName = "_" + Height.ToString() + "_" + NonglobalVariable.CompilerizeName(Name);
+ }
}
return compileName;
}
}
+ public string RefinementCompileName {
+ get {
+ if (ExclusiveRefinement != null) {
+ return this.ExclusiveRefinement.RefinementCompileName;
+ } else {
+ return this.CompileName;
+ }
+ }
+ }
+
/// <summary>
/// Determines if "a" and "b" are in the same strongly connected component of the call graph, that is,
/// if "a" and "b" are mutually recursive.
@@ -1809,7 +2041,9 @@ namespace Microsoft.Dafny {
}
public class DefaultModuleDecl : ModuleDefinition {
- public DefaultModuleDecl() : base(Token.NoToken, "_module", false, false, null, null, null, true) {
+ public DefaultModuleDecl()
+ : base(Token.NoToken, "_module", false, false, /*isExclusiveRefinement:*/ false, null, null, null, true)
+ {
}
public override bool IsDefaultModule {
get {
@@ -1827,13 +2061,14 @@ namespace Microsoft.Dafny {
Contract.Invariant(cce.NonNullElements(TypeArgs));
}
- public TopLevelDecl(IToken tok, string name, ModuleDefinition module, List<TypeParameter> typeArgs, Attributes attributes)
- : base(tok, name, attributes) {
+ public TopLevelDecl(IToken tok, string name, ModuleDefinition module, List<TypeParameter> typeArgs, Attributes attributes, Declaration clonedFrom = null)
+ : base(tok, name, attributes, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(cce.NonNullElements(typeArgs));
Module = module;
TypeArgs = typeArgs;
+ ExclusiveRefinement = null;
}
public string FullName {
@@ -1846,6 +2081,13 @@ namespace Microsoft.Dafny {
return Module.CompileName + "." + CompileName;
}
}
+
+ public string FullSanitizedRefinementName {
+ get {
+ return Module.RefinementCompileName + "." + CompileName;
+ }
+ }
+
public string FullNameInContext(ModuleDefinition context) {
if (Module == context) {
return Name;
@@ -1855,9 +2097,14 @@ namespace Microsoft.Dafny {
}
public string FullCompileName {
get {
- return Module.CompileName + ".@" + CompileName;
+ if (!Module.IsDefaultModule) {
+ return Module.CompileName + ".@" + CompileName;
+ } else {
+ return CompileName;
+ }
}
}
+ public TopLevelDecl ExclusiveRefinement { get; set; }
}
public class TraitDecl : ClassDecl
@@ -1865,8 +2112,8 @@ namespace Microsoft.Dafny {
public override string WhatKind { get { return "trait"; } }
public bool IsParent { set; get; }
public TraitDecl(IToken tok, string name, ModuleDefinition module,
- List<TypeParameter> typeArgs, [Captured] List<MemberDecl> members, Attributes attributes)
- : base(tok, name, module, typeArgs, members, attributes, null) { }
+ List<TypeParameter> typeArgs, [Captured] List<MemberDecl> members, Attributes attributes, TraitDecl clonedFrom = null)
+ : base(tok, name, module, typeArgs, members, attributes, null, clonedFrom) { }
}
public class ClassDecl : TopLevelDecl {
@@ -1884,8 +2131,8 @@ namespace Microsoft.Dafny {
}
public ClassDecl(IToken tok, string name, ModuleDefinition module,
- List<TypeParameter> typeArgs, [Captured] List<MemberDecl> members, Attributes attributes, List<Type> traits)
- : base(tok, name, module, typeArgs, attributes) {
+ List<TypeParameter> typeArgs, [Captured] List<MemberDecl> members, Attributes attributes, List<Type> traits, ClassDecl clonedFrom = null)
+ : base(tok, name, module, typeArgs, attributes, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(module != null);
@@ -1899,11 +2146,17 @@ namespace Microsoft.Dafny {
return false;
}
}
+
+ public new ClassDecl ClonedFrom {
+ get {
+ return (ClassDecl)base.ClonedFrom;
+ }
+ }
}
public class DefaultClassDecl : ClassDecl {
- public DefaultClassDecl(ModuleDefinition module, [Captured] List<MemberDecl> members)
- : base(Token.NoToken, "_default", module, new List<TypeParameter>(), members, null, null) {
+ public DefaultClassDecl(ModuleDefinition module, [Captured] List<MemberDecl> members, DefaultClassDecl clonedFrom = null)
+ : base(Token.NoToken, "_default", module, new List<TypeParameter>(), members, null, null, clonedFrom) {
Contract.Requires(module != null);
Contract.Requires(cce.NonNullElements(members));
}
@@ -1936,9 +2189,9 @@ namespace Microsoft.Dafny {
public readonly Function Requires;
public readonly Function Reads;
- public ArrowTypeDecl(List<TypeParameter> tps, Function req, Function reads, ModuleDefinition module, Attributes attributes)
+ public ArrowTypeDecl(List<TypeParameter> tps, Function req, Function reads, ModuleDefinition module, Attributes attributes, ArrowTypeDecl clonedFrom)
: base(Token.NoToken, ArrowType.ArrowTypeName(tps.Count - 1), module, tps,
- new List<MemberDecl> { req, reads }, attributes, null) {
+ new List<MemberDecl> { req, reads }, attributes, null, clonedFrom) {
Contract.Requires(tps != null && 1 <= tps.Count);
Contract.Requires(req != null);
Contract.Requires(reads != null);
@@ -1961,8 +2214,8 @@ namespace Microsoft.Dafny {
}
public DatatypeDecl(IToken tok, string name, ModuleDefinition module, List<TypeParameter> typeArgs,
- [Captured] List<DatatypeCtor> ctors, Attributes attributes)
- : base(tok, name, module, typeArgs, attributes) {
+ [Captured] List<DatatypeCtor> ctors, Attributes attributes, DatatypeDecl clonedFrom = null)
+ : base(tok, name, module, typeArgs, attributes, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(module != null);
@@ -1976,6 +2229,12 @@ namespace Microsoft.Dafny {
return (TypeArgs.Count == 0 && Ctors.TrueForAll(ctr => ctr.Formals.Count == 0));
}
}
+
+ public new DatatypeDecl ClonedFrom {
+ get {
+ return (DatatypeDecl)base.ClonedFrom;
+ }
+ }
}
public class IndDatatypeDecl : DatatypeDecl
@@ -1988,8 +2247,8 @@ namespace Microsoft.Dafny {
public ES EqualitySupport = ES.NotYetComputed;
public IndDatatypeDecl(IToken tok, string name, ModuleDefinition module, List<TypeParameter> typeArgs,
- [Captured] List<DatatypeCtor> ctors, Attributes attributes)
- : base(tok, name, module, typeArgs, ctors, attributes) {
+ [Captured] List<DatatypeCtor> ctors, Attributes attributes, IndDatatypeDecl clonedFrom = null)
+ : base(tok, name, module, typeArgs, ctors, attributes, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(module != null);
@@ -1997,6 +2256,12 @@ namespace Microsoft.Dafny {
Contract.Requires(cce.NonNullElements(ctors));
Contract.Requires(1 <= ctors.Count);
}
+
+ public new IndDatatypeDecl ClonedFrom {
+ get {
+ return (IndDatatypeDecl)base.ClonedFrom;
+ }
+ }
}
public class TupleTypeDecl : IndDatatypeDecl
@@ -2052,8 +2317,8 @@ namespace Microsoft.Dafny {
public CoDatatypeDecl SscRepr; // filled in during resolution
public CoDatatypeDecl(IToken tok, string name, ModuleDefinition module, List<TypeParameter> typeArgs,
- [Captured] List<DatatypeCtor> ctors, Attributes attributes)
- : base(tok, name, module, typeArgs, ctors, attributes) {
+ [Captured] List<DatatypeCtor> ctors, Attributes attributes, CoDatatypeDecl clonedFrom = null)
+ : base(tok, name, module, typeArgs, ctors, attributes, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(module != null);
@@ -2080,8 +2345,8 @@ namespace Microsoft.Dafny {
public SpecialField QueryField; // filled in during resolution
public List<DatatypeDestructor> Destructors = new List<DatatypeDestructor>(); // contents filled in during resolution; includes both implicit (not mentionable in source) and explicit destructors
- public DatatypeCtor(IToken tok, string name, [Captured] List<Formal> formals, Attributes attributes)
- : base(tok, name, attributes) {
+ public DatatypeCtor(IToken tok, string name, [Captured] List<Formal> formals, Attributes attributes, DatatypeCtor clonedFrom = null)
+ : base(tok, name, attributes, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(cce.NonNullElements(formals));
@@ -2110,6 +2375,7 @@ namespace Microsoft.Dafny {
bool MustReverify { get; }
string FullSanitizedName { get; }
bool AllowsNontermination { get; }
+ bool ContainsQuantifier { get; set; }
}
/// <summary>
/// An ICallable is a Function, Method, IteratorDecl, or RedirectingTypeDecl.
@@ -2117,6 +2383,7 @@ namespace Microsoft.Dafny {
public interface ICallable : ICodeContext
{
IToken Tok { get; }
+ string WhatKind { get; }
string NameRelativeToModule { get; }
Specification<Expression> Decreases { get; }
/// <summary>
@@ -2126,8 +2393,10 @@ namespace Microsoft.Dafny {
/// </summary>
bool InferredDecreases { get; set; }
}
+
public class DontUseICallable : ICallable
{
+ public string WhatKind { get { throw new cce.UnreachableException(); } }
public bool IsGhost { get { throw new cce.UnreachableException(); } }
public List<TypeParameter> TypeArgs { get { throw new cce.UnreachableException(); } }
public List<Formal> Ins { get { throw new cce.UnreachableException(); } }
@@ -2142,6 +2411,10 @@ namespace Microsoft.Dafny {
get { throw new cce.UnreachableException(); }
set { throw new cce.UnreachableException(); }
}
+ public bool ContainsQuantifier {
+ get { throw new cce.UnreachableException(); }
+ set { throw new cce.UnreachableException(); }
+ }
}
/// <summary>
/// An IMethodCodeContext is a Method or IteratorDecl.
@@ -2170,6 +2443,11 @@ namespace Microsoft.Dafny {
bool ICodeContext.MustReverify { get { Contract.Assume(false, "should not be called on NoContext"); throw new cce.UnreachableException(); } }
public string FullSanitizedName { get { Contract.Assume(false, "should not be called on NoContext"); throw new cce.UnreachableException(); } }
public bool AllowsNontermination { get { Contract.Assume(false, "should not be called on NoContext"); throw new cce.UnreachableException(); } }
+ public bool ContainsQuantifier {
+ get { Contract.Assume(false, "should not be called on NoContext"); throw new cce.UnreachableException(); }
+ set { Contract.Assume(false, "should not be called on NoContext"); throw new cce.UnreachableException(); }
+ }
+
}
public class IteratorDecl : ClassDecl, IMethodCodeContext
@@ -2197,6 +2475,8 @@ namespace Microsoft.Dafny {
public Predicate Member_Valid; // created during registration phase of resolution; its specification is filled in during resolution
public Method Member_MoveNext; // created during registration phase of resolution; its specification is filled in during resolution
public readonly LocalVariable YieldCountVariable;
+ bool containsQuantifier;
+
public IteratorDecl(IToken tok, string name, ModuleDefinition module, List<TypeParameter> typeArgs,
List<Formal> ins, List<Formal> outs,
Specification<FrameExpression> reads, Specification<FrameExpression> mod, Specification<Expression> decreases,
@@ -2241,6 +2521,48 @@ namespace Microsoft.Dafny {
}
/// <summary>
+ /// Returns the non-null expressions of this declaration proper (that is, do not include the expressions of substatements).
+ /// Does not include the generated class members.
+ /// </summary>
+ public virtual IEnumerable<Expression> SubExpressions {
+ get {
+ foreach (var e in Attributes.SubExpressions(Attributes)) {
+ yield return e;
+ }
+ foreach (var e in Attributes.SubExpressions(Reads.Attributes)) {
+ yield return e;
+ }
+ foreach (var e in Reads.Expressions) {
+ yield return e.E;
+ }
+ foreach (var e in Attributes.SubExpressions(Modifies.Attributes)) {
+ yield return e;
+ }
+ foreach (var e in Modifies.Expressions) {
+ yield return e.E;
+ }
+ foreach (var e in Attributes.SubExpressions(Decreases.Attributes)) {
+ yield return e;
+ }
+ foreach (var e in Decreases.Expressions) {
+ yield return e;
+ }
+ foreach (var e in Requires) {
+ yield return e.E;
+ }
+ foreach (var e in Ensures) {
+ yield return e.E;
+ }
+ foreach (var e in YieldRequires) {
+ yield return e.E;
+ }
+ foreach (var e in YieldEnsures) {
+ yield return e.E;
+ }
+ }
+ }
+
+ /// <summary>
/// This Dafny type exists only for the purpose of giving the yield-count variable a type, so
/// that the type can be recognized during translation of Dafny into Boogie. It represents
/// an integer component in a "decreases" clause whose order is (\lambda x,y :: x GREATER y),
@@ -2270,6 +2592,10 @@ namespace Microsoft.Dafny {
set { _inferredDecr = value; }
get { return _inferredDecr; }
}
+ bool ICodeContext.ContainsQuantifier {
+ set { containsQuantifier = value; }
+ get { return containsQuantifier; }
+ }
ModuleDefinition ICodeContext.EnclosingModule { get { return this.Module; } }
bool ICodeContext.MustReverify { get { return false; } }
public bool AllowsNontermination {
@@ -2290,8 +2616,8 @@ namespace Microsoft.Dafny {
public readonly bool IsGhost;
public TopLevelDecl EnclosingClass; // filled in during resolution
public MemberDecl RefinementBase; // filled in during the pre-resolution refinement transformation; null if the member is new here
- public MemberDecl(IToken tok, string name, bool hasStaticKeyword, bool isGhost, Attributes attributes)
- : base(tok, name, attributes) {
+ public MemberDecl(IToken tok, string name, bool hasStaticKeyword, bool isGhost, Attributes attributes, Declaration clonedFrom = null)
+ : base(tok, name, attributes, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
HasStaticKeyword = hasStaticKeyword;
@@ -2316,6 +2642,14 @@ namespace Microsoft.Dafny {
return EnclosingClass.FullSanitizedName + "." + CompileName;
}
}
+ public string FullSanitizedRefinementName {
+ get {
+ Contract.Requires(EnclosingClass != null);
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ return EnclosingClass.FullSanitizedRefinementName + "." + CompileName;
+ }
+ }
public string FullNameInContext(ModuleDefinition context) {
Contract.Requires(EnclosingClass != null);
Contract.Ensures(Contract.Result<string>() != null);
@@ -2339,6 +2673,11 @@ namespace Microsoft.Dafny {
return EnclosingClass.FullCompileName + ".@" + CompileName;
}
}
+ public virtual IEnumerable<Expression> SubExpressions {
+ get {
+ yield break;
+ }
+ }
}
public class Field : MemberDecl {
@@ -2428,8 +2767,8 @@ namespace Microsoft.Dafny {
Contract.Invariant(TheType != null && Name == TheType.Name);
}
- public OpaqueTypeDecl(IToken tok, string name, ModuleDefinition module, TypeParameter.EqualitySupportValue equalitySupport, List<TypeParameter> typeArgs, Attributes attributes)
- : base(tok, name, module, typeArgs, attributes) {
+ public OpaqueTypeDecl(IToken tok, string name, ModuleDefinition module, TypeParameter.EqualitySupportValue equalitySupport, List<TypeParameter> typeArgs, Attributes attributes, Declaration clonedFrom = null)
+ : base(tok, name, module, typeArgs, attributes, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(module != null);
@@ -2470,16 +2809,16 @@ namespace Microsoft.Dafny {
public readonly BoundVar Var; // can be null (if non-null, then object.ReferenceEquals(Var.Type, BaseType))
public readonly Expression Constraint; // is null iff Var is
public NativeType NativeType; // non-null for fixed-size representations (otherwise, use BigIntegers for integers)
- public NewtypeDecl(IToken tok, string name, ModuleDefinition module, Type baseType, Attributes attributes)
- : base(tok, name, module, new List<TypeParameter>(), attributes) {
+ public NewtypeDecl(IToken tok, string name, ModuleDefinition module, Type baseType, Attributes attributes, NewtypeDecl clonedFrom = null)
+ : base(tok, name, module, new List<TypeParameter>(), attributes, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(module != null);
Contract.Requires(baseType != null);
BaseType = baseType;
}
- public NewtypeDecl(IToken tok, string name, ModuleDefinition module, BoundVar bv, Expression constraint, Attributes attributes)
- : base(tok, name, module, new List<TypeParameter>(), attributes) {
+ public NewtypeDecl(IToken tok, string name, ModuleDefinition module, BoundVar bv, Expression constraint, Attributes attributes, NewtypeDecl clonedFrom = null)
+ : base(tok, name, module, new List<TypeParameter>(), attributes, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(module != null);
@@ -2510,14 +2849,23 @@ namespace Microsoft.Dafny {
get { throw new cce.UnreachableException(); } // see comment above about ICallable.Decreases
set { throw new cce.UnreachableException(); } // see comment above about ICallable.Decreases
}
+ bool ICodeContext.ContainsQuantifier {
+ get { throw new cce.UnreachableException(); }
+ set { throw new cce.UnreachableException(); }
+ }
+ public new NewtypeDecl ClonedFrom {
+ get {
+ return (NewtypeDecl)base.ClonedFrom;
+ }
+ }
}
public class TypeSynonymDecl : TopLevelDecl, RedirectingTypeDecl
{
public override string WhatKind { get { return "type synonym"; } }
public readonly Type Rhs;
- public TypeSynonymDecl(IToken tok, string name, List<TypeParameter> typeArgs, ModuleDefinition module, Type rhs, Attributes attributes)
- : base(tok, name, module, typeArgs, attributes) {
+ public TypeSynonymDecl(IToken tok, string name, List<TypeParameter> typeArgs, ModuleDefinition module, Type rhs, Attributes attributes, TypeSynonymDecl clonedFrom = null)
+ : base(tok, name, module, typeArgs, attributes, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(typeArgs != null);
@@ -2562,6 +2910,10 @@ namespace Microsoft.Dafny {
get { throw new cce.UnreachableException(); } // see comment above about ICallable.Decreases
set { throw new cce.UnreachableException(); } // see comment above about ICallable.Decreases
}
+ bool ICodeContext.ContainsQuantifier {
+ get { throw new cce.UnreachableException(); }
+ set { throw new cce.UnreachableException(); }
+ }
}
[ContractClass(typeof(IVariableContracts))]
@@ -2823,19 +3175,7 @@ namespace Microsoft.Dafny {
}
}
- /// <summary>
- /// A "ThisSurrogate" is used during translation time to make the treatment of the receiver more similar to
- /// the treatment of other in-parameters.
- /// </summary>
- public class ThisSurrogate : Formal
- {
- public ThisSurrogate(IToken tok, Type type)
- : base(tok, "this", type, true, false) {
- Contract.Requires(tok != null);
- Contract.Requires(type != null);
- }
- }
-
+ [DebuggerDisplay("Bound<{name}>")]
public class BoundVar : NonglobalVariable {
public override bool IsMutable {
get {
@@ -2855,6 +3195,7 @@ namespace Microsoft.Dafny {
public override string WhatKind { get { return "function"; } }
public readonly bool IsProtected;
public bool IsRecursive; // filled in during resolution
+ public bool IsFueled; // filled in during resolution if anyone tries to adjust this function's fuel
public readonly List<TypeParameter> TypeArgs;
public readonly List<Formal> Formals;
public readonly Type ResultType;
@@ -2867,6 +3208,31 @@ namespace Microsoft.Dafny {
public readonly IToken SignatureEllipsis;
public bool IsBuiltin;
public Function OverriddenFunction;
+ public bool containsQuantifier;
+ public bool ContainsQuantifier {
+ set { containsQuantifier = value; }
+ get { return containsQuantifier; }
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ foreach (var e in Req) {
+ yield return e;
+ }
+ foreach (var e in Reads) {
+ yield return e.E;
+ }
+ foreach (var e in Ens) {
+ yield return e;
+ }
+ foreach (var e in Decreases.Expressions) {
+ yield return e;
+ }
+ if (Body != null) {
+ yield return Body;
+ }
+ }
+ }
public Type Type {
get {
@@ -2880,7 +3246,7 @@ namespace Microsoft.Dafny {
return Contract.Exists(Decreases.Expressions, e => e is WildcardExpr);
}
}
-
+
/// <summary>
/// The "AllCalls" field is used for non-FixpointPredicate, non-PrefixPredicate functions only (so its value should not be relied upon for FixpointPredicate and PrefixPredicate functions).
/// It records all function calls made by the Function, including calls made in the body as well as in the specification.
@@ -2912,8 +3278,8 @@ namespace Microsoft.Dafny {
public Function(IToken tok, string name, bool hasStaticKeyword, bool isProtected, bool isGhost,
List<TypeParameter> typeArgs, List<Formal> formals, Type resultType,
List<Expression> req, List<FrameExpression> reads, List<Expression> ens, Specification<Expression> decreases,
- Expression body, Attributes attributes, IToken signatureEllipsis)
- : base(tok, name, hasStaticKeyword, isGhost, attributes) {
+ Expression body, Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null)
+ : base(tok, name, hasStaticKeyword, isGhost, attributes, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
@@ -2925,6 +3291,7 @@ namespace Microsoft.Dafny {
Contract.Requires(cce.NonNullElements(ens));
Contract.Requires(decreases != null);
this.IsProtected = isProtected;
+ this.IsFueled = false; // Defaults to false. Only set to true if someone mentions this function in a fuel annotation
this.TypeArgs = typeArgs;
this.Formals = formals;
this.ResultType = resultType;
@@ -2934,7 +3301,27 @@ namespace Microsoft.Dafny {
this.Decreases = decreases;
this.Body = body;
this.SignatureEllipsis = signatureEllipsis;
+
+ if (attributes != null) {
+ List<Expression> args = Attributes.FindExpressions(attributes, "fuel");
+ if (args != null) {
+ if (args.Count == 1) {
+ LiteralExpr literal = args[0] as LiteralExpr;
+ if (literal != null && literal.Value is BigInteger) {
+ this.IsFueled = true;
+ }
+ } else if (args.Count == 2) {
+ LiteralExpr literalLow = args[0] as LiteralExpr;
+ LiteralExpr literalHigh = args[1] as LiteralExpr;
+
+ if (literalLow != null && literalLow.Value is BigInteger && literalHigh != null && literalHigh.Value is BigInteger) {
+ this.IsFueled = true;
+ }
+ }
+ }
+ }
}
+
bool ICodeContext.IsGhost { get { return this.IsGhost; } }
List<TypeParameter> ICodeContext.TypeArgs { get { return this.TypeArgs; } }
@@ -2949,6 +3336,9 @@ namespace Microsoft.Dafny {
}
ModuleDefinition ICodeContext.EnclosingModule { get { return this.EnclosingClass.Module; } }
bool ICodeContext.MustReverify { get { return false; } }
+
+ [Pure]
+ public bool IsFuelAware() { return IsRecursive || IsFueled; }
}
public class Predicate : Function
@@ -2964,8 +3354,8 @@ namespace Microsoft.Dafny {
public Predicate(IToken tok, string name, bool hasStaticKeyword, bool isProtected, bool isGhost,
List<TypeParameter> typeArgs, List<Formal> formals,
List<Expression> req, List<FrameExpression> reads, List<Expression> ens, Specification<Expression> decreases,
- Expression body, BodyOriginKind bodyOrigin, Attributes attributes, IToken signatureEllipsis)
- : base(tok, name, hasStaticKeyword, isProtected, isGhost, typeArgs, formals, new BoolType(), req, reads, ens, decreases, body, attributes, signatureEllipsis) {
+ Expression body, BodyOriginKind bodyOrigin, Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null)
+ : base(tok, name, hasStaticKeyword, isProtected, isGhost, typeArgs, formals, new BoolType(), req, reads, ens, decreases, body, attributes, signatureEllipsis, clonedFrom) {
Contract.Requires(bodyOrigin == Predicate.BodyOriginKind.OriginalOrInherited || body != null);
BodyOrigin = bodyOrigin;
}
@@ -2983,7 +3373,7 @@ namespace Microsoft.Dafny {
List<TypeParameter> typeArgs, Formal k, List<Formal> formals,
List<Expression> req, List<FrameExpression> reads, List<Expression> ens, Specification<Expression> decreases,
Expression body, Attributes attributes, FixpointPredicate fixpointPred)
- : base(tok, name, hasStaticKeyword, isProtected, true, typeArgs, formals, new BoolType(), req, reads, ens, decreases, body, attributes, null) {
+ : base(tok, name, hasStaticKeyword, isProtected, true, typeArgs, formals, new BoolType(), req, reads, ens, decreases, body, attributes, null, null) {
Contract.Requires(k != null);
Contract.Requires(fixpointPred != null);
Contract.Requires(formals != null && 1 <= formals.Count && formals[0] == k);
@@ -3000,9 +3390,9 @@ namespace Microsoft.Dafny {
public FixpointPredicate(IToken tok, string name, bool hasStaticKeyword, bool isProtected,
List<TypeParameter> typeArgs, List<Formal> formals,
List<Expression> req, List<FrameExpression> reads, List<Expression> ens,
- Expression body, Attributes attributes, IToken signatureEllipsis)
+ Expression body, Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null)
: base(tok, name, hasStaticKeyword, isProtected, true, typeArgs, formals, new BoolType(),
- req, reads, ens, new Specification<Expression>(new List<Expression>(), null), body, attributes, signatureEllipsis) {
+ req, reads, ens, new Specification<Expression>(new List<Expression>(), null), body, attributes, signatureEllipsis, clonedFrom) {
}
/// <summary>
@@ -3023,7 +3413,7 @@ namespace Microsoft.Dafny {
prefixPredCall.TypeArgumentSubstitutions = new Dictionary<TypeParameter, Type>();
var old_to_new = new Dictionary<TypeParameter, TypeParameter>();
for (int i = 0; i < this.TypeArgs.Count; i++) {
- old_to_new[this.TypeArgs[i]] = this.PrefixPredicate.TypeArgs[i];
+ old_to_new[this.TypeArgs[i]] = this.PrefixPredicate.TypeArgs[i];
}
foreach (var p in fexp.TypeArgumentSubstitutions) {
prefixPredCall.TypeArgumentSubstitutions[old_to_new[p.Key]] = p.Value;
@@ -3041,9 +3431,9 @@ namespace Microsoft.Dafny {
public InductivePredicate(IToken tok, string name, bool hasStaticKeyword, bool isProtected,
List<TypeParameter> typeArgs, List<Formal> formals,
List<Expression> req, List<FrameExpression> reads, List<Expression> ens,
- Expression body, Attributes attributes, IToken signatureEllipsis)
+ Expression body, Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null)
: base(tok, name, hasStaticKeyword, isProtected, typeArgs, formals,
- req, reads, ens, body, attributes, signatureEllipsis) {
+ req, reads, ens, body, attributes, signatureEllipsis, clonedFrom) {
}
}
@@ -3053,9 +3443,9 @@ namespace Microsoft.Dafny {
public CoPredicate(IToken tok, string name, bool hasStaticKeyword, bool isProtected,
List<TypeParameter> typeArgs, List<Formal> formals,
List<Expression> req, List<FrameExpression> reads, List<Expression> ens,
- Expression body, Attributes attributes, IToken signatureEllipsis)
+ Expression body, Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null)
: base(tok, name, hasStaticKeyword, isProtected, typeArgs, formals,
- req, reads, ens, body, attributes, signatureEllipsis) {
+ req, reads, ens, body, attributes, signatureEllipsis, clonedFrom) {
}
}
@@ -3077,6 +3467,25 @@ namespace Microsoft.Dafny {
public bool IsTailRecursive; // filled in during resolution
public readonly ISet<IVariable> AssignedAssumptionVariables = new HashSet<IVariable>();
public Method OverriddenMethod;
+ public bool containsQuantifier;
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ foreach (var e in Req) {
+ yield return e.E;
+ }
+ foreach (var e in Mod.Expressions) {
+ yield return e.E;
+ }
+ foreach (var e in Ens) {
+ yield return e.E;
+ }
+ foreach (var e in Decreases.Expressions) {
+ yield return e;
+ }
+ }
+ }
+
[ContractInvariantMethod]
void ObjectInvariant() {
@@ -3097,8 +3506,8 @@ namespace Microsoft.Dafny {
[Captured] List<MaybeFreeExpression> ens,
[Captured] Specification<Expression> decreases,
[Captured] BlockStmt body,
- Attributes attributes, IToken signatureEllipsis)
- : base(tok, name, hasStaticKeyword, isGhost, attributes) {
+ Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null)
+ : base(tok, name, hasStaticKeyword, isGhost, attributes, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(cce.NonNullElements(typeArgs));
@@ -3133,6 +3542,11 @@ namespace Microsoft.Dafny {
set { _inferredDecr = value; }
get { return _inferredDecr; }
}
+ bool ICodeContext.ContainsQuantifier {
+ set { containsQuantifier = value; }
+ get { return containsQuantifier; }
+ }
+
ModuleDefinition ICodeContext.EnclosingModule {
get {
Contract.Assert(this.EnclosingClass != null); // this getter is supposed to be called only after signature-resolution is complete
@@ -3170,8 +3584,8 @@ namespace Microsoft.Dafny {
[Captured] List<MaybeFreeExpression> ens,
[Captured] Specification<Expression> decreases,
[Captured] BlockStmt body,
- Attributes attributes, IToken signatureEllipsis)
- : base(tok, name, hasStaticKeyword, true, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis) {
+ Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null)
+ : base(tok, name, hasStaticKeyword, true, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis, clonedFrom) {
}
}
@@ -3185,8 +3599,8 @@ namespace Microsoft.Dafny {
[Captured] List<MaybeFreeExpression> ens,
[Captured] Specification<Expression> decreases,
[Captured] BlockStmt body,
- Attributes attributes, IToken signatureEllipsis)
- : base(tok, name, false, false, typeArgs, ins, new List<Formal>(), req, mod, ens, decreases, body, attributes, signatureEllipsis) {
+ Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null)
+ : base(tok, name, false, false, typeArgs, ins, new List<Formal>(), req, mod, ens, decreases, body, attributes, signatureEllipsis, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(cce.NonNullElements(typeArgs));
@@ -3237,8 +3651,8 @@ namespace Microsoft.Dafny {
List<MaybeFreeExpression> ens,
Specification<Expression> decreases,
BlockStmt body,
- Attributes attributes, IToken signatureEllipsis)
- : base(tok, name, hasStaticKeyword, true, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis) {
+ Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom)
+ : base(tok, name, hasStaticKeyword, true, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(cce.NonNullElements(typeArgs));
@@ -3263,8 +3677,8 @@ namespace Microsoft.Dafny {
List<MaybeFreeExpression> ens,
Specification<Expression> decreases,
BlockStmt body,
- Attributes attributes, IToken signatureEllipsis)
- : base(tok, name, hasStaticKeyword, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis) {
+ Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null)
+ : base(tok, name, hasStaticKeyword, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(cce.NonNullElements(typeArgs));
@@ -3289,8 +3703,8 @@ namespace Microsoft.Dafny {
List<MaybeFreeExpression> ens,
Specification<Expression> decreases,
BlockStmt body,
- Attributes attributes, IToken signatureEllipsis)
- : base(tok, name, hasStaticKeyword, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis) {
+ Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null)
+ : base(tok, name, hasStaticKeyword, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis, clonedFrom) {
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires(cce.NonNullElements(typeArgs));
@@ -3614,7 +4028,7 @@ namespace Microsoft.Dafny {
Contract.Invariant(Expr != null);
}
- public ExprRhs(Expression expr, Attributes attrs = null)
+ public ExprRhs(Expression expr, Attributes attrs = null) // TODO: these 'attrs' apparently aren't handled correctly in the Cloner, and perhaps not in various visitors either (for example, CheckIsCompilable should not go into attributes)
: base(expr.tok, attrs)
{
Contract.Requires(expr != null);
@@ -3775,6 +4189,28 @@ namespace Microsoft.Dafny {
}
}
+ public class LetStmt : Statement
+ {
+ public readonly List<CasePattern> LHSs;
+ public readonly List<Expression> RHSs;
+
+ public LetStmt(IToken tok, IToken endTok, List<CasePattern> lhss, List<Expression> rhss)
+ : base(tok, endTok) {
+ LHSs = lhss;
+ RHSs = rhss;
+ }
+
+ public IEnumerable<BoundVar> BoundVars {
+ get {
+ foreach (var lhs in LHSs) {
+ foreach (var bv in lhs.Vars) {
+ yield return bv;
+ }
+ }
+ }
+ }
+ }
+
/// <summary>
/// Common superclass of UpdateStmt and AssignSuchThatStmt.
/// </summary>
@@ -3804,6 +4240,9 @@ namespace Microsoft.Dafny {
public override bool IsFinite {
get { return false; }
}
+ public override int Preference() {
+ return 0;
+ }
}
/// <summary>
@@ -3914,17 +4353,41 @@ namespace Microsoft.Dafny {
/// </summary>
public static bool LhsIsToGhost(Expression lhs) {
Contract.Requires(lhs != null);
+ return LhsIsToGhost_Which(lhs) == NonGhostKind.IsGhost;
+ }
+ public enum NonGhostKind { IsGhost, Variable, Field, ArrayElement }
+ public static string NonGhostKind_To_String(NonGhostKind gk) {
+ Contract.Requires(gk != NonGhostKind.IsGhost);
+ switch (gk) {
+ case NonGhostKind.Variable: return "non-ghost variable";
+ case NonGhostKind.Field: return "non-ghost field";
+ case NonGhostKind.ArrayElement: return "array element";
+ default:
+ Contract.Assume(false); // unexpected NonGhostKind
+ throw new cce.UnreachableException(); // please compiler
+ }
+ }
+ /// <summary>
+ /// This method assumes "lhs" has been successfully resolved.
+ /// </summary>
+ public static NonGhostKind LhsIsToGhost_Which(Expression lhs) {
+ Contract.Requires(lhs != null);
lhs = lhs.Resolved;
if (lhs is IdentifierExpr) {
var x = (IdentifierExpr)lhs;
- return x.Var.IsGhost;
+ if (!x.Var.IsGhost) {
+ return NonGhostKind.Variable;
+ }
} else if (lhs is MemberSelectExpr) {
var x = (MemberSelectExpr)lhs;
- return x.Member.IsGhost;
+ if (!x.Member.IsGhost) {
+ return NonGhostKind.Field;
+ }
} else {
// LHS denotes an array element, which is always non-ghost
- return false;
+ return NonGhostKind.ArrayElement;
}
+ return NonGhostKind.IsGhost;
}
}
@@ -4095,20 +4558,24 @@ namespace Microsoft.Dafny {
}
public class IfStmt : Statement {
+ public readonly bool IsExistentialGuard;
public readonly Expression Guard;
public readonly BlockStmt Thn;
public readonly Statement Els;
[ContractInvariantMethod]
void ObjectInvariant() {
+ Contract.Invariant(!IsExistentialGuard || (Guard is ExistsExpr && ((ExistsExpr)Guard).Range == null));
Contract.Invariant(Thn != null);
Contract.Invariant(Els == null || Els is BlockStmt || Els is IfStmt || Els is SkeletonStatement);
}
- public IfStmt(IToken tok, IToken endTok, Expression guard, BlockStmt thn, Statement els)
+ public IfStmt(IToken tok, IToken endTok, bool isExistentialGuard, Expression guard, BlockStmt thn, Statement els)
: base(tok, endTok) {
Contract.Requires(tok != null);
Contract.Requires(endTok != null);
+ Contract.Requires(!isExistentialGuard || (guard is ExistsExpr && ((ExistsExpr)guard).Range == null));
Contract.Requires(thn != null);
Contract.Requires(els == null || els is BlockStmt || els is IfStmt || els is SkeletonStatement);
+ this.IsExistentialGuard = isExistentialGuard;
this.Guard = guard;
this.Thn = thn;
this.Els = els;
@@ -4134,20 +4601,24 @@ namespace Microsoft.Dafny {
public class GuardedAlternative
{
public readonly IToken Tok;
+ public readonly bool IsExistentialGuard;
public readonly Expression Guard;
public readonly List<Statement> Body;
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(Tok != null);
Contract.Invariant(Guard != null);
+ Contract.Invariant(!IsExistentialGuard || (Guard is ExistsExpr && ((ExistsExpr)Guard).Range == null));
Contract.Invariant(Body != null);
}
- public GuardedAlternative(IToken tok, Expression guard, List<Statement> body)
+ public GuardedAlternative(IToken tok, bool isExistentialGuard, Expression guard, List<Statement> body)
{
Contract.Requires(tok != null);
Contract.Requires(guard != null);
+ Contract.Requires(!isExistentialGuard || (guard is ExistsExpr && ((ExistsExpr)guard).Range == null));
Contract.Requires(body != null);
this.Tok = tok;
+ this.IsExistentialGuard = isExistentialGuard;
this.Guard = guard;
this.Body = body;
}
@@ -4327,6 +4798,7 @@ namespace Microsoft.Dafny {
public readonly Expression Range;
public readonly List<MaybeFreeExpression> Ens;
public readonly Statement Body;
+ public List<Expression> ForallExpressions; // fill in by rewriter.
public List<ComprehensionExpr.BoundedPool> Bounds; // initialized and filled in by resolver
// invariant: if successfully resolved, Bounds.Count == BoundVars.Count;
@@ -4541,7 +5013,12 @@ namespace Microsoft.Dafny {
public override Expression StepExpr(Expression line0, Expression line1)
{
- return new BinaryExpr(line0.tok, Op, line0, line1);
+ if (Op == BinaryExpr.Opcode.Exp) {
+ // The order of operands is reversed so that it can be turned into implication during resolution
+ return new BinaryExpr(line0.tok, Op, line1, line0);
+ } else {
+ return new BinaryExpr(line0.tok, Op, line0, line1);
+ }
}
public override string ToString()
@@ -4621,7 +5098,7 @@ namespace Microsoft.Dafny {
Contract.Invariant(StepOps.Count == Hints.Count);
}
- public CalcStmt(IToken tok, IToken endTok, CalcOp op, List<Expression> lines, List<BlockStmt> hints, List<CalcOp> stepOps, CalcOp resultOp)
+ public CalcStmt(IToken tok, IToken endTok, CalcOp op, List<Expression> lines, List<BlockStmt> hints, List<CalcOp> stepOps, CalcOp resultOp, Attributes attrs)
: base(tok, endTok)
{
Contract.Requires(tok != null);
@@ -4646,6 +5123,7 @@ namespace Microsoft.Dafny {
}
this.Steps = new List<Expression>();
this.Result = null;
+ this.Attributes = attrs;
}
public override IEnumerable<Statement> SubStatements
@@ -4660,14 +5138,30 @@ namespace Microsoft.Dafny {
{
get {
foreach (var e in base.SubExpressions) { yield return e; }
- foreach (var l in Lines) {
- yield return l;
+ foreach (var e in Attributes.SubExpressions(Attributes)) { yield return e; }
+
+ for (int i = 0; i < Lines.Count - 1; i++) { // note, we skip the duplicated line at the end
+ yield return Lines[i];
}
- foreach (var e in Steps) {
- yield return e;
+ foreach (var calcop in AllCalcOps) {
+ var o3 = calcop as TernaryCalcOp;
+ if (o3 != null) {
+ yield return o3.Index;
+ }
}
- if (Result != null) {
- yield return Result;
+ }
+ }
+
+ IEnumerable<CalcOp> AllCalcOps {
+ get {
+ if (Op != null) {
+ yield return Op;
+ }
+ foreach (var stepop in StepOps) {
+ yield return stepop;
+ }
+ if (ResultOp != null) {
+ yield return ResultOp;
}
}
}
@@ -4710,8 +5204,8 @@ namespace Microsoft.Dafny {
Contract.Invariant(cce.NonNullElements(MissingCases));
}
- public readonly Expression Source;
- public readonly List<MatchCaseStmt> Cases;
+ private Expression source;
+ private List<MatchCaseStmt> cases;
public readonly List<DatatypeCtor> MissingCases = new List<DatatypeCtor>(); // filled in during resolution
public readonly bool UsesOptionalBraces;
@@ -4721,14 +5215,31 @@ namespace Microsoft.Dafny {
Contract.Requires(endTok != null);
Contract.Requires(source != null);
Contract.Requires(cce.NonNullElements(cases));
- this.Source = source;
- this.Cases = cases;
+ this.source = source;
+ this.cases = cases;
this.UsesOptionalBraces = usesOptionalBraces;
}
+ public Expression Source {
+ get { return source; }
+ }
+
+ public List<MatchCaseStmt> Cases {
+ get { return cases; }
+ }
+
+ // should only be used in desugar in resolve to change the cases of the matchexpr
+ public void UpdateSource(Expression source) {
+ this.source = source;
+ }
+
+ public void UpdateCases(List<MatchCaseStmt> cases) {
+ this.cases = cases;
+ }
+
public override IEnumerable<Statement> SubStatements {
get {
- foreach (var kase in Cases) {
+ foreach (var kase in cases) {
foreach (var s in kase.Body) {
yield return s;
}
@@ -4745,7 +5256,7 @@ namespace Microsoft.Dafny {
public class MatchCaseStmt : MatchCase
{
- public readonly List<Statement> Body;
+ private List<Statement> body;
[ContractInvariantMethod]
void ObjectInvariant() {
@@ -4759,7 +5270,25 @@ namespace Microsoft.Dafny {
Contract.Requires(id != null);
Contract.Requires(cce.NonNullElements(arguments));
Contract.Requires(cce.NonNullElements(body));
- this.Body = body;
+ this.body = body;
+ }
+
+ public MatchCaseStmt(IToken tok, string id, [Captured] List<CasePattern> cps, [Captured] List<Statement> body)
+ : base(tok, id, cps) {
+ Contract.Requires(tok != null);
+ Contract.Requires(id != null);
+ Contract.Requires(cce.NonNullElements(cps));
+ Contract.Requires(cce.NonNullElements(body));
+ this.body = body;
+ }
+
+ public List<Statement> Body {
+ get { return body; }
+ }
+
+ // should only be called by resolve to reset the body of the MatchCaseExpr
+ public void UpdateBody(List<Statement> body) {
+ this.body = body;
}
}
@@ -4896,7 +5425,7 @@ namespace Microsoft.Dafny {
}
// ------------------------------------------------------------------------------------------------------
-
+ [DebuggerDisplay("{Printer.ExprToString(this)}")]
public abstract class Expression
{
public readonly IToken tok;
@@ -4961,6 +5490,10 @@ namespace Microsoft.Dafny {
get { yield break; }
}
+ public virtual bool IsImplicit {
+ get { return false; }
+ }
+
public static IEnumerable<Expression> Conjuncts(Expression expr) {
Contract.Requires(expr != null);
Contract.Requires(expr.Type.IsBoolType);
@@ -4995,7 +5528,9 @@ namespace Microsoft.Dafny {
public static Expression CreateAdd(Expression e0, Expression e1) {
Contract.Requires(e0 != null);
Contract.Requires(e1 != null);
- Contract.Requires((e0.Type.IsIntegerType && e1.Type.IsIntegerType) || (e0.Type.IsRealType && e1.Type.IsRealType));
+ Contract.Requires(
+ (e0.Type.IsNumericBased(Type.NumericPersuation.Int) && e1.Type.IsNumericBased(Type.NumericPersuation.Int)) ||
+ (e0.Type.IsNumericBased(Type.NumericPersuation.Real) && e1.Type.IsNumericBased(Type.NumericPersuation.Real)));
Contract.Ensures(Contract.Result<Expression>() != null);
var s = new BinaryExpr(e0.tok, BinaryExpr.Opcode.Add, e0, e1);
s.ResolvedOp = BinaryExpr.ResolvedOpcode.Add; // resolve here
@@ -5003,6 +5538,7 @@ namespace Microsoft.Dafny {
return s;
}
+
/// <summary>
/// Create a resolved expression of the form "CVT(e0) - CVT(e1)", where "CVT" is either "int" (if
/// e0.Type is an integer-based numeric type) or "real" (if e0.Type is a real-based numeric type).
@@ -5016,20 +5552,32 @@ namespace Microsoft.Dafny {
Contract.Ensures(Contract.Result<Expression>() != null);
Type toType = e0.Type.IsNumericBased(Type.NumericPersuation.Int) ? (Type)Type.Int : Type.Real;
- e0 = new ConversionExpr(e0.tok, e0, toType);
- e0.Type = toType;
- e1 = new ConversionExpr(e1.tok, e1, toType);
- e1.Type = toType;
+ e0 = CastIfNeeded(e0, toType);
+ e1 = CastIfNeeded(e1, toType);
return CreateSubtract(e0, e1);
}
+ private static Expression CastIfNeeded(Expression expr, Type toType) {
+ if (!expr.Type.Equals(toType)) {
+ var cast = new ConversionExpr(expr.tok, expr, toType);
+ cast.Type = toType;
+ return cast;
+ } else {
+ return expr;
+ }
+ }
+
/// <summary>
/// Create a resolved expression of the form "e0 - e1"
/// </summary>
public static Expression CreateSubtract(Expression e0, Expression e1) {
Contract.Requires(e0 != null);
+ Contract.Requires(e0.Type != null);
Contract.Requires(e1 != null);
- Contract.Requires((e0.Type.IsIntegerType && e1.Type.IsIntegerType) || (e0.Type.IsRealType && e1.Type.IsRealType));
+ Contract.Requires(e1.Type != null);
+ Contract.Requires(
+ (e0.Type.IsNumericBased(Type.NumericPersuation.Int) && e1.Type.IsNumericBased(Type.NumericPersuation.Int)) ||
+ (e0.Type.IsNumericBased(Type.NumericPersuation.Real) && e1.Type.IsNumericBased(Type.NumericPersuation.Real)));
Contract.Ensures(Contract.Result<Expression>() != null);
var s = new BinaryExpr(e0.tok, BinaryExpr.Opcode.Sub, e0, e1);
s.ResolvedOp = BinaryExpr.ResolvedOpcode.Sub; // resolve here
@@ -5042,7 +5590,8 @@ namespace Microsoft.Dafny {
/// </summary>
public static Expression CreateIncrement(Expression e, int n) {
Contract.Requires(e != null);
- Contract.Requires(e.Type.IsIntegerType);
+ Contract.Requires(e.Type != null);
+ Contract.Requires(e.Type.IsNumericBased(Type.NumericPersuation.Int));
Contract.Requires(0 <= n);
Contract.Ensures(Contract.Result<Expression>() != null);
if (n == 0) {
@@ -5057,7 +5606,7 @@ namespace Microsoft.Dafny {
/// </summary>
public static Expression CreateDecrement(Expression e, int n) {
Contract.Requires(e != null);
- Contract.Requires(e.Type.IsIntegerType);
+ Contract.Requires(e.Type.IsNumericBased(Type.NumericPersuation.Int));
Contract.Requires(0 <= n);
Contract.Ensures(Contract.Result<Expression>() != null);
if (n == 0) {
@@ -5116,7 +5665,7 @@ namespace Microsoft.Dafny {
public static Expression CreateLess(Expression e0, Expression e1) {
Contract.Requires(e0 != null);
Contract.Requires(e1 != null);
- Contract.Requires(e0.Type.IsIntegerType && e1.Type.IsIntegerType);
+ Contract.Requires(e0.Type.IsNumericBased(Type.NumericPersuation.Int) && e1.Type.IsNumericBased(Type.NumericPersuation.Int));
Contract.Ensures(Contract.Result<Expression>() != null);
var s = new BinaryExpr(e0.tok, BinaryExpr.Opcode.Lt, e0, e1);
s.ResolvedOp = BinaryExpr.ResolvedOpcode.Lt; // resolve here
@@ -5130,7 +5679,9 @@ namespace Microsoft.Dafny {
public static Expression CreateAtMost(Expression e0, Expression e1) {
Contract.Requires(e0 != null);
Contract.Requires(e1 != null);
- Contract.Requires((e0.Type.IsIntegerType && e1.Type.IsIntegerType) || (e0.Type.IsRealType && e1.Type.IsRealType));
+ Contract.Requires(
+ (e0.Type.IsNumericBased(Type.NumericPersuation.Int) && e1.Type.IsNumericBased(Type.NumericPersuation.Int)) ||
+ (e0.Type.IsNumericBased(Type.NumericPersuation.Real) && e1.Type.IsNumericBased(Type.NumericPersuation.Real)));
Contract.Ensures(Contract.Result<Expression>() != null);
var s = new BinaryExpr(e0.tok, BinaryExpr.Opcode.Le, e0, e1);
s.ResolvedOp = BinaryExpr.ResolvedOpcode.Le; // resolve here
@@ -5337,19 +5888,21 @@ namespace Microsoft.Dafny {
public class StaticReceiverExpr : LiteralExpr
{
public readonly Type UnresolvedType;
+ private bool Implicit;
- public StaticReceiverExpr(IToken tok, Type t)
+ public StaticReceiverExpr(IToken tok, Type t, bool isImplicit)
: base(tok) {
Contract.Requires(tok != null);
Contract.Requires(t != null);
UnresolvedType = t;
+ Implicit = isImplicit;
}
/// <summary>
/// Constructs a resolved LiteralExpr representing the 'null' literal whose type is "cl"
/// parameterized by the type arguments of "cl" itself.
/// </summary>
- public StaticReceiverExpr(IToken tok, ClassDecl cl)
+ public StaticReceiverExpr(IToken tok, ClassDecl cl, bool isImplicit)
: base(tok)
{
Contract.Requires(tok != null);
@@ -5357,6 +5910,7 @@ namespace Microsoft.Dafny {
var typeArgs = cl.TypeArgs.ConvertAll(tp => (Type)new UserDefinedType(tp));
Type = new UserDefinedType(tok, cl.Name, cl, typeArgs);
UnresolvedType = Type;
+ Implicit = isImplicit;
}
/// <summary>
@@ -5373,7 +5927,7 @@ namespace Microsoft.Dafny {
/// a trait that in turn extends trait "W(g(Y))". If "t" denotes type "C(G)" and "cl" denotes "W",
/// then type of the StaticReceiverExpr will be "T(g(f(G)))".
/// </summary>
- public StaticReceiverExpr(IToken tok, UserDefinedType t, ClassDecl cl)
+ public StaticReceiverExpr(IToken tok, UserDefinedType t, ClassDecl cl, bool isImplicit)
: base(tok) {
Contract.Requires(tok != null);
Contract.Requires(t.ResolvedClass != null);
@@ -5387,6 +5941,11 @@ namespace Microsoft.Dafny {
}
Type = t;
UnresolvedType = Type;
+ Implicit = isImplicit;
+ }
+
+ public override bool IsImplicit {
+ get { return Implicit; }
}
}
@@ -5502,8 +6061,8 @@ namespace Microsoft.Dafny {
Contract.Invariant(cce.NonNullElements(Arguments));
Contract.Invariant(cce.NonNullElements(InferredTypeArgs));
Contract.Invariant(
- Ctor == null ||
- InferredTypeArgs.Count == Ctor.EnclosingDatatype.TypeArgs.Count);
+ Ctor == null ||
+ InferredTypeArgs.Count == Ctor.EnclosingDatatype.TypeArgs.Count);
}
public DatatypeValue(IToken tok, string datatypeName, string memberName, [Captured] List<Expression> arguments)
@@ -5543,6 +6102,10 @@ namespace Microsoft.Dafny {
: base(tok) {
Contract.Requires(tok != null);
}
+
+ public override bool IsImplicit {
+ get { return true; }
+ }
}
public class IdentifierExpr : Expression
@@ -5561,6 +6124,16 @@ namespace Microsoft.Dafny {
Contract.Requires(name != null);
Name = name;
}
+ /// <summary>
+ /// Constructs a resolved IdentifierExpr.
+ /// </summary>
+ public IdentifierExpr(IVariable v)
+ : base(v.Tok) {
+ Contract.Requires(v != null);
+ Name = v.Name;
+ Var = v;
+ Type = v.Type;
+ }
}
/// <summary>
@@ -5656,10 +6229,12 @@ namespace Microsoft.Dafny {
}
public class SetDisplayExpr : DisplayExpression {
- public SetDisplayExpr(IToken tok, List<Expression> elements)
+ public bool Finite;
+ public SetDisplayExpr(IToken tok, bool finite, List<Expression> elements)
: base(tok, elements) {
Contract.Requires(tok != null);
Contract.Requires(cce.NonNullElements(elements));
+ Finite = finite;
}
}
@@ -5762,7 +6337,6 @@ namespace Microsoft.Dafny {
}
}
-
public override IEnumerable<Expression> SubExpressions {
get { yield return Obj; }
}
@@ -5931,10 +6505,10 @@ namespace Microsoft.Dafny {
Function == null || TypeArgumentSubstitutions == null ||
Contract.ForAll(
Function.TypeArgs,
- a => TypeArgumentSubstitutions.ContainsKey(a)) &&
+ a => TypeArgumentSubstitutions.ContainsKey(a)) &&
Contract.ForAll(
TypeArgumentSubstitutions.Keys,
- a => Function.TypeArgs.Contains(a) || Function.EnclosingClass.TypeArgs.Contains(a)));
+ a => Function.TypeArgs.Contains(a) || Function.EnclosingClass.TypeArgs.Contains(a)));
}
public Function Function; // filled in by resolution
@@ -6338,14 +6912,17 @@ namespace Microsoft.Dafny {
Contract.Requires(e0 != null);
Contract.Requires(e1 != null);
this.Op = op;
- if (op == Opcode.Exp) {
- // The order of operands is reversed so that it can be turned into implication during resolution
- this.E0 = e1;
- this.E1 = e0;
- } else {
- this.E0 = e0;
- this.E1 = e1;
- }
+ this.E0 = e0;
+ this.E1 = e1;
+ }
+
+ /// <summary>
+ /// Returns a resolved binary expression
+ /// </summary>
+ public BinaryExpr(Boogie.IToken tok, BinaryExpr.ResolvedOpcode rop, Expression e0, Expression e1)
+ : this(tok, BinaryExpr.ResolvedOp2SyntacticOp(rop), e0, e1) {
+ ResolvedOp = rop;
+ Type = Type.Bool;
}
public override IEnumerable<Expression> SubExpressions {
@@ -6391,7 +6968,7 @@ namespace Microsoft.Dafny {
public readonly Expression Body;
public readonly bool Exact; // Exact==true means a regular let expression; Exact==false means an assign-such-that expression
public readonly Attributes Attributes;
- public List<ComprehensionExpr.BoundedPool> Constraint_Bounds; // initialized and filled in by resolver; null for Exact=true and for a ghost statement
+ public List<ComprehensionExpr.BoundedPool> Constraint_Bounds; // initialized and filled in by resolver; null for Exact=true and for when expression is in a ghost context
// invariant Constraint_Bounds == null || Constraint_Bounds.Count == BoundVars.Count;
public List<IVariable> Constraint_MissingBounds; // filled in during resolution; remains "null" if Exact==true or if bounds can be found
// invariant Constraint_Bounds == null || Constraint_MissingBounds == null;
@@ -6478,15 +7055,87 @@ namespace Microsoft.Dafny {
Contract.Invariant(Term != null);
}
- public readonly Attributes Attributes;
+ public Attributes Attributes;
public abstract class BoundedPool {
public virtual bool IsFinite {
get { return true; } // most bounds are finite
}
+ public abstract int Preference(); // higher is better
+
+ public static BoundedPool GetBest(List<BoundedPool> bounds, bool onlyFiniteBounds) {
+ Contract.Requires(bounds != null);
+ bounds = CombineIntegerBounds(bounds);
+ BoundedPool best = null;
+ foreach (var bound in bounds) {
+ if (!onlyFiniteBounds || bound.IsFinite) {
+ if (best == null || bound.Preference() > best.Preference()) {
+ best = bound;
+ }
+ }
+ }
+ return best;
+ }
+ static List<BoundedPool> CombineIntegerBounds(List<BoundedPool> bounds) {
+ var lowerBounds = new List<IntBoundedPool>();
+ var upperBounds = new List<IntBoundedPool>();
+ var others = new List<BoundedPool>();
+ foreach (var b in bounds) {
+ var ib = b as IntBoundedPool;
+ if (ib != null && ib.UpperBound == null) {
+ lowerBounds.Add(ib);
+ } else if (ib != null && ib.LowerBound == null) {
+ upperBounds.Add(ib);
+ } else {
+ others.Add(b);
+ }
+ }
+ // pair up the bounds
+ var n = Math.Min(lowerBounds.Count, upperBounds.Count);
+ for (var i = 0; i < n; i++) {
+ others.Add(new IntBoundedPool(lowerBounds[i].LowerBound, upperBounds[i].UpperBound));
+ }
+ for (var i = n; i < lowerBounds.Count; i++) {
+ others.Add(lowerBounds[i]);
+ }
+ for (var i = n; i < upperBounds.Count; i++) {
+ others.Add(upperBounds[i]);
+ }
+ return others;
+ }
+ }
+ public class ExactBoundedPool : BoundedPool
+ {
+ public readonly Expression E;
+ public ExactBoundedPool(Expression e) {
+ Contract.Requires(e != null);
+ E = e;
+ }
+ public override int Preference() {
+ return 20; // the best of all bounds
+ }
}
public class BoolBoundedPool : BoundedPool
{
+ public override int Preference() {
+ return 5;
+ }
+ }
+ public class CharBoundedPool : BoundedPool
+ {
+ public override int Preference() {
+ return 4;
+ }
+ }
+ public class RefBoundedPool : BoundedPool
+ {
+ public Type Type;
+ public RefBoundedPool(Type t) {
+ Type = t;
+ }
+ public override int Preference() {
+ return 2;
+ }
}
public class IntBoundedPool : BoundedPool
{
@@ -6501,36 +7150,60 @@ namespace Microsoft.Dafny {
return LowerBound != null && UpperBound != null;
}
}
+ public override int Preference() {
+ return 1;
+ }
}
public class SetBoundedPool : BoundedPool
{
public readonly Expression Set;
public SetBoundedPool(Expression set) { Set = set; }
+ public override int Preference() {
+ return 10;
+ }
}
public class SubSetBoundedPool : BoundedPool
{
public readonly Expression UpperBound;
public SubSetBoundedPool(Expression set) { UpperBound = set; }
+ public override int Preference() {
+ return 1;
+ }
}
public class SuperSetBoundedPool : BoundedPool
{
public readonly Expression LowerBound;
public SuperSetBoundedPool(Expression set) { LowerBound = set; }
+ public override int Preference() {
+ return 0;
+ }
+ public override bool IsFinite {
+ get { return false; }
+ }
}
public class MapBoundedPool : BoundedPool
{
public readonly Expression Map;
public MapBoundedPool(Expression map) { Map = map; }
+ public override int Preference() {
+ return 10;
+ }
}
public class SeqBoundedPool : BoundedPool
{
public readonly Expression Seq;
public SeqBoundedPool(Expression seq) { Seq = seq; }
+ public override int Preference() {
+ return 10;
+ }
}
public class DatatypeBoundedPool : BoundedPool
{
public readonly DatatypeDecl Decl;
public DatatypeBoundedPool(DatatypeDecl d) { Decl = d; }
+ public override int Preference() {
+ return 5;
+ }
}
public List<BoundedPool> Bounds; // initialized and filled in by resolver
@@ -6538,6 +7211,24 @@ namespace Microsoft.Dafny {
public List<BoundVar> MissingBounds; // filled in during resolution; remains "null" if bounds can be found
// invariant Bounds == null || MissingBounds == null;
+ public List<BoundVar> UncompilableBoundVars() {
+ var bvs = new List<BoundVar>();
+ if (MissingBounds != null) {
+ bvs.AddRange(MissingBounds);
+ }
+ if (Bounds != null) {
+ Contract.Assert(Bounds.Count == BoundVars.Count);
+ for (int i = 0; i < Bounds.Count; i++) {
+ var bound = Bounds[i];
+ if (bound is RefBoundedPool) {
+ // yes, this is in principle a bound, but it's not one we'd like to compile
+ bvs.Add(BoundVars[i]);
+ }
+ }
+ }
+ return bvs;
+ }
+
public ComprehensionExpr(IToken tok, List<BoundVar> bvars, Expression range, Expression term, Attributes attrs)
: base(tok) {
Contract.Requires(tok != null);
@@ -6562,13 +7253,16 @@ namespace Microsoft.Dafny {
}
public abstract class QuantifierExpr : ComprehensionExpr, TypeParameter.ParentType {
+ private readonly int UniqueId;
public List<TypeParameter> TypeArgs;
private static int currentQuantId = -1;
- static int FreshQuantId()
- {
+
+ protected abstract BinaryExpr.ResolvedOpcode SplitResolvedOp { get; }
+
+ static int FreshQuantId() {
return System.Threading.Interlocked.Increment(ref currentQuantId);
}
- private readonly int UniqueId;
+
public string FullName {
get {
return "q$" + UniqueId;
@@ -6595,10 +7289,56 @@ namespace Microsoft.Dafny {
this.TypeArgs = tvars;
this.UniqueId = FreshQuantId();
}
- public abstract Expression LogicalBody();
- }
+ private Expression SplitQuantifierToExpression() {
+ Contract.Requires(SplitQuantifier != null && SplitQuantifier.Any());
+ Expression accumulator = SplitQuantifier[0];
+ for (int tid = 1; tid < SplitQuantifier.Count; tid++) {
+ accumulator = new BinaryExpr(Term.tok, SplitResolvedOp, accumulator, SplitQuantifier[tid]);
+ }
+ return accumulator;
+ }
+
+ private List<Expression> _SplitQuantifier;
+ public List<Expression> SplitQuantifier {
+ get {
+ return _SplitQuantifier;
+ }
+ set {
+ _SplitQuantifier = value;
+ SplitQuantifierExpression = SplitQuantifierToExpression();
+ }
+ }
+
+ internal Expression SplitQuantifierExpression { get; private set; }
+
+ public virtual Expression LogicalBody(bool bypassSplitQuantifier = false) {
+ // Don't call this on a quantifier with a Split clause: it's not a real quantifier. The only exception is the Compiler.
+ Contract.Requires(bypassSplitQuantifier || SplitQuantifier == null);
+ throw new cce.UnreachableException(); // This body is just here for the "Requires" clause
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ if (SplitQuantifier == null) {
+ foreach (var e in base.SubExpressions) {
+ yield return e;
+ }
+ } else {
+ foreach (var e in Attributes.SubExpressions(Attributes)) {
+ yield return e;
+ }
+ foreach (var e in SplitQuantifier) {
+ yield return e;
+ }
+ }
+ }
+ }
+ }
+
public class ForallExpr : QuantifierExpr {
+ protected override BinaryExpr.ResolvedOpcode SplitResolvedOp { get { return BinaryExpr.ResolvedOpcode.And; } }
+
public ForallExpr(IToken tok, List<BoundVar> bvars, Expression range, Expression term, Attributes attrs)
: this(tok, new List<TypeParameter>(), bvars, range, term, attrs) {
Contract.Requires(cce.NonNullElements(bvars));
@@ -6611,7 +7351,7 @@ namespace Microsoft.Dafny {
Contract.Requires(tok != null);
Contract.Requires(term != null);
}
- public override Expression LogicalBody() {
+ public override Expression LogicalBody(bool bypassSplitQuantifier = false) {
if (Range == null) {
return Term;
}
@@ -6623,6 +7363,8 @@ namespace Microsoft.Dafny {
}
public class ExistsExpr : QuantifierExpr {
+ protected override BinaryExpr.ResolvedOpcode SplitResolvedOp { get { return BinaryExpr.ResolvedOpcode.Or; } }
+
public ExistsExpr(IToken tok, List<BoundVar> bvars, Expression range, Expression term, Attributes attrs)
: this(tok, new List<TypeParameter>(), bvars, range, term, attrs) {
Contract.Requires(cce.NonNullElements(bvars));
@@ -6635,7 +7377,7 @@ namespace Microsoft.Dafny {
Contract.Requires(tok != null);
Contract.Requires(term != null);
}
- public override Expression LogicalBody() {
+ public override Expression LogicalBody(bool bypassSplitQuantifier = false) {
if (Range == null) {
return Term;
}
@@ -6648,9 +7390,10 @@ namespace Microsoft.Dafny {
public class SetComprehension : ComprehensionExpr
{
+ public readonly bool Finite;
public readonly bool TermIsImplicit;
- public SetComprehension(IToken tok, List<BoundVar> bvars, Expression range, Expression term, Attributes attrs)
+ public SetComprehension(IToken tok, bool finite, List<BoundVar> bvars, Expression range, Expression term, Attributes attrs)
: base(tok, bvars, range, term ?? new IdentifierExpr(tok, bvars[0].Name), attrs) {
Contract.Requires(tok != null);
Contract.Requires(cce.NonNullElements(bvars));
@@ -6658,6 +7401,7 @@ namespace Microsoft.Dafny {
Contract.Requires(range != null);
TermIsImplicit = term == null;
+ Finite = finite;
}
}
public class MapComprehension : ComprehensionExpr
@@ -6806,8 +7550,8 @@ namespace Microsoft.Dafny {
}
public class MatchExpr : Expression { // a MatchExpr is an "extended expression" and is only allowed in certain places
- public readonly Expression Source;
- public readonly List<MatchCaseExpr> Cases;
+ private Expression source;
+ private List<MatchCaseExpr> cases;
public readonly List<DatatypeCtor> MissingCases = new List<DatatypeCtor>(); // filled in during resolution
public readonly bool UsesOptionalBraces;
@@ -6823,15 +7567,32 @@ namespace Microsoft.Dafny {
Contract.Requires(tok != null);
Contract.Requires(source != null);
Contract.Requires(cce.NonNullElements(cases));
- this.Source = source;
- this.Cases = cases;
+ this.source = source;
+ this.cases = cases;
this.UsesOptionalBraces = usesOptionalBraces;
}
+ public Expression Source {
+ get { return source; }
+ }
+
+ public List<MatchCaseExpr> Cases {
+ get { return cases; }
+ }
+
+ // should only be used in desugar in resolve to change the source and cases of the matchexpr
+ public void UpdateSource(Expression source) {
+ this.source = source;
+ }
+
+ public void UpdateCases(List<MatchCaseExpr> cases) {
+ this.cases = cases;
+ }
+
public override IEnumerable<Expression> SubExpressions {
get {
yield return Source;
- foreach (var mc in Cases) {
+ foreach (var mc in cases) {
yield return mc.Body;
}
}
@@ -6913,12 +7674,13 @@ namespace Microsoft.Dafny {
public readonly IToken tok;
public readonly string Id;
public DatatypeCtor Ctor; // filled in by resolution
- public readonly List<BoundVar> Arguments;
+ public List<BoundVar> Arguments; // created by the resolver.
+ public List<CasePattern> CasePatterns; // generated from parsers. It should be converted to List<BoundVar> during resolver. Invariant: CasePatterns != null ==> Arguments == null
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(tok != null);
Contract.Invariant(Id != null);
- Contract.Invariant(cce.NonNullElements(Arguments));
+ Contract.Invariant(cce.NonNullElements(Arguments) || cce.NonNullElements(CasePatterns));
}
public MatchCase(IToken tok, string id, [Captured] List<BoundVar> arguments) {
@@ -6929,24 +7691,51 @@ namespace Microsoft.Dafny {
this.Id = id;
this.Arguments = arguments;
}
+
+ public MatchCase(IToken tok, string id, [Captured] List<CasePattern> cps) {
+ Contract.Requires(tok != null);
+ Contract.Requires(id != null);
+ Contract.Requires(cce.NonNullElements(cps));
+ this.tok = tok;
+ this.Id = id;
+ this.CasePatterns = cps;
+ }
}
public class MatchCaseExpr : MatchCase
{
- public readonly Expression Body;
+ private Expression body;
[ContractInvariantMethod]
void ObjectInvariant() {
- Contract.Invariant(Body != null);
+ Contract.Invariant(body != null);
}
public MatchCaseExpr(IToken tok, string id, [Captured] List<BoundVar> arguments, Expression body)
- : base(tok, id, arguments)
- {
+ : base(tok, id, arguments) {
Contract.Requires(tok != null);
Contract.Requires(id != null);
Contract.Requires(cce.NonNullElements(arguments));
Contract.Requires(body != null);
- this.Body = body;
+ this.body = body;
+ }
+
+ public MatchCaseExpr(IToken tok, string id, [Captured] List<CasePattern> cps, Expression body)
+ : base(tok, id, cps)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(id != null);
+ Contract.Requires(cce.NonNullElements(cps));
+ Contract.Requires(body != null);
+ this.body = body;
+ }
+
+ public Expression Body {
+ get { return body; }
+ }
+
+ // should only be called by resolve to reset the body of the MatchCaseExpr
+ public void UpdateBody(Expression body) {
+ this.body = body;
}
}
@@ -7122,6 +7911,36 @@ namespace Microsoft.Dafny {
}
}
+ public class DatatypeUpdateExpr : ConcreteSyntaxExpression
+ {
+ public readonly Expression Root;
+ public readonly List<Tuple<IToken, string, Expression>> Updates;
+ public DatatypeUpdateExpr(IToken tok, Expression root, List<Tuple<IToken, string, Expression>> updates)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(root != null);
+ Contract.Requires(updates != null);
+ Contract.Requires(updates.Count != 0);
+ Root = root;
+ Updates = updates;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ if (ResolvedExpression == null) {
+ yield return Root;
+ foreach (var update in Updates) {
+ yield return update.Item3;
+ }
+ } else {
+ foreach (var e in ResolvedExpression.SubExpressions) {
+ yield return e;
+ }
+ }
+ }
+ }
+ }
+
/// <summary>
/// An AutoGeneratedExpression is simply a wrapper around an expression. This expression tells the generation of hover text (in the Dafny IDE)
@@ -7348,6 +8167,48 @@ namespace Microsoft.Dafny {
public class BottomUpVisitor
{
+ public void Visit(IEnumerable<Expression> exprs) {
+ exprs.Iter(Visit);
+ }
+ public void Visit(IEnumerable<Statement> stmts) {
+ stmts.Iter(Visit);
+ }
+ public void Visit(MaybeFreeExpression expr) {
+ Visit(expr.E);
+ }
+ public void Visit(FrameExpression expr) {
+ Visit(expr.E);
+ }
+ public void Visit(IEnumerable<MaybeFreeExpression> exprs) {
+ exprs.Iter(Visit);
+ }
+ public void Visit(IEnumerable<FrameExpression> exprs) {
+ exprs.Iter(Visit);
+ }
+ public void Visit(ICallable decl) {
+ if (decl is Function) {
+ Visit((Function)decl);
+ } else if (decl is Method) {
+ Visit((Method)decl);
+ }
+ //TODO More?
+ }
+ public void Visit(Method method) {
+ Visit(method.Ens);
+ Visit(method.Req);
+ Visit(method.Mod.Expressions);
+ Visit(method.Decreases.Expressions);
+ if (method.Body != null) { Visit(method.Body); }
+ //TODO More?
+ }
+ public void Visit(Function function) {
+ Visit(function.Ens);
+ Visit(function.Req);
+ Visit(function.Reads);
+ Visit(function.Decreases.Expressions);
+ if (function.Body != null) { Visit(function.Body); }
+ //TODO More?
+ }
public void Visit(Expression expr) {
Contract.Requires(expr != null);
// recursively visit all subexpressions and all substatements
@@ -7397,6 +8258,48 @@ namespace Microsoft.Dafny {
stmt.SubStatements.Iter(s => Visit(s, st));
}
}
+ public void Visit(IEnumerable<Expression> exprs, State st) {
+ exprs.Iter(e => Visit(e, st));
+ }
+ public void Visit(IEnumerable<Statement> stmts, State st) {
+ stmts.Iter(e => Visit(e, st));
+ }
+ public void Visit(MaybeFreeExpression expr, State st) {
+ Visit(expr.E, st);
+ }
+ public void Visit(FrameExpression expr, State st) {
+ Visit(expr.E, st);
+ }
+ public void Visit(IEnumerable<MaybeFreeExpression> exprs, State st) {
+ exprs.Iter(e => Visit(e, st));
+ }
+ public void Visit(IEnumerable<FrameExpression> exprs, State st) {
+ exprs.Iter(e => Visit(e, st));
+ }
+ public void Visit(ICallable decl, State st) {
+ if (decl is Function) {
+ Visit((Function)decl, st);
+ } else if (decl is Method) {
+ Visit((Method)decl, st);
+ }
+ //TODO More?
+ }
+ public void Visit(Method method, State st) {
+ Visit(method.Ens, st);
+ Visit(method.Req, st);
+ Visit(method.Mod.Expressions, st);
+ Visit(method.Decreases.Expressions, st);
+ if (method.Body != null) { Visit(method.Body, st); }
+ //TODO More?
+ }
+ public void Visit(Function function, State st) {
+ Visit(function.Ens, st);
+ Visit(function.Req, st);
+ Visit(function.Reads, st);
+ Visit(function.Decreases.Expressions, st);
+ if (function.Body != null) { Visit(function.Body, st); }
+ //TODO More?
+ }
/// <summary>
/// Visit one expression proper. This method is invoked before it is invoked on the
/// sub-parts (sub-expressions and sub-statements). A return value of "true" says to
diff --git a/Source/Dafny/DafnyMain.cs b/Source/Dafny/DafnyMain.cs
index 012ca4df..fd2e00fb 100644
--- a/Source/Dafny/DafnyMain.cs
+++ b/Source/Dafny/DafnyMain.cs
@@ -12,7 +12,7 @@ using Bpl = Microsoft.Boogie;
namespace Microsoft.Dafny {
public class Main {
- private static void MaybePrintProgram(Program program, string filename)
+ private static void MaybePrintProgram(Program program, string filename, bool afterResolver)
{
if (filename != null) {
TextWriter tw;
@@ -22,14 +22,14 @@ namespace Microsoft.Dafny {
tw = new System.IO.StreamWriter(filename);
}
Printer pr = new Printer(tw, DafnyOptions.O.PrintMode);
- pr.PrintProgram(program);
+ pr.PrintProgram(program, afterResolver);
}
}
/// <summary>
/// Returns null on success, or an error string otherwise.
/// </summary>
- public static string ParseCheck(IList<string/*!*/>/*!*/ fileNames, string/*!*/ programName, out Program program)
+ public static string ParseCheck(IList<string/*!*/>/*!*/ fileNames, string/*!*/ programName, ErrorReporter reporter, out Program program)
//modifies Bpl.CommandLineOptions.Clo.XmlSink.*;
{
Contract.Requires(programName != null);
@@ -47,31 +47,31 @@ namespace Microsoft.Dafny {
Console.WriteLine("Parsing " + dafnyFileName);
}
- string err = ParseFile(dafnyFileName, Bpl.Token.NoToken, module, builtIns, new Errors());
+ string err = ParseFile(dafnyFileName, Bpl.Token.NoToken, module, builtIns, new Errors(reporter));
if (err != null) {
return err;
}
}
if (!DafnyOptions.O.DisallowIncludes) {
- string errString = ParseIncludes(module, builtIns, fileNames, new Errors());
+ string errString = ParseIncludes(module, builtIns, fileNames, new Errors(reporter));
if (errString != null) {
return errString;
}
}
- program = new Program(programName, module, builtIns);
+ program = new Program(programName, module, builtIns, reporter);
- MaybePrintProgram(program, DafnyOptions.O.DafnyPrintFile);
+ MaybePrintProgram(program, DafnyOptions.O.DafnyPrintFile, false);
if (Bpl.CommandLineOptions.Clo.NoResolve || Bpl.CommandLineOptions.Clo.NoTypecheck) { return null; }
Dafny.Resolver r = new Dafny.Resolver(program);
r.ResolveProgram(program);
- MaybePrintProgram(program, DafnyOptions.O.DafnyPrintResolvedFile);
+ MaybePrintProgram(program, DafnyOptions.O.DafnyPrintResolvedFile, true);
- if (r.ErrorCount != 0) {
- return string.Format("{0} resolution/type errors detected in {1}", r.ErrorCount, program.Name);
+ if (reporter.Count(ErrorLevel.Error) != 0) {
+ return string.Format("{0} resolution/type errors detected in {1}", reporter.Count(ErrorLevel.Error), program.Name);
}
return null; // success
diff --git a/Source/Dafny/DafnyOptions.cs b/Source/Dafny/DafnyOptions.cs
index 4fc2de96..9258503b 100644
--- a/Source/Dafny/DafnyOptions.cs
+++ b/Source/Dafny/DafnyOptions.cs
@@ -9,18 +9,26 @@ namespace Microsoft.Dafny
{
public class DafnyOptions : Bpl.CommandLineOptions
{
- public DafnyOptions()
+ private ErrorReporter errorReporter;
+
+ public DafnyOptions(ErrorReporter errorReporter = null)
: base("Dafny", "Dafny program verifier") {
+ this.errorReporter = errorReporter;
+ SetZ3ExecutableName();
}
public override string VersionNumber {
get {
- return System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileVersion;
+ return System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileVersion
+#if ENABLE_IRONDAFNY
+ + "[IronDafny]"
+#endif
+ ;
}
}
public override string VersionSuffix {
get {
- return " version " + VersionNumber + ", Copyright (c) 2003-2015, Microsoft.";
+ return " version " + VersionNumber + ", Copyright (c) 2003-2016, Microsoft.";
}
}
@@ -35,6 +43,7 @@ namespace Microsoft.Dafny
Bpl.CommandLineOptions.Install(options);
}
+ public bool UnicodeOutput = false;
public bool DisallowSoundnessCheating = false;
public bool Dafnycc = false;
public int Induction = 3;
@@ -42,9 +51,10 @@ namespace Microsoft.Dafny
public string DafnyPrelude = null;
public string DafnyPrintFile = null;
public enum PrintModes { Everything, NoIncludes, NoGhost };
- public PrintModes PrintMode;
+ public PrintModes PrintMode = PrintModes.Everything; // Default to printing everything
public bool DafnyVerify = true;
public string DafnyPrintResolvedFile = null;
+ public List<string> DafnyPrintExportedViews = new List<string>();
public bool Compile = true;
public bool ForceCompile = false;
public bool RunAfterCompile = false;
@@ -54,6 +64,21 @@ namespace Microsoft.Dafny
public string AutoReqPrintFile = null;
public bool ignoreAutoReq = false;
public bool AllowGlobals = false;
+ public bool CountVerificationErrors = true;
+ public bool Optimize = false;
+ public bool AutoTriggers = true;
+ public bool RewriteFocalPredicates = true;
+ public bool PrintTooltips = false;
+ public bool PrintStats = false;
+ public bool PrintFunctionCallGraph = false;
+ public bool WarnShadowing = false;
+ public bool IronDafny =
+#if ENABLE_IRONDAFNY
+ true
+#else
+ false
+#endif
+ ;
protected override bool ParseOption(string name, Bpl.CommandLineOptionEngine.CommandLineParseState ps) {
var args = ps.args; // convenient synonym
@@ -96,6 +121,11 @@ namespace Microsoft.Dafny
DafnyPrintResolvedFile = args[ps.i];
}
return true;
+ case "view":
+ if (ps.ConfirmArgumentCount(1)) {
+ DafnyPrintExportedViews = args[ps.i].Split(',').ToList();
+ }
+ return true;
case "compile": {
int compile = 0;
@@ -154,7 +184,7 @@ namespace Microsoft.Dafny
case "noNLarith":
DisableNLarith = true;
- this.AddZ3Option("NL_ARITH=false");
+ this.AddZ3Option("smt.arith.nl=false");
return true;
case "autoReqPrint":
@@ -170,6 +200,61 @@ namespace Microsoft.Dafny
case "allowGlobals":
AllowGlobals = true;
return true;
+
+ case "stats":
+ PrintStats = true;
+ return true;
+
+ case "funcCallGraph":
+ PrintFunctionCallGraph = true;
+ return true;
+
+ case "warnShadowing":
+ WarnShadowing = true;
+ return true;
+
+ case "countVerificationErrors": {
+ int countErrors = 1; // defaults to reporting verification errors
+ if (ps.GetNumericArgument(ref countErrors, 2)) {
+ CountVerificationErrors = countErrors == 1;
+ }
+ return true;
+ }
+
+ case "printTooltips":
+ PrintTooltips = true;
+ return true;
+
+ case "autoTriggers": {
+ int autoTriggers = 0;
+ if (ps.GetNumericArgument(ref autoTriggers, 2)) {
+ AutoTriggers = autoTriggers == 1;
+ }
+ return true;
+ }
+
+ case "rewriteFocalPredicates": {
+ int rewriteFocalPredicates = 0;
+ if (ps.GetNumericArgument(ref rewriteFocalPredicates, 2)) {
+ RewriteFocalPredicates = rewriteFocalPredicates == 1;
+ }
+ return true;
+ }
+
+ case "optimize": {
+ Optimize = true;
+ return true;
+ }
+
+ case "noIronDafny": {
+ IronDafny = false;
+ return true;
+ }
+
+ case "ironDafny": {
+ IronDafny = true;
+ return true;
+ }
default:
break;
@@ -190,6 +275,39 @@ namespace Microsoft.Dafny
// TODO: provide attribute help here
}
+
+ /// <summary>
+ /// Dafny comes with it's own copy of z3, to save new users the trouble of having to install extra dependency.
+ /// For this to work, Dafny makes the Z3ExecutablePath point to the path were Z3 is put by our release script.
+ /// For developers though (and people getting this from source), it's convenient to be able to run right away,
+ /// so we vendor a Windows version.
+ /// </summary>
+ private void SetZ3ExecutableName() {
+ var platform = (int)System.Environment.OSVersion.Platform;
+
+ // http://www.mono-project.com/docs/faq/technical/
+ var isUnix = platform == 4 || platform == 128;
+
+ var z3binName = isUnix ? "z3" : "z3.exe";
+ var dafnyBinDir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
+ var z3BinDir = System.IO.Path.Combine(dafnyBinDir, "z3", "bin");
+ var z3BinPath = System.IO.Path.Combine(z3BinDir, z3binName);
+
+ if (!System.IO.File.Exists(z3BinPath) && !isUnix) {
+ // This is most likely a Windows user running from source without downloading z3
+ // separately; this is ok, since we vendor z3.exe.
+ z3BinPath = System.IO.Path.Combine(dafnyBinDir, z3binName);
+ }
+
+ if (!System.IO.File.Exists(z3BinPath) && errorReporter != null) {
+ var tok = new Bpl.Token(1, 1) { filename = "*** " };
+ errorReporter.Warning(MessageSource.Other, tok, "Could not find '{0}' in '{1}'.{2}Downloading and extracting a Z3 distribution to Dafny's 'Binaries' folder would solve this issue; for now, we'll rely on Boogie to find Z3.",
+ z3binName, z3BinDir, System.Environment.NewLine);
+ } else {
+ Z3ExecutablePath = z3BinPath;
+ }
+ }
+
public override void Usage() {
Console.WriteLine(@" ---- Dafny options ---------------------------------------------------------
@@ -202,6 +320,7 @@ namespace Microsoft.Dafny
print Dafny program after parsing it
(use - as <file> to print to console)
/printMode:<Everything|NoIncludes|NoGhost>
+ Everything is the default.
NoIncludes disables printing of {:verify false} methods incorporated via the
include mechanism, as well as datatypes and fields included from other files.
NoGhost disables printing of functions, ghost methods, and proof statements in
@@ -209,6 +328,11 @@ namespace Microsoft.Dafny
/rprint:<file>
print Dafny program after resolving it
(use - as <file> to print to console)
+ /view:<view1, view2>
+ print the filtered views of a module after it is resolved (/rprint).
+ if print before the module is resolved (/dprint), then everthing in the module is printed
+ if no view is specified, then everything in the module is printed.
+
/dafnyVerify:<n>
0 - stop after typechecking
1 - continue on to translation, verification, and compilation
@@ -237,7 +361,7 @@ namespace Microsoft.Dafny
2 - apply induction as requested (by attributes) and also
for heuristically chosen quantifiers
3 (default) - apply induction as requested, and for
- heuristically chosen quantifiers and ghost methods
+ heuristically chosen quantifiers and lemmas
/inductionHeuristic:<n>
0 - least discriminating induction heuristic (that is, lean
toward applying induction more often)
@@ -245,17 +369,47 @@ namespace Microsoft.Dafny
how discriminating they are: 0 < 1 < 2 < (3,4) < 5 < 6
6 (default) - most discriminating
/noIncludes Ignore include directives
- /noNLarith Reduce Z3's knowledge of non-linear arithmetic (*,/,%).
+ /noNLarith Reduce Z3's knowledge of non-linear arithmetic (*,/,%).
Results in more manual work, but also produces more predictable behavior.
/autoReqPrint:<file>
Print out requirements that were automatically generated by autoReq.
/noAutoReq Ignore autoReq attributes
/allowGlobals Allow the implicit class '_default' to contain fields, instance functions,
and instance methods. These class members are declared at the module scope,
- outside of explicit classes. This command-line option is provided to simply
+ outside of explicit classes. This command-line option is provided to simplify
a transition from the behavior in the language prior to version 1.9.3, from
which point onward all functions and methods declared at the module scope are
implicitly static and fields declarations are not allowed at the module scope.
+ /countVerificationErrors:<n>
+ 0 - If preprocessing succeeds, set exit code to 0 regardless of the number
+ of verification errors.
+ 1 (default) - If preprocessing succeeds, set exit code to the number of
+ verification errors.
+ /autoTriggers:<n>
+ 0 - Do not generate {:trigger} annotations for user-level quantifiers.
+ 1 (default) - Add a {:trigger} to each user-level quantifier. Existing
+ annotations are preserved.
+ /rewriteFocalPredicates:<n>
+ 0 - Don't rewrite predicates in the body of prefix lemmas.
+ 1 (default) - In the body of prefix lemmas, rewrite any use of a focal predicate
+ P to P#[_k-1].
+ /optimize Produce optimized C# code, meaning:
+ - selects optimized C# prelude by passing
+ /define:DAFNY_USE_SYSTEM_COLLECTIONS_IMMUTABLE to csc.exe (requires
+ System.Collections.Immutable.dll in the source directory to successfully
+ compile).
+ - passes /optimize flag to csc.exe.
+ /stats Print interesting statistics about the Dafny files supplied.
+ /funcCallGraph Print out the function call graph. Format is: func,mod=callee*
+ /warnShadowing Emits a warning if the name of a declared variable caused another variable
+ to be shadowed
+ /ironDafny Enable experimental features needed to support Ironclad/Ironfleet. Use of
+ these features may cause your code to become incompatible with future
+ releases of Dafny.
+ /noIronDafny Disable Ironclad/Ironfleet features, if enabled by default.
+ /printTooltips
+ Dump additional positional information (displayed as mouse-over tooltips by
+ the VS plugin) to stdout as 'Info' messages.
");
base.Usage(); // also print the Boogie options
}
diff --git a/Source/Dafny/DafnyPipeline.csproj b/Source/Dafny/DafnyPipeline.csproj
index 74f7dae6..501a624c 100644
--- a/Source/Dafny/DafnyPipeline.csproj
+++ b/Source/Dafny/DafnyPipeline.csproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -8,7 +8,7 @@
<ProjectGuid>{FE44674A-1633-4917-99F4-57635E6FA740}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>DafnyPipeline</RootNamespace>
+ <RootNamespace>Microsoft.Dafny</RootNamespace>
<AssemblyName>DafnyPipeline</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
@@ -41,7 +41,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
- <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <DefineConstants>TRACE;DEBUG;NO_ENABLE_IRONDAFNY</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeContractsEnableRuntimeChecking>False</CodeContractsEnableRuntimeChecking>
@@ -83,7 +83,7 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
+ <DefineConstants>TRACE;NO_ENABLE_IRONDAFNY</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
@@ -91,7 +91,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Checked|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Checked\</OutputPath>
- <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <DefineConstants>TRACE;DEBUG;NO_ENABLE_IRONDAFNY</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
@@ -143,8 +143,16 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Cloner.cs" />
+ <Compile Include="Reporting.cs" />
+ <Compile Include="Triggers\QuantifiersCollection.cs" />
+ <Compile Include="Triggers\QuantifierSplitter.cs" />
+ <Compile Include="Triggers\TriggerExtensions.cs" />
+ <Compile Include="Triggers\QuantifiersCollector.cs" />
+ <Compile Include="Triggers\TriggersCollector.cs" />
+ <Compile Include="Triggers\TriggerUtils.cs" />
<Compile Include="Util.cs" />
<Compile Include="Compiler.cs" />
+ <Compile Include="BigIntegerParser.cs" />
<Compile Include="DafnyAst.cs" />
<Compile Include="DafnyMain.cs" />
<Compile Include="DafnyOptions.cs" />
@@ -191,4 +199,4 @@
<Target Name="AfterBuild">
</Target>
-->
-</Project>
+</Project> \ No newline at end of file
diff --git a/Source/Dafny/Makefile b/Source/Dafny/Makefile
index 4c01c780..68ab7a2d 100644
--- a/Source/Dafny/Makefile
+++ b/Source/Dafny/Makefile
@@ -4,8 +4,8 @@
# from http://boogiepartners.codeplex.com/. Update the FRAME_DIR variable to
# point to whatever directory you install that into.
# ###############################################################################
-COCO_EXE_DIR = ..\..\..\boogiepartners\CocoRdownload
-FRAME_DIR = ..\..\..\boogiepartners\CocoR\Modified
+COCO_EXE_DIR = ..\..\..\boogie-partners\CocoR\bin
+FRAME_DIR = ..\..\..\boogie-partners\CocoR\Modified
COCO = $(COCO_EXE_DIR)\Coco.exe
# "all" depends on 2 files, really (Parser.cs and Scanner.cs), but they
diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs
index 9e283ef5..f64ba7fa 100644
--- a/Source/Dafny/Parser.cs
+++ b/Source/Dafny/Parser.cs
@@ -27,76 +27,181 @@ public class Parser {
public const int _object = 11;
public const int _string = 12;
public const int _set = 13;
- public const int _multiset = 14;
- public const int _seq = 15;
- public const int _map = 16;
- public const int _imap = 17;
- public const int _charToken = 18;
- public const int _stringToken = 19;
- public const int _colon = 20;
- public const int _comma = 21;
- public const int _verticalbar = 22;
- public const int _doublecolon = 23;
- public const int _bullet = 24;
- public const int _dot = 25;
- public const int _semi = 26;
- public const int _darrow = 27;
- public const int _arrow = 28;
- public const int _assume = 29;
- public const int _calc = 30;
- public const int _case = 31;
- public const int _then = 32;
- public const int _else = 33;
- public const int _decreases = 34;
- public const int _invariant = 35;
- public const int _function = 36;
- public const int _predicate = 37;
- public const int _inductive = 38;
- public const int _lemma = 39;
- public const int _copredicate = 40;
- public const int _modifies = 41;
- public const int _reads = 42;
- public const int _requires = 43;
- public const int _lbrace = 44;
- public const int _rbrace = 45;
- public const int _lbracket = 46;
- public const int _rbracket = 47;
- public const int _openparen = 48;
- public const int _closeparen = 49;
- public const int _openAngleBracket = 50;
- public const int _closeAngleBracket = 51;
- public const int _eq = 52;
- public const int _neq = 53;
- public const int _neqAlt = 54;
- public const int _star = 55;
- public const int _notIn = 56;
- public const int _ellipsis = 57;
- public const int maxT = 136;
+ public const int _iset = 14;
+ public const int _multiset = 15;
+ public const int _seq = 16;
+ public const int _map = 17;
+ public const int _imap = 18;
+ public const int _charToken = 19;
+ public const int _stringToken = 20;
+ public const int _colon = 21;
+ public const int _comma = 22;
+ public const int _verticalbar = 23;
+ public const int _doublecolon = 24;
+ public const int _boredSmiley = 25;
+ public const int _bullet = 26;
+ public const int _dot = 27;
+ public const int _semi = 28;
+ public const int _darrow = 29;
+ public const int _arrow = 30;
+ public const int _assume = 31;
+ public const int _calc = 32;
+ public const int _case = 33;
+ public const int _then = 34;
+ public const int _else = 35;
+ public const int _decreases = 36;
+ public const int _invariant = 37;
+ public const int _function = 38;
+ public const int _predicate = 39;
+ public const int _inductive = 40;
+ public const int _lemma = 41;
+ public const int _copredicate = 42;
+ public const int _modifies = 43;
+ public const int _reads = 44;
+ public const int _requires = 45;
+ public const int _lbrace = 46;
+ public const int _rbrace = 47;
+ public const int _lbracket = 48;
+ public const int _rbracket = 49;
+ public const int _openparen = 50;
+ public const int _closeparen = 51;
+ public const int _openAngleBracket = 52;
+ public const int _closeAngleBracket = 53;
+ public const int _eq = 54;
+ public const int _neq = 55;
+ public const int _neqAlt = 56;
+ public const int _star = 57;
+ public const int _notIn = 58;
+ public const int _ellipsis = 59;
+ public const int maxT = 140;
const bool _T = true;
const bool _x = false;
const int minErrDist = 2;
- public Scanner/*!*/ scanner;
- public Errors/*!*/ errors;
+ public Scanner scanner;
+ public Errors errors;
- public Token/*!*/ t; // last recognized token
- public Token/*!*/ la; // lookahead token
+ public Token t; // last recognized token
+ public Token la; // lookahead token
int errDist = minErrDist;
readonly Expression/*!*/ dummyExpr;
readonly AssignmentRhs/*!*/ dummyRhs;
-readonly FrameExpression/*!*/ dummyFrameExpr;
+readonly FrameExpression/*!*/ dummyFrameExpr;
readonly Statement/*!*/ dummyStmt;
readonly ModuleDecl theModule;
readonly BuiltIns theBuiltIns;
readonly bool theVerifyThisFile;
int anonymousIds = 0;
-struct MemberModifiers {
+/// <summary>
+/// Holds the modifiers given for a declaration
+///
+/// Not all modifiers are applicable to all kinds of declarations.
+/// Errors are given when a modify does not apply.
+/// We also record the tokens for the specified modifiers so that
+/// they can be used in error messages.
+/// </summary>
+struct DeclModifierData {
+ public bool IsAbstract;
+ public IToken AbstractToken;
public bool IsGhost;
+ public IToken GhostToken;
public bool IsStatic;
+ public IToken StaticToken;
public bool IsProtected;
+ public IToken ProtectedToken;
+ public bool IsExtern;
+ public IToken ExternToken;
+ public StringLiteralExpr ExternName;
+
+}
+
+// Check that token has not been set, then set it.
+public void CheckAndSetToken(ref IToken token)
+{
+ if (token != null) {
+ SemErr(t, "Duplicate declaration modifier: " + t.val);
+ }
+ token = t;
+}
+
+/// <summary>
+// A flags type used to tell what declaration modifiers are allowed for a declaration.
+/// </summary>
+[Flags]
+enum AllowedDeclModifiers {
+ None = 0,
+ Abstract = 1,
+ Ghost = 2,
+
+ // Means ghost not allowed because already implicitly ghost.
+ AlreadyGhost = 4,
+ Static = 8,
+ Protected = 16,
+ Extern = 32
+};
+
+/// <summary>
+/// Check the declaration modifiers against those that are allowed.
+///
+/// The 'allowed' parameter specifies which declaratio modifiers are allowed.
+/// The 'declCaption' parameter should be a string describing the kind of declaration.
+/// It is used in error messages.
+/// Any declaration modifiers that are present but not allowed are cleared.
+///</summary>
+void CheckDeclModifiers(DeclModifierData dmod, string declCaption, AllowedDeclModifiers allowed)
+{
+ if (dmod.IsAbstract && ((allowed & AllowedDeclModifiers.Abstract) == 0)) {
+ SemErr(dmod.AbstractToken, declCaption + " cannot be declared 'abstract'.");
+ dmod.IsAbstract = false;
+ }
+ if (dmod.IsGhost) {
+ if ((allowed & AllowedDeclModifiers.AlreadyGhost) != 0) {
+ SemErr(dmod.GhostToken, declCaption + " cannot be declared ghost (they are 'ghost' by default).");
+ dmod.IsGhost = false;
+ } else if ((allowed & AllowedDeclModifiers.Ghost) == 0) {
+ SemErr(dmod.GhostToken, declCaption + " cannot be declared 'ghost'.");
+ dmod.IsGhost = false;
+ }
+ }
+ if (dmod.IsStatic && ((allowed & AllowedDeclModifiers.Static) == 0)) {
+ SemErr(dmod.StaticToken, declCaption + " cannot be declared 'static'.");
+ dmod.IsStatic = false;
+ }
+ if (dmod.IsProtected && ((allowed & AllowedDeclModifiers.Protected) == 0)) {
+ SemErr(dmod.ProtectedToken, declCaption + " cannot be declared 'protected'.");
+ dmod.IsProtected = false;
+ }
+ if (dmod.IsExtern && ((allowed & AllowedDeclModifiers.Extern) == 0)) {
+ SemErr(dmod.ExternToken, declCaption + " cannot be declared 'extern'.");
+ dmod.IsExtern = false;
+ }
+}
+
+/// <summary>
+/// Encode an 'extern' declaration modifier as an {:extern name} attribute.
+///
+/// We also include an {:axiom} attribute since the specification of an
+/// external entity is assumed to hold, but only for methods or functions.
+///</summary>
+static void EncodeExternAsAttribute(DeclModifierData dmod, ref Attributes attrs, IToken/*!*/ id, bool needAxiom) {
+ if (dmod.IsExtern) {
+ StringLiteralExpr name = dmod.ExternName;
+ if (name == null) {
+ bool isVerbatimString = false;
+ name = new StringLiteralExpr(id, id.val, isVerbatimString);
+ }
+ var args = new List<Expression>();
+ args.Add(name);
+ attrs = new Attributes("extern", args, attrs);
+
+ // Also 'extern' implies 'axiom' for methods or functions.
+ if (needAxiom) {
+ attrs = new Attributes("axiom", new List<Expression>(), attrs);
+ }
+ }
}
///<summary>
@@ -111,11 +216,11 @@ public static int Parse (string/*!*/ filename, ModuleDecl module, BuiltIns built
string s;
if (filename == "stdin.dfy") {
s = Microsoft.Boogie.ParserHelper.Fill(System.Console.In, new List<string>());
- return Parse(s, filename, module, builtIns, errors, verifyThisFile);
+ return Parse(s, filename, filename, module, builtIns, errors, verifyThisFile);
} else {
using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) {
s = Microsoft.Boogie.ParserHelper.Fill(reader, new List<string>());
- return Parse(s, DafnyOptions.Clo.UseBaseNameForFileName ? Path.GetFileName(filename) : filename, module, builtIns, errors, verifyThisFile);
+ return Parse(s, filename, DafnyOptions.Clo.UseBaseNameForFileName ? Path.GetFileName(filename) : filename, module, builtIns, errors, verifyThisFile);
}
}
}
@@ -125,12 +230,12 @@ public static int Parse (string/*!*/ filename, ModuleDecl module, BuiltIns built
/// Returns the number of parsing errors encountered.
/// Note: first initialize the Scanner.
///</summary>
-public static int Parse (string/*!*/ s, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, bool verifyThisFile=true) {
+public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, ErrorReporter reporter, bool verifyThisFile=true) {
Contract.Requires(s != null);
Contract.Requires(filename != null);
Contract.Requires(module != null);
- Errors errors = new Errors();
- return Parse(s, filename, module, builtIns, errors, verifyThisFile);
+ Errors errors = new Errors(reporter);
+ return Parse(s, fullFilename, filename, module, builtIns, errors, verifyThisFile);
}
///<summary>
/// Parses top-level things (modules, classes, datatypes, class members)
@@ -138,18 +243,18 @@ public static int Parse (string/*!*/ s, string/*!*/ filename, ModuleDecl module,
/// Returns the number of parsing errors encountered.
/// Note: first initialize the Scanner with the given Errors sink.
///</summary>
-public static int Parse (string/*!*/ s, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns,
- Errors/*!*/ errors, bool verifyThisFile=true) {
+public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, ModuleDecl module,
+ BuiltIns builtIns, Errors/*!*/ errors, bool verifyThisFile=true) {
Contract.Requires(s != null);
Contract.Requires(filename != null);
Contract.Requires(module != null);
Contract.Requires(errors != null);
byte[]/*!*/ buffer = cce.NonNull( UTF8Encoding.Default.GetBytes(s));
MemoryStream ms = new MemoryStream(buffer,false);
- Scanner scanner = new Scanner(ms, errors, filename);
+ Scanner scanner = new Scanner(ms, errors, fullFilename, filename);
Parser parser = new Parser(scanner, errors, module, builtIns, verifyThisFile);
parser.Parse();
- return parser.errors.count;
+ return parser.errors.ErrorCount;
}
public Parser(Scanner/*!*/ scanner, Errors/*!*/ errors, ModuleDecl module, BuiltIns builtIns, bool verifyThisFile=true)
: this(scanner, errors) // the real work
@@ -174,6 +279,25 @@ bool IsAlternative() {
return la.kind == _lbrace && x.kind == _case;
}
+// an existential guard starts with an identifier and is then followed by
+// * a colon (if the first identifier is given an explicit type),
+// * a comma (if there's a list a bound variables and the first one is not given an explicit type),
+// * a start-attribute (if there's one bound variable and it is not given an explicit type and there are attributes), or
+// * a bored smiley (if there's one bound variable and it is not given an explicit type).
+bool IsExistentialGuard() {
+ scanner.ResetPeek();
+ if (la.kind == _ident) {
+ Token x = scanner.Peek();
+ if (x.kind == _colon || x.kind == _comma || x.kind == _boredSmiley) {
+ return true;
+ } else if (x.kind == _lbrace) {
+ x = scanner.Peek();
+ return x.kind == _colon;
+ }
+ }
+ return false;
+}
+
bool IsLoopSpec() {
return la.kind == _invariant | la.kind == _decreases | la.kind == _modifies;
}
@@ -245,6 +369,9 @@ bool IsMapDisplay() {
bool IsIMapDisplay() {
return la.kind == _imap && scanner.Peek().kind == _lbracket;
}
+bool IsISetDisplay() {
+ return la.kind == _iset && scanner.Peek().kind == _lbrace;
+}
bool IsSuffix() {
return la.kind == _dot || la.kind == _lbracket || la.kind == _openparen;
@@ -366,6 +493,9 @@ bool IsGenericInstantiation() {
return false;
}
}
+/* Returns true if the next thing is of the form:
+ * "<" Type { "," Type } ">"
+ */
bool IsTypeList(ref IToken pt) {
if (pt.kind != _openAngleBracket) {
return false;
@@ -373,6 +503,10 @@ bool IsTypeList(ref IToken pt) {
pt = scanner.Peek();
return IsTypeSequence(ref pt, _closeAngleBracket);
}
+/* Returns true if the next thing is of the form:
+ * Type { "," Type }
+ * followed by an endBracketKind.
+ */
bool IsTypeSequence(ref IToken pt, int endBracketKind) {
while (true) {
if (!IsType(ref pt)) {
@@ -404,12 +538,13 @@ bool IsType(ref IToken pt) {
return true;
case _arrayToken:
case _set:
+ case _iset:
case _multiset:
case _seq:
case _map:
case _imap:
pt = scanner.Peek();
- return IsTypeList(ref pt);
+ return pt.kind != _openAngleBracket || IsTypeList(ref pt);
case _ident:
while (true) {
// invariant: next token is an ident
@@ -428,19 +563,31 @@ bool IsType(ref IToken pt) {
}
case _openparen:
pt = scanner.Peek();
+ if (pt.kind == _closeparen) {
+ // end of type list
+ pt = scanner.Peek();
+ return true;
+ }
return IsTypeSequence(ref pt, _closeparen);
default:
return false;
}
}
+
+bool IsDefaultImport() {
+ scanner.ResetPeek();
+ Token x = scanner.Peek(); // lookahead token again
+ return la.val == "default" && x.val != "export";
+}
+
/*--------------------------------------------------------------------------*/
- public Parser(Scanner/*!*/ scanner, Errors/*!*/ errors) {
+ public Parser(Scanner scanner, Errors errors) {
this.scanner = scanner;
this.errors = errors;
- Token/*!*/ tok = new Token();
+ Token tok = new Token();
tok.val = "";
this.la = tok;
this.t = new Token(); // just to satisfy its non-null constraint
@@ -451,13 +598,13 @@ bool IsType(ref IToken pt) {
errDist = 0;
}
- public void SemErr (string/*!*/ msg) {
+ public void SemErr (string msg) {
Contract.Requires(msg != null);
if (errDist >= minErrDist) errors.SemErr(t, msg);
errDist = 0;
}
- public void SemErr(IToken/*!*/ tok, string/*!*/ msg) {
+ public void SemErr(IToken tok, string msg) {
Contract.Requires(tok != null);
Contract.Requires(msg != null);
errors.SemErr(tok, msg);
@@ -506,20 +653,17 @@ bool IsType(ref IToken pt) {
void Dafny() {
- ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; TopLevelDecl td; IteratorDecl iter;
List<MemberDecl/*!*/> membersDefaultClass = new List<MemberDecl/*!*/>();
- ModuleDecl submodule;
// to support multiple files, create a default module only if theModule is null
DefaultModuleDecl defaultModule = (DefaultModuleDecl)((LiteralModuleDecl)theModule).ModuleDef;
// theModule should be a DefaultModuleDecl (actually, the singular DefaultModuleDecl)
- TraitDecl/*!*/ trait;
Contract.Assert(defaultModule != null);
- while (la.kind == 58) {
+ while (la.kind == 60) {
Get();
- Expect(19);
+ Expect(20);
{
- string parsedFile = t.filename;
+ string parsedFile = scanner.FullFilename;
bool isVerbatimString;
string includedFile = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString);
includedFile = Util.RemoveEscaping(includedFile, isVerbatimString);
@@ -534,47 +678,7 @@ bool IsType(ref IToken pt) {
}
while (StartOf(1)) {
- switch (la.kind) {
- case 59: case 60: case 62: {
- SubModuleDecl(defaultModule, out submodule);
- defaultModule.TopLevelDecls.Add(submodule);
- break;
- }
- case 67: {
- ClassDecl(defaultModule, out c);
- defaultModule.TopLevelDecls.Add(c);
- break;
- }
- case 73: case 74: {
- DatatypeDecl(defaultModule, out dt);
- defaultModule.TopLevelDecls.Add(dt);
- break;
- }
- case 76: {
- NewtypeDecl(defaultModule, out td);
- defaultModule.TopLevelDecls.Add(td);
- break;
- }
- case 77: {
- OtherTypeDecl(defaultModule, out td);
- defaultModule.TopLevelDecls.Add(td);
- break;
- }
- case 78: {
- IteratorDecl(defaultModule, out iter);
- defaultModule.TopLevelDecls.Add(iter);
- break;
- }
- case 69: {
- TraitDecl(defaultModule, out trait);
- defaultModule.TopLevelDecls.Add(trait);
- break;
- }
- case 36: case 37: case 38: case 39: case 40: case 70: case 71: case 72: case 75: case 81: case 82: case 83: case 84: {
- ClassMemberDecl(membersDefaultClass, false, !DafnyOptions.O.AllowGlobals);
- break;
- }
- }
+ TopDecl(defaultModule, membersDefaultClass, /* isTopLevel */ true, /* isAbstract */ false);
}
DefaultClassDecl defaultClass = null;
foreach (TopLevelDecl topleveldecl in defaultModule.TopLevelDecls) {
@@ -591,106 +695,162 @@ bool IsType(ref IToken pt) {
Expect(0);
}
- void SubModuleDecl(ModuleDefinition parent, out ModuleDecl submodule) {
+ void TopDecl(ModuleDefinition module, List<MemberDecl/*!*/> membersDefaultClass, bool isTopLevel, bool isAbstract ) {
+ DeclModifierData dmod = new DeclModifierData(); ModuleDecl submodule;
ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; TopLevelDecl td; IteratorDecl iter;
- Attributes attrs = null; IToken/*!*/ id;
TraitDecl/*!*/ trait;
+
+ while (StartOf(2)) {
+ DeclModifier(ref dmod);
+ }
+ switch (la.kind) {
+ case 66: case 69: case 73: case 74: {
+ SubModuleDecl(dmod, module, out submodule);
+ module.TopLevelDecls.Add(submodule);
+ break;
+ }
+ case 77: {
+ ClassDecl(dmod, module, out c);
+ module.TopLevelDecls.Add(c);
+ break;
+ }
+ case 79: case 80: {
+ DatatypeDecl(dmod, module, out dt);
+ module.TopLevelDecls.Add(dt);
+ break;
+ }
+ case 82: {
+ NewtypeDecl(dmod, module, out td);
+ module.TopLevelDecls.Add(td);
+ break;
+ }
+ case 83: {
+ OtherTypeDecl(dmod, module, out td);
+ module.TopLevelDecls.Add(td);
+ break;
+ }
+ case 84: {
+ IteratorDecl(dmod, module, out iter);
+ module.TopLevelDecls.Add(iter);
+ break;
+ }
+ case 78: {
+ TraitDecl(dmod, module, out trait);
+ module.TopLevelDecls.Add(trait);
+ break;
+ }
+ case 38: case 39: case 40: case 41: case 42: case 81: case 87: case 88: case 89: case 90: {
+ ClassMemberDecl(dmod, membersDefaultClass, false, !DafnyOptions.O.AllowGlobals,
+!isTopLevel && DafnyOptions.O.IronDafny && isAbstract);
+ break;
+ }
+ default: SynErr(141); break;
+ }
+ }
+
+ void DeclModifier(ref DeclModifierData dmod) {
+ if (la.kind == 61) {
+ Get();
+ dmod.IsAbstract = true; CheckAndSetToken(ref dmod.AbstractToken);
+ } else if (la.kind == 62) {
+ Get();
+ dmod.IsGhost = true; CheckAndSetToken(ref dmod.GhostToken);
+ } else if (la.kind == 63) {
+ Get();
+ dmod.IsStatic = true; CheckAndSetToken(ref dmod.StaticToken);
+ } else if (la.kind == 64) {
+ Get();
+ dmod.IsProtected = true; CheckAndSetToken(ref dmod.ProtectedToken);
+ } else if (la.kind == 65) {
+ Get();
+ dmod.IsExtern = true; CheckAndSetToken(ref dmod.ExternToken);
+ if (la.kind == 20) {
+ Get();
+ bool isVerbatimString;
+ string s = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString);
+ dmod.ExternName = new StringLiteralExpr(t, s, isVerbatimString);
+
+ }
+ } else SynErr(142);
+ }
+
+ void SubModuleDecl(DeclModifierData dmod, ModuleDefinition parent, out ModuleDecl submodule) {
+ Attributes attrs = null; IToken/*!*/ id;
List<MemberDecl/*!*/> namedModuleDefaultClassMembers = new List<MemberDecl>();;
List<IToken> idRefined = null, idPath = null, idAssignment = null;
ModuleDefinition module;
- ModuleDecl sm;
submodule = null; // appease compiler
- bool isAbstract = false;
+ bool isAbstract = dmod.IsAbstract;
+ bool isExclusively = false;
bool opened = false;
+ CheckDeclModifiers(dmod, "Modules", AllowedDeclModifiers.Abstract | AllowedDeclModifiers.Extern);
- if (la.kind == 59 || la.kind == 60) {
- if (la.kind == 59) {
- Get();
- isAbstract = true;
- }
- Expect(60);
- while (la.kind == 44) {
+ if (la.kind == 66) {
+ Get();
+ while (la.kind == 46) {
Attribute(ref attrs);
}
NoUSIdent(out id);
- if (la.kind == 61) {
- Get();
- QualifiedModuleName(out idRefined);
+ EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ false);
+ if (la.kind == 67 || la.kind == 68) {
+ if (la.kind == 67) {
+ Get();
+ Expect(68);
+ QualifiedModuleName(out idRefined);
+ isExclusively = true;
+ } else {
+ Get();
+ QualifiedModuleName(out idRefined);
+ isExclusively = false;
+ }
}
- module = new ModuleDefinition(id, id.val, isAbstract, false, idRefined == null ? null : idRefined, parent, attrs, false);
- Expect(44);
+ module = new ModuleDefinition(id, id.val, isAbstract, false, isExclusively, idRefined == null ? null : idRefined, parent, attrs, false, this);
+ Expect(46);
module.BodyStartTok = t;
while (StartOf(1)) {
- switch (la.kind) {
- case 59: case 60: case 62: {
- SubModuleDecl(module, out sm);
- module.TopLevelDecls.Add(sm);
- break;
- }
- case 67: {
- ClassDecl(module, out c);
- module.TopLevelDecls.Add(c);
- break;
- }
- case 69: {
- TraitDecl(module, out trait);
- module.TopLevelDecls.Add(trait);
- break;
- }
- case 73: case 74: {
- DatatypeDecl(module, out dt);
- module.TopLevelDecls.Add(dt);
- break;
- }
- case 76: {
- NewtypeDecl(module, out td);
- module.TopLevelDecls.Add(td);
- break;
- }
- case 77: {
- OtherTypeDecl(module, out td);
- module.TopLevelDecls.Add(td);
- break;
- }
- case 78: {
- IteratorDecl(module, out iter);
- module.TopLevelDecls.Add(iter);
- break;
- }
- case 36: case 37: case 38: case 39: case 40: case 70: case 71: case 72: case 75: case 81: case 82: case 83: case 84: {
- ClassMemberDecl(namedModuleDefaultClassMembers, false, !DafnyOptions.O.AllowGlobals);
- break;
- }
- }
+ TopDecl(module, namedModuleDefaultClassMembers, /* isTopLevel */ false, isAbstract);
}
- Expect(45);
+ Expect(47);
module.BodyEndTok = t;
module.TopLevelDecls.Add(new DefaultClassDecl(module, namedModuleDefaultClassMembers));
submodule = new LiteralModuleDecl(module, parent);
- } else if (la.kind == 62) {
+ } else if (la.kind == 69) {
Get();
- if (la.kind == 63) {
+ if (la.kind == 70) {
Get();
opened = true;
}
NoUSIdent(out id);
- if (la.kind == 64 || la.kind == 65) {
- if (la.kind == 64) {
+ EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ false);
+ if (StartOf(3)) {
+ if (la.kind == 71) {
Get();
QualifiedModuleName(out idPath);
submodule = new AliasModuleDecl(idPath, id, parent, opened);
- } else {
+ } else if (la.kind == 72) {
Get();
QualifiedModuleName(out idPath);
- if (la.kind == 66) {
- Get();
+ if (IsDefaultImport()) {
+ Expect(73);
QualifiedModuleName(out idAssignment);
}
submodule = new ModuleFacadeDecl(idPath, id, parent, idAssignment, opened);
+ errors.Warning(t, "\"import A as B\" has been deprecated; in the new syntax, it is \"import A:B\"");
+
+ } else if (la.kind == 21) {
+ Get();
+ QualifiedModuleName(out idPath);
+ submodule = new ModuleFacadeDecl(idPath, id, parent, idAssignment, opened);
+ } else {
+ Get();
+ QualifiedModuleName(out idPath);
+ idPath.Insert(0, id);
+ submodule = new AliasModuleDecl(idPath, id, parent, opened);
+
}
}
- if (la.kind == 26) {
- while (!(la.kind == 0 || la.kind == 26)) {SynErr(137); Get();}
+ if (la.kind == 28) {
+ while (!(la.kind == 0 || la.kind == 28)) {SynErr(143); Get();}
Get();
errors.Warning(t, "the semi-colon that used to terminate a sub-module declaration has been deprecated; in the new syntax, just leave off the semi-colon");
}
@@ -700,10 +860,54 @@ bool IsType(ref IToken pt) {
submodule = new AliasModuleDecl(idPath, id, parent, opened);
}
- } else SynErr(138);
+ } else if (la.kind == 73 || la.kind == 74) {
+ bool isDefault = false;
+ bool includeBody;
+ IToken exportId;
+ List<ExportSignature> exports = new List<ExportSignature>();;
+ List<string> extends = new List<string>();
+
+ if (la.kind == 73) {
+ Get();
+ isDefault = true;
+ }
+ Expect(74);
+ NoUSIdent(out exportId);
+ if (la.kind == 75) {
+ Get();
+ NoUSIdent(out id);
+ extends.Add(id.val);
+ while (la.kind == 22) {
+ Get();
+ NoUSIdent(out id);
+ extends.Add(id.val);
+ }
+ }
+ Expect(46);
+ NoUSIdent(out id);
+ includeBody = false;
+ if (la.kind == 76) {
+ Get();
+ includeBody = true;
+ }
+ exports.Add(new ExportSignature(id, includeBody));
+ while (la.kind == 22) {
+ Get();
+ NoUSIdent(out id);
+ includeBody = false;
+ if (la.kind == 76) {
+ Get();
+ includeBody = true;
+ }
+ exports.Add(new ExportSignature(id, includeBody));
+ }
+ Expect(47);
+ submodule = new ModuleExportDecl(exportId, parent, isDefault, exports, extends);
+
+ } else SynErr(144);
}
- void ClassDecl(ModuleDefinition/*!*/ module, out ClassDecl/*!*/ c) {
+ void ClassDecl(DeclModifierData dmodClass, ModuleDefinition/*!*/ module, out ClassDecl/*!*/ c) {
Contract.Requires(module != null);
Contract.Ensures(Contract.ValueAtReturn(out c) != null);
IToken/*!*/ id;
@@ -713,39 +917,46 @@ bool IsType(ref IToken pt) {
List<TypeParameter/*!*/> typeArgs = new List<TypeParameter/*!*/>();
List<MemberDecl/*!*/> members = new List<MemberDecl/*!*/>();
IToken bodyStart;
+ CheckDeclModifiers(dmodClass, "Classes", AllowedDeclModifiers.Extern);
+ DeclModifierData dmod;
- while (!(la.kind == 0 || la.kind == 67)) {SynErr(139); Get();}
- Expect(67);
- while (la.kind == 44) {
+ while (!(la.kind == 0 || la.kind == 77)) {SynErr(145); Get();}
+ Expect(77);
+ while (la.kind == 46) {
Attribute(ref attrs);
}
NoUSIdent(out id);
- if (la.kind == 50) {
+ EncodeExternAsAttribute(dmodClass, ref attrs, id, /* needAxiom */ false);
+ if (la.kind == 52) {
GenericParameters(typeArgs);
}
- if (la.kind == 68) {
+ if (la.kind == 75) {
Get();
Type(out trait);
traits.Add(trait);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
Type(out trait);
traits.Add(trait);
}
}
- Expect(44);
+ Expect(46);
bodyStart = t;
- while (StartOf(2)) {
- ClassMemberDecl(members, true, false);
+ while (StartOf(4)) {
+ dmod = new DeclModifierData();
+ while (StartOf(2)) {
+ DeclModifier(ref dmod);
+ }
+ ClassMemberDecl(dmod, members, true, false, false);
}
- Expect(45);
+ Expect(47);
c = new ClassDecl(id, id.val, module, typeArgs, members, attrs, traits);
c.BodyStartTok = bodyStart;
c.BodyEndTok = t;
}
- void DatatypeDecl(ModuleDefinition/*!*/ module, out DatatypeDecl/*!*/ dt) {
+ void DatatypeDecl(DeclModifierData dmod, ModuleDefinition/*!*/ module, out DatatypeDecl/*!*/ dt) {
Contract.Requires(module != null);
Contract.Ensures(Contract.ValueAtReturn(out dt)!=null);
IToken/*!*/ id;
@@ -754,30 +965,31 @@ bool IsType(ref IToken pt) {
List<DatatypeCtor/*!*/> ctors = new List<DatatypeCtor/*!*/>();
IToken bodyStart = Token.NoToken; // dummy assignment
bool co = false;
+ CheckDeclModifiers(dmod, "Datatypes or codatatypes", AllowedDeclModifiers.None);
- while (!(la.kind == 0 || la.kind == 73 || la.kind == 74)) {SynErr(140); Get();}
- if (la.kind == 73) {
+ while (!(la.kind == 0 || la.kind == 79 || la.kind == 80)) {SynErr(146); Get();}
+ if (la.kind == 79) {
Get();
- } else if (la.kind == 74) {
+ } else if (la.kind == 80) {
Get();
co = true;
- } else SynErr(141);
- while (la.kind == 44) {
+ } else SynErr(147);
+ while (la.kind == 46) {
Attribute(ref attrs);
}
NoUSIdent(out id);
- if (la.kind == 50) {
+ if (la.kind == 52) {
GenericParameters(typeArgs);
}
- Expect(64);
+ Expect(71);
bodyStart = t;
DatatypeMemberDecl(ctors);
- while (la.kind == 22) {
+ while (la.kind == 23) {
Get();
DatatypeMemberDecl(ctors);
}
- if (la.kind == 26) {
- while (!(la.kind == 0 || la.kind == 26)) {SynErr(142); Get();}
+ if (la.kind == 28) {
+ while (!(la.kind == 0 || la.kind == 28)) {SynErr(148); Get();}
Get();
errors.Warning(t, "the semi-colon that used to terminate a (co)datatype declaration has been deprecated; in the new syntax, just leave off the semi-colon");
}
@@ -791,78 +1003,80 @@ bool IsType(ref IToken pt) {
}
- void NewtypeDecl(ModuleDefinition module, out TopLevelDecl td) {
+ void NewtypeDecl(DeclModifierData dmod, ModuleDefinition module, out TopLevelDecl td) {
IToken id, bvId;
Attributes attrs = null;
td = null;
Type baseType = null;
Expression wh;
+ CheckDeclModifiers(dmod, "Newtypes", AllowedDeclModifiers.None);
- Expect(76);
- while (la.kind == 44) {
+ Expect(82);
+ while (la.kind == 46) {
Attribute(ref attrs);
}
NoUSIdent(out id);
- Expect(64);
+ Expect(71);
if (IsIdentColonOrBar()) {
NoUSIdent(out bvId);
- if (la.kind == 20) {
+ if (la.kind == 21) {
Get();
Type(out baseType);
}
- if (baseType == null) { baseType = new OperationTypeProxy(true, true, false, false, false); }
- Expect(22);
+ if (baseType == null) { baseType = new OperationTypeProxy(true, true, false, false, false, false); }
+ Expect(23);
Expression(out wh, false, true);
td = new NewtypeDecl(theVerifyThisFile ? id : new IncludeToken(id), id.val, module, new BoundVar(bvId, bvId.val, baseType), wh, attrs);
- } else if (StartOf(3)) {
+ } else if (StartOf(5)) {
Type(out baseType);
td = new NewtypeDecl(theVerifyThisFile ? id : new IncludeToken(id), id.val, module, baseType, attrs);
- } else SynErr(143);
+ } else SynErr(149);
}
- void OtherTypeDecl(ModuleDefinition module, out TopLevelDecl td) {
+ void OtherTypeDecl(DeclModifierData dmod, ModuleDefinition module, out TopLevelDecl td) {
IToken id;
Attributes attrs = null;
var eqSupport = TypeParameter.EqualitySupportValue.Unspecified;
var typeArgs = new List<TypeParameter>();
td = null;
Type ty;
+ CheckDeclModifiers(dmod, "Type aliases", AllowedDeclModifiers.None);
- Expect(77);
- while (la.kind == 44) {
+ Expect(83);
+ while (la.kind == 46) {
Attribute(ref attrs);
}
NoUSIdent(out id);
- if (la.kind == 48) {
+ if (la.kind == 50) {
Get();
- Expect(52);
- Expect(49);
+ Expect(54);
+ Expect(51);
eqSupport = TypeParameter.EqualitySupportValue.Required;
- if (la.kind == 50) {
+ if (la.kind == 52) {
GenericParameters(typeArgs);
}
- } else if (StartOf(4)) {
- if (la.kind == 50) {
+ } else if (StartOf(6)) {
+ if (la.kind == 52) {
GenericParameters(typeArgs);
}
- if (la.kind == 64) {
+ if (la.kind == 71) {
Get();
Type(out ty);
td = new TypeSynonymDecl(id, id.val, typeArgs, module, ty, attrs);
}
- } else SynErr(144);
+ } else SynErr(150);
if (td == null) {
td = new OpaqueTypeDecl(id, id.val, module, eqSupport, typeArgs, attrs);
}
- if (la.kind == 26) {
- while (!(la.kind == 0 || la.kind == 26)) {SynErr(145); Get();}
+ if (la.kind == 28) {
+ while (!(la.kind == 0 || la.kind == 28)) {SynErr(151); Get();}
Get();
errors.Warning(t, "the semi-colon that used to terminate an opaque-type declaration has been deprecated; in the new syntax, just leave off the semi-colon");
}
}
- void IteratorDecl(ModuleDefinition module, out IteratorDecl/*!*/ iter) {
+ void IteratorDecl(DeclModifierData dmod, ModuleDefinition module, out IteratorDecl/*!*/ iter) {
Contract.Ensures(Contract.ValueAtReturn(out iter) != null);
IToken/*!*/ id;
Attributes attrs = null;
@@ -884,20 +1098,21 @@ bool IsType(ref IToken pt) {
IToken signatureEllipsis = null;
IToken bodyStart = Token.NoToken;
IToken bodyEnd = Token.NoToken;
+ CheckDeclModifiers(dmod, "Iterators", AllowedDeclModifiers.None);
- while (!(la.kind == 0 || la.kind == 78)) {SynErr(146); Get();}
- Expect(78);
- while (la.kind == 44) {
+ while (!(la.kind == 0 || la.kind == 84)) {SynErr(152); Get();}
+ Expect(84);
+ while (la.kind == 46) {
Attribute(ref attrs);
}
NoUSIdent(out id);
- if (la.kind == 48 || la.kind == 50) {
- if (la.kind == 50) {
+ if (la.kind == 50 || la.kind == 52) {
+ if (la.kind == 52) {
GenericParameters(typeArgs);
}
Formals(true, true, ins);
- if (la.kind == 79 || la.kind == 80) {
- if (la.kind == 79) {
+ if (la.kind == 85 || la.kind == 86) {
+ if (la.kind == 85) {
Get();
} else {
Get();
@@ -905,14 +1120,14 @@ bool IsType(ref IToken pt) {
}
Formals(false, true, outs);
}
- } else if (la.kind == 57) {
+ } else if (la.kind == 59) {
Get();
signatureEllipsis = t;
- } else SynErr(147);
- while (StartOf(5)) {
+ } else SynErr(153);
+ while (StartOf(7)) {
IteratorSpec(reads, mod, decreases, req, ens, yieldReq, yieldEns, ref readsAttrs, ref modAttrs, ref decrAttrs);
}
- if (la.kind == 44) {
+ if (la.kind == 46) {
BlockStmt(out body, out bodyStart, out bodyEnd);
}
iter = new IteratorDecl(id, id.val, module, typeArgs, ins, outs,
@@ -926,98 +1141,85 @@ bool IsType(ref IToken pt) {
}
- void TraitDecl(ModuleDefinition/*!*/ module, out TraitDecl/*!*/ trait) {
- Contract.Requires(module != null);
+ void TraitDecl(DeclModifierData dmodIn, ModuleDefinition/*!*/ module, out TraitDecl/*!*/ trait) {
+ Contract.Requires(module != null);
Contract.Ensures(Contract.ValueAtReturn(out trait) != null);
+ CheckDeclModifiers(dmodIn, "Traits", AllowedDeclModifiers.None);
IToken/*!*/ id;
Attributes attrs = null;
List<TypeParameter/*!*/> typeArgs = new List<TypeParameter/*!*/>(); //traits should not support type parameters at the moment
List<MemberDecl/*!*/> members = new List<MemberDecl/*!*/>();
IToken bodyStart;
+ DeclModifierData dmod;
- while (!(la.kind == 0 || la.kind == 69)) {SynErr(148); Get();}
- Expect(69);
- while (la.kind == 44) {
+ while (!(la.kind == 0 || la.kind == 78)) {SynErr(154); Get();}
+ Expect(78);
+ while (la.kind == 46) {
Attribute(ref attrs);
}
NoUSIdent(out id);
- if (la.kind == 50) {
+ if (la.kind == 52) {
GenericParameters(typeArgs);
}
- Expect(44);
+ Expect(46);
bodyStart = t;
- while (StartOf(2)) {
- ClassMemberDecl(members, true, false);
+ while (StartOf(4)) {
+ dmod = new DeclModifierData();
+ while (StartOf(2)) {
+ DeclModifier(ref dmod);
+ }
+ ClassMemberDecl(dmod, members, true, false, false);
}
- Expect(45);
+ Expect(47);
trait = new TraitDecl(id, id.val, module, typeArgs, members, attrs);
trait.BodyStartTok = bodyStart;
trait.BodyEndTok = t;
}
- void ClassMemberDecl(List<MemberDecl> mm, bool allowConstructors, bool moduleLevelDecl) {
+ void ClassMemberDecl(DeclModifierData dmod, List<MemberDecl> mm, bool allowConstructors, bool moduleLevelDecl, bool isWithinAbstractModule) {
Contract.Requires(cce.NonNullElements(mm));
Method/*!*/ m;
Function/*!*/ f;
- MemberModifiers mmod = new MemberModifiers();
- IToken staticToken = null, protectedToken = null;
- while (la.kind == 70 || la.kind == 71 || la.kind == 72) {
- if (la.kind == 70) {
- Get();
- mmod.IsGhost = true;
- } else if (la.kind == 71) {
- Get();
- mmod.IsStatic = true; staticToken = t;
- } else {
- Get();
- mmod.IsProtected = true; protectedToken = t;
- }
- }
- if (la.kind == 75) {
+ if (la.kind == 81) {
if (moduleLevelDecl) {
SemErr(la, "fields are not allowed to be declared at the module level; instead, wrap the field in a 'class' declaration");
- mmod.IsStatic = false;
- mmod.IsProtected = false;
+ dmod.IsStatic = false;
}
- FieldDecl(mmod, mm);
+ FieldDecl(dmod, mm);
} else if (IsFunctionDecl()) {
- if (moduleLevelDecl && staticToken != null) {
- errors.Warning(staticToken, "module-level functions are always non-instance, so the 'static' keyword is not allowed here");
- mmod.IsStatic = false;
+ if (moduleLevelDecl && dmod.StaticToken != null) {
+ errors.Warning(dmod.StaticToken, "module-level functions are always non-instance, so the 'static' keyword is not allowed here");
+ dmod.IsStatic = false;
}
- FunctionDecl(mmod, out f);
+ FunctionDecl(dmod, isWithinAbstractModule, out f);
mm.Add(f);
- } else if (StartOf(6)) {
- if (moduleLevelDecl && staticToken != null) {
- errors.Warning(staticToken, "module-level methods are always non-instance, so the 'static' keyword is not allowed here");
- mmod.IsStatic = false;
- }
- if (protectedToken != null) {
- SemErr(protectedToken, "only functions, not methods, can be declared 'protected'");
- mmod.IsProtected = false;
+ } else if (StartOf(8)) {
+ if (moduleLevelDecl && dmod.StaticToken != null) {
+ errors.Warning(dmod.StaticToken, "module-level methods are always non-instance, so the 'static' keyword is not allowed here");
+ dmod.IsStatic = false;
}
- MethodDecl(mmod, allowConstructors, out m);
+ MethodDecl(dmod, allowConstructors, isWithinAbstractModule, out m);
mm.Add(m);
- } else SynErr(149);
+ } else SynErr(155);
}
void Attribute(ref Attributes attrs) {
- string name;
+ IToken x; string name;
var args = new List<Expression>();
- Expect(44);
- Expect(20);
- Expect(1);
- name = t.val;
- if (StartOf(7)) {
+ Expect(46);
+ Expect(21);
+ NoUSIdent(out x);
+ name = x.val;
+ if (StartOf(9)) {
Expressions(args);
}
- Expect(45);
+ Expect(47);
attrs = new Attributes(name, args, attrs);
}
@@ -1035,7 +1237,7 @@ bool IsType(ref IToken pt) {
IToken id; ids = new List<IToken>();
Ident(out id);
ids.Add(id);
- while (la.kind == 25) {
+ while (la.kind == 27) {
Get();
Ident(out id);
ids.Add(id);
@@ -1053,29 +1255,29 @@ bool IsType(ref IToken pt) {
IToken/*!*/ id;
TypeParameter.EqualitySupportValue eqSupport;
- Expect(50);
+ Expect(52);
NoUSIdent(out id);
eqSupport = TypeParameter.EqualitySupportValue.Unspecified;
- if (la.kind == 48) {
+ if (la.kind == 50) {
Get();
- Expect(52);
- Expect(49);
+ Expect(54);
+ Expect(51);
eqSupport = TypeParameter.EqualitySupportValue.Required;
}
typeArgs.Add(new TypeParameter(id, id.val, eqSupport));
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
NoUSIdent(out id);
eqSupport = TypeParameter.EqualitySupportValue.Unspecified;
- if (la.kind == 48) {
+ if (la.kind == 50) {
Get();
- Expect(52);
- Expect(49);
+ Expect(54);
+ Expect(51);
eqSupport = TypeParameter.EqualitySupportValue.Required;
}
typeArgs.Add(new TypeParameter(id, id.val, eqSupport));
}
- Expect(51);
+ Expect(53);
}
void Type(out Type ty) {
@@ -1083,29 +1285,28 @@ bool IsType(ref IToken pt) {
TypeAndToken(out tok, out ty);
}
- void FieldDecl(MemberModifiers mmod, List<MemberDecl/*!*/>/*!*/ mm) {
+ void FieldDecl(DeclModifierData dmod, List<MemberDecl/*!*/>/*!*/ mm) {
Contract.Requires(cce.NonNullElements(mm));
Attributes attrs = null;
IToken/*!*/ id; Type/*!*/ ty;
+ CheckDeclModifiers(dmod, "Fields", AllowedDeclModifiers.Ghost);
- while (!(la.kind == 0 || la.kind == 75)) {SynErr(150); Get();}
- Expect(75);
- if (mmod.IsStatic) { SemErr(t, "fields cannot be declared 'static'"); }
-
- while (la.kind == 44) {
+ while (!(la.kind == 0 || la.kind == 81)) {SynErr(156); Get();}
+ Expect(81);
+ while (la.kind == 46) {
Attribute(ref attrs);
}
FIdentType(out id, out ty);
- mm.Add(new Field(id, id.val, mmod.IsGhost, ty, attrs));
- while (la.kind == 21) {
+ mm.Add(new Field(id, id.val, dmod.IsGhost, ty, attrs));
+ while (la.kind == 22) {
Get();
FIdentType(out id, out ty);
- mm.Add(new Field(id, id.val, mmod.IsGhost, ty, attrs));
+ mm.Add(new Field(id, id.val, dmod.IsGhost, ty, attrs));
}
OldSemi();
}
- void FunctionDecl(MemberModifiers mmod, out Function/*!*/ f) {
+ void FunctionDecl(DeclModifierData dmod, bool isWithinAbstractModule, out Function/*!*/ f) {
Contract.Ensures(Contract.ValueAtReturn(out f)!=null);
Attributes attrs = null;
IToken/*!*/ id = Token.NoToken; // to please compiler
@@ -1124,129 +1325,144 @@ bool IsType(ref IToken pt) {
IToken signatureEllipsis = null;
bool missingOpenParen;
- if (la.kind == 36) {
+ if (la.kind == 38) {
Get();
- if (la.kind == 81) {
+ if (la.kind == 87) {
Get();
isFunctionMethod = true;
}
- if (mmod.IsGhost) { SemErr(t, "functions cannot be declared 'ghost' (they are ghost by default)"); }
+ AllowedDeclModifiers allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected;
+ string caption = "Functions";
+ if (isFunctionMethod) {
+ allowed |= AllowedDeclModifiers.Extern;
+ caption = "Function methods";
+ }
+ CheckDeclModifiers(dmod, caption, allowed);
- while (la.kind == 44) {
+ while (la.kind == 46) {
Attribute(ref attrs);
}
NoUSIdent(out id);
- if (la.kind == 48 || la.kind == 50) {
- if (la.kind == 50) {
+ if (la.kind == 50 || la.kind == 52) {
+ if (la.kind == 52) {
GenericParameters(typeArgs);
}
Formals(true, isFunctionMethod, formals);
- Expect(20);
+ Expect(21);
Type(out returnType);
- } else if (la.kind == 57) {
+ } else if (la.kind == 59) {
Get();
signatureEllipsis = t;
- } else SynErr(151);
- } else if (la.kind == 37) {
+ } else SynErr(157);
+ } else if (la.kind == 39) {
Get();
isPredicate = true;
- if (la.kind == 81) {
+ if (la.kind == 87) {
Get();
isFunctionMethod = true;
}
- if (mmod.IsGhost) { SemErr(t, "predicates cannot be declared 'ghost' (they are ghost by default)"); }
+ AllowedDeclModifiers allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected;
+ string caption = "Predicates";
+ if (isFunctionMethod) {
+ allowed |= AllowedDeclModifiers.Extern;
+ caption = "Predicate methods";
+ }
+ CheckDeclModifiers(dmod, caption, allowed);
- while (la.kind == 44) {
+ while (la.kind == 46) {
Attribute(ref attrs);
}
NoUSIdent(out id);
- if (StartOf(8)) {
- if (la.kind == 50) {
+ if (StartOf(10)) {
+ if (la.kind == 52) {
GenericParameters(typeArgs);
}
missingOpenParen = true;
- if (la.kind == 48) {
+ if (la.kind == 50) {
Formals(true, isFunctionMethod, formals);
missingOpenParen = false;
}
if (missingOpenParen) { errors.Warning(t, "with the new support of higher-order functions in Dafny, parentheses-less predicates are no longer supported; in the new syntax, parentheses are required for the declaration and uses of predicates, even if the predicate takes no additional arguments"); }
- if (la.kind == 20) {
+ if (la.kind == 21) {
Get();
SemErr(t, "predicates do not have an explicitly declared return type; it is always bool");
}
- } else if (la.kind == 57) {
+ } else if (la.kind == 59) {
Get();
signatureEllipsis = t;
- } else SynErr(152);
- } else if (la.kind == 38) {
+ } else SynErr(158);
+ } else if (la.kind == 40) {
Get();
- Expect(37);
+ Expect(39);
isIndPredicate = true;
- if (mmod.IsGhost) { SemErr(t, "inductive predicates cannot be declared 'ghost' (they are ghost by default)"); }
+ CheckDeclModifiers(dmod, "Inductive predicates",
+ AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected);
- while (la.kind == 44) {
+ while (la.kind == 46) {
Attribute(ref attrs);
}
NoUSIdent(out id);
- if (la.kind == 48 || la.kind == 50) {
- if (la.kind == 50) {
+ if (la.kind == 50 || la.kind == 52) {
+ if (la.kind == 52) {
GenericParameters(typeArgs);
}
Formals(true, isFunctionMethod, formals);
- if (la.kind == 20) {
+ if (la.kind == 21) {
Get();
SemErr(t, "inductive predicates do not have an explicitly declared return type; it is always bool");
}
- } else if (la.kind == 57) {
+ } else if (la.kind == 59) {
Get();
signatureEllipsis = t;
- } else SynErr(153);
- } else if (la.kind == 40) {
+ } else SynErr(159);
+ } else if (la.kind == 42) {
Get();
isCoPredicate = true;
- if (mmod.IsGhost) { SemErr(t, "copredicates cannot be declared 'ghost' (they are ghost by default)"); }
+ CheckDeclModifiers(dmod, "Copredicates",
+ AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected);
- while (la.kind == 44) {
+ while (la.kind == 46) {
Attribute(ref attrs);
}
NoUSIdent(out id);
- if (la.kind == 48 || la.kind == 50) {
- if (la.kind == 50) {
+ if (la.kind == 50 || la.kind == 52) {
+ if (la.kind == 52) {
GenericParameters(typeArgs);
}
Formals(true, isFunctionMethod, formals);
- if (la.kind == 20) {
+ if (la.kind == 21) {
Get();
SemErr(t, "copredicates do not have an explicitly declared return type; it is always bool");
}
- } else if (la.kind == 57) {
+ } else if (la.kind == 59) {
Get();
signatureEllipsis = t;
- } else SynErr(154);
- } else SynErr(155);
+ } else SynErr(160);
+ } else SynErr(161);
decreases = isIndPredicate || isCoPredicate ? null : new List<Expression/*!*/>();
- while (StartOf(9)) {
+ while (StartOf(11)) {
FunctionSpec(reqs, reads, ens, decreases);
}
- if (la.kind == 44) {
+ if (la.kind == 46) {
FunctionBody(out body, out bodyStart, out bodyEnd);
}
- if (DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported")) {
+ if (!isWithinAbstractModule && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 &&
+ !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported")) {
SemErr(t, "a function with an ensures clause must have a body, unless given the :axiom attribute");
}
-
+ EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ true);
IToken tok = theVerifyThisFile ? id : new IncludeToken(id);
if (isPredicate) {
- f = new Predicate(tok, id.val, mmod.IsStatic, mmod.IsProtected, !isFunctionMethod, typeArgs, formals,
+ f = new Predicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, !isFunctionMethod, typeArgs, formals,
reqs, reads, ens, new Specification<Expression>(decreases, null), body, Predicate.BodyOriginKind.OriginalOrInherited, attrs, signatureEllipsis);
} else if (isIndPredicate) {
- f = new InductivePredicate(tok, id.val, mmod.IsStatic, mmod.IsProtected, typeArgs, formals,
+ f = new InductivePredicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, typeArgs, formals,
reqs, reads, ens, body, attrs, signatureEllipsis);
} else if (isCoPredicate) {
- f = new CoPredicate(tok, id.val, mmod.IsStatic, mmod.IsProtected, typeArgs, formals,
+ f = new CoPredicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, typeArgs, formals,
reqs, reads, ens, body, attrs, signatureEllipsis);
} else {
- f = new Function(tok, id.val, mmod.IsStatic, mmod.IsProtected, !isFunctionMethod, typeArgs, formals, returnType,
+ f = new Function(tok, id.val, dmod.IsStatic, dmod.IsProtected, !isFunctionMethod, typeArgs, formals, returnType,
reqs, reads, ens, new Specification<Expression>(decreases, null), body, attrs, signatureEllipsis);
}
f.BodyStartTok = bodyStart;
@@ -1259,7 +1475,7 @@ bool IsType(ref IToken pt) {
}
- void MethodDecl(MemberModifiers mmod, bool allowConstructor, out Method/*!*/ m) {
+ void MethodDecl(DeclModifierData dmod, bool allowConstructor, bool isWithinAbstractModule, out Method/*!*/ m) {
Contract.Ensures(Contract.ValueAtReturn(out m) !=null);
IToken/*!*/ id = Token.NoToken;
bool hasName = false; IToken keywordToken;
@@ -1281,71 +1497,65 @@ bool IsType(ref IToken pt) {
IToken signatureEllipsis = null;
IToken bodyStart = Token.NoToken;
IToken bodyEnd = Token.NoToken;
+ AllowedDeclModifiers allowed = AllowedDeclModifiers.None;
+ string caption = "";
- while (!(StartOf(10))) {SynErr(156); Get();}
+ while (!(StartOf(12))) {SynErr(162); Get();}
switch (la.kind) {
- case 81: {
+ case 87: {
Get();
+ caption = "Methods";
+ allowed = AllowedDeclModifiers.Ghost | AllowedDeclModifiers.Static
+ | AllowedDeclModifiers.Extern;
break;
}
- case 39: {
+ case 41: {
Get();
- isLemma = true;
+ isLemma = true; caption = "Lemmas";
+ allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static
+ | AllowedDeclModifiers.Protected;
break;
}
- case 82: {
+ case 88: {
Get();
- isCoLemma = true;
+ isCoLemma = true; caption = "Colemmas";
+ allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static
+ | AllowedDeclModifiers.Protected;
break;
}
- case 83: {
+ case 89: {
Get();
- isCoLemma = true;
+ isCoLemma = true; caption = "Comethods";
+ allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static
+ | AllowedDeclModifiers.Protected;
errors.Warning(t, "the 'comethod' keyword has been deprecated; it has been renamed to 'colemma'");
break;
}
- case 38: {
+ case 40: {
Get();
- Expect(39);
- isIndLemma = true;
+ Expect(41);
+ isIndLemma = true; caption = "Inductive lemmas";
+ allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static;
break;
}
- case 84: {
+ case 90: {
Get();
if (allowConstructor) {
isConstructor = true;
} else {
SemErr(t, "constructors are allowed only in classes");
- }
+ }
+ caption = "Constructors";
+ allowed = AllowedDeclModifiers.None;
break;
}
- default: SynErr(157); break;
+ default: SynErr(163); break;
}
keywordToken = t;
- if (isLemma) {
- if (mmod.IsGhost) {
- SemErr(t, "lemmas cannot be declared 'ghost' (they are automatically 'ghost')");
- }
- } else if (isConstructor) {
- if (mmod.IsGhost) {
- SemErr(t, "constructors cannot be declared 'ghost'");
- }
- if (mmod.IsStatic) {
- SemErr(t, "constructors cannot be declared 'static'");
- }
- } else if (isIndLemma) {
- if (mmod.IsGhost) {
- SemErr(t, "inductive lemmas cannot be declared 'ghost' (they are automatically 'ghost')");
- }
- } else if (isCoLemma) {
- if (mmod.IsGhost) {
- SemErr(t, "colemmas cannot be declared 'ghost' (they are automatically 'ghost')");
- }
- }
-
- while (la.kind == 44) {
+ CheckDeclModifiers(dmod, caption, allowed);
+ while (la.kind == 46) {
Attribute(ref attrs);
}
if (la.kind == 1) {
@@ -1358,28 +1568,29 @@ bool IsType(ref IToken pt) {
SemErr(la, "a method must be given a name (expecting identifier)");
}
}
+ EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ true);
- if (la.kind == 48 || la.kind == 50) {
- if (la.kind == 50) {
+ if (la.kind == 50 || la.kind == 52) {
+ if (la.kind == 52) {
GenericParameters(typeArgs);
}
- Formals(true, !mmod.IsGhost, ins);
- if (la.kind == 80) {
+ Formals(true, !dmod.IsGhost, ins);
+ if (la.kind == 86) {
Get();
if (isConstructor) { SemErr(t, "constructors cannot have out-parameters"); }
- Formals(false, !mmod.IsGhost, outs);
+ Formals(false, !dmod.IsGhost, outs);
}
- } else if (la.kind == 57) {
+ } else if (la.kind == 59) {
Get();
signatureEllipsis = t;
- } else SynErr(158);
- while (StartOf(11)) {
+ } else SynErr(164);
+ while (StartOf(13)) {
MethodSpec(req, mod, ens, dec, ref decAttrs, ref modAttrs);
}
- if (la.kind == 44) {
+ if (la.kind == 46) {
BlockStmt(out body, out bodyStart, out bodyEnd);
}
- if (DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported") && !Attributes.Contains(attrs, "decl") && theVerifyThisFile) {
+ if (!isWithinAbstractModule && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported") && !Attributes.Contains(attrs, "decl") && theVerifyThisFile) {
SemErr(t, "a method with an ensures clause must have a body, unless given the :axiom attribute");
}
@@ -1388,16 +1599,16 @@ bool IsType(ref IToken pt) {
m = new Constructor(tok, hasName ? id.val : "_ctor", typeArgs, ins,
req, new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, attrs, signatureEllipsis);
} else if (isIndLemma) {
- m = new InductiveLemma(tok, id.val, mmod.IsStatic, typeArgs, ins, outs,
+ m = new InductiveLemma(tok, id.val, dmod.IsStatic, typeArgs, ins, outs,
req, new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, attrs, signatureEllipsis);
} else if (isCoLemma) {
- m = new CoLemma(tok, id.val, mmod.IsStatic, typeArgs, ins, outs,
+ m = new CoLemma(tok, id.val, dmod.IsStatic, typeArgs, ins, outs,
req, new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, attrs, signatureEllipsis);
} else if (isLemma) {
- m = new Lemma(tok, id.val, mmod.IsStatic, typeArgs, ins, outs,
+ m = new Lemma(tok, id.val, dmod.IsStatic, typeArgs, ins, outs,
req, new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, attrs, signatureEllipsis);
} else {
- m = new Method(tok, id.val, mmod.IsStatic, mmod.IsGhost, typeArgs, ins, outs,
+ m = new Method(tok, id.val, dmod.IsStatic, dmod.IsGhost, typeArgs, ins, outs,
req, new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, attrs, signatureEllipsis);
}
m.BodyStartTok = bodyStart;
@@ -1411,11 +1622,11 @@ bool IsType(ref IToken pt) {
IToken/*!*/ id;
List<Formal/*!*/> formals = new List<Formal/*!*/>();
- while (la.kind == 44) {
+ while (la.kind == 46) {
Attribute(ref attrs);
}
NoUSIdent(out id);
- if (la.kind == 48) {
+ if (la.kind == 50) {
FormalsOptionalIds(formals);
}
ctors.Add(new DatatypeCtor(id, id.val, formals, attrs));
@@ -1423,17 +1634,17 @@ bool IsType(ref IToken pt) {
void FormalsOptionalIds(List<Formal/*!*/>/*!*/ formals) {
Contract.Requires(cce.NonNullElements(formals)); IToken/*!*/ id; Type/*!*/ ty; string/*!*/ name; bool isGhost;
- Expect(48);
- if (StartOf(12)) {
+ Expect(50);
+ if (StartOf(14)) {
TypeIdentOptional(out id, out name, out ty, out isGhost);
formals.Add(new Formal(id, name, ty, true, isGhost));
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
TypeIdentOptional(out id, out name, out ty, out isGhost);
formals.Add(new Formal(id, name, ty, true, isGhost));
}
}
- Expect(49);
+ Expect(51);
}
void FIdentType(out IToken/*!*/ id, out Type/*!*/ ty) {
@@ -1445,14 +1656,14 @@ bool IsType(ref IToken pt) {
} else if (la.kind == 2) {
Get();
id = t;
- } else SynErr(159);
- Expect(20);
+ } else SynErr(165);
+ Expect(21);
Type(out ty);
}
void OldSemi() {
- if (la.kind == 26) {
- while (!(la.kind == 0 || la.kind == 26)) {SynErr(160); Get();}
+ if (la.kind == 28) {
+ while (!(la.kind == 0 || la.kind == 28)) {SynErr(166); Get();}
Get();
}
}
@@ -1461,7 +1672,7 @@ bool IsType(ref IToken pt) {
Expression e0; IToken endTok;
EquivExpression(out e, allowSemi, allowLambda);
if (SemiFollowsCall(allowSemi, e)) {
- Expect(26);
+ Expect(28);
endTok = t;
Expression(out e0, allowSemi, allowLambda);
e = new StmtExpr(e.tok,
@@ -1475,7 +1686,7 @@ bool IsType(ref IToken pt) {
Contract.Ensures(Contract.ValueAtReturn(out id)!=null);
Contract.Ensures(Contract.ValueAtReturn(out ty)!=null);
isGhost = false;
- if (la.kind == 70) {
+ if (la.kind == 62) {
Get();
if (allowGhostKeyword) { isGhost = true; } else { SemErr(t, "formal cannot be declared 'ghost' in this context"); }
}
@@ -1485,7 +1696,7 @@ bool IsType(ref IToken pt) {
void IdentType(out IToken/*!*/ id, out Type/*!*/ ty, bool allowWildcardId) {
Contract.Ensures(Contract.ValueAtReturn(out id) != null); Contract.Ensures(Contract.ValueAtReturn(out ty) != null);
WildIdent(out id, allowWildcardId);
- Expect(20);
+ Expect(21);
Type(out ty);
}
@@ -1501,7 +1712,7 @@ bool IsType(ref IToken pt) {
IToken id; Type ty; Type optType = null;
WildIdent(out id, true);
- if (la.kind == 20) {
+ if (la.kind == 21) {
Get();
Type(out ty);
optType = ty;
@@ -1514,7 +1725,7 @@ bool IsType(ref IToken pt) {
IToken id; Type ty; Type optType = null;
WildIdent(out id, true);
- if (la.kind == 20) {
+ if (la.kind == 21) {
Get();
Type(out ty);
optType = ty;
@@ -1527,13 +1738,13 @@ bool IsType(ref IToken pt) {
Contract.Ensures(Contract.ValueAtReturn(out ty)!=null);
Contract.Ensures(Contract.ValueAtReturn(out identName)!=null);
string name = null; id = Token.NoToken; ty = new BoolType()/*dummy*/; isGhost = false;
- if (la.kind == 70) {
+ if (la.kind == 62) {
Get();
isGhost = true;
}
- if (StartOf(3)) {
+ if (StartOf(5)) {
TypeAndToken(out id, out ty);
- if (la.kind == 20) {
+ if (la.kind == 21) {
Get();
UserDefinedType udt = ty as UserDefinedType;
if (udt != null && udt.TypeArgs.Count == 0) {
@@ -1547,9 +1758,9 @@ bool IsType(ref IToken pt) {
} else if (la.kind == 2) {
Get();
id = t; name = id.val;
- Expect(20);
+ Expect(21);
Type(out ty);
- } else SynErr(161);
+ } else SynErr(167);
if (name != null) {
identName = name;
} else {
@@ -1597,20 +1808,33 @@ bool IsType(ref IToken pt) {
case 13: {
Get();
tok = t; gt = new List<Type>();
- if (la.kind == 50) {
+ if (la.kind == 52) {
GenericInstantiation(gt);
}
if (gt.Count > 1) {
SemErr("set type expects only one type argument");
}
- ty = new SetType(gt.Count == 1 ? gt[0] : null);
+ ty = new SetType(true, gt.Count == 1 ? gt[0] : null);
break;
}
case 14: {
Get();
tok = t; gt = new List<Type>();
- if (la.kind == 50) {
+ if (la.kind == 52) {
+ GenericInstantiation(gt);
+ }
+ if (gt.Count > 1) {
+ SemErr("set type expects only one type argument");
+ }
+ ty = new SetType(false, gt.Count == 1 ? gt[0] : null);
+
+ break;
+ }
+ case 15: {
+ Get();
+ tok = t; gt = new List<Type>();
+ if (la.kind == 52) {
GenericInstantiation(gt);
}
if (gt.Count > 1) {
@@ -1620,10 +1844,10 @@ bool IsType(ref IToken pt) {
break;
}
- case 15: {
+ case 16: {
Get();
tok = t; gt = new List<Type>();
- if (la.kind == 50) {
+ if (la.kind == 52) {
GenericInstantiation(gt);
}
if (gt.Count > 1) {
@@ -1638,10 +1862,10 @@ bool IsType(ref IToken pt) {
tok = t; ty = new UserDefinedType(tok, tok.val, null);
break;
}
- case 16: {
+ case 17: {
Get();
tok = t; gt = new List<Type>();
- if (la.kind == 50) {
+ if (la.kind == 52) {
GenericInstantiation(gt);
}
if (gt.Count == 0) {
@@ -1655,10 +1879,10 @@ bool IsType(ref IToken pt) {
break;
}
- case 17: {
+ case 18: {
Get();
tok = t; gt = new List<Type>();
- if (la.kind == 50) {
+ if (la.kind == 52) {
GenericInstantiation(gt);
}
if (gt.Count == 0) {
@@ -1675,7 +1899,7 @@ bool IsType(ref IToken pt) {
case 5: {
Get();
tok = t; gt = null;
- if (la.kind == 50) {
+ if (la.kind == 52) {
gt = new List<Type>();
GenericInstantiation(gt);
}
@@ -1684,19 +1908,19 @@ bool IsType(ref IToken pt) {
break;
}
- case 48: {
+ case 50: {
Get();
tok = t; tupleArgTypes = new List<Type>();
- if (StartOf(3)) {
+ if (StartOf(5)) {
Type(out ty);
tupleArgTypes.Add(ty);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
Type(out ty);
tupleArgTypes.Add(ty);
}
}
- Expect(49);
+ Expect(51);
if (tupleArgTypes.Count == 1) {
// just return the type 'ty'
} else {
@@ -1711,11 +1935,11 @@ bool IsType(ref IToken pt) {
Expression e; tok = t;
NameSegmentForTypeName(out e);
tok = t;
- while (la.kind == 25) {
+ while (la.kind == 27) {
Get();
Expect(1);
tok = t; List<Type> typeArgs = null;
- if (la.kind == 50) {
+ if (la.kind == 52) {
typeArgs = new List<Type>();
GenericInstantiation(typeArgs);
}
@@ -1724,9 +1948,9 @@ bool IsType(ref IToken pt) {
ty = new UserDefinedType(e.tok, e);
break;
}
- default: SynErr(162); break;
+ default: SynErr(168); break;
}
- if (la.kind == 28) {
+ if (la.kind == 30) {
Type t2;
Get();
tok = t;
@@ -1744,17 +1968,17 @@ bool IsType(ref IToken pt) {
void Formals(bool incoming, bool allowGhostKeyword, List<Formal> formals) {
Contract.Requires(cce.NonNullElements(formals)); IToken id; Type ty; bool isGhost;
- Expect(48);
- if (la.kind == 1 || la.kind == 70) {
+ Expect(50);
+ if (la.kind == 1 || la.kind == 62) {
GIdentType(allowGhostKeyword, out id, out ty, out isGhost);
formals.Add(new Formal(id, id.val, ty, incoming, isGhost));
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
GIdentType(allowGhostKeyword, out id, out ty, out isGhost);
formals.Add(new Formal(id, id.val, ty, incoming, isGhost));
}
}
- Expect(49);
+ Expect(51);
}
void IteratorSpec(List<FrameExpression/*!*/>/*!*/ reads, List<FrameExpression/*!*/>/*!*/ mod, List<Expression/*!*/> decreases,
@@ -1763,45 +1987,45 @@ List<MaybeFreeExpression/*!*/>/*!*/ yieldReq, List<MaybeFreeExpression/*!*/>/*!*
ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) {
Expression/*!*/ e; FrameExpression/*!*/ fe; bool isFree = false; bool isYield = false; Attributes ensAttrs = null;
- while (!(StartOf(13))) {SynErr(163); Get();}
- if (la.kind == 42) {
+ while (!(StartOf(15))) {SynErr(169); Get();}
+ if (la.kind == 44) {
Get();
while (IsAttribute()) {
Attribute(ref readsAttrs);
}
FrameExpression(out fe, false, false);
reads.Add(fe);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
FrameExpression(out fe, false, false);
reads.Add(fe);
}
OldSemi();
- } else if (la.kind == 41) {
+ } else if (la.kind == 43) {
Get();
while (IsAttribute()) {
Attribute(ref modAttrs);
}
FrameExpression(out fe, false, false);
mod.Add(fe);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
FrameExpression(out fe, false, false);
mod.Add(fe);
}
OldSemi();
- } else if (StartOf(14)) {
- if (la.kind == 85) {
+ } else if (StartOf(16)) {
+ if (la.kind == 91) {
Get();
isFree = true;
errors.Warning(t, "the 'free' keyword is soon to be deprecated");
}
- if (la.kind == 87) {
+ if (la.kind == 93) {
Get();
isYield = true;
}
- if (la.kind == 43) {
+ if (la.kind == 45) {
Get();
Expression(out e, false, false);
OldSemi();
@@ -1811,7 +2035,7 @@ ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) {
req.Add(new MaybeFreeExpression(e, isFree));
}
- } else if (la.kind == 86) {
+ } else if (la.kind == 92) {
Get();
while (IsAttribute()) {
Attribute(ref ensAttrs);
@@ -1824,27 +2048,27 @@ ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) {
ens.Add(new MaybeFreeExpression(e, isFree, ensAttrs));
}
- } else SynErr(164);
- } else if (la.kind == 34) {
+ } else SynErr(170);
+ } else if (la.kind == 36) {
Get();
while (IsAttribute()) {
Attribute(ref decrAttrs);
}
DecreasesList(decreases, false, false);
OldSemi();
- } else SynErr(165);
+ } else SynErr(171);
}
void BlockStmt(out BlockStmt/*!*/ block, out IToken bodyStart, out IToken bodyEnd) {
Contract.Ensures(Contract.ValueAtReturn(out block) != null);
List<Statement/*!*/> body = new List<Statement/*!*/>();
- Expect(44);
+ Expect(46);
bodyStart = t;
- while (StartOf(15)) {
+ while (StartOf(17)) {
Stmt(body);
}
- Expect(45);
+ Expect(47);
bodyEnd = t;
block = new BlockStmt(bodyStart, bodyEnd, body);
}
@@ -1854,33 +2078,33 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Contract.Requires(cce.NonNullElements(req)); Contract.Requires(cce.NonNullElements(mod)); Contract.Requires(cce.NonNullElements(ens)); Contract.Requires(cce.NonNullElements(decreases));
Expression/*!*/ e; FrameExpression/*!*/ fe; bool isFree = false; Attributes ensAttrs = null;
- while (!(StartOf(16))) {SynErr(166); Get();}
- if (la.kind == 41) {
+ while (!(StartOf(18))) {SynErr(172); Get();}
+ if (la.kind == 43) {
Get();
while (IsAttribute()) {
Attribute(ref modAttrs);
}
FrameExpression(out fe, false, false);
mod.Add(fe);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
FrameExpression(out fe, false, false);
mod.Add(fe);
}
OldSemi();
- } else if (la.kind == 43 || la.kind == 85 || la.kind == 86) {
- if (la.kind == 85) {
+ } else if (la.kind == 45 || la.kind == 91 || la.kind == 92) {
+ if (la.kind == 91) {
Get();
isFree = true;
errors.Warning(t, "the 'free' keyword is soon to be deprecated");
}
- if (la.kind == 43) {
+ if (la.kind == 45) {
Get();
Expression(out e, false, false);
OldSemi();
req.Add(new MaybeFreeExpression(e, isFree));
- } else if (la.kind == 86) {
+ } else if (la.kind == 92) {
Get();
while (IsAttribute()) {
Attribute(ref ensAttrs);
@@ -1888,15 +2112,15 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Expression(out e, false, false);
OldSemi();
ens.Add(new MaybeFreeExpression(e, isFree, ensAttrs));
- } else SynErr(167);
- } else if (la.kind == 34) {
+ } else SynErr(173);
+ } else if (la.kind == 36) {
Get();
while (IsAttribute()) {
Attribute(ref decAttrs);
}
DecreasesList(decreases, true, false);
OldSemi();
- } else SynErr(168);
+ } else SynErr(174);
}
void FrameExpression(out FrameExpression fe, bool allowSemi, bool allowLambda) {
@@ -1906,21 +2130,21 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
string fieldName = null; IToken feTok = null;
fe = null;
- if (StartOf(7)) {
+ if (StartOf(9)) {
Expression(out e, allowSemi, allowLambda);
feTok = e.tok;
- if (la.kind == 88) {
+ if (la.kind == 94) {
Get();
Ident(out id);
fieldName = id.val; feTok = id;
}
fe = new FrameExpression(feTok, e, fieldName);
- } else if (la.kind == 88) {
+ } else if (la.kind == 94) {
Get();
Ident(out id);
fieldName = id.val;
fe = new FrameExpression(id, new ImplicitThisExpr(id), fieldName);
- } else SynErr(169);
+ } else SynErr(175);
}
void DecreasesList(List<Expression> decreases, bool allowWildcard, bool allowLambda) {
@@ -1932,7 +2156,7 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
decreases.Add(e);
}
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
PossiblyWildExpression(out e, allowLambda);
if (!allowWildcard && e is WildcardExpr) {
@@ -1946,15 +2170,15 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
void GenericInstantiation(List<Type/*!*/>/*!*/ gt) {
Contract.Requires(cce.NonNullElements(gt)); Type/*!*/ ty;
- Expect(50);
+ Expect(52);
Type(out ty);
gt.Add(ty);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
Type(out ty);
gt.Add(ty);
}
- Expect(51);
+ Expect(53);
}
void NameSegmentForTypeName(out Expression e) {
@@ -1962,7 +2186,7 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
List<Type> typeArgs = null;
Ident(out id);
- if (la.kind == 50) {
+ if (la.kind == 52) {
typeArgs = new List<Type>();
GenericInstantiation(typeArgs);
}
@@ -1975,28 +2199,28 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Contract.Requires(cce.NonNullElements(reads));
Contract.Requires(decreases == null || cce.NonNullElements(decreases));
Expression/*!*/ e; FrameExpression/*!*/ fe;
- while (!(StartOf(17))) {SynErr(170); Get();}
- if (la.kind == 43) {
+ while (!(StartOf(19))) {SynErr(176); Get();}
+ if (la.kind == 45) {
Get();
Expression(out e, false, false);
OldSemi();
reqs.Add(e);
- } else if (la.kind == 42) {
+ } else if (la.kind == 44) {
Get();
PossiblyWildFrameExpression(out fe, false);
reads.Add(fe);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
PossiblyWildFrameExpression(out fe, false);
reads.Add(fe);
}
OldSemi();
- } else if (la.kind == 86) {
+ } else if (la.kind == 92) {
Get();
Expression(out e, false, false);
OldSemi();
ens.Add(e);
- } else if (la.kind == 34) {
+ } else if (la.kind == 36) {
Get();
if (decreases == null) {
SemErr(t, "'decreases' clauses are meaningless for copredicates, so they are not allowed");
@@ -2005,37 +2229,37 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
DecreasesList(decreases, false, false);
OldSemi();
- } else SynErr(171);
+ } else SynErr(177);
}
void FunctionBody(out Expression/*!*/ e, out IToken bodyStart, out IToken bodyEnd) {
Contract.Ensures(Contract.ValueAtReturn(out e) != null); e = dummyExpr;
- Expect(44);
+ Expect(46);
bodyStart = t;
Expression(out e, true, true);
- Expect(45);
+ Expect(47);
bodyEnd = t;
}
void PossiblyWildFrameExpression(out FrameExpression fe, bool allowSemi) {
Contract.Ensures(Contract.ValueAtReturn(out fe) != null); fe = dummyFrameExpr;
- if (la.kind == 55) {
+ if (la.kind == 57) {
Get();
fe = new FrameExpression(t, new WildcardExpr(t), null);
- } else if (StartOf(18)) {
+ } else if (StartOf(20)) {
FrameExpression(out fe, allowSemi, false);
- } else SynErr(172);
+ } else SynErr(178);
}
void PossiblyWildExpression(out Expression e, bool allowLambda) {
Contract.Ensures(Contract.ValueAtReturn(out e)!=null);
e = dummyExpr;
- if (la.kind == 55) {
+ if (la.kind == 57) {
Get();
e = new WildcardExpr(t);
- } else if (StartOf(7)) {
+ } else if (StartOf(9)) {
Expression(out e, false, allowLambda);
- } else SynErr(173);
+ } else SynErr(179);
}
void Stmt(List<Statement/*!*/>/*!*/ ss) {
@@ -2052,92 +2276,92 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
IToken bodyStart, bodyEnd;
int breakCount;
- while (!(StartOf(19))) {SynErr(174); Get();}
+ while (!(StartOf(21))) {SynErr(180); Get();}
switch (la.kind) {
- case 44: {
+ case 46: {
BlockStmt(out bs, out bodyStart, out bodyEnd);
s = bs;
break;
}
- case 99: {
+ case 104: {
AssertStmt(out s);
break;
}
- case 29: {
+ case 31: {
AssumeStmt(out s);
break;
}
- case 100: {
+ case 105: {
PrintStmt(out s);
break;
}
- case 1: case 2: case 3: case 4: case 8: case 10: case 18: case 19: case 22: case 48: case 129: case 130: case 131: case 132: case 133: case 134: {
+ case 1: case 2: case 3: case 4: case 8: case 10: case 19: case 20: case 23: case 50: case 133: case 134: case 135: case 136: case 137: case 138: {
UpdateStmt(out s);
break;
}
- case 70: case 75: {
+ case 62: case 81: {
VarDeclStatement(out s);
break;
}
- case 96: {
+ case 101: {
IfStmt(out s);
break;
}
- case 97: {
+ case 102: {
WhileStmt(out s);
break;
}
- case 98: {
+ case 103: {
MatchStmt(out s);
break;
}
- case 101: case 102: {
+ case 106: case 107: {
ForallStmt(out s);
break;
}
- case 30: {
+ case 32: {
CalcStmt(out s);
break;
}
- case 103: {
+ case 108: {
ModifyStmt(out s);
break;
}
- case 89: {
+ case 95: {
Get();
x = t;
NoUSIdent(out id);
- Expect(20);
+ Expect(21);
OneStmt(out s);
s.Labels = new LList<Label>(new Label(x, id.val), s.Labels);
break;
}
- case 90: {
+ case 96: {
Get();
x = t; breakCount = 1; label = null;
if (la.kind == 1) {
NoUSIdent(out id);
label = id.val;
- } else if (la.kind == 26 || la.kind == 90) {
- while (la.kind == 90) {
+ } else if (la.kind == 28 || la.kind == 96) {
+ while (la.kind == 96) {
Get();
breakCount++;
}
- } else SynErr(175);
- while (!(la.kind == 0 || la.kind == 26)) {SynErr(176); Get();}
- Expect(26);
+ } else SynErr(181);
+ while (!(la.kind == 0 || la.kind == 28)) {SynErr(182); Get();}
+ Expect(28);
s = label != null ? new BreakStmt(x, t, label) : new BreakStmt(x, t, breakCount);
break;
}
- case 87: case 93: {
+ case 93: case 99: {
ReturnStmt(out s);
break;
}
- case 57: {
+ case 59: {
SkeletonStmt(out s);
break;
}
- default: SynErr(177); break;
+ default: SynErr(183); break;
}
}
@@ -2146,18 +2370,18 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Expression e = dummyExpr; Attributes attrs = null;
IToken dotdotdot = null;
- Expect(99);
+ Expect(104);
x = t;
while (IsAttribute()) {
Attribute(ref attrs);
}
- if (StartOf(7)) {
+ if (StartOf(9)) {
Expression(out e, false, true);
- } else if (la.kind == 57) {
+ } else if (la.kind == 59) {
Get();
dotdotdot = t;
- } else SynErr(178);
- Expect(26);
+ } else SynErr(184);
+ Expect(28);
if (dotdotdot != null) {
s = new SkeletonStatement(new AssertStmt(x, t, new LiteralExpr(x, true), attrs), dotdotdot, null);
} else {
@@ -2171,18 +2395,18 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Expression e = dummyExpr; Attributes attrs = null;
IToken dotdotdot = null;
- Expect(29);
+ Expect(31);
x = t;
while (IsAttribute()) {
Attribute(ref attrs);
}
- if (StartOf(7)) {
+ if (StartOf(9)) {
Expression(out e, false, true);
- } else if (la.kind == 57) {
+ } else if (la.kind == 59) {
Get();
dotdotdot = t;
- } else SynErr(179);
- Expect(26);
+ } else SynErr(185);
+ Expect(28);
if (dotdotdot != null) {
s = new SkeletonStatement(new AssumeStmt(x, t, new LiteralExpr(x, true), attrs), dotdotdot, null);
} else {
@@ -2196,16 +2420,16 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
IToken x; Expression e;
var args = new List<Expression>();
- Expect(100);
+ Expect(105);
x = t;
Expression(out e, false, true);
args.Add(e);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
Expression(out e, false, true);
args.Add(e);
}
- Expect(26);
+ Expect(28);
s = new PrintStmt(x, t, args);
}
@@ -2220,44 +2444,44 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Lhs(out e);
x = e.tok;
- if (la.kind == 26 || la.kind == 44) {
- while (la.kind == 44) {
+ if (la.kind == 28 || la.kind == 46) {
+ while (la.kind == 46) {
Attribute(ref attrs);
}
- Expect(26);
+ Expect(28);
endTok = t; rhss.Add(new ExprRhs(e, attrs));
- } else if (la.kind == 21 || la.kind == 92 || la.kind == 94) {
+ } else if (la.kind == 22 || la.kind == 25 || la.kind == 98) {
lhss.Add(e);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
Lhs(out e);
lhss.Add(e);
}
- if (la.kind == 92) {
+ if (la.kind == 98) {
Get();
x = t;
Rhs(out r);
rhss.Add(r);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
Rhs(out r);
rhss.Add(r);
}
- } else if (la.kind == 94) {
+ } else if (la.kind == 25) {
Get();
x = t;
if (la.kind == _assume) {
- Expect(29);
+ Expect(31);
suchThatAssume = t;
}
Expression(out suchThat, false, true);
- } else SynErr(180);
- Expect(26);
+ } else SynErr(186);
+ Expect(28);
endTok = t;
- } else if (la.kind == 20) {
+ } else if (la.kind == 21) {
Get();
SemErr(t, "invalid statement (did you forget the 'label' keyword?)");
- } else SynErr(181);
+ } else SynErr(187);
if (suchThat != null) {
s = new AssignSuchThatStmt(x, endTok, lhss, suchThat, suchThatAssume, null);
} else {
@@ -2280,76 +2504,117 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Expression suchThat = null;
Attributes attrs = null;
IToken endTok;
+ s = dummyStmt;
- if (la.kind == 70) {
+ if (la.kind == 62) {
Get();
isGhost = true; x = t;
}
- Expect(75);
+ Expect(81);
if (!isGhost) { x = t; }
- while (la.kind == 44) {
- Attribute(ref attrs);
- }
- LocalIdentTypeOptional(out d, isGhost);
- lhss.Add(d); d.Attributes = attrs; attrs = null;
- while (la.kind == 21) {
- Get();
- while (la.kind == 44) {
+ if (la.kind == 1 || la.kind == 46) {
+ while (la.kind == 46) {
Attribute(ref attrs);
}
LocalIdentTypeOptional(out d, isGhost);
lhss.Add(d); d.Attributes = attrs; attrs = null;
- }
- if (la.kind == 44 || la.kind == 92 || la.kind == 94) {
- if (la.kind == 92) {
+ while (la.kind == 22) {
Get();
- assignTok = t;
- Rhs(out r);
- rhss.Add(r);
- while (la.kind == 21) {
+ while (la.kind == 46) {
+ Attribute(ref attrs);
+ }
+ LocalIdentTypeOptional(out d, isGhost);
+ lhss.Add(d); d.Attributes = attrs; attrs = null;
+ }
+ if (la.kind == 25 || la.kind == 46 || la.kind == 98) {
+ if (la.kind == 98) {
Get();
+ assignTok = t;
Rhs(out r);
rhss.Add(r);
+ while (la.kind == 22) {
+ Get();
+ Rhs(out r);
+ rhss.Add(r);
+ }
+ } else {
+ while (la.kind == 46) {
+ Attribute(ref attrs);
+ }
+ Expect(25);
+ assignTok = t;
+ if (la.kind == _assume) {
+ Expect(31);
+ suchThatAssume = t;
+ }
+ Expression(out suchThat, false, true);
}
+ }
+ while (!(la.kind == 0 || la.kind == 28)) {SynErr(188); Get();}
+ Expect(28);
+ endTok = t;
+ ConcreteUpdateStatement update;
+ if (suchThat != null) {
+ var ies = new List<Expression>();
+ foreach (var lhs in lhss) {
+ ies.Add(new IdentifierExpr(lhs.Tok, lhs.Name));
+ }
+ update = new AssignSuchThatStmt(assignTok, endTok, ies, suchThat, suchThatAssume, attrs);
+ } else if (rhss.Count == 0) {
+ update = null;
} else {
- while (la.kind == 44) {
- Attribute(ref attrs);
- }
- Expect(94);
- assignTok = t;
- if (la.kind == _assume) {
- Expect(29);
- suchThatAssume = t;
+ var ies = new List<Expression>();
+ foreach (var lhs in lhss) {
+ ies.Add(new AutoGhostIdentifierExpr(lhs.Tok, lhs.Name));
+ }
+ update = new UpdateStmt(assignTok, endTok, ies, rhss);
+ }
+ s = new VarDeclStmt(x, endTok, lhss, update);
+
+ } else if (la.kind == 50) {
+ Get();
+ var letLHSs = new List<CasePattern>();
+ var letRHSs = new List<Expression>();
+ List<CasePattern> arguments = new List<CasePattern>();
+ CasePattern pat;
+ Expression e = dummyExpr;
+ IToken id = t;
+
+ if (la.kind == 1 || la.kind == 50) {
+ CasePattern(out pat);
+ arguments.Add(pat);
+ while (la.kind == 22) {
+ Get();
+ CasePattern(out pat);
+ arguments.Add(pat);
}
- Expression(out suchThat, false, true);
}
- }
- while (!(la.kind == 0 || la.kind == 26)) {SynErr(182); Get();}
- Expect(26);
- endTok = t;
- ConcreteUpdateStatement update;
- if (suchThat != null) {
- var ies = new List<Expression>();
- foreach (var lhs in lhss) {
- ies.Add(new IdentifierExpr(lhs.Tok, lhs.Name));
- }
- update = new AssignSuchThatStmt(assignTok, endTok, ies, suchThat, suchThatAssume, attrs);
- } else if (rhss.Count == 0) {
- update = null;
- } else {
- var ies = new List<Expression>();
- foreach (var lhs in lhss) {
- ies.Add(new AutoGhostIdentifierExpr(lhs.Tok, lhs.Name));
- }
- update = new UpdateStmt(assignTok, endTok, ies, rhss);
- }
- s = new VarDeclStmt(x, endTok, lhss, update);
-
+ Expect(51);
+ theBuiltIns.TupleType(id, arguments.Count, true); // make sure the tuple type exists
+ string ctor = BuiltIns.TupleTypeCtorName; //use the TupleTypeCtors
+ pat = new CasePattern(id, ctor, arguments);
+ if (isGhost) { pat.Vars.Iter(bv => bv.IsGhost = true); }
+ letLHSs.Add(pat);
+
+ if (la.kind == 98) {
+ Get();
+ } else if (la.kind == 25 || la.kind == 46) {
+ while (la.kind == 46) {
+ Attribute(ref attrs);
+ }
+ Expect(25);
+ SemErr(pat.tok, "LHS of assign-such-that expression must be variables, not general patterns");
+ } else SynErr(189);
+ Expression(out e, false, true);
+ letRHSs.Add(e);
+ Expect(28);
+ s = new LetStmt(e.tok, e.tok, letLHSs, letRHSs);
+ } else SynErr(190);
}
void IfStmt(out Statement/*!*/ ifStmt) {
Contract.Ensures(Contract.ValueAtReturn(out ifStmt) != null); IToken/*!*/ x;
- Expression guard = null; IToken guardEllipsis = null;
+ Expression guard = null; IToken guardEllipsis = null; bool isExistentialGuard = false;
BlockStmt/*!*/ thn;
BlockStmt/*!*/ bs;
Statement/*!*/ s;
@@ -2358,13 +2623,16 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
List<GuardedAlternative> alternatives;
ifStmt = dummyStmt; // to please the compiler
- Expect(96);
+ Expect(101);
x = t;
if (IsAlternative()) {
- AlternativeBlock(out alternatives, out endTok);
+ AlternativeBlock(true, out alternatives, out endTok);
ifStmt = new AlternativeStmt(x, endTok, alternatives);
- } else if (StartOf(20)) {
- if (StartOf(21)) {
+ } else if (StartOf(22)) {
+ if (IsExistentialGuard()) {
+ ExistentialGuard(out guard, true);
+ isExistentialGuard = true;
+ } else if (StartOf(23)) {
Guard(out guard);
} else {
Get();
@@ -2372,23 +2640,23 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
}
BlockStmt(out thn, out bodyStart, out bodyEnd);
endTok = thn.EndTok;
- if (la.kind == 33) {
+ if (la.kind == 35) {
Get();
- if (la.kind == 96) {
+ if (la.kind == 101) {
IfStmt(out s);
els = s; endTok = s.EndTok;
- } else if (la.kind == 44) {
+ } else if (la.kind == 46) {
BlockStmt(out bs, out bodyStart, out bodyEnd);
els = bs; endTok = bs.EndTok;
- } else SynErr(183);
+ } else SynErr(191);
}
if (guardEllipsis != null) {
- ifStmt = new SkeletonStatement(new IfStmt(x, endTok, guard, thn, els), guardEllipsis, null);
+ ifStmt = new SkeletonStatement(new IfStmt(x, endTok, isExistentialGuard, guard, thn, els), guardEllipsis, null);
} else {
- ifStmt = new IfStmt(x, endTok, guard, thn, els);
+ ifStmt = new IfStmt(x, endTok, isExistentialGuard, guard, thn, els);
}
- } else SynErr(184);
+ } else SynErr(192);
}
void WhileStmt(out Statement stmt) {
@@ -2407,33 +2675,33 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
stmt = dummyStmt; // to please the compiler
bool isDirtyLoop = true;
- Expect(97);
+ Expect(102);
x = t;
if (IsLoopSpec() || IsAlternative()) {
- while (StartOf(22)) {
+ while (StartOf(24)) {
LoopSpec(invariants, decreases, ref mod, ref decAttrs, ref modAttrs);
}
- AlternativeBlock(out alternatives, out endTok);
+ AlternativeBlock(false, out alternatives, out endTok);
stmt = new AlternativeLoopStmt(x, endTok, invariants, new Specification<Expression>(decreases, decAttrs), new Specification<FrameExpression>(mod, modAttrs), alternatives);
- } else if (StartOf(20)) {
- if (StartOf(21)) {
+ } else if (StartOf(22)) {
+ if (StartOf(23)) {
Guard(out guard);
Contract.Assume(guard == null || cce.Owner.None(guard));
} else {
Get();
guardEllipsis = t;
}
- while (StartOf(22)) {
+ while (StartOf(24)) {
LoopSpec(invariants, decreases, ref mod, ref decAttrs, ref modAttrs);
}
if (la.kind == _lbrace) {
BlockStmt(out body, out bodyStart, out bodyEnd);
endTok = body.EndTok; isDirtyLoop = false;
} else if (la.kind == _ellipsis) {
- Expect(57);
+ Expect(59);
bodyEllipsis = t; endTok = t; isDirtyLoop = false;
- } else if (StartOf(23)) {
- } else SynErr(185);
+ } else if (StartOf(25)) {
+ } else SynErr(193);
if (guardEllipsis != null || bodyEllipsis != null) {
if (mod != null) {
SemErr(mod[0].E.tok, "'modifies' clauses are not allowed on refining loops");
@@ -2451,7 +2719,7 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
stmt = new WhileStmt(x, endTok, guard, invariants, new Specification<Expression>(decreases, decAttrs), new Specification<FrameExpression>(mod, modAttrs), body);
}
- } else SynErr(186);
+ } else SynErr(194);
}
void MatchStmt(out Statement/*!*/ s) {
@@ -2460,23 +2728,23 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
List<MatchCaseStmt/*!*/> cases = new List<MatchCaseStmt/*!*/>();
bool usesOptionalBrace = false;
- Expect(98);
+ Expect(103);
x = t;
Expression(out e, true, true);
if (la.kind == _lbrace) {
- Expect(44);
+ Expect(46);
usesOptionalBrace = true;
- while (la.kind == 31) {
+ while (la.kind == 33) {
CaseStatement(out c);
cases.Add(c);
}
- Expect(45);
- } else if (StartOf(23)) {
+ Expect(47);
+ } else if (StartOf(25)) {
while (la.kind == _case) {
CaseStatement(out c);
cases.Add(c);
}
- } else SynErr(187);
+ } else SynErr(195);
s = new MatchStmt(x, t, e, cases, usesOptionalBrace);
}
@@ -2493,38 +2761,38 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
IToken bodyStart, bodyEnd;
IToken tok = Token.NoToken;
- if (la.kind == 101) {
+ if (la.kind == 106) {
Get();
x = t; tok = x;
- } else if (la.kind == 102) {
+ } else if (la.kind == 107) {
Get();
x = t;
errors.Warning(t, "the 'parallel' keyword has been deprecated; the comprehension statement now uses the keyword 'forall' (and the parentheses around the bound variables are now optional)");
- } else SynErr(188);
+ } else SynErr(196);
if (la.kind == _openparen) {
- Expect(48);
+ Expect(50);
if (la.kind == 1) {
QuantifierDomain(out bvars, out attrs, out range);
}
- Expect(49);
- } else if (StartOf(24)) {
+ Expect(51);
+ } else if (StartOf(26)) {
if (la.kind == _ident) {
QuantifierDomain(out bvars, out attrs, out range);
}
- } else SynErr(189);
+ } else SynErr(197);
if (bvars == null) { bvars = new List<BoundVar>(); }
if (range == null) { range = new LiteralExpr(x, true); }
- while (la.kind == 85 || la.kind == 86) {
+ while (la.kind == 91 || la.kind == 92) {
isFree = false;
- if (la.kind == 85) {
+ if (la.kind == 91) {
Get();
isFree = true;
errors.Warning(t, "the 'free' keyword is soon to be deprecated");
}
- Expect(86);
+ Expect(92);
Expression(out e, false, true);
ens.Add(new MaybeFreeExpression(e, isFree));
OldSemi();
@@ -2547,6 +2815,7 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
void CalcStmt(out Statement s) {
Contract.Ensures(Contract.ValueAtReturn(out s) != null);
Token x;
+ Attributes attrs = null;
CalcStmt.CalcOp op, calcOp = Microsoft.Dafny.CalcStmt.DefaultOp, resOp = Microsoft.Dafny.CalcStmt.DefaultOp;
var lines = new List<Expression>();
var hints = new List<BlockStmt>();
@@ -2557,9 +2826,12 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
IToken opTok;
IToken danglingOperator = null;
- Expect(30);
+ Expect(32);
x = t;
- if (StartOf(25)) {
+ while (IsAttribute()) {
+ Attribute(ref attrs);
+ }
+ if (StartOf(27)) {
CalcOp(out opTok, out calcOp);
maybeOp = calcOp.ResultOp(calcOp); // guard against non-transitive calcOp (like !=)
if (maybeOp == null) {
@@ -2568,12 +2840,12 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
resOp = calcOp;
}
- Expect(44);
- while (StartOf(7)) {
+ Expect(46);
+ while (StartOf(9)) {
Expression(out e, false, true);
lines.Add(e); stepOp = calcOp; danglingOperator = null;
- Expect(26);
- if (StartOf(25)) {
+ Expect(28);
+ if (StartOf(27)) {
CalcOp(out opTok, out op);
maybeOp = resOp.ResultOp(op);
if (maybeOp == null) {
@@ -2592,20 +2864,20 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
BlockStmt subBlock; Statement subCalc;
while (la.kind == _lbrace || la.kind == _calc) {
- if (la.kind == 44) {
+ if (la.kind == 46) {
BlockStmt(out subBlock, out t0, out t1);
hintEnd = subBlock.EndTok; subhints.Add(subBlock);
- } else if (la.kind == 30) {
+ } else if (la.kind == 32) {
CalcStmt(out subCalc);
hintEnd = subCalc.EndTok; subhints.Add(subCalc);
- } else SynErr(190);
+ } else SynErr(198);
}
var h = new BlockStmt(hintStart, hintEnd, subhints); // if the hint is empty, hintStart is the first token of the next line, but it doesn't matter because the block statement is just used as a container
hints.Add(h);
if (h.Body.Count != 0) { danglingOperator = null; }
}
- Expect(45);
+ Expect(47);
if (danglingOperator != null) {
SemErr(danglingOperator, "a calculation cannot end with an operator");
}
@@ -2613,7 +2885,7 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
// Repeat the last line to create a dummy line for the dangling hint
lines.Add(lines[lines.Count - 1]);
}
- s = new CalcStmt(x, t, calcOp, lines, hints, stepOps, resOp);
+ s = new CalcStmt(x, t, calcOp, lines, hints, stepOps, resOp, attrs);
}
@@ -2624,30 +2896,30 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
BlockStmt body = null; IToken bodyStart;
IToken ellipsisToken = null;
- Expect(103);
+ Expect(108);
tok = t;
while (IsAttribute()) {
Attribute(ref attrs);
}
- if (StartOf(18)) {
+ if (StartOf(20)) {
FrameExpression(out fe, false, true);
mod.Add(fe);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
FrameExpression(out fe, false, true);
mod.Add(fe);
}
- } else if (la.kind == 57) {
+ } else if (la.kind == 59) {
Get();
ellipsisToken = t;
- } else SynErr(191);
- if (la.kind == 44) {
+ } else SynErr(199);
+ if (la.kind == 46) {
BlockStmt(out body, out bodyStart, out endTok);
- } else if (la.kind == 26) {
- while (!(la.kind == 0 || la.kind == 26)) {SynErr(192); Get();}
+ } else if (la.kind == 28) {
+ while (!(la.kind == 0 || la.kind == 28)) {SynErr(200); Get();}
Get();
endTok = t;
- } else SynErr(193);
+ } else SynErr(201);
s = new ModifyStmt(tok, endTok, mod, attrs, body);
if (ellipsisToken != null) {
s = new SkeletonStatement(s, ellipsisToken, null);
@@ -2661,23 +2933,23 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
AssignmentRhs r;
bool isYield = false;
- if (la.kind == 93) {
+ if (la.kind == 99) {
Get();
returnTok = t;
- } else if (la.kind == 87) {
+ } else if (la.kind == 93) {
Get();
returnTok = t; isYield = true;
- } else SynErr(194);
- if (StartOf(26)) {
+ } else SynErr(202);
+ if (StartOf(28)) {
Rhs(out r);
rhss = new List<AssignmentRhs>(); rhss.Add(r);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
Rhs(out r);
rhss.Add(r);
}
}
- Expect(26);
+ Expect(28);
if (isYield) {
s = new YieldStmt(returnTok, t, rhss);
} else {
@@ -2691,22 +2963,22 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
List<Expression> exprs = null;
IToken tok, dotdotdot, whereTok;
Expression e;
- Expect(57);
+ Expect(59);
dotdotdot = t;
- if (la.kind == 91) {
+ if (la.kind == 97) {
Get();
names = new List<IToken>(); exprs = new List<Expression>(); whereTok = t;
Ident(out tok);
names.Add(tok);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
Ident(out tok);
names.Add(tok);
}
- Expect(92);
+ Expect(98);
Expression(out e, false, true);
exprs.Add(e);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
Expression(out e, false, true);
exprs.Add(e);
@@ -2717,7 +2989,7 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
}
}
- Expect(26);
+ Expect(28);
s = new SkeletonStatement(dotdotdot, t, names, exprs);
}
@@ -2730,25 +3002,25 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
r = dummyRhs; // to please compiler
Attributes attrs = null;
- if (la.kind == 95) {
+ if (la.kind == 100) {
Get();
newToken = t;
TypeAndToken(out x, out ty);
- if (la.kind == 46 || la.kind == 48) {
- if (la.kind == 46) {
+ if (la.kind == 48 || la.kind == 50) {
+ if (la.kind == 48) {
Get();
ee = new List<Expression>();
Expressions(ee);
- Expect(47);
+ Expect(49);
var tmp = theBuiltIns.ArrayType(ee.Count, new IntType(), true);
} else {
x = null; args = new List<Expression/*!*/>();
Get();
- if (StartOf(7)) {
+ if (StartOf(9)) {
Expressions(args);
}
- Expect(49);
+ Expect(51);
}
}
if (ee != null) {
@@ -2759,14 +3031,14 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
r = new TypeRhs(newToken, ty);
}
- } else if (la.kind == 55) {
+ } else if (la.kind == 57) {
Get();
r = new HavocRhs(t);
- } else if (StartOf(7)) {
+ } else if (StartOf(9)) {
Expression(out e, false, true);
r = new ExprRhs(e);
- } else SynErr(195);
- while (la.kind == 44) {
+ } else SynErr(203);
+ while (la.kind == 46) {
Attribute(ref attrs);
}
r.Attributes = attrs;
@@ -2777,94 +3049,170 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
if (la.kind == 1) {
NameSegment(out e);
- while (la.kind == 25 || la.kind == 46 || la.kind == 48) {
+ while (la.kind == 27 || la.kind == 48 || la.kind == 50) {
Suffix(ref e);
}
- } else if (StartOf(27)) {
+ } else if (StartOf(29)) {
ConstAtomExpression(out e, false, false);
Suffix(ref e);
- while (la.kind == 25 || la.kind == 46 || la.kind == 48) {
+ while (la.kind == 27 || la.kind == 48 || la.kind == 50) {
Suffix(ref e);
}
- } else SynErr(196);
+ } else SynErr(204);
}
void Expressions(List<Expression> args) {
Expression e;
Expression(out e, true, true);
args.Add(e);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
Expression(out e, true, true);
args.Add(e);
}
}
- void AlternativeBlock(out List<GuardedAlternative> alternatives, out IToken endTok) {
+ void CasePattern(out CasePattern pat) {
+ IToken id; List<CasePattern> arguments;
+ BoundVar bv;
+ pat = null;
+
+ if (IsIdentParen()) {
+ Ident(out id);
+ Expect(50);
+ arguments = new List<CasePattern>();
+ if (la.kind == 1 || la.kind == 50) {
+ CasePattern(out pat);
+ arguments.Add(pat);
+ while (la.kind == 22) {
+ Get();
+ CasePattern(out pat);
+ arguments.Add(pat);
+ }
+ }
+ Expect(51);
+ pat = new CasePattern(id, id.val, arguments);
+ } else if (la.kind == 50) {
+ Get();
+ id = t;
+ arguments = new List<CasePattern>();
+
+ if (la.kind == 1 || la.kind == 50) {
+ CasePattern(out pat);
+ arguments.Add(pat);
+ while (la.kind == 22) {
+ Get();
+ CasePattern(out pat);
+ arguments.Add(pat);
+ }
+ }
+ Expect(51);
+ theBuiltIns.TupleType(id, arguments.Count, true); // make sure the tuple type exists
+ string ctor = BuiltIns.TupleTypeCtorName; //use the TupleTypeCtors
+ pat = new CasePattern(id, ctor, arguments);
+
+ } else if (la.kind == 1) {
+ IdentTypeOptional(out bv);
+ pat = new CasePattern(bv.tok, bv);
+
+ } else SynErr(205);
+ if (pat == null) {
+ pat = new CasePattern(t, "_ParseError", new List<CasePattern>());
+ }
+
+ }
+
+ void AlternativeBlock(bool allowExistentialGuards, out List<GuardedAlternative> alternatives, out IToken endTok) {
alternatives = new List<GuardedAlternative>();
IToken x;
- Expression e;
+ Expression e; bool isExistentialGuard;
List<Statement> body;
- Expect(44);
- while (la.kind == 31) {
- Get();
- x = t;
- Expression(out e, true, false);
- Expect(27);
+ Expect(46);
+ while (la.kind == 33) {
+ Get();
+ x = t; isExistentialGuard = false; e = dummyExpr;
+ if (allowExistentialGuards && IsExistentialGuard()) {
+ ExistentialGuard(out e, false );
+ isExistentialGuard = true;
+ } else if (StartOf(9)) {
+ Expression(out e, true, false);
+ } else SynErr(206);
+ Expect(29);
body = new List<Statement>();
- while (StartOf(15)) {
+ while (StartOf(17)) {
Stmt(body);
}
- alternatives.Add(new GuardedAlternative(x, e, body));
+ alternatives.Add(new GuardedAlternative(x, isExistentialGuard, e, body));
}
- Expect(45);
+ Expect(47);
endTok = t;
}
+ void ExistentialGuard(out Expression e, bool allowLambda) {
+ var bvars = new List<BoundVar>();
+ BoundVar bv; IToken x;
+ Attributes attrs = null;
+ Expression body;
+
+ IdentTypeOptional(out bv);
+ bvars.Add(bv); x = bv.tok;
+ while (la.kind == 22) {
+ Get();
+ IdentTypeOptional(out bv);
+ bvars.Add(bv);
+ }
+ while (la.kind == 46) {
+ Attribute(ref attrs);
+ }
+ Expect(25);
+ Expression(out body, true, allowLambda);
+ e = new ExistsExpr(x, bvars, null, body, attrs);
+ }
+
void Guard(out Expression e) {
Expression/*!*/ ee; e = null;
- if (la.kind == 55) {
+ if (la.kind == 57) {
Get();
e = null;
} else if (IsParenStar()) {
- Expect(48);
- Expect(55);
- Expect(49);
+ Expect(50);
+ Expect(57);
+ Expect(51);
e = null;
- } else if (StartOf(7)) {
+ } else if (StartOf(9)) {
Expression(out ee, true, true);
e = ee;
- } else SynErr(197);
+ } else SynErr(207);
}
void LoopSpec(List<MaybeFreeExpression> invariants, List<Expression> decreases, ref List<FrameExpression> mod, ref Attributes decAttrs, ref Attributes modAttrs) {
Expression e; FrameExpression fe;
bool isFree = false; Attributes attrs = null;
- if (la.kind == 35 || la.kind == 85) {
- while (!(la.kind == 0 || la.kind == 35 || la.kind == 85)) {SynErr(198); Get();}
- if (la.kind == 85) {
+ if (la.kind == 37 || la.kind == 91) {
+ while (!(la.kind == 0 || la.kind == 37 || la.kind == 91)) {SynErr(208); Get();}
+ if (la.kind == 91) {
Get();
isFree = true; errors.Warning(t, "the 'free' keyword is soon to be deprecated");
}
- Expect(35);
+ Expect(37);
while (IsAttribute()) {
Attribute(ref attrs);
}
Expression(out e, false, true);
invariants.Add(new MaybeFreeExpression(e, isFree, attrs));
OldSemi();
- } else if (la.kind == 34) {
- while (!(la.kind == 0 || la.kind == 34)) {SynErr(199); Get();}
+ } else if (la.kind == 36) {
+ while (!(la.kind == 0 || la.kind == 36)) {SynErr(209); Get();}
Get();
while (IsAttribute()) {
Attribute(ref decAttrs);
}
DecreasesList(decreases, true, true);
OldSemi();
- } else if (la.kind == 41) {
- while (!(la.kind == 0 || la.kind == 41)) {SynErr(200); Get();}
+ } else if (la.kind == 43) {
+ while (!(la.kind == 0 || la.kind == 43)) {SynErr(210); Get();}
Get();
mod = mod ?? new List<FrameExpression>();
while (IsAttribute()) {
@@ -2872,43 +3220,59 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
}
FrameExpression(out fe, false, true);
mod.Add(fe);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
FrameExpression(out fe, false, true);
mod.Add(fe);
}
OldSemi();
- } else SynErr(201);
+ } else SynErr(211);
}
void CaseStatement(out MatchCaseStmt/*!*/ c) {
Contract.Ensures(Contract.ValueAtReturn(out c) != null);
IToken/*!*/ x, id;
- List<BoundVar/*!*/> arguments = new List<BoundVar/*!*/>();
- BoundVar/*!*/ bv;
+ List<CasePattern/*!*/> arguments = new List<CasePattern/*!*/>();
+ CasePattern/*!*/ pat;
List<Statement/*!*/> body = new List<Statement/*!*/>();
+ string/*!*/ name = "";
- Expect(31);
+ Expect(33);
x = t;
- Ident(out id);
- if (la.kind == 48) {
+ if (la.kind == 1) {
+ Ident(out id);
+ name = id.val;
+ if (la.kind == 50) {
+ Get();
+ if (la.kind == 1 || la.kind == 50) {
+ CasePattern(out pat);
+ arguments.Add(pat);
+ while (la.kind == 22) {
+ Get();
+ CasePattern(out pat);
+ arguments.Add(pat);
+ }
+ }
+ Expect(51);
+ }
+ } else if (la.kind == 50) {
Get();
- IdentTypeOptional(out bv);
- arguments.Add(bv);
- while (la.kind == 21) {
+ CasePattern(out pat);
+ arguments.Add(pat);
+ while (la.kind == 22) {
Get();
- IdentTypeOptional(out bv);
- arguments.Add(bv);
+ CasePattern(out pat);
+ arguments.Add(pat);
}
- Expect(49);
- }
- Expect(27);
- while (!(StartOf(28))) {SynErr(202); Get();}
+ Expect(51);
+ } else SynErr(212);
+ Expect(29);
+ while (!(StartOf(30))) {SynErr(213); Get();}
while (IsNotEndOfCase()) {
Stmt(body);
- while (!(StartOf(28))) {SynErr(203); Get();}
+ while (!(StartOf(30))) {SynErr(214); Get();}
}
- c = new MatchCaseStmt(x, id.val, arguments, body);
+ c = new MatchCaseStmt(x, name, arguments, body);
}
void QuantifierDomain(out List<BoundVar> bvars, out Attributes attrs, out Expression range) {
@@ -2919,7 +3283,7 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
IdentTypeOptional(out bv);
bvars.Add(bv);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
IdentTypeOptional(out bv);
bvars.Add(bv);
@@ -2928,7 +3292,7 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Attribute(ref attrs);
}
if (la.kind == _verticalbar) {
- Expect(22);
+ Expect(23);
Expression(out range, true, true);
}
}
@@ -2939,73 +3303,73 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
x = null;
switch (la.kind) {
- case 52: {
+ case 54: {
Get();
x = t; binOp = BinaryExpr.Opcode.Eq;
- if (la.kind == 104) {
+ if (la.kind == 109) {
Get();
- Expect(46);
+ Expect(48);
Expression(out k, true, true);
- Expect(47);
+ Expect(49);
}
break;
}
- case 50: {
+ case 52: {
Get();
x = t; binOp = BinaryExpr.Opcode.Lt;
break;
}
- case 51: {
+ case 53: {
Get();
x = t; binOp = BinaryExpr.Opcode.Gt;
break;
}
- case 105: {
+ case 110: {
Get();
x = t; binOp = BinaryExpr.Opcode.Le;
break;
}
- case 106: {
+ case 111: {
Get();
x = t; binOp = BinaryExpr.Opcode.Ge;
break;
}
- case 53: {
+ case 55: {
Get();
x = t; binOp = BinaryExpr.Opcode.Neq;
break;
}
- case 54: {
+ case 56: {
Get();
x = t; binOp = BinaryExpr.Opcode.Neq;
break;
}
- case 107: {
+ case 112: {
Get();
x = t; binOp = BinaryExpr.Opcode.Le;
break;
}
- case 108: {
+ case 113: {
Get();
x = t; binOp = BinaryExpr.Opcode.Ge;
break;
}
- case 109: case 110: {
+ case 114: case 115: {
EquivOp();
x = t; binOp = BinaryExpr.Opcode.Iff;
break;
}
- case 111: case 112: {
+ case 116: case 117: {
ImpliesOp();
x = t; binOp = BinaryExpr.Opcode.Imp;
break;
}
- case 113: case 114: {
+ case 118: case 119: {
ExpliesOp();
x = t; binOp = BinaryExpr.Opcode.Exp;
break;
}
- default: SynErr(204); break;
+ default: SynErr(215); break;
}
if (k == null) {
op = new Microsoft.Dafny.CalcStmt.BinaryCalcOp(binOp);
@@ -3016,75 +3380,75 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
}
void EquivOp() {
- if (la.kind == 109) {
+ if (la.kind == 114) {
Get();
- } else if (la.kind == 110) {
+ } else if (la.kind == 115) {
Get();
- } else SynErr(205);
+ } else SynErr(216);
}
void ImpliesOp() {
- if (la.kind == 111) {
+ if (la.kind == 116) {
Get();
- } else if (la.kind == 112) {
+ } else if (la.kind == 117) {
Get();
- } else SynErr(206);
+ } else SynErr(217);
}
void ExpliesOp() {
- if (la.kind == 113) {
+ if (la.kind == 118) {
Get();
- } else if (la.kind == 114) {
+ } else if (la.kind == 119) {
Get();
- } else SynErr(207);
+ } else SynErr(218);
}
void AndOp() {
- if (la.kind == 115) {
+ if (la.kind == 120) {
Get();
- } else if (la.kind == 116) {
+ } else if (la.kind == 121) {
Get();
- } else SynErr(208);
+ } else SynErr(219);
}
void OrOp() {
- if (la.kind == 117) {
+ if (la.kind == 122) {
Get();
- } else if (la.kind == 118) {
+ } else if (la.kind == 123) {
Get();
- } else SynErr(209);
+ } else SynErr(220);
}
void NegOp() {
- if (la.kind == 119) {
+ if (la.kind == 124) {
Get();
- } else if (la.kind == 120) {
+ } else if (la.kind == 125) {
Get();
- } else SynErr(210);
+ } else SynErr(221);
}
void Forall() {
- if (la.kind == 101) {
+ if (la.kind == 106) {
Get();
- } else if (la.kind == 121) {
+ } else if (la.kind == 126) {
Get();
- } else SynErr(211);
+ } else SynErr(222);
}
void Exists() {
- if (la.kind == 122) {
+ if (la.kind == 127) {
Get();
- } else if (la.kind == 123) {
+ } else if (la.kind == 128) {
Get();
- } else SynErr(212);
+ } else SynErr(223);
}
void QSep() {
- if (la.kind == 23) {
+ if (la.kind == 24) {
Get();
- } else if (la.kind == 24) {
+ } else if (la.kind == 26) {
Get();
- } else SynErr(213);
+ } else SynErr(224);
}
void EquivExpression(out Expression e0, bool allowSemi, bool allowLambda) {
@@ -3102,23 +3466,24 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Contract.Ensures(Contract.ValueAtReturn(out e0) != null); IToken/*!*/ x; Expression/*!*/ e1;
LogicalExpression(out e0, allowSemi, allowLambda);
if (IsImpliesOp() || IsExpliesOp()) {
- if (la.kind == 111 || la.kind == 112) {
+ if (la.kind == 116 || la.kind == 117) {
ImpliesOp();
x = t;
ImpliesExpression(out e1, allowSemi, allowLambda);
e0 = new BinaryExpr(x, BinaryExpr.Opcode.Imp, e0, e1);
- } else if (la.kind == 113 || la.kind == 114) {
+ } else if (la.kind == 118 || la.kind == 119) {
ExpliesOp();
x = t;
LogicalExpression(out e1, allowSemi, allowLambda);
- e0 = new BinaryExpr(x, BinaryExpr.Opcode.Exp, e0, e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Exp, e1, e0);
while (IsExpliesOp()) {
ExpliesOp();
x = t;
LogicalExpression(out e1, allowSemi, allowLambda);
- e0 = new BinaryExpr(x, BinaryExpr.Opcode.Exp, e0, e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Exp, e1, e0);
+
}
- } else SynErr(214);
+ } else SynErr(225);
}
}
@@ -3126,7 +3491,7 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Contract.Ensures(Contract.ValueAtReturn(out e0) != null); IToken/*!*/ x; Expression/*!*/ e1;
RelationalExpression(out e0, allowSemi, allowLambda);
if (IsAndOp() || IsOrOp()) {
- if (la.kind == 115 || la.kind == 116) {
+ if (la.kind == 120 || la.kind == 121) {
AndOp();
x = t;
RelationalExpression(out e1, allowSemi, allowLambda);
@@ -3137,7 +3502,7 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
RelationalExpression(out e1, allowSemi, allowLambda);
e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1);
}
- } else if (la.kind == 117 || la.kind == 118) {
+ } else if (la.kind == 122 || la.kind == 123) {
OrOp();
x = t;
RelationalExpression(out e1, allowSemi, allowLambda);
@@ -3148,7 +3513,7 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
RelationalExpression(out e1, allowSemi, allowLambda);
e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1);
}
- } else SynErr(215);
+ } else SynErr(226);
}
}
@@ -3281,63 +3646,63 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
k = null;
switch (la.kind) {
- case 52: {
+ case 54: {
Get();
x = t; op = BinaryExpr.Opcode.Eq;
- if (la.kind == 104) {
+ if (la.kind == 109) {
Get();
- Expect(46);
+ Expect(48);
Expression(out k, true, true);
- Expect(47);
+ Expect(49);
}
break;
}
- case 50: {
+ case 52: {
Get();
x = t; op = BinaryExpr.Opcode.Lt;
break;
}
- case 51: {
+ case 53: {
Get();
x = t; op = BinaryExpr.Opcode.Gt;
break;
}
- case 105: {
+ case 110: {
Get();
x = t; op = BinaryExpr.Opcode.Le;
break;
}
- case 106: {
+ case 111: {
Get();
x = t; op = BinaryExpr.Opcode.Ge;
break;
}
- case 53: {
+ case 55: {
Get();
x = t; op = BinaryExpr.Opcode.Neq;
- if (la.kind == 104) {
+ if (la.kind == 109) {
Get();
- Expect(46);
+ Expect(48);
Expression(out k, true, true);
- Expect(47);
+ Expect(49);
}
break;
}
- case 124: {
+ case 129: {
Get();
x = t; op = BinaryExpr.Opcode.In;
break;
}
- case 56: {
+ case 58: {
Get();
x = t; op = BinaryExpr.Opcode.NotIn;
break;
}
- case 119: {
+ case 124: {
Get();
x = t; y = Token.NoToken;
if (la.val == "!") {
- Expect(119);
+ Expect(124);
y = t;
}
if (y == Token.NoToken) {
@@ -3351,22 +3716,22 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
break;
}
- case 54: {
+ case 56: {
Get();
x = t; op = BinaryExpr.Opcode.Neq;
break;
}
- case 107: {
+ case 112: {
Get();
x = t; op = BinaryExpr.Opcode.Le;
break;
}
- case 108: {
+ case 113: {
Get();
x = t; op = BinaryExpr.Opcode.Ge;
break;
}
- default: SynErr(216); break;
+ default: SynErr(227); break;
}
}
@@ -3382,80 +3747,87 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
void AddOp(out IToken x, out BinaryExpr.Opcode op) {
Contract.Ensures(Contract.ValueAtReturn(out x) != null); x = Token.NoToken; op=BinaryExpr.Opcode.Add/*(dummy)*/;
- if (la.kind == 125) {
+ if (la.kind == 76) {
Get();
x = t; op = BinaryExpr.Opcode.Add;
- } else if (la.kind == 126) {
+ } else if (la.kind == 130) {
Get();
x = t; op = BinaryExpr.Opcode.Sub;
- } else SynErr(217);
+ } else SynErr(228);
}
void UnaryExpression(out Expression e, bool allowSemi, bool allowLambda) {
Contract.Ensures(Contract.ValueAtReturn(out e) != null); IToken/*!*/ x; e = dummyExpr;
- if (la.kind == 126) {
+ if (la.kind == 130) {
Get();
x = t;
UnaryExpression(out e, allowSemi, allowLambda);
e = new NegationExpression(x, e);
- } else if (la.kind == 119 || la.kind == 120) {
+ } else if (la.kind == 124 || la.kind == 125) {
NegOp();
x = t;
UnaryExpression(out e, allowSemi, allowLambda);
e = new UnaryOpExpr(x, UnaryOpExpr.Opcode.Not, e);
} else if (IsMapDisplay()) {
- Expect(16);
+ Expect(17);
x = t;
MapDisplayExpr(x, true, out e);
while (IsSuffix()) {
Suffix(ref e);
}
} else if (IsIMapDisplay()) {
- Expect(17);
+ Expect(18);
x = t;
MapDisplayExpr(x, false, out e);
while (IsSuffix()) {
Suffix(ref e);
}
+ } else if (IsISetDisplay()) {
+ Expect(14);
+ x = t;
+ ISetDisplayExpr(x, false, out e);
+ while (IsSuffix()) {
+ Suffix(ref e);
+ }
} else if (IsLambda(allowLambda)) {
LambdaExpression(out e, allowSemi);
- } else if (StartOf(29)) {
+ } else if (StartOf(31)) {
EndlessExpression(out e, allowSemi, allowLambda);
} else if (la.kind == 1) {
NameSegment(out e);
while (IsSuffix()) {
Suffix(ref e);
}
- } else if (la.kind == 44 || la.kind == 46) {
+ } else if (la.kind == 46 || la.kind == 48) {
DisplayExpr(out e);
while (IsSuffix()) {
Suffix(ref e);
}
- } else if (la.kind == 14) {
+ } else if (la.kind == 15) {
MultiSetExpr(out e);
while (IsSuffix()) {
Suffix(ref e);
}
- } else if (StartOf(27)) {
+ } else if (StartOf(29)) {
ConstAtomExpression(out e, allowSemi, allowLambda);
while (IsSuffix()) {
Suffix(ref e);
}
- } else SynErr(218);
+ } else SynErr(229);
}
void MulOp(out IToken x, out BinaryExpr.Opcode op) {
Contract.Ensures(Contract.ValueAtReturn(out x) != null); x = Token.NoToken; op = BinaryExpr.Opcode.Add/*(dummy)*/;
- if (la.kind == 55) {
+ if (la.kind == 57) {
Get();
x = t; op = BinaryExpr.Opcode.Mul;
- } else if (la.kind == 127) {
+ } else if (la.kind == 131) {
Get();
x = t; op = BinaryExpr.Opcode.Div;
- } else if (la.kind == 128) {
+ } else if (la.kind == 132) {
Get();
x = t; op = BinaryExpr.Opcode.Mod;
- } else SynErr(219);
+ } else SynErr(230);
}
void MapDisplayExpr(IToken/*!*/ mapToken, bool finite, out Expression e) {
@@ -3463,12 +3835,12 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
List<ExpressionPair/*!*/>/*!*/ elements= new List<ExpressionPair/*!*/>() ;
e = dummyExpr;
- Expect(46);
- if (StartOf(7)) {
+ Expect(48);
+ if (StartOf(9)) {
MapLiteralExpressions(out elements);
}
e = new MapDisplayExpr(mapToken, finite, elements);
- Expect(47);
+ Expect(49);
}
void Suffix(ref Expression e) {
@@ -3477,66 +3849,83 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Expression e0 = null; Expression e1 = null; Expression ee; bool anyDots = false;
List<Expression> multipleLengths = null; bool takeRest = false; // takeRest is relevant only if multipleLengths is non-null
List<Expression> multipleIndices = null;
+ List<Tuple<IToken, string, Expression>> updates;
+ Expression v;
- if (la.kind == 25) {
- DotSuffix(out id, out x);
- if (x != null) {
- // process id as a Suffix in its own right
- e = new ExprDotName(id, e, id.val, null);
- id = x; // move to the next Suffix
- }
- IToken openParen = null; List<Type> typeArgs = null; List<Expression> args = null;
-
- if (IsGenericInstantiation()) {
- typeArgs = new List<Type>();
- GenericInstantiation(typeArgs);
- } else if (la.kind == 104) {
- HashCall(id, out openParen, out typeArgs, out args);
- } else if (StartOf(30)) {
- } else SynErr(220);
- e = new ExprDotName(id, e, id.val, typeArgs);
- if (openParen != null) {
- e = new ApplySuffix(openParen, e, args);
- }
-
- } else if (la.kind == 46) {
+ if (la.kind == 27) {
+ Get();
+ if (la.kind == 50) {
+ Get();
+ x = t; updates = new List<Tuple<IToken, string, Expression>>();
+ MemberBindingUpdate(out id, out v);
+ updates.Add(Tuple.Create(id, id.val, v));
+ while (la.kind == 22) {
+ Get();
+ MemberBindingUpdate(out id, out v);
+ updates.Add(Tuple.Create(id, id.val, v));
+ }
+ Expect(51);
+ e = new DatatypeUpdateExpr(x, e, updates);
+ } else if (StartOf(32)) {
+ DotSuffix(out id, out x);
+ if (x != null) {
+ // process id as a Suffix in its own right
+ e = new ExprDotName(id, e, id.val, null);
+ id = x; // move to the next Suffix
+ }
+ IToken openParen = null; List<Type> typeArgs = null; List<Expression> args = null;
+
+ if (IsGenericInstantiation()) {
+ typeArgs = new List<Type>();
+ GenericInstantiation(typeArgs);
+ } else if (la.kind == 109) {
+ HashCall(id, out openParen, out typeArgs, out args);
+ } else if (StartOf(33)) {
+ } else SynErr(231);
+ e = new ExprDotName(id, e, id.val, typeArgs);
+ if (openParen != null) {
+ e = new ApplySuffix(openParen, e, args);
+ }
+
+ } else SynErr(232);
+ } else if (la.kind == 48) {
Get();
x = t;
- if (StartOf(7)) {
+ if (StartOf(9)) {
Expression(out ee, true, true);
e0 = ee;
- if (la.kind == 135) {
+ if (la.kind == 139) {
Get();
anyDots = true;
- if (StartOf(7)) {
+ if (StartOf(9)) {
Expression(out ee, true, true);
e1 = ee;
}
- } else if (la.kind == 92) {
+ } else if (la.kind == 98) {
Get();
Expression(out ee, true, true);
e1 = ee;
- } else if (la.kind == 20) {
+ } else if (la.kind == 21) {
Get();
multipleLengths = new List<Expression>();
multipleLengths.Add(e0); // account for the Expression read before the colon
takeRest = true;
- if (StartOf(7)) {
+ if (StartOf(9)) {
Expression(out ee, true, true);
multipleLengths.Add(ee); takeRest = false;
while (IsNonFinalColon()) {
- Expect(20);
+ Expect(21);
Expression(out ee, true, true);
multipleLengths.Add(ee);
}
- if (la.kind == 20) {
+ if (la.kind == 21) {
Get();
takeRest = true;
}
}
- } else if (la.kind == 21 || la.kind == 47) {
- while (la.kind == 21) {
+ } else if (la.kind == 22 || la.kind == 49) {
+ while (la.kind == 22) {
Get();
Expression(out ee, true, true);
if (multipleIndices == null) {
@@ -3546,15 +3935,15 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
multipleIndices.Add(ee);
}
- } else SynErr(221);
- } else if (la.kind == 135) {
+ } else SynErr(233);
+ } else if (la.kind == 139) {
Get();
anyDots = true;
- if (StartOf(7)) {
+ if (StartOf(9)) {
Expression(out ee, true, true);
e1 = ee;
}
- } else SynErr(222);
+ } else SynErr(234);
if (multipleIndices != null) {
e = new MultiSelectExpr(x, e, multipleIndices);
// make sure an array class with this dimensionality exists
@@ -3589,16 +3978,29 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
}
}
- Expect(47);
- } else if (la.kind == 48) {
+ Expect(49);
+ } else if (la.kind == 50) {
Get();
IToken openParen = t; var args = new List<Expression>();
- if (StartOf(7)) {
+ if (StartOf(9)) {
Expressions(args);
}
- Expect(49);
+ Expect(51);
e = new ApplySuffix(openParen, e, args);
- } else SynErr(223);
+ } else SynErr(235);
+ }
+
+ void ISetDisplayExpr(IToken/*!*/ setToken, bool finite, out Expression e) {
+ Contract.Ensures(Contract.ValueAtReturn(out e) != null);
+ List<Expression> elements = new List<Expression/*!*/>();;
+ e = dummyExpr;
+
+ Expect(46);
+ if (StartOf(9)) {
+ Expressions(elements);
+ }
+ e = new SetDisplayExpr(setToken, finite, elements);
+ Expect(47);
}
void LambdaExpression(out Expression e, bool allowSemi) {
@@ -3614,22 +4016,22 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
if (la.kind == 1) {
WildIdent(out id, true);
x = t; bvs.Add(new BoundVar(id, id.val, new InferredTypeProxy()));
- } else if (la.kind == 48) {
+ } else if (la.kind == 50) {
Get();
x = t;
if (la.kind == 1) {
IdentTypeOptional(out bv);
bvs.Add(bv);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
IdentTypeOptional(out bv);
bvs.Add(bv);
}
}
- Expect(49);
- } else SynErr(224);
- while (la.kind == 42 || la.kind == 43) {
- if (la.kind == 42) {
+ Expect(51);
+ } else SynErr(236);
+ while (la.kind == 44 || la.kind == 45) {
+ if (la.kind == 44) {
Get();
PossiblyWildFrameExpression(out fe, true);
reads.Add(fe);
@@ -3653,56 +4055,64 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
e = dummyExpr;
switch (la.kind) {
- case 96: {
+ case 101: {
Get();
x = t;
Expression(out e, true, true);
- Expect(32);
+ Expect(34);
Expression(out e0, true, true);
- Expect(33);
+ Expect(35);
Expression(out e1, allowSemi, allowLambda);
e = new ITEExpr(x, e, e0, e1);
break;
}
- case 98: {
+ case 103: {
MatchExpression(out e, allowSemi, allowLambda);
break;
}
- case 101: case 121: case 122: case 123: {
+ case 106: case 126: case 127: case 128: {
QuantifierGuts(out e, allowSemi, allowLambda);
break;
}
case 13: {
- SetComprehensionExpr(out e, allowSemi, allowLambda);
+ Get();
+ x = t;
+ SetComprehensionExpr(x, true, out e, allowSemi, allowLambda);
+ break;
+ }
+ case 14: {
+ Get();
+ x = t;
+ SetComprehensionExpr(x, false, out e, allowSemi, allowLambda);
break;
}
- case 29: case 30: case 99: {
+ case 31: case 32: case 104: {
StmtInExpr(out s);
Expression(out e, allowSemi, allowLambda);
e = new StmtExpr(s.Tok, s, e);
break;
}
- case 70: case 75: {
+ case 62: case 81: {
LetExpr(out e, allowSemi, allowLambda);
break;
}
- case 16: {
+ case 17: {
Get();
x = t;
MapComprehensionExpr(x, true, out e, allowSemi, allowLambda);
break;
}
- case 17: {
+ case 18: {
Get();
x = t;
MapComprehensionExpr(x, false, out e, allowSemi, allowLambda);
break;
}
- case 89: {
+ case 95: {
NamedExpr(out e, allowSemi, allowLambda);
break;
}
- default: SynErr(225); break;
+ default: SynErr(237); break;
}
}
@@ -3714,10 +4124,10 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
if (IsGenericInstantiation()) {
typeArgs = new List<Type>();
GenericInstantiation(typeArgs);
- } else if (la.kind == 104) {
+ } else if (la.kind == 109) {
HashCall(id, out openParen, out typeArgs, out args);
- } else if (StartOf(30)) {
- } else SynErr(226);
+ } else if (StartOf(33)) {
+ } else SynErr(238);
e = new NameSegment(id, id.val, typeArgs);
if (openParen != null) {
e = new ApplySuffix(openParen, e, args);
@@ -3730,23 +4140,23 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
IToken x; List<Expression> elements;
e = dummyExpr;
- if (la.kind == 44) {
+ if (la.kind == 46) {
Get();
x = t; elements = new List<Expression/*!*/>();
- if (StartOf(7)) {
+ if (StartOf(9)) {
Expressions(elements);
}
- e = new SetDisplayExpr(x, elements);
- Expect(45);
- } else if (la.kind == 46) {
+ e = new SetDisplayExpr(x, true, elements);
+ Expect(47);
+ } else if (la.kind == 48) {
Get();
x = t; elements = new List<Expression/*!*/>();
- if (StartOf(7)) {
+ if (StartOf(9)) {
Expressions(elements);
}
e = new SeqDisplayExpr(x, elements);
- Expect(47);
- } else SynErr(227);
+ Expect(49);
+ } else SynErr(239);
}
void MultiSetExpr(out Expression e) {
@@ -3754,23 +4164,23 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
IToken/*!*/ x = null; List<Expression/*!*/>/*!*/ elements;
e = dummyExpr;
- Expect(14);
+ Expect(15);
x = t;
- if (la.kind == 44) {
+ if (la.kind == 46) {
Get();
elements = new List<Expression/*!*/>();
- if (StartOf(7)) {
+ if (StartOf(9)) {
Expressions(elements);
}
e = new MultiSetDisplayExpr(x, elements);
- Expect(45);
- } else if (la.kind == 48) {
+ Expect(47);
+ } else if (la.kind == 50) {
Get();
x = t; elements = new List<Expression/*!*/>();
Expression(out e, true, true);
e = new MultiSetFormingExpr(x, e);
- Expect(49);
- } else SynErr(228);
+ Expect(51);
+ } else SynErr(240);
}
void ConstAtomExpression(out Expression e, bool allowSemi, bool allowLambda) {
@@ -3779,17 +4189,17 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
e = dummyExpr; Type toType = null;
switch (la.kind) {
- case 129: {
+ case 133: {
Get();
e = new LiteralExpr(t, false);
break;
}
- case 130: {
+ case 134: {
Get();
e = new LiteralExpr(t, true);
break;
}
- case 131: {
+ case 135: {
Get();
e = new LiteralExpr(t);
break;
@@ -3804,12 +4214,12 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
e = new LiteralExpr(t, d);
break;
}
- case 18: {
+ case 19: {
Get();
e = new CharLiteralExpr(t, t.val.Substring(1, t.val.Length - 2));
break;
}
- case 19: {
+ case 20: {
Get();
bool isVerbatimString;
string s = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString);
@@ -3817,35 +4227,35 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
break;
}
- case 132: {
+ case 136: {
Get();
e = new ThisExpr(t);
break;
}
- case 133: {
+ case 137: {
Get();
x = t;
- Expect(48);
+ Expect(50);
Expression(out e, true, true);
- Expect(49);
+ Expect(51);
e = new UnaryOpExpr(x, UnaryOpExpr.Opcode.Fresh, e);
break;
}
- case 134: {
+ case 138: {
Get();
x = t;
- Expect(48);
+ Expect(50);
Expression(out e, true, true);
- Expect(49);
+ Expect(51);
e = new OldExpr(x, e);
break;
}
- case 22: {
+ case 23: {
Get();
x = t;
Expression(out e, true, true);
e = new UnaryOpExpr(x, UnaryOpExpr.Opcode.Cardinality, e);
- Expect(22);
+ Expect(23);
break;
}
case 8: case 10: {
@@ -3856,17 +4266,17 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Get();
x = t; toType = new RealType();
}
- Expect(48);
+ Expect(50);
Expression(out e, true, true);
- Expect(49);
+ Expect(51);
e = new ConversionExpr(x, e, toType);
break;
}
- case 48: {
+ case 50: {
ParensExpression(out e, allowSemi, allowLambda);
break;
}
- default: SynErr(229); break;
+ default: SynErr(241); break;
}
}
@@ -3878,7 +4288,7 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Get();
S = Util.RemoveUnderscores(t.val);
try {
- n = BigInteger.Parse(S);
+ n = BigIntegerParser.Parse(S);
} catch (System.FormatException) {
SemErr("incorrectly formatted number");
n = BigInteger.Zero;
@@ -3889,13 +4299,13 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
S = Util.RemoveUnderscores(t.val.Substring(2));
try {
// note: leading 0 required when parsing positive hex numbers
- n = BigInteger.Parse("0" + S, System.Globalization.NumberStyles.HexNumber);
+ n = BigIntegerParser.Parse("0" + S, System.Globalization.NumberStyles.HexNumber);
} catch (System.FormatException) {
SemErr("incorrectly formatted number");
n = BigInteger.Zero;
}
- } else SynErr(230);
+ } else SynErr(242);
}
void Dec(out Basetypes.BigDec d) {
@@ -3915,12 +4325,12 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
IToken x;
var args = new List<Expression>();
- Expect(48);
+ Expect(50);
x = t;
- if (StartOf(7)) {
+ if (StartOf(9)) {
Expressions(args);
}
- Expect(49);
+ Expect(51);
if (args.Count == 1) {
e = new ParensExpression(x, args[0]);
} else {
@@ -3933,26 +4343,26 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
void LambdaArrow(out bool oneShot) {
oneShot = true;
- if (la.kind == 27) {
+ if (la.kind == 29) {
Get();
oneShot = false;
- } else if (la.kind == 28) {
+ } else if (la.kind == 30) {
Get();
oneShot = true;
- } else SynErr(231);
+ } else SynErr(243);
}
void MapLiteralExpressions(out List<ExpressionPair> elements) {
Expression/*!*/ d, r;
elements = new List<ExpressionPair/*!*/>();
Expression(out d, true, true);
- Expect(92);
+ Expect(98);
Expression(out r, true, true);
elements.Add(new ExpressionPair(d,r));
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
Expression(out d, true, true);
- Expect(92);
+ Expect(98);
Expression(out r, true, true);
elements.Add(new ExpressionPair(d,r));
}
@@ -3968,10 +4378,10 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
IdentTypeOptional(out bv);
bvars.Add(bv);
- while (la.kind == 44) {
+ while (la.kind == 46) {
Attribute(ref attrs);
}
- if (la.kind == 22) {
+ if (la.kind == 23) {
Get();
Expression(out range, true, true);
}
@@ -3986,23 +4396,23 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
List<MatchCaseExpr/*!*/> cases = new List<MatchCaseExpr/*!*/>();
bool usesOptionalBrace = false;
- Expect(98);
+ Expect(103);
x = t;
Expression(out e, allowSemi, allowLambda);
if (la.kind == _lbrace) {
- Expect(44);
+ Expect(46);
usesOptionalBrace = true;
- while (la.kind == 31) {
+ while (la.kind == 33) {
CaseExpression(out c, true, true);
cases.Add(c);
}
- Expect(45);
- } else if (StartOf(31)) {
+ Expect(47);
+ } else if (StartOf(34)) {
while (la.kind == _case) {
CaseExpression(out c, allowSemi, allowLambda);
cases.Add(c);
}
- } else SynErr(232);
+ } else SynErr(244);
e = new MatchExpr(x, e, cases, usesOptionalBrace);
}
@@ -4014,13 +4424,13 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Expression range;
Expression/*!*/ body;
- if (la.kind == 101 || la.kind == 121) {
+ if (la.kind == 106 || la.kind == 126) {
Forall();
x = t; univ = true;
- } else if (la.kind == 122 || la.kind == 123) {
+ } else if (la.kind == 127 || la.kind == 128) {
Exists();
x = t;
- } else SynErr(233);
+ } else SynErr(245);
QuantifierDomain(out bvars, out attrs, out range);
QSep();
Expression(out body, allowSemi, allowLambda);
@@ -4032,47 +4442,44 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
}
- void SetComprehensionExpr(out Expression q, bool allowSemi, bool allowLambda) {
+ void SetComprehensionExpr(IToken setToken, bool finite, out Expression q, bool allowSemi, bool allowLambda) {
Contract.Ensures(Contract.ValueAtReturn(out q) != null);
- IToken x = Token.NoToken;
BoundVar bv;
List<BoundVar/*!*/> bvars = new List<BoundVar>();
Expression range;
Expression body = null;
Attributes attrs = null;
- Expect(13);
- x = t;
IdentTypeOptional(out bv);
bvars.Add(bv);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
IdentTypeOptional(out bv);
bvars.Add(bv);
}
- while (la.kind == 44) {
+ while (la.kind == 46) {
Attribute(ref attrs);
}
- Expect(22);
+ Expect(23);
Expression(out range, allowSemi, allowLambda);
if (IsQSep()) {
QSep();
Expression(out body, allowSemi, allowLambda);
}
if (body == null && bvars.Count != 1) { SemErr(t, "a set comprehension with more than one bound variable must have a term expression"); }
- q = new SetComprehension(x, bvars, range, body, attrs);
+ q = new SetComprehension(setToken, finite, bvars, range, body, attrs);
}
void StmtInExpr(out Statement s) {
s = dummyStmt;
- if (la.kind == 99) {
+ if (la.kind == 104) {
AssertStmt(out s);
- } else if (la.kind == 29) {
+ } else if (la.kind == 31) {
AssumeStmt(out s);
- } else if (la.kind == 30) {
+ } else if (la.kind == 32) {
CalcStmt(out s);
- } else SynErr(234);
+ } else SynErr(246);
}
void LetExpr(out Expression e, bool allowSemi, bool allowLambda) {
@@ -4085,30 +4492,30 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Attributes attrs = null;
e = dummyExpr;
- if (la.kind == 70) {
+ if (la.kind == 62) {
Get();
isGhost = true; x = t;
}
- Expect(75);
+ Expect(81);
if (!isGhost) { x = t; }
CasePattern(out pat);
if (isGhost) { pat.Vars.Iter(bv => bv.IsGhost = true); }
letLHSs.Add(pat);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
CasePattern(out pat);
if (isGhost) { pat.Vars.Iter(bv => bv.IsGhost = true); }
letLHSs.Add(pat);
}
- if (la.kind == 92) {
+ if (la.kind == 98) {
Get();
- } else if (la.kind == 44 || la.kind == 94) {
- while (la.kind == 44) {
+ } else if (la.kind == 25 || la.kind == 46) {
+ while (la.kind == 46) {
Attribute(ref attrs);
}
- Expect(94);
+ Expect(25);
exact = false;
foreach (var lhs in letLHSs) {
if (lhs.Arguments != null) {
@@ -4116,15 +4523,15 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
}
}
- } else SynErr(235);
+ } else SynErr(247);
Expression(out e, false, true);
letRHSs.Add(e);
- while (la.kind == 21) {
+ while (la.kind == 22) {
Get();
Expression(out e, false, true);
letRHSs.Add(e);
}
- Expect(26);
+ Expect(28);
Expression(out e, allowSemi, allowLambda);
e = new LetExpr(x, letLHSs, letRHSs, e, exact, attrs);
}
@@ -4134,89 +4541,87 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
e = dummyExpr;
Expression expr;
- Expect(89);
+ Expect(95);
x = t;
NoUSIdent(out d);
- Expect(20);
+ Expect(21);
Expression(out e, allowSemi, allowLambda);
expr = e;
e = new NamedExpr(x, d.val, expr);
}
- void CasePattern(out CasePattern pat) {
- IToken id; List<CasePattern> arguments;
- BoundVar bv;
- pat = null;
+ void CaseExpression(out MatchCaseExpr c, bool allowSemi, bool allowLambda) {
+ Contract.Ensures(Contract.ValueAtReturn(out c) != null); IToken/*!*/ x, id;
+ List<CasePattern/*!*/> arguments = new List<CasePattern/*!*/>();
+ CasePattern/*!*/ pat;
+ Expression/*!*/ body;
+ string/*!*/ name = "";
- if (IsIdentParen()) {
+ Expect(33);
+ x = t;
+ if (la.kind == 1) {
Ident(out id);
- Expect(48);
- arguments = new List<CasePattern>();
- if (la.kind == 1) {
- CasePattern(out pat);
- arguments.Add(pat);
- while (la.kind == 21) {
- Get();
+ name = id.val;
+ if (la.kind == 50) {
+ Get();
+ if (la.kind == 1 || la.kind == 50) {
CasePattern(out pat);
arguments.Add(pat);
+ while (la.kind == 22) {
+ Get();
+ CasePattern(out pat);
+ arguments.Add(pat);
+ }
}
+ Expect(51);
}
- Expect(49);
- pat = new CasePattern(id, id.val, arguments);
- } else if (la.kind == 1) {
- IdentTypeOptional(out bv);
- pat = new CasePattern(bv.tok, bv);
-
- } else SynErr(236);
- if (pat == null) {
- pat = new CasePattern(t, "_ParseError", new List<CasePattern>());
- }
-
- }
-
- void CaseExpression(out MatchCaseExpr c, bool allowSemi, bool allowLambda) {
- Contract.Ensures(Contract.ValueAtReturn(out c) != null); IToken/*!*/ x, id;
- List<BoundVar/*!*/> arguments = new List<BoundVar/*!*/>();
- BoundVar/*!*/ bv;
- Expression/*!*/ body;
-
- Expect(31);
- x = t;
- Ident(out id);
- if (la.kind == 48) {
+ } else if (la.kind == 50) {
Get();
- IdentTypeOptional(out bv);
- arguments.Add(bv);
- while (la.kind == 21) {
+ CasePattern(out pat);
+ arguments.Add(pat);
+ while (la.kind == 22) {
Get();
- IdentTypeOptional(out bv);
- arguments.Add(bv);
+ CasePattern(out pat);
+ arguments.Add(pat);
}
- Expect(49);
- }
- Expect(27);
+ Expect(51);
+ } else SynErr(248);
+ Expect(29);
Expression(out body, allowSemi, allowLambda);
- c = new MatchCaseExpr(x, id.val, arguments, body);
+ c = new MatchCaseExpr(x, name, arguments, body);
}
void HashCall(IToken id, out IToken openParen, out List<Type> typeArgs, out List<Expression> args) {
Expression k; args = new List<Expression>(); typeArgs = null;
- Expect(104);
+ Expect(109);
id.val = id.val + "#";
- if (la.kind == 50) {
+ if (la.kind == 52) {
typeArgs = new List<Type>();
GenericInstantiation(typeArgs);
}
- Expect(46);
+ Expect(48);
Expression(out k, true, true);
- Expect(47);
+ Expect(49);
args.Add(k);
- Expect(48);
+ Expect(50);
openParen = t;
- if (StartOf(7)) {
+ if (StartOf(9)) {
Expressions(args);
}
- Expect(49);
+ Expect(51);
+ }
+
+ void MemberBindingUpdate(out IToken id, out Expression e) {
+ id = Token.NoToken; e = dummyExpr;
+ if (la.kind == 1) {
+ Get();
+ id = t;
+ } else if (la.kind == 2) {
+ Get();
+ id = t;
+ } else SynErr(249);
+ Expect(98);
+ Expression(out e, true, true);
}
void DotSuffix(out IToken x, out IToken y) {
@@ -4224,7 +4629,6 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
x = Token.NoToken;
y = null;
- Expect(25);
if (la.kind == 1) {
Get();
x = t;
@@ -4252,13 +4656,13 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
}
}
- } else if (la.kind == 43) {
+ } else if (la.kind == 45) {
Get();
x = t;
- } else if (la.kind == 42) {
+ } else if (la.kind == 44) {
Get();
x = t;
- } else SynErr(237);
+ } else SynErr(250);
}
@@ -4270,61 +4674,66 @@ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo
Dafny();
Expect(0);
- Expect(0);
}
- static readonly bool[,]/*!*/ set = {
- {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_T,_x, _x,_x,_T,_x, _x,_T,_T,_T, _x,_x,_T,_T, _x,_x,_T,_T, _x,_T,_T,_T, _T,_T,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_T,_T,_x, _x,_T,_T,_T, _x,_x,_T,_x, _x,_T,_T,_T, _T,_T,_T,_T, _x,_T,_T,_x, _x,_T,_x,_x, _T,_T,_T,_T, _T,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
- {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_T,_x, _x,_x,_x,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_x,_T,_x,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_T, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_T,_x, _T,_x,_x,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_x, _T,_T,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_T,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _T,_x,_T,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _x,_x,_T,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
- {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _T,_T,_T,_T, _T,_x,_T,_T, _T,_T,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_T,_x, _x,_x,_x,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x,_T,_T,_T, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_x,_T,_T,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_T,_T,_x, _x,_T,_x,_x, _T,_T,_T,_T, _T,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
- {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_x, _T,_T,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_T,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _x,_x,_x,_x, _T,_x,_T,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _x,_x,_T,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
- {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_T,_T,_x, _x,_T,_x,_x, _T,_T,_T,_T, _T,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
- {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_x, _T,_T,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_T,_x, _T,_x,_x,_x, _x,_x,_x,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _T,_x,_T,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _x,_x,_T,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
- {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_x, _T,_T,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_T,_x, _T,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _T,_x,_T,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _x,_x,_T,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
- {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_T,_T,_x, _x,_T,_x,_x, _T,_T,_T,_T, _T,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
- {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _x,_T,_T,_x, _x,_T,_x,_x, _T,_T,_T,_T, _T,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
- {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_x, _T,_T,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_T,_x, _T,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_T, _T,_x,_T,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _x,_x,_T,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
- {_x,_x,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
- {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_T,_T,_x, _x,_T,_x,_x, _T,_T,_T,_T, _T,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
- {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _T,_x,_T,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
- {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_T, _T,_x,_T,_x, _x,_x,_x,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _T,_T,_T,_x, _T,_T,_T,_T, _T,_T,_T,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _x,_x,_x,_x, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _x,_x},
- {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_T,_T, _T,_T,_T,_T, _T,_x,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_T, _T,_x,_T,_x, _x,_x,_x,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _T,_T,_x,_x, _T,_T,_T,_T, _T,_T,_T,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _x,_x,_x,_x, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _x,_x}
+ static readonly bool[,] set = {
+ {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _T,_x,_x,_T, _T,_T,_x,_x, _T,_T,_x,_x, _T,_T,_x,_T, _T,_T,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _T,_x,_x,_T, _T,_T,_T,_T, _T,_T,_x,_T, _T,_x,_x,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
+ {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_T,_x,_x, _x,_T,_T,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_T,_x,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _T,_T,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_T,_x,_T, _x,_T,_T,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_T, _T,_x,_T,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
+ {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_T,_T, _T,_T,_T,_x, _T,_T,_T,_T, _x,_x,_T,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_T,_x,_x, _x,_T,_T,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_T,_T,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
+ {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_T, _T,_x,_T,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
+ {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
+ {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_T,_x,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_T, _T,_x,_T,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
+ {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_T, _T,_x,_T,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
+ {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
+ {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_T, _T,_x,_x,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
+ {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _T,_T,_x,_T, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_T, _T,_x,_T,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
+ {_x,_x,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
+ {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_x, _x,_x},
+ {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_x,_T,_T,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x},
+ {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _x,_T,_T,_T, _T,_T,_T,_x, _x,_T,_x,_x, _x,_T,_T,_x, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_T,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_x,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _x,_x},
+ {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_T,_x, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _x,_T,_T,_T, _T,_T,_T,_x, _x,_T,_x,_x, _x,_T,_T,_x, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_T,_T, _x,_T,_T,_T, _T,_T,_T,_T, _T,_x,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _x,_x}
};
} // end Parser
public class Errors {
- public int count = 0; // number of errors detected
- public System.IO.TextWriter/*!*/ errorStream = Console.Out; // error messages go to this stream
- public string errMsgFormat = "{0}({1},{2}): error: {3}"; // 0=filename, 1=line, 2=column, 3=text
- public string warningMsgFormat = "{0}({1},{2}): warning: {3}"; // 0=filename, 1=line, 2=column, 3=text
+ readonly ErrorReporter Reporting;
+ public int ErrorCount;
+
+ public Errors(ErrorReporter Reporting) {
+ Contract.Requires(Reporting != null);
+ this.Reporting = Reporting;
+ }
public void SynErr(string filename, int line, int col, int n) {
SynErr(filename, line, col, GetSyntaxErrorString(n));
}
- public virtual void SynErr(string filename, int line, int col, string/*!*/ msg) {
+ public void SynErr(string filename, int line, int col, string msg) {
Contract.Requires(msg != null);
- errorStream.WriteLine(errMsgFormat, filename, line, col, msg);
- count++;
+ ErrorCount++;
+ Reporting.Error(MessageSource.Parser, filename, line, col, msg);
}
string GetSyntaxErrorString(int n) {
@@ -4344,257 +4753,266 @@ public class Errors {
case 11: s = "object expected"; break;
case 12: s = "string expected"; break;
case 13: s = "set expected"; break;
- case 14: s = "multiset expected"; break;
- case 15: s = "seq expected"; break;
- case 16: s = "map expected"; break;
- case 17: s = "imap expected"; break;
- case 18: s = "charToken expected"; break;
- case 19: s = "stringToken expected"; break;
- case 20: s = "colon expected"; break;
- case 21: s = "comma expected"; break;
- case 22: s = "verticalbar expected"; break;
- case 23: s = "doublecolon expected"; break;
- case 24: s = "bullet expected"; break;
- case 25: s = "dot expected"; break;
- case 26: s = "semi expected"; break;
- case 27: s = "darrow expected"; break;
- case 28: s = "arrow expected"; break;
- case 29: s = "assume expected"; break;
- case 30: s = "calc expected"; break;
- case 31: s = "case expected"; break;
- case 32: s = "then expected"; break;
- case 33: s = "else expected"; break;
- case 34: s = "decreases expected"; break;
- case 35: s = "invariant expected"; break;
- case 36: s = "function expected"; break;
- case 37: s = "predicate expected"; break;
- case 38: s = "inductive expected"; break;
- case 39: s = "lemma expected"; break;
- case 40: s = "copredicate expected"; break;
- case 41: s = "modifies expected"; break;
- case 42: s = "reads expected"; break;
- case 43: s = "requires expected"; break;
- case 44: s = "lbrace expected"; break;
- case 45: s = "rbrace expected"; break;
- case 46: s = "lbracket expected"; break;
- case 47: s = "rbracket expected"; break;
- case 48: s = "openparen expected"; break;
- case 49: s = "closeparen expected"; break;
- case 50: s = "openAngleBracket expected"; break;
- case 51: s = "closeAngleBracket expected"; break;
- case 52: s = "eq expected"; break;
- case 53: s = "neq expected"; break;
- case 54: s = "neqAlt expected"; break;
- case 55: s = "star expected"; break;
- case 56: s = "notIn expected"; break;
- case 57: s = "ellipsis expected"; break;
- case 58: s = "\"include\" expected"; break;
- case 59: s = "\"abstract\" expected"; break;
- case 60: s = "\"module\" expected"; break;
- case 61: s = "\"refines\" expected"; break;
- case 62: s = "\"import\" expected"; break;
- case 63: s = "\"opened\" expected"; break;
- case 64: s = "\"=\" expected"; break;
- case 65: s = "\"as\" expected"; break;
- case 66: s = "\"default\" expected"; break;
- case 67: s = "\"class\" expected"; break;
- case 68: s = "\"extends\" expected"; break;
- case 69: s = "\"trait\" expected"; break;
- case 70: s = "\"ghost\" expected"; break;
- case 71: s = "\"static\" expected"; break;
- case 72: s = "\"protected\" expected"; break;
- case 73: s = "\"datatype\" expected"; break;
- case 74: s = "\"codatatype\" expected"; break;
- case 75: s = "\"var\" expected"; break;
- case 76: s = "\"newtype\" expected"; break;
- case 77: s = "\"type\" expected"; break;
- case 78: s = "\"iterator\" expected"; break;
- case 79: s = "\"yields\" expected"; break;
- case 80: s = "\"returns\" expected"; break;
- case 81: s = "\"method\" expected"; break;
- case 82: s = "\"colemma\" expected"; break;
- case 83: s = "\"comethod\" expected"; break;
- case 84: s = "\"constructor\" expected"; break;
- case 85: s = "\"free\" expected"; break;
- case 86: s = "\"ensures\" expected"; break;
- case 87: s = "\"yield\" expected"; break;
- case 88: s = "\"`\" expected"; break;
- case 89: s = "\"label\" expected"; break;
- case 90: s = "\"break\" expected"; break;
- case 91: s = "\"where\" expected"; break;
- case 92: s = "\":=\" expected"; break;
- case 93: s = "\"return\" expected"; break;
- case 94: s = "\":|\" expected"; break;
- case 95: s = "\"new\" expected"; break;
- case 96: s = "\"if\" expected"; break;
- case 97: s = "\"while\" expected"; break;
- case 98: s = "\"match\" expected"; break;
- case 99: s = "\"assert\" expected"; break;
- case 100: s = "\"print\" expected"; break;
- case 101: s = "\"forall\" expected"; break;
- case 102: s = "\"parallel\" expected"; break;
- case 103: s = "\"modify\" expected"; break;
- case 104: s = "\"#\" expected"; break;
- case 105: s = "\"<=\" expected"; break;
- case 106: s = "\">=\" expected"; break;
- case 107: s = "\"\\u2264\" expected"; break;
- case 108: s = "\"\\u2265\" expected"; break;
- case 109: s = "\"<==>\" expected"; break;
- case 110: s = "\"\\u21d4\" expected"; break;
- case 111: s = "\"==>\" expected"; break;
- case 112: s = "\"\\u21d2\" expected"; break;
- case 113: s = "\"<==\" expected"; break;
- case 114: s = "\"\\u21d0\" expected"; break;
- case 115: s = "\"&&\" expected"; break;
- case 116: s = "\"\\u2227\" expected"; break;
- case 117: s = "\"||\" expected"; break;
- case 118: s = "\"\\u2228\" expected"; break;
- case 119: s = "\"!\" expected"; break;
- case 120: s = "\"\\u00ac\" expected"; break;
- case 121: s = "\"\\u2200\" expected"; break;
- case 122: s = "\"exists\" expected"; break;
- case 123: s = "\"\\u2203\" expected"; break;
- case 124: s = "\"in\" expected"; break;
- case 125: s = "\"+\" expected"; break;
- case 126: s = "\"-\" expected"; break;
- case 127: s = "\"/\" expected"; break;
- case 128: s = "\"%\" expected"; break;
- case 129: s = "\"false\" expected"; break;
- case 130: s = "\"true\" expected"; break;
- case 131: s = "\"null\" expected"; break;
- case 132: s = "\"this\" expected"; break;
- case 133: s = "\"fresh\" expected"; break;
- case 134: s = "\"old\" expected"; break;
- case 135: s = "\"..\" expected"; break;
- case 136: s = "??? expected"; break;
- case 137: s = "this symbol not expected in SubModuleDecl"; break;
- case 138: s = "invalid SubModuleDecl"; break;
- case 139: s = "this symbol not expected in ClassDecl"; break;
- case 140: s = "this symbol not expected in DatatypeDecl"; break;
- case 141: s = "invalid DatatypeDecl"; break;
- case 142: s = "this symbol not expected in DatatypeDecl"; break;
- case 143: s = "invalid NewtypeDecl"; break;
- case 144: s = "invalid OtherTypeDecl"; break;
- case 145: s = "this symbol not expected in OtherTypeDecl"; break;
- case 146: s = "this symbol not expected in IteratorDecl"; break;
- case 147: s = "invalid IteratorDecl"; break;
- case 148: s = "this symbol not expected in TraitDecl"; break;
- case 149: s = "invalid ClassMemberDecl"; break;
- case 150: s = "this symbol not expected in FieldDecl"; break;
- case 151: s = "invalid FunctionDecl"; break;
- case 152: s = "invalid FunctionDecl"; break;
- case 153: s = "invalid FunctionDecl"; break;
- case 154: s = "invalid FunctionDecl"; break;
- case 155: s = "invalid FunctionDecl"; break;
- case 156: s = "this symbol not expected in MethodDecl"; break;
- case 157: s = "invalid MethodDecl"; break;
- case 158: s = "invalid MethodDecl"; break;
- case 159: s = "invalid FIdentType"; break;
- case 160: s = "this symbol not expected in OldSemi"; break;
- case 161: s = "invalid TypeIdentOptional"; break;
- case 162: s = "invalid TypeAndToken"; break;
- case 163: s = "this symbol not expected in IteratorSpec"; break;
- case 164: s = "invalid IteratorSpec"; break;
- case 165: s = "invalid IteratorSpec"; break;
- case 166: s = "this symbol not expected in MethodSpec"; break;
- case 167: s = "invalid MethodSpec"; break;
- case 168: s = "invalid MethodSpec"; break;
- case 169: s = "invalid FrameExpression"; break;
- case 170: s = "this symbol not expected in FunctionSpec"; break;
- case 171: s = "invalid FunctionSpec"; break;
- case 172: s = "invalid PossiblyWildFrameExpression"; break;
- case 173: s = "invalid PossiblyWildExpression"; break;
- case 174: s = "this symbol not expected in OneStmt"; break;
- case 175: s = "invalid OneStmt"; break;
- case 176: s = "this symbol not expected in OneStmt"; break;
- case 177: s = "invalid OneStmt"; break;
- case 178: s = "invalid AssertStmt"; break;
- case 179: s = "invalid AssumeStmt"; break;
- case 180: s = "invalid UpdateStmt"; break;
- case 181: s = "invalid UpdateStmt"; break;
- case 182: s = "this symbol not expected in VarDeclStatement"; break;
- case 183: s = "invalid IfStmt"; break;
- case 184: s = "invalid IfStmt"; break;
- case 185: s = "invalid WhileStmt"; break;
- case 186: s = "invalid WhileStmt"; break;
- case 187: s = "invalid MatchStmt"; break;
- case 188: s = "invalid ForallStmt"; break;
- case 189: s = "invalid ForallStmt"; break;
- case 190: s = "invalid CalcStmt"; break;
- case 191: s = "invalid ModifyStmt"; break;
- case 192: s = "this symbol not expected in ModifyStmt"; break;
- case 193: s = "invalid ModifyStmt"; break;
- case 194: s = "invalid ReturnStmt"; break;
- case 195: s = "invalid Rhs"; break;
- case 196: s = "invalid Lhs"; break;
- case 197: s = "invalid Guard"; break;
- case 198: s = "this symbol not expected in LoopSpec"; break;
- case 199: s = "this symbol not expected in LoopSpec"; break;
- case 200: s = "this symbol not expected in LoopSpec"; break;
- case 201: s = "invalid LoopSpec"; break;
- case 202: s = "this symbol not expected in CaseStatement"; break;
- case 203: s = "this symbol not expected in CaseStatement"; break;
- case 204: s = "invalid CalcOp"; break;
- case 205: s = "invalid EquivOp"; break;
- case 206: s = "invalid ImpliesOp"; break;
- case 207: s = "invalid ExpliesOp"; break;
- case 208: s = "invalid AndOp"; break;
- case 209: s = "invalid OrOp"; break;
- case 210: s = "invalid NegOp"; break;
- case 211: s = "invalid Forall"; break;
- case 212: s = "invalid Exists"; break;
- case 213: s = "invalid QSep"; break;
- case 214: s = "invalid ImpliesExpliesExpression"; break;
- case 215: s = "invalid LogicalExpression"; break;
- case 216: s = "invalid RelOp"; break;
- case 217: s = "invalid AddOp"; break;
- case 218: s = "invalid UnaryExpression"; break;
- case 219: s = "invalid MulOp"; break;
- case 220: s = "invalid Suffix"; break;
- case 221: s = "invalid Suffix"; break;
- case 222: s = "invalid Suffix"; break;
- case 223: s = "invalid Suffix"; break;
- case 224: s = "invalid LambdaExpression"; break;
- case 225: s = "invalid EndlessExpression"; break;
- case 226: s = "invalid NameSegment"; break;
- case 227: s = "invalid DisplayExpr"; break;
- case 228: s = "invalid MultiSetExpr"; break;
- case 229: s = "invalid ConstAtomExpression"; break;
- case 230: s = "invalid Nat"; break;
- case 231: s = "invalid LambdaArrow"; break;
- case 232: s = "invalid MatchExpression"; break;
- case 233: s = "invalid QuantifierGuts"; break;
- case 234: s = "invalid StmtInExpr"; break;
- case 235: s = "invalid LetExpr"; break;
- case 236: s = "invalid CasePattern"; break;
- case 237: s = "invalid DotSuffix"; break;
+ case 14: s = "iset expected"; break;
+ case 15: s = "multiset expected"; break;
+ case 16: s = "seq expected"; break;
+ case 17: s = "map expected"; break;
+ case 18: s = "imap expected"; break;
+ case 19: s = "charToken expected"; break;
+ case 20: s = "stringToken expected"; break;
+ case 21: s = "colon expected"; break;
+ case 22: s = "comma expected"; break;
+ case 23: s = "verticalbar expected"; break;
+ case 24: s = "doublecolon expected"; break;
+ case 25: s = "boredSmiley expected"; break;
+ case 26: s = "bullet expected"; break;
+ case 27: s = "dot expected"; break;
+ case 28: s = "semi expected"; break;
+ case 29: s = "darrow expected"; break;
+ case 30: s = "arrow expected"; break;
+ case 31: s = "assume expected"; break;
+ case 32: s = "calc expected"; break;
+ case 33: s = "case expected"; break;
+ case 34: s = "then expected"; break;
+ case 35: s = "else expected"; break;
+ case 36: s = "decreases expected"; break;
+ case 37: s = "invariant expected"; break;
+ case 38: s = "function expected"; break;
+ case 39: s = "predicate expected"; break;
+ case 40: s = "inductive expected"; break;
+ case 41: s = "lemma expected"; break;
+ case 42: s = "copredicate expected"; break;
+ case 43: s = "modifies expected"; break;
+ case 44: s = "reads expected"; break;
+ case 45: s = "requires expected"; break;
+ case 46: s = "lbrace expected"; break;
+ case 47: s = "rbrace expected"; break;
+ case 48: s = "lbracket expected"; break;
+ case 49: s = "rbracket expected"; break;
+ case 50: s = "openparen expected"; break;
+ case 51: s = "closeparen expected"; break;
+ case 52: s = "openAngleBracket expected"; break;
+ case 53: s = "closeAngleBracket expected"; break;
+ case 54: s = "eq expected"; break;
+ case 55: s = "neq expected"; break;
+ case 56: s = "neqAlt expected"; break;
+ case 57: s = "star expected"; break;
+ case 58: s = "notIn expected"; break;
+ case 59: s = "ellipsis expected"; break;
+ case 60: s = "\"include\" expected"; break;
+ case 61: s = "\"abstract\" expected"; break;
+ case 62: s = "\"ghost\" expected"; break;
+ case 63: s = "\"static\" expected"; break;
+ case 64: s = "\"protected\" expected"; break;
+ case 65: s = "\"extern\" expected"; break;
+ case 66: s = "\"module\" expected"; break;
+ case 67: s = "\"exclusively\" expected"; break;
+ case 68: s = "\"refines\" expected"; break;
+ case 69: s = "\"import\" expected"; break;
+ case 70: s = "\"opened\" expected"; break;
+ case 71: s = "\"=\" expected"; break;
+ case 72: s = "\"as\" expected"; break;
+ case 73: s = "\"default\" expected"; break;
+ case 74: s = "\"export\" expected"; break;
+ case 75: s = "\"extends\" expected"; break;
+ case 76: s = "\"+\" expected"; break;
+ case 77: s = "\"class\" expected"; break;
+ case 78: s = "\"trait\" expected"; break;
+ case 79: s = "\"datatype\" expected"; break;
+ case 80: s = "\"codatatype\" expected"; break;
+ case 81: s = "\"var\" expected"; break;
+ case 82: s = "\"newtype\" expected"; break;
+ case 83: s = "\"type\" expected"; break;
+ case 84: s = "\"iterator\" expected"; break;
+ case 85: s = "\"yields\" expected"; break;
+ case 86: s = "\"returns\" expected"; break;
+ case 87: s = "\"method\" expected"; break;
+ case 88: s = "\"colemma\" expected"; break;
+ case 89: s = "\"comethod\" expected"; break;
+ case 90: s = "\"constructor\" expected"; break;
+ case 91: s = "\"free\" expected"; break;
+ case 92: s = "\"ensures\" expected"; break;
+ case 93: s = "\"yield\" expected"; break;
+ case 94: s = "\"`\" expected"; break;
+ case 95: s = "\"label\" expected"; break;
+ case 96: s = "\"break\" expected"; break;
+ case 97: s = "\"where\" expected"; break;
+ case 98: s = "\":=\" expected"; break;
+ case 99: s = "\"return\" expected"; break;
+ case 100: s = "\"new\" expected"; break;
+ case 101: s = "\"if\" expected"; break;
+ case 102: s = "\"while\" expected"; break;
+ case 103: s = "\"match\" expected"; break;
+ case 104: s = "\"assert\" expected"; break;
+ case 105: s = "\"print\" expected"; break;
+ case 106: s = "\"forall\" expected"; break;
+ case 107: s = "\"parallel\" expected"; break;
+ case 108: s = "\"modify\" expected"; break;
+ case 109: s = "\"#\" expected"; break;
+ case 110: s = "\"<=\" expected"; break;
+ case 111: s = "\">=\" expected"; break;
+ case 112: s = "\"\\u2264\" expected"; break;
+ case 113: s = "\"\\u2265\" expected"; break;
+ case 114: s = "\"<==>\" expected"; break;
+ case 115: s = "\"\\u21d4\" expected"; break;
+ case 116: s = "\"==>\" expected"; break;
+ case 117: s = "\"\\u21d2\" expected"; break;
+ case 118: s = "\"<==\" expected"; break;
+ case 119: s = "\"\\u21d0\" expected"; break;
+ case 120: s = "\"&&\" expected"; break;
+ case 121: s = "\"\\u2227\" expected"; break;
+ case 122: s = "\"||\" expected"; break;
+ case 123: s = "\"\\u2228\" expected"; break;
+ case 124: s = "\"!\" expected"; break;
+ case 125: s = "\"\\u00ac\" expected"; break;
+ case 126: s = "\"\\u2200\" expected"; break;
+ case 127: s = "\"exists\" expected"; break;
+ case 128: s = "\"\\u2203\" expected"; break;
+ case 129: s = "\"in\" expected"; break;
+ case 130: s = "\"-\" expected"; break;
+ case 131: s = "\"/\" expected"; break;
+ case 132: s = "\"%\" expected"; break;
+ case 133: s = "\"false\" expected"; break;
+ case 134: s = "\"true\" expected"; break;
+ case 135: s = "\"null\" expected"; break;
+ case 136: s = "\"this\" expected"; break;
+ case 137: s = "\"fresh\" expected"; break;
+ case 138: s = "\"old\" expected"; break;
+ case 139: s = "\"..\" expected"; break;
+ case 140: s = "??? expected"; break;
+ case 141: s = "invalid TopDecl"; break;
+ case 142: s = "invalid DeclModifier"; break;
+ case 143: s = "this symbol not expected in SubModuleDecl"; break;
+ case 144: s = "invalid SubModuleDecl"; break;
+ case 145: s = "this symbol not expected in ClassDecl"; break;
+ case 146: s = "this symbol not expected in DatatypeDecl"; break;
+ case 147: s = "invalid DatatypeDecl"; break;
+ case 148: s = "this symbol not expected in DatatypeDecl"; break;
+ case 149: s = "invalid NewtypeDecl"; break;
+ case 150: s = "invalid OtherTypeDecl"; break;
+ case 151: s = "this symbol not expected in OtherTypeDecl"; break;
+ case 152: s = "this symbol not expected in IteratorDecl"; break;
+ case 153: s = "invalid IteratorDecl"; break;
+ case 154: s = "this symbol not expected in TraitDecl"; break;
+ case 155: s = "invalid ClassMemberDecl"; break;
+ case 156: s = "this symbol not expected in FieldDecl"; break;
+ case 157: s = "invalid FunctionDecl"; break;
+ case 158: s = "invalid FunctionDecl"; break;
+ case 159: s = "invalid FunctionDecl"; break;
+ case 160: s = "invalid FunctionDecl"; break;
+ case 161: s = "invalid FunctionDecl"; break;
+ case 162: s = "this symbol not expected in MethodDecl"; break;
+ case 163: s = "invalid MethodDecl"; break;
+ case 164: s = "invalid MethodDecl"; break;
+ case 165: s = "invalid FIdentType"; break;
+ case 166: s = "this symbol not expected in OldSemi"; break;
+ case 167: s = "invalid TypeIdentOptional"; break;
+ case 168: s = "invalid TypeAndToken"; break;
+ case 169: s = "this symbol not expected in IteratorSpec"; break;
+ case 170: s = "invalid IteratorSpec"; break;
+ case 171: s = "invalid IteratorSpec"; break;
+ case 172: s = "this symbol not expected in MethodSpec"; break;
+ case 173: s = "invalid MethodSpec"; break;
+ case 174: s = "invalid MethodSpec"; break;
+ case 175: s = "invalid FrameExpression"; break;
+ case 176: s = "this symbol not expected in FunctionSpec"; break;
+ case 177: s = "invalid FunctionSpec"; break;
+ case 178: s = "invalid PossiblyWildFrameExpression"; break;
+ case 179: s = "invalid PossiblyWildExpression"; break;
+ case 180: s = "this symbol not expected in OneStmt"; break;
+ case 181: s = "invalid OneStmt"; break;
+ case 182: s = "this symbol not expected in OneStmt"; break;
+ case 183: s = "invalid OneStmt"; break;
+ case 184: s = "invalid AssertStmt"; break;
+ case 185: s = "invalid AssumeStmt"; break;
+ case 186: s = "invalid UpdateStmt"; break;
+ case 187: s = "invalid UpdateStmt"; break;
+ case 188: s = "this symbol not expected in VarDeclStatement"; break;
+ case 189: s = "invalid VarDeclStatement"; break;
+ case 190: s = "invalid VarDeclStatement"; break;
+ case 191: s = "invalid IfStmt"; break;
+ case 192: s = "invalid IfStmt"; break;
+ case 193: s = "invalid WhileStmt"; break;
+ case 194: s = "invalid WhileStmt"; break;
+ case 195: s = "invalid MatchStmt"; break;
+ case 196: s = "invalid ForallStmt"; break;
+ case 197: s = "invalid ForallStmt"; break;
+ case 198: s = "invalid CalcStmt"; break;
+ case 199: s = "invalid ModifyStmt"; break;
+ case 200: s = "this symbol not expected in ModifyStmt"; break;
+ case 201: s = "invalid ModifyStmt"; break;
+ case 202: s = "invalid ReturnStmt"; break;
+ case 203: s = "invalid Rhs"; break;
+ case 204: s = "invalid Lhs"; break;
+ case 205: s = "invalid CasePattern"; break;
+ case 206: s = "invalid AlternativeBlock"; break;
+ case 207: s = "invalid Guard"; break;
+ case 208: s = "this symbol not expected in LoopSpec"; break;
+ case 209: s = "this symbol not expected in LoopSpec"; break;
+ case 210: s = "this symbol not expected in LoopSpec"; break;
+ case 211: s = "invalid LoopSpec"; break;
+ case 212: s = "invalid CaseStatement"; break;
+ case 213: s = "this symbol not expected in CaseStatement"; break;
+ case 214: s = "this symbol not expected in CaseStatement"; break;
+ case 215: s = "invalid CalcOp"; break;
+ case 216: s = "invalid EquivOp"; break;
+ case 217: s = "invalid ImpliesOp"; break;
+ case 218: s = "invalid ExpliesOp"; break;
+ case 219: s = "invalid AndOp"; break;
+ case 220: s = "invalid OrOp"; break;
+ case 221: s = "invalid NegOp"; break;
+ case 222: s = "invalid Forall"; break;
+ case 223: s = "invalid Exists"; break;
+ case 224: s = "invalid QSep"; break;
+ case 225: s = "invalid ImpliesExpliesExpression"; break;
+ case 226: s = "invalid LogicalExpression"; break;
+ case 227: s = "invalid RelOp"; break;
+ case 228: s = "invalid AddOp"; break;
+ case 229: s = "invalid UnaryExpression"; break;
+ case 230: s = "invalid MulOp"; break;
+ case 231: s = "invalid Suffix"; break;
+ case 232: s = "invalid Suffix"; break;
+ case 233: s = "invalid Suffix"; break;
+ case 234: s = "invalid Suffix"; break;
+ case 235: s = "invalid Suffix"; break;
+ case 236: s = "invalid LambdaExpression"; break;
+ case 237: s = "invalid EndlessExpression"; break;
+ case 238: s = "invalid NameSegment"; break;
+ case 239: s = "invalid DisplayExpr"; break;
+ case 240: s = "invalid MultiSetExpr"; break;
+ case 241: s = "invalid ConstAtomExpression"; break;
+ case 242: s = "invalid Nat"; break;
+ case 243: s = "invalid LambdaArrow"; break;
+ case 244: s = "invalid MatchExpression"; break;
+ case 245: s = "invalid QuantifierGuts"; break;
+ case 246: s = "invalid StmtInExpr"; break;
+ case 247: s = "invalid LetExpr"; break;
+ case 248: s = "invalid CaseExpression"; break;
+ case 249: s = "invalid MemberBindingUpdate"; break;
+ case 250: s = "invalid DotSuffix"; break;
default: s = "error " + n; break;
}
return s;
}
- public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { // semantic errors
+ public void SemErr(IToken tok, string msg) { // semantic errors
Contract.Requires(tok != null);
Contract.Requires(msg != null);
- SemErr(tok.filename, tok.line, tok.col, msg);
+ ErrorCount++;
+ Reporting.Error(MessageSource.Parser, tok, msg);
}
- public virtual void SemErr(string filename, int line, int col, string/*!*/ msg) {
+ public void SemErr(string filename, int line, int col, string msg) {
Contract.Requires(msg != null);
- errorStream.WriteLine(errMsgFormat, filename, line, col, msg);
- count++;
+ ErrorCount++;
+ Reporting.Error(MessageSource.Parser, filename, line, col, msg);
}
- public void Warning(IToken/*!*/ tok, string/*!*/ msg) { // warnings
+ public void Warning(IToken tok, string msg) {
Contract.Requires(tok != null);
Contract.Requires(msg != null);
- Warning(tok.filename, tok.line, tok.col, msg);
- }
-
- public virtual void Warning(string filename, int line, int col, string msg) {
- Contract.Requires(msg != null);
- errorStream.WriteLine(warningMsgFormat, filename, line, col, msg);
+ Reporting.Warning(MessageSource.Parser, tok, msg);
}
} // Errors
@@ -4602,6 +5020,4 @@ public class Errors {
public class FatalError: Exception {
public FatalError(string m): base(m) {}
}
-
-
} \ No newline at end of file
diff --git a/Source/Dafny/Printer.cs b/Source/Dafny/Printer.cs
index bf42c71a..0ba6f439 100644
--- a/Source/Dafny/Printer.cs
+++ b/Source/Dafny/Printer.cs
@@ -1,1988 +1,2109 @@
-//-----------------------------------------------------------------------------
-//
-// Copyright (C) Microsoft Corporation. All Rights Reserved.
-//
-//-----------------------------------------------------------------------------
-using System;
-using System.IO;
-using System.Collections.Generic;
-using System.Diagnostics.Contracts;
-using System.Numerics;
-using System.Linq;
-using Bpl = Microsoft.Boogie;
-
-namespace Microsoft.Dafny {
- public class Printer {
- TextWriter wr;
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Numerics;
+using System.Linq;
+using Bpl = Microsoft.Boogie;
+
+namespace Microsoft.Dafny {
+ public class Printer {
+ TextWriter wr;
DafnyOptions.PrintModes printMode;
-
- [ContractInvariantMethod]
- void ObjectInvariant()
- {
- Contract.Invariant(wr!=null);
- }
-
- public Printer(TextWriter wr, DafnyOptions.PrintModes printMode = DafnyOptions.PrintModes.Everything) {
- Contract.Requires(wr != null);
- this.wr = wr;
- this.printMode = printMode;
- }
-
- public static string ExprToString(Expression expr)
- {
- Contract.Requires(expr != null);
- using (var wr = new System.IO.StringWriter()) {
- var pr = new Printer(wr);
- pr.PrintExpression(expr, false);
- return wr.ToString();
- }
- }
-
- public static string GuardToString(Expression expr) {
- using (var wr = new System.IO.StringWriter()) {
- var pr = new Printer(wr);
- pr.PrintGuard(expr);
- return wr.ToString();
- }
- }
-
- public static string ExtendedExprToString(Expression expr) {
- Contract.Requires(expr != null);
- using (var wr = new System.IO.StringWriter()) {
- var pr = new Printer(wr);
- pr.PrintExtendedExpr(expr, 0, true, false);
- return wr.ToString();
- }
- }
-
- public static string FrameExprListToString(List<FrameExpression> fexprs) {
- Contract.Requires(fexprs != null);
- using (var wr = new System.IO.StringWriter()) {
- var pr = new Printer(wr);
- pr.PrintFrameExpressionList(fexprs);
- return wr.ToString();
- }
- }
-
- public static string StatementToString(Statement stmt) {
- Contract.Requires(stmt != null);
- using (var wr = new System.IO.StringWriter()) {
- var pr = new Printer(wr);
- pr.PrintStatement(stmt, 0);
- return ToStringWithoutNewline(wr);
- }
- }
-
- public static string IteratorClassToString(IteratorDecl iter) {
- Contract.Requires(iter != null);
- using (var wr = new System.IO.StringWriter()) {
- var pr = new Printer(wr);
- pr.PrintIteratorClass(iter, 0, null);
- return ToStringWithoutNewline(wr);
- }
- }
-
- public static string IteratorSignatureToString(IteratorDecl iter) {
- Contract.Requires(iter != null);
- using (var wr = new System.IO.StringWriter()) {
- var pr = new Printer(wr);
- pr.PrintIteratorSignature(iter, 0);
- return ToStringWithoutNewline(wr);
- }
- }
-
- public static string FunctionSignatureToString(Function f) {
- Contract.Requires(f != null);
- using (var wr = new System.IO.StringWriter()) {
- var pr = new Printer(wr);
- pr.PrintFunction(f, 0, true);
- return ToStringWithoutNewline(wr);
- }
- }
-
- public static string MethodSignatureToString(Method m) {
- Contract.Requires(m != null);
- using (var wr = new System.IO.StringWriter()) {
- var pr = new Printer(wr);
- pr.PrintMethod(m, 0, true);
- return ToStringWithoutNewline(wr);
- }
- }
-
- public static string ToStringWithoutNewline(System.IO.StringWriter wr) {
- Contract.Requires(wr != null);
- var sb = wr.GetStringBuilder();
- var len = sb.Length;
- while (len > 0 && (sb[len - 1] == '\n' || sb[len - 1] == '\r')) {
- len--;
- }
- return sb.ToString(0, len);
- }
-
- public void PrintProgram(Program prog) {
+ bool afterResolver;
+
+ [ContractInvariantMethod]
+ void ObjectInvariant()
+ {
+ Contract.Invariant(wr!=null);
+ }
+
+ public Printer(TextWriter wr, DafnyOptions.PrintModes printMode = DafnyOptions.PrintModes.Everything) {
+ Contract.Requires(wr != null);
+ this.wr = wr;
+ this.printMode = printMode;
+ }
+
+ public static string ExprToString(Expression expr)
+ {
+ Contract.Requires(expr != null);
+ using (var wr = new System.IO.StringWriter()) {
+ var pr = new Printer(wr);
+ pr.PrintExpression(expr, false);
+ return wr.ToString();
+ }
+ }
+
+ public static string GuardToString(bool isExistentialGuard, Expression expr) {
+ Contract.Requires(!isExistentialGuard || (expr is ExistsExpr && ((ExistsExpr)expr).Range == null));
+ using (var wr = new System.IO.StringWriter()) {
+ var pr = new Printer(wr);
+ pr.PrintGuard(isExistentialGuard, expr);
+ return wr.ToString();
+ }
+ }
+
+ public static string ExtendedExprToString(Expression expr) {
+ Contract.Requires(expr != null);
+ using (var wr = new System.IO.StringWriter()) {
+ var pr = new Printer(wr);
+ pr.PrintExtendedExpr(expr, 0, true, false);
+ return wr.ToString();
+ }
+ }
+
+ public static string FrameExprListToString(List<FrameExpression> fexprs) {
+ Contract.Requires(fexprs != null);
+ using (var wr = new System.IO.StringWriter()) {
+ var pr = new Printer(wr);
+ pr.PrintFrameExpressionList(fexprs);
+ return wr.ToString();
+ }
+ }
+
+ public static string StatementToString(Statement stmt) {
+ Contract.Requires(stmt != null);
+ using (var wr = new System.IO.StringWriter()) {
+ var pr = new Printer(wr);
+ pr.PrintStatement(stmt, 0);
+ return ToStringWithoutNewline(wr);
+ }
+ }
+
+ public static string IteratorClassToString(IteratorDecl iter) {
+ Contract.Requires(iter != null);
+ using (var wr = new System.IO.StringWriter()) {
+ var pr = new Printer(wr);
+ pr.PrintIteratorClass(iter, 0, null);
+ return ToStringWithoutNewline(wr);
+ }
+ }
+
+ public static string IteratorSignatureToString(IteratorDecl iter) {
+ Contract.Requires(iter != null);
+ using (var wr = new System.IO.StringWriter()) {
+ var pr = new Printer(wr);
+ pr.PrintIteratorSignature(iter, 0);
+ return ToStringWithoutNewline(wr);
+ }
+ }
+
+ public static string FunctionSignatureToString(Function f) {
+ Contract.Requires(f != null);
+ using (var wr = new System.IO.StringWriter()) {
+ var pr = new Printer(wr);
+ pr.PrintFunction(f, 0, true);
+ return ToStringWithoutNewline(wr);
+ }
+ }
+
+ public static string MethodSignatureToString(Method m) {
+ Contract.Requires(m != null);
+ using (var wr = new System.IO.StringWriter()) {
+ var pr = new Printer(wr);
+ pr.PrintMethod(m, 0, true);
+ return ToStringWithoutNewline(wr);
+ }
+ }
+
+ public static string OneAttributeToString(Attributes a, string nameSubstitution = null) {
+ Contract.Requires(a != null);
+ using (var wr = new System.IO.StringWriter()) {
+ var pr = new Printer(wr);
+ pr.PrintOneAttribute(a, nameSubstitution);
+ return ToStringWithoutNewline(wr);
+ }
+ }
+
+ public static string ToStringWithoutNewline(System.IO.StringWriter wr) {
+ Contract.Requires(wr != null);
+ var sb = wr.GetStringBuilder();
+ var len = sb.Length;
+ while (len > 0 && (sb[len - 1] == '\n' || sb[len - 1] == '\r')) {
+ len--;
+ }
+ return sb.ToString(0, len);
+ }
+
+ public void PrintProgram(Program prog, bool afterResolver) {
Contract.Requires(prog != null);
- if (Bpl.CommandLineOptions.Clo.ShowEnv != Bpl.CommandLineOptions.ShowEnvironment.Never) {
- wr.WriteLine("// " + Bpl.CommandLineOptions.Clo.Version);
- wr.WriteLine("// " + Bpl.CommandLineOptions.Clo.Environment);
- }
- wr.WriteLine("// {0}", prog.Name);
- if (DafnyOptions.O.DafnyPrintResolvedFile != null) {
- wr.WriteLine();
- wr.WriteLine("/*");
- PrintModuleDefinition(prog.BuiltIns.SystemModule, 0, Path.GetFullPath(DafnyOptions.O.DafnyPrintResolvedFile));
- wr.WriteLine("*/");
- }
- wr.WriteLine();
- PrintCallGraph(prog.DefaultModuleDef, 0);
- PrintTopLevelDecls(prog.DefaultModuleDef.TopLevelDecls, 0, Path.GetFullPath(prog.FullName));
- wr.Flush();
- }
-
- public void PrintCallGraph(ModuleDefinition module, int indent) {
- Contract.Requires(module != null);
- Contract.Requires(0 <= indent);
- if (DafnyOptions.O.DafnyPrintResolvedFile != null) {
- // print call graph
- Indent(indent); wr.WriteLine("/* CALL GRAPH for module {0}:", module.Name);
- var SCCs = module.CallGraph.TopologicallySortedComponents();
- SCCs.Reverse();
- foreach (var clbl in SCCs) {
- Indent(indent); wr.WriteLine(" * SCC at height {0}:", module.CallGraph.GetSCCRepresentativeId(clbl));
- var r = module.CallGraph.GetSCC(clbl);
- foreach (var m in r) {
- Indent(indent); wr.WriteLine(" * {0}", m.NameRelativeToModule);
- }
- }
- Indent(indent); wr.WriteLine(" */");
- }
- }
-
- public void PrintTopLevelDecls(List<TopLevelDecl> decls, int indent, string fileBeingPrinted) {
- Contract.Requires(decls!= null);
- int i = 0;
- foreach (TopLevelDecl d in decls) {
- Contract.Assert(d != null);
- if (PrintModeSkipGeneral(d.tok, fileBeingPrinted)) { continue; }
- if (d is OpaqueTypeDecl) {
- var at = (OpaqueTypeDecl)d;
- if (i++ != 0) { wr.WriteLine(); }
- Indent(indent);
- PrintClassMethodHelper("type", at.Attributes, at.Name, new List<TypeParameter>());
- wr.Write(EqualitySupportSuffix(at.EqualitySupport));
- wr.WriteLine();
- } else if (d is NewtypeDecl) {
- var dd = (NewtypeDecl)d;
- if (i++ != 0) { wr.WriteLine(); }
- Indent(indent);
- PrintClassMethodHelper("newtype", dd.Attributes, dd.Name, new List<TypeParameter>());
- wr.Write(" = ");
- if (dd.Var == null) {
- PrintType(dd.BaseType);
- } else {
- wr.Write(dd.Var.DisplayName);
- if (!(dd.Var.Type is TypeProxy) || DafnyOptions.O.DafnyPrintResolvedFile != null) {
- wr.Write(": ");
- PrintType(dd.BaseType);
- }
- wr.Write(" | ");
- PrintExpression(dd.Constraint, true);
- }
- wr.WriteLine();
- } else if (d is TypeSynonymDecl) {
- var syn = (TypeSynonymDecl)d;
- if (i++ != 0) { wr.WriteLine(); }
- Indent(indent);
- PrintClassMethodHelper("type", syn.Attributes, syn.Name, syn.TypeArgs);
- wr.Write(" = ");
- PrintType(syn.Rhs);
- wr.WriteLine();
- } else if (d is DatatypeDecl) {
- if (i++ != 0) { wr.WriteLine(); }
- PrintDatatype((DatatypeDecl)d, indent);
- } else if (d is IteratorDecl) {
- var iter = (IteratorDecl)d;
- PrintIteratorSignature(iter, indent);
-
- if (iter.Body != null) {
- Indent(indent);
- PrintStatement(iter.Body, indent);
- wr.WriteLine();
- }
-
- if (DafnyOptions.O.DafnyPrintResolvedFile != null) {
- // also print the members that were created as part of the interpretation of the iterator
- Contract.Assert(iter.Members.Count != 0); // filled in during resolution
- wr.WriteLine("/*---------- iterator members ----------");
- PrintIteratorClass(iter, indent, fileBeingPrinted);
- wr.WriteLine("---------- iterator members ----------*/");
- }
-
- } else if (d is ClassDecl) {
- ClassDecl cl = (ClassDecl)d;
- if (!cl.IsDefaultClass) {
- if (i++ != 0) { wr.WriteLine(); }
- PrintClass(cl, indent, fileBeingPrinted);
- } else if (cl.Members.Count == 0) {
- // print nothing
- } else {
- if (i++ != 0) { wr.WriteLine(); }
- PrintMembers(cl.Members, indent, fileBeingPrinted);
- }
-
+ this.afterResolver = afterResolver;
+ if (Bpl.CommandLineOptions.Clo.ShowEnv != Bpl.CommandLineOptions.ShowEnvironment.Never) {
+ wr.WriteLine("// " + Bpl.CommandLineOptions.Clo.Version);
+ wr.WriteLine("// " + Bpl.CommandLineOptions.Clo.Environment);
+ }
+ wr.WriteLine("// {0}", prog.Name);
+ if (DafnyOptions.O.DafnyPrintResolvedFile != null && DafnyOptions.O.PrintMode == DafnyOptions.PrintModes.Everything) {
+ wr.WriteLine();
+ wr.WriteLine("/*");
+ PrintModuleDefinition(prog.BuiltIns.SystemModule, 0, Path.GetFullPath(DafnyOptions.O.DafnyPrintResolvedFile));
+ wr.WriteLine("*/");
+ }
+ wr.WriteLine();
+ PrintCallGraph(prog.DefaultModuleDef, 0);
+ PrintTopLevelDecls(prog.DefaultModuleDef.TopLevelDecls, 0, Path.GetFullPath(prog.FullName));
+ wr.Flush();
+ }
+
+ public void PrintCallGraph(ModuleDefinition module, int indent) {
+ Contract.Requires(module != null);
+ Contract.Requires(0 <= indent);
+ if (DafnyOptions.O.DafnyPrintResolvedFile != null && DafnyOptions.O.PrintMode == DafnyOptions.PrintModes.Everything) {
+ // print call graph
+ Indent(indent); wr.WriteLine("/* CALL GRAPH for module {0}:", module.Name);
+ var SCCs = module.CallGraph.TopologicallySortedComponents();
+ SCCs.Reverse();
+ foreach (var clbl in SCCs) {
+ Indent(indent); wr.WriteLine(" * SCC at height {0}:", module.CallGraph.GetSCCRepresentativeId(clbl));
+ var r = module.CallGraph.GetSCC(clbl);
+ foreach (var m in r) {
+ Indent(indent); wr.WriteLine(" * {0}", m.NameRelativeToModule);
+ }
+ }
+ Indent(indent); wr.WriteLine(" */");
+ }
+ }
+
+ public void PrintTopLevelDecls(List<TopLevelDecl> decls, int indent, string fileBeingPrinted) {
+ Contract.Requires(decls!= null);
+ int i = 0;
+ foreach (TopLevelDecl d in decls) {
+ Contract.Assert(d != null);
+ if (PrintModeSkipGeneral(d.tok, fileBeingPrinted)) { continue; }
+ if (d is OpaqueTypeDecl) {
+ var at = (OpaqueTypeDecl)d;
+ if (i++ != 0) { wr.WriteLine(); }
+ Indent(indent);
+ PrintClassMethodHelper("type", at.Attributes, at.Name, new List<TypeParameter>());
+ wr.Write(EqualitySupportSuffix(at.EqualitySupport));
+ wr.WriteLine();
+ } else if (d is NewtypeDecl) {
+ var dd = (NewtypeDecl)d;
+ if (i++ != 0) { wr.WriteLine(); }
+ Indent(indent);
+ PrintClassMethodHelper("newtype", dd.Attributes, dd.Name, new List<TypeParameter>());
+ wr.Write(" = ");
+ if (dd.Var == null) {
+ PrintType(dd.BaseType);
+ } else {
+ wr.Write(dd.Var.DisplayName);
+ if (!(dd.Var.Type is TypeProxy) || DafnyOptions.O.DafnyPrintResolvedFile != null) {
+ wr.Write(": ");
+ PrintType(dd.BaseType);
+ }
+ wr.Write(" | ");
+ PrintExpression(dd.Constraint, true);
+ }
+ wr.WriteLine();
+ } else if (d is TypeSynonymDecl) {
+ var syn = (TypeSynonymDecl)d;
+ if (i++ != 0) { wr.WriteLine(); }
+ Indent(indent);
+ PrintClassMethodHelper("type", syn.Attributes, syn.Name, syn.TypeArgs);
+ wr.Write(" = ");
+ PrintType(syn.Rhs);
+ wr.WriteLine();
+ } else if (d is DatatypeDecl) {
+ if (i++ != 0) { wr.WriteLine(); }
+ PrintDatatype((DatatypeDecl)d, indent);
+ } else if (d is IteratorDecl) {
+ var iter = (IteratorDecl)d;
+ PrintIteratorSignature(iter, indent);
+
+ if (iter.Body != null) {
+ Indent(indent);
+ PrintStatement(iter.Body, indent);
+ wr.WriteLine();
+ }
+
+ if (DafnyOptions.O.DafnyPrintResolvedFile != null) {
+ // also print the members that were created as part of the interpretation of the iterator
+ Contract.Assert(iter.Members.Count != 0); // filled in during resolution
+ wr.WriteLine("/*---------- iterator members ----------");
+ PrintIteratorClass(iter, indent, fileBeingPrinted);
+ wr.WriteLine("---------- iterator members ----------*/");
+ }
+
+ } else if (d is ClassDecl) {
+ ClassDecl cl = (ClassDecl)d;
+ if (!cl.IsDefaultClass) {
+ if (i++ != 0) { wr.WriteLine(); }
+ PrintClass(cl, indent, fileBeingPrinted);
+ } else if (cl.Members.Count == 0) {
+ // print nothing
+ } else {
+ if (i++ != 0) { wr.WriteLine(); }
+ PrintMembers(cl.Members, indent, fileBeingPrinted);
+ }
+
} else if (d is ModuleDecl) {
- wr.WriteLine();
- Indent(indent);
- if (d is LiteralModuleDecl) {
- ModuleDefinition module = ((LiteralModuleDecl)d).ModuleDef;
- PrintModuleDefinition(module, indent, fileBeingPrinted);
- } else if (d is AliasModuleDecl) {
- wr.Write("import"); if (((AliasModuleDecl)d).Opened) wr.Write(" opened");
- wr.Write(" {0} ", ((AliasModuleDecl)d).Name);
- wr.WriteLine("= {0}", Util.Comma(".", ((AliasModuleDecl)d).Path, id => id.val));
- } else if (d is ModuleFacadeDecl) {
- wr.Write("import"); if (((ModuleFacadeDecl)d).Opened) wr.Write(" opened");
- wr.Write(" {0} ", ((ModuleFacadeDecl)d).Name);
+ wr.WriteLine();
+ Indent(indent);
+ if (d is LiteralModuleDecl) {
+ ModuleDefinition module = ((LiteralModuleDecl)d).ModuleDef;
+ PrintModuleDefinition(module, indent, fileBeingPrinted);
+ } else if (d is AliasModuleDecl) {
+ wr.Write("import"); if (((AliasModuleDecl)d).Opened) wr.Write(" opened");
+ wr.Write(" {0} ", ((AliasModuleDecl)d).Name);
+ wr.WriteLine("= {0}", Util.Comma(".", ((AliasModuleDecl)d).Path, id => id.val));
+ } else if (d is ModuleFacadeDecl) {
+ wr.Write("import"); if (((ModuleFacadeDecl)d).Opened) wr.Write(" opened");
+ wr.Write(" {0} ", ((ModuleFacadeDecl)d).Name);
wr.WriteLine("as {0}", Util.Comma(".", ((ModuleFacadeDecl)d).Path, id => id.val));
- }
-
- } else {
- Contract.Assert(false); // unexpected TopLevelDecl
- }
- }
- }
-
- void PrintModuleDefinition(ModuleDefinition module, int indent, string fileBeingPrinted) {
- Contract.Requires(module != null);
- Contract.Requires(0 <= indent);
- if (module.IsAbstract) {
- wr.Write("abstract ");
- }
- wr.Write("module");
- PrintAttributes(module.Attributes);
- wr.Write(" {0} ", module.Name);
- if (module.RefinementBaseName != null) {
- wr.Write("refines {0} ", Util.Comma(".", module.RefinementBaseName, id => id.val));
- }
- if (module.TopLevelDecls.Count == 0) {
- wr.WriteLine("{ }");
- } else {
- wr.WriteLine("{");
- PrintCallGraph(module, indent + IndentAmount);
- PrintTopLevelDecls(module.TopLevelDecls, indent + IndentAmount, fileBeingPrinted);
- Indent(indent);
- wr.WriteLine("}");
- }
- }
-
- void PrintIteratorSignature(IteratorDecl iter, int indent) {
- Indent(indent);
- PrintClassMethodHelper("iterator", iter.Attributes, iter.Name, iter.TypeArgs);
- if (iter.SignatureIsOmitted) {
- wr.WriteLine(" ...");
- } else {
- PrintFormals(iter.Ins);
- if (iter.Outs.Count != 0) {
- if (iter.Ins.Count + iter.Outs.Count <= 3) {
- wr.Write(" yields ");
- } else {
- wr.WriteLine();
- Indent(indent + 2 * IndentAmount);
- wr.Write("yields ");
- }
- PrintFormals(iter.Outs);
- }
- wr.WriteLine();
- }
-
- int ind = indent + IndentAmount;
- PrintSpec("requires", iter.Requires, ind);
- if (iter.Reads.Expressions != null) {
- PrintFrameSpecLine("reads", iter.Reads.Expressions, ind, iter.Reads.HasAttributes() ? iter.Reads.Attributes : null);
- }
- if (iter.Modifies.Expressions != null) {
- PrintFrameSpecLine("modifies", iter.Modifies.Expressions, ind, iter.Modifies.HasAttributes() ? iter.Modifies.Attributes : null);
- }
- PrintSpec("yield requires", iter.YieldRequires, ind);
- PrintSpec("yield ensures", iter.YieldEnsures, ind);
- PrintSpec("ensures", iter.Ensures, ind);
- PrintDecreasesSpec(iter.Decreases, ind);
- }
-
- private void PrintIteratorClass(IteratorDecl iter, int indent, string fileBeingPrinted) {
- PrintClassMethodHelper("class", null, iter.Name, iter.TypeArgs);
- wr.WriteLine(" {");
- PrintMembers(iter.Members, indent + IndentAmount, fileBeingPrinted);
- Indent(indent); wr.WriteLine("}");
- }
-
- public void PrintClass(ClassDecl c, int indent, string fileBeingPrinted) {
- Contract.Requires(c != null);
- Indent(indent);
- PrintClassMethodHelper((c is TraitDecl) ? "trait" : "class", c.Attributes, c.Name, c.TypeArgs);
- string sep = " extends ";
- foreach (var trait in c.TraitsTyp) {
- wr.Write(sep);
- PrintType(trait);
- sep = ", ";
- }
- if (c.Members.Count == 0) {
- wr.WriteLine(" { }");
- } else {
- wr.WriteLine(" {");
- PrintMembers(c.Members, indent + IndentAmount, fileBeingPrinted);
- Indent(indent);
- wr.WriteLine("}");
- }
- }
-
- public void PrintMembers(List<MemberDecl> members, int indent, string fileBeingPrinted)
- {
- Contract.Requires(members != null);
-
- int state = 0; // 0 - no members yet; 1 - previous member was a field; 2 - previous member was non-field
- foreach (MemberDecl m in members) {
- if (PrintModeSkipGeneral(m.tok, fileBeingPrinted)) { continue; }
- if (m is Method) {
- if (state != 0) { wr.WriteLine(); }
- PrintMethod((Method)m, indent, false);
- var com = m as FixpointLemma;
- if (com != null && com.PrefixLemma != null) {
- Indent(indent); wr.WriteLine("/***");
- PrintMethod(com.PrefixLemma, indent, false);
- Indent(indent); wr.WriteLine("***/");
- }
- state = 2;
- } else if (m is Field) {
- if (state == 2) { wr.WriteLine(); }
- PrintField((Field)m, indent);
- state = 1;
- } else if (m is Function) {
- if (state != 0) { wr.WriteLine(); }
- PrintFunction((Function)m, indent, false);
- var fixp = m as FixpointPredicate;
- if (fixp != null && fixp.PrefixPredicate != null) {
- Indent(indent); wr.WriteLine("/***");
- PrintFunction(fixp.PrefixPredicate, indent, false);
- Indent(indent); wr.WriteLine("***/");
- }
- state = 2;
- } else {
- Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member
- }
- }
- }
-
- /// <summary>
- /// Prints no space before "kind", but does print a space before "attrs" and "name".
- /// </summary>
- void PrintClassMethodHelper(string kind, Attributes attrs, string name, List<TypeParameter> typeArgs) {
- Contract.Requires(kind != null);
- Contract.Requires(name != null);
- Contract.Requires(typeArgs != null);
- if (kind.Length != 0) {
- wr.Write(kind);
- }
-
- PrintAttributes(attrs);
-
- wr.Write(" {0}", name);
- PrintTypeParams(typeArgs);
- }
-
- private void PrintTypeParams(List<TypeParameter> typeArgs) {
- Contract.Requires(typeArgs != null);
- if (typeArgs.Count != 0) {
- wr.Write("<" +
- Util.Comma(", ", typeArgs,
- tp => tp.Name + EqualitySupportSuffix(tp.EqualitySupport))
- + ">");
- }
+ } else if (d is ModuleExportDecl) {
+ ModuleExportDecl e = (ModuleExportDecl)d;
+ if (e.IsDefault) wr.Write("default ");
+ wr.Write("export {0}", e.Name);
+ if (e.Extends.Count > 0) wr.Write(" extends {0}", Util.Comma(e.Extends, id => id));
+ PrintModuleExportDecl(e, indent, fileBeingPrinted);
+ }
+
+ } else {
+ Contract.Assert(false); // unexpected TopLevelDecl
+ }
+ }
}
- private void PrintTypeInstantiation(List<Type> typeArgs) {
- Contract.Requires(typeArgs == null || typeArgs.Count != 0);
- if (typeArgs != null) {
- wr.Write("<{0}>", Util.Comma(",", typeArgs, ty => ty.ToString()));
- }
- }
-
- public void PrintDatatype(DatatypeDecl dt, int indent) {
- Contract.Requires(dt != null);
- Indent(indent);
- PrintClassMethodHelper(dt is IndDatatypeDecl ? "datatype" : "codatatype", dt.Attributes, dt.Name, dt.TypeArgs);
- wr.Write(" =");
- string sep = "";
- foreach (DatatypeCtor ctor in dt.Ctors) {
- wr.Write(sep);
- PrintClassMethodHelper("", ctor.Attributes, ctor.Name, new List<TypeParameter>());
- if (ctor.Formals.Count != 0) {
- PrintFormals(ctor.Formals);
- }
- sep = " |";
- }
- wr.WriteLine();
- }
-
- /// <summary>
- /// Prints a space before each attribute.
- /// </summary>
- public void PrintAttributes(Attributes a) {
- if (a != null) {
- PrintAttributes(a.Prev);
-
- wr.Write(" {{:{0}", a.Name);
- if (a.Args != null)
- {
- PrintAttributeArgs(a.Args, false);
- }
+ void PrintModuleExportDecl(ModuleExportDecl m, int indent, string fileBeingPrinted) {
+ ModuleSignature sig = m.Signature;
+ if (sig == null) {
+ wr.Write(" {");
+ // has been resolved yet, just print the strings
+ wr.Write("{0}", Util.Comma(m.Exports, id => id.Name));
wr.Write("}");
- }
- }
-
- public void PrintAttributeArgs(List<Expression> args, bool isFollowedBySemicolon) {
- Contract.Requires(args != null);
- string prefix = " ";
- foreach (var arg in args) {
- Contract.Assert(arg != null);
- wr.Write(prefix);
- prefix = ", ";
- PrintExpression(arg, isFollowedBySemicolon);
- }
- }
-
- public void PrintField(Field field, int indent) {
- Contract.Requires(field != null);
- Indent(indent);
- if (field.IsGhost) {
- wr.Write("ghost ");
- }
- wr.Write("var");
- PrintAttributes(field.Attributes);
- wr.Write(" {0}: ", field.Name);
- PrintType(field.Type);
- if (field.IsUserMutable) {
- // nothing more to say
- } else if (field.IsMutable) {
- wr.Write(" // non-assignable");
} else {
- wr.Write(" // immutable");
- }
- wr.WriteLine();
- }
-
- public void PrintFunction(Function f, int indent, bool printSignatureOnly) {
- Contract.Requires(f != null);
-
- if (PrintModeSkipFunctionOrMethod(f.IsGhost, f.Attributes, f.Name)) { return; }
- var isPredicate = f is Predicate || f is PrefixPredicate;
- Indent(indent);
- string k = isPredicate ? "predicate" : f is InductivePredicate ? "inductive predicate" : f is CoPredicate ? "copredicate" : "function";
- if (f.IsProtected) { k = "protected " + k; }
- if (f.HasStaticKeyword) { k = "static " + k; }
- if (!f.IsGhost) { k += " method"; }
- PrintClassMethodHelper(k, f.Attributes, f.Name, f.TypeArgs);
- if (f.SignatureIsOmitted) {
- wr.WriteLine(" ...");
- } else {
- PrintFormals(f.Formals, f.Name);
- if (!isPredicate) {
- wr.Write(": ");
- PrintType(f.ResultType);
- }
- wr.WriteLine();
- }
-
- int ind = indent + IndentAmount;
- PrintSpec("requires", f.Req, ind);
- PrintFrameSpecLine("reads", f.Reads, ind, null);
- PrintSpec("ensures", f.Ens, ind);
- PrintDecreasesSpec(f.Decreases, ind);
- if (f.Body != null && !printSignatureOnly) {
- Indent(indent);
- wr.WriteLine("{");
- PrintExtendedExpr(f.Body, ind, true, false);
- Indent(indent);
- wr.WriteLine("}");
- }
- }
-
- // ----------------------------- PrintMethod -----------------------------
-
- const int IndentAmount = 2; // The amount of indent for each new scope
- const string BunchaSpaces = " ";
- void Indent(int amount)
- {
- Contract.Requires(0 <= amount);
-
- while (0 < amount) {
- wr.Write(BunchaSpaces.Substring(0, amount));
- amount -= BunchaSpaces.Length;
- }
- }
-
- private bool PrintModeSkipFunctionOrMethod(bool IsGhost, Attributes attributes, string name)
- {
- if (printMode == DafnyOptions.PrintModes.NoGhost && IsGhost)
- { return true; }
- if (printMode == DafnyOptions.PrintModes.NoIncludes || printMode == DafnyOptions.PrintModes.NoGhost)
- {
- bool verify = true;
- if (Attributes.ContainsBool(attributes, "verify", ref verify) && !verify)
- { return true; }
- if (name.Contains("INTERNAL") || name.StartsWith("reveal_"))
- { return true; }
- }
- return false;
- }
-
- private bool PrintModeSkipGeneral(Bpl.IToken tok, string fileBeingPrinted)
- {
- return (printMode == DafnyOptions.PrintModes.NoIncludes || printMode == DafnyOptions.PrintModes.NoGhost)
- && (tok.filename != null && fileBeingPrinted != null && Path.GetFullPath(tok.filename) != fileBeingPrinted);
+ wr.WriteLine(" {");
+ // print the decls and members in the module
+ List<TopLevelDecl> decls = sig.TopLevels.Values.ToList();
+ List<MemberDecl> members = sig.StaticMembers.Values.ToList();
+ PrintTopLevelDecls(decls, indent + IndentAmount, fileBeingPrinted);
+ PrintMembers(members, indent + IndentAmount, fileBeingPrinted);
+ Indent(indent); wr.WriteLine("}");
+ }
+ }
+
+ void PrintModuleDefinition(ModuleDefinition module, int indent, string fileBeingPrinted) {
+ Contract.Requires(module != null);
+ Contract.Requires(0 <= indent);
+ if (module.IsAbstract) {
+ wr.Write("abstract ");
+ }
+ wr.Write("module");
+ PrintAttributes(module.Attributes);
+ wr.Write(" {0} ", module.Name);
+ if (module.RefinementBaseName != null) {
+ wr.Write("refines {0} ", Util.Comma(".", module.RefinementBaseName, id => id.val));
+ }
+ if (module.TopLevelDecls.Count == 0) {
+ wr.WriteLine("{ }");
+ } else {
+ wr.WriteLine("{");
+ PrintCallGraph(module, indent + IndentAmount);
+ PrintTopLevelDeclsOrExportedView(module, indent, fileBeingPrinted);
+ Indent(indent);
+ wr.WriteLine("}");
+ }
}
- public void PrintMethod(Method method, int indent, bool printSignatureOnly) {
- Contract.Requires(method != null);
-
- if (PrintModeSkipFunctionOrMethod(method.IsGhost, method.Attributes, method.Name)) { return; }
- Indent(indent);
- string k = method is Constructor ? "constructor" :
- method is InductiveLemma ? "inductive lemma" :
- method is CoLemma ? "colemma" :
- method is Lemma ? "lemma" :
- "method";
- if (method.HasStaticKeyword) { k = "static " + k; }
- if (method.IsGhost && !(method is Lemma) && !(method is FixpointLemma)) { k = "ghost " + k; }
- string nm = method is Constructor && !((Constructor)method).HasName ? "" : method.Name;
- PrintClassMethodHelper(k, method.Attributes, nm, method.TypeArgs);
- if (method.SignatureIsOmitted) {
- wr.WriteLine(" ...");
- } else {
- PrintFormals(method.Ins, method.Name);
- if (method.Outs.Count != 0) {
- if (method.Ins.Count + method.Outs.Count <= 3) {
- wr.Write(" returns ");
- } else {
- wr.WriteLine();
- Indent(indent + 2 * IndentAmount);
- wr.Write("returns ");
+ void PrintTopLevelDeclsOrExportedView(ModuleDefinition module, int indent, string fileBeingPrinted) {
+ bool printViewsOnly = false;
+ List<TopLevelDecl> decls = new List<TopLevelDecl>();
+ // only filter based on view name after resolver.
+ if (afterResolver) {
+ foreach (var nameOfView in DafnyOptions.O.DafnyPrintExportedViews) {
+ foreach (var decl in module.TopLevelDecls) {
+ if (decl.FullName.Equals(nameOfView)) {
+ printViewsOnly = true;
+ decls.Add(decl);
+ }
}
- PrintFormals(method.Outs);
- }
- wr.WriteLine();
- }
-
- int ind = indent + IndentAmount;
- PrintSpec("requires", method.Req, ind);
- if (method.Mod.Expressions != null)
- {
- PrintFrameSpecLine("modifies", method.Mod.Expressions, ind, method.Mod.HasAttributes() ? method.Mod.Attributes : null);
- }
- PrintSpec("ensures", method.Ens, ind);
- PrintDecreasesSpec(method.Decreases, ind);
-
- if (method.Body != null && !printSignatureOnly) {
- Indent(indent);
- PrintStatement(method.Body, indent);
- wr.WriteLine();
- }
- }
-
- internal void PrintFormals(List<Formal> ff, string name = null) {
- Contract.Requires(ff != null);
- if (name != null && name.EndsWith("#")) {
- wr.Write("[");
- PrintFormal(ff[0]);
- wr.Write("]");
- ff = new List<Formal>(ff.Skip(1));
- }
- wr.Write("(");
- string sep = "";
- foreach (Formal f in ff) {
- Contract.Assert(f != null);
- wr.Write(sep);
- sep = ", ";
- PrintFormal(f);
- }
- wr.Write(")");
- }
-
- void PrintFormal(Formal f) {
- Contract.Requires(f != null);
- if (f.IsGhost) {
- wr.Write("ghost ");
- }
- if (f.HasName) {
- wr.Write("{0}: ", f.DisplayName);
- }
- PrintType(f.Type);
- }
-
- internal void PrintSpec(string kind, List<Expression> ee, int indent) {
- Contract.Requires(kind != null);
- Contract.Requires(ee != null);
- foreach (Expression e in ee) {
- Contract.Assert(e != null);
- Indent(indent);
- wr.Write("{0} ", kind);
- PrintExpression(e, true);
- wr.WriteLine();
- }
- }
-
- internal void PrintDecreasesSpec(Specification<Expression> decs, int indent, bool newLine = true) {
- Contract.Requires(decs != null);
- if (printMode == DafnyOptions.PrintModes.NoGhost) { return; }
- if (decs.Expressions != null && decs.Expressions.Count != 0) {
- Indent(indent);
- wr.Write("decreases");
- if (decs.HasAttributes())
- {
- PrintAttributes(decs.Attributes);
- }
- wr.Write(" ");
- PrintExpressionList(decs.Expressions, true);
- if (newLine) {
- wr.WriteLine();
- } else {
- wr.Write(" ");
- }
- }
- }
-
- internal void PrintFrameSpecLine(string kind, List<FrameExpression/*!*/> ee, int indent, Attributes attrs, bool newLine = true) {
- Contract.Requires(kind != null);
- Contract.Requires(cce.NonNullElements(ee));
- if (ee != null && ee.Count != 0) {
- Indent(indent);
- wr.Write("{0}", kind);
- if (attrs != null) {
- PrintAttributes(attrs);
- }
- wr.Write(" ");
- PrintFrameExpressionList(ee);
- if (newLine) {
- wr.WriteLine();
- } else {
- wr.Write(" ");
- }
- }
- }
-
- internal void PrintSpec(string kind, List<MaybeFreeExpression> ee, int indent, bool newLine = true) {
- Contract.Requires(kind != null);
- Contract.Requires(ee != null);
- if (printMode == DafnyOptions.PrintModes.NoGhost) { return; }
- foreach (MaybeFreeExpression e in ee)
- {
- Contract.Assert(e != null);
- Indent(indent);
- wr.Write("{0}{1}", e.IsFree ? "free " : "", kind);
-
- if (e.HasAttributes())
- {
- PrintAttributes(e.Attributes);
- }
-
- wr.Write(" ");
- PrintExpression(e.E, true);
- if (newLine) {
- wr.WriteLine();
- } else {
- wr.Write(" ");
- }
- }
- }
-
- // ----------------------------- PrintType -----------------------------
-
- public void PrintType(Type ty) {
- Contract.Requires(ty != null);
- wr.Write(ty.ToString());
- }
-
- public void PrintType(string prefix, Type ty) {
- Contract.Requires(prefix != null);
- Contract.Requires(ty != null);
- string s = ty.ToString();
- if (s != "?") {
- wr.Write("{0}{1}", prefix, s);
- }
- }
-
- string EqualitySupportSuffix(TypeParameter.EqualitySupportValue es) {
- if (es == TypeParameter.EqualitySupportValue.Required ||
- (es == TypeParameter.EqualitySupportValue.InferredRequired && DafnyOptions.O.DafnyPrintResolvedFile != null)) {
- return "(==)";
- } else {
- return "";
- }
- }
-
- // ----------------------------- PrintStatement -----------------------------
-
- /// <summary>
- /// Prints from the current position of the current line.
- /// If the statement requires several lines, subsequent lines are indented at "indent".
- /// No newline is printed after the statement.
- /// </summary>
- public void PrintStatement(Statement stmt, int indent) {
- Contract.Requires(stmt != null);
-
- if (stmt.IsGhost && printMode == DafnyOptions.PrintModes.NoGhost) { return; }
- for (LList<Label> label = stmt.Labels; label != null; label = label.Next) {
- if (label.Data.Name != null) {
- wr.WriteLine("label {0}:", label.Data.Name);
- Indent(indent);
}
}
-
- if (stmt is PredicateStmt) {
- if (printMode == DafnyOptions.PrintModes.NoGhost) { return; }
- Expression expr = ((PredicateStmt)stmt).Expr;
- wr.Write(stmt is AssertStmt ? "assert" : "assume");
- if (stmt.Attributes != null) {
- PrintAttributes(stmt.Attributes);
- }
- wr.Write(" ");
- PrintExpression(expr, true);
- wr.Write(";");
-
- } else if (stmt is PrintStmt) {
- PrintStmt s = (PrintStmt)stmt;
- wr.Write("print");
- PrintAttributeArgs(s.Args, true);
- wr.Write(";");
-
- } else if (stmt is BreakStmt) {
- BreakStmt s = (BreakStmt)stmt;
- if (s.TargetLabel != null) {
- wr.Write("break {0};", s.TargetLabel);
- } else {
- string sep = "";
- for (int i = 0; i < s.BreakCount; i++) {
- wr.Write("{0}break", sep);
- sep = " ";
- }
- wr.Write(";");
- }
-
- } else if (stmt is ProduceStmt) {
- var s = (ProduceStmt) stmt;
- wr.Write(s is YieldStmt ? "yield" : "return");
- if (s.rhss != null) {
- var sep = " ";
- foreach (var rhs in s.rhss) {
- wr.Write(sep);
- PrintRhs(rhs);
- sep = ", ";
- }
- }
- wr.Write(";");
-
- } else if (stmt is AssignStmt) {
- AssignStmt s = (AssignStmt)stmt;
- PrintExpression(s.Lhs, true);
- wr.Write(" := ");
- PrintRhs(s.Rhs);
- wr.Write(";");
-
- } else if (stmt is BlockStmt) {
- wr.WriteLine("{");
- int ind = indent + IndentAmount;
- foreach (Statement s in ((BlockStmt)stmt).Body) {
- Indent(ind);
- PrintStatement(s, ind);
- wr.WriteLine();
- }
- Indent(indent);
- wr.Write("}");
-
- } else if (stmt is IfStmt) {
- IfStmt s = (IfStmt)stmt;
- PrintIfStatement(indent, s, false);
-
- } else if (stmt is AlternativeStmt) {
- var s = (AlternativeStmt)stmt;
- wr.WriteLine("if {");
- PrintAlternatives(indent, s.Alternatives);
- Indent(indent);
- wr.Write("}");
-
- } else if (stmt is WhileStmt) {
- WhileStmt s = (WhileStmt)stmt;
- PrintWhileStatement(indent, s, false, false);
-
- } else if (stmt is AlternativeLoopStmt) {
- var s = (AlternativeLoopStmt)stmt;
- wr.WriteLine("while");
- PrintSpec("invariant", s.Invariants, indent + IndentAmount);
- PrintDecreasesSpec(s.Decreases, indent + IndentAmount);
-
- Indent(indent);
- wr.WriteLine("{");
- PrintAlternatives(indent, s.Alternatives);
- Indent(indent);
- wr.Write("}");
-
- } else if (stmt is ForallStmt) {
+ PrintTopLevelDecls(printViewsOnly ? decls : module.TopLevelDecls, indent + IndentAmount, fileBeingPrinted);
+ }
+
+ void PrintIteratorSignature(IteratorDecl iter, int indent) {
+ Indent(indent);
+ PrintClassMethodHelper("iterator", iter.Attributes, iter.Name, iter.TypeArgs);
+ if (iter.SignatureIsOmitted) {
+ wr.WriteLine(" ...");
+ } else {
+ PrintFormals(iter.Ins);
+ if (iter.Outs.Count != 0) {
+ if (iter.Ins.Count + iter.Outs.Count <= 3) {
+ wr.Write(" yields ");
+ } else {
+ wr.WriteLine();
+ Indent(indent + 2 * IndentAmount);
+ wr.Write("yields ");
+ }
+ PrintFormals(iter.Outs);
+ }
+ wr.WriteLine();
+ }
+
+ int ind = indent + IndentAmount;
+ PrintSpec("requires", iter.Requires, ind);
+ if (iter.Reads.Expressions != null) {
+ PrintFrameSpecLine("reads", iter.Reads.Expressions, ind, iter.Reads.HasAttributes() ? iter.Reads.Attributes : null);
+ }
+ if (iter.Modifies.Expressions != null) {
+ PrintFrameSpecLine("modifies", iter.Modifies.Expressions, ind, iter.Modifies.HasAttributes() ? iter.Modifies.Attributes : null);
+ }
+ PrintSpec("yield requires", iter.YieldRequires, ind);
+ PrintSpec("yield ensures", iter.YieldEnsures, ind);
+ PrintSpec("ensures", iter.Ensures, ind);
+ PrintDecreasesSpec(iter.Decreases, ind);
+ }
+
+ private void PrintIteratorClass(IteratorDecl iter, int indent, string fileBeingPrinted) {
+ PrintClassMethodHelper("class", null, iter.Name, iter.TypeArgs);
+ wr.WriteLine(" {");
+ PrintMembers(iter.Members, indent + IndentAmount, fileBeingPrinted);
+ Indent(indent); wr.WriteLine("}");
+ }
+
+ public void PrintClass(ClassDecl c, int indent, string fileBeingPrinted) {
+ Contract.Requires(c != null);
+ Indent(indent);
+ PrintClassMethodHelper((c is TraitDecl) ? "trait" : "class", c.Attributes, c.Name, c.TypeArgs);
+ string sep = " extends ";
+ foreach (var trait in c.TraitsTyp) {
+ wr.Write(sep);
+ PrintType(trait);
+ sep = ", ";
+ }
+ if (c.Members.Count == 0) {
+ wr.WriteLine(" { }");
+ } else {
+ wr.WriteLine(" {");
+ PrintMembers(c.Members, indent + IndentAmount, fileBeingPrinted);
+ Indent(indent);
+ wr.WriteLine("}");
+ }
+ }
+
+ public void PrintMembers(List<MemberDecl> members, int indent, string fileBeingPrinted)
+ {
+ Contract.Requires(members != null);
+
+ int state = 0; // 0 - no members yet; 1 - previous member was a field; 2 - previous member was non-field
+ foreach (MemberDecl m in members) {
+ if (PrintModeSkipGeneral(m.tok, fileBeingPrinted)) { continue; }
+ if (m is Method) {
+ if (state != 0) { wr.WriteLine(); }
+ PrintMethod((Method)m, indent, false);
+ var com = m as FixpointLemma;
+ if (com != null && com.PrefixLemma != null) {
+ Indent(indent); wr.WriteLine("/***");
+ PrintMethod(com.PrefixLemma, indent, false);
+ Indent(indent); wr.WriteLine("***/");
+ }
+ state = 2;
+ } else if (m is Field) {
+ if (state == 2) { wr.WriteLine(); }
+ PrintField((Field)m, indent);
+ state = 1;
+ } else if (m is Function) {
+ if (state != 0) { wr.WriteLine(); }
+ PrintFunction((Function)m, indent, false);
+ var fixp = m as FixpointPredicate;
+ if (fixp != null && fixp.PrefixPredicate != null) {
+ Indent(indent); wr.WriteLine("/***");
+ PrintFunction(fixp.PrefixPredicate, indent, false);
+ Indent(indent); wr.WriteLine("***/");
+ }
+ state = 2;
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member
+ }
+ }
+ }
+
+ /// <summary>
+ /// Prints no space before "kind", but does print a space before "attrs" and "name".
+ /// </summary>
+ void PrintClassMethodHelper(string kind, Attributes attrs, string name, List<TypeParameter> typeArgs) {
+ Contract.Requires(kind != null);
+ Contract.Requires(name != null);
+ Contract.Requires(typeArgs != null);
+ if (kind.Length != 0) {
+ wr.Write(kind);
+ }
+
+ PrintAttributes(attrs);
+
+ wr.Write(" {0}", name);
+ PrintTypeParams(typeArgs);
+ }
+
+ private void PrintTypeParams(List<TypeParameter> typeArgs) {
+ Contract.Requires(typeArgs != null);
+ if (typeArgs.Count != 0) {
+ wr.Write("<" +
+ Util.Comma(", ", typeArgs,
+ tp => tp.Name + EqualitySupportSuffix(tp.EqualitySupport))
+ + ">");
+ }
+ }
+
+ private void PrintTypeInstantiation(List<Type> typeArgs) {
+ Contract.Requires(typeArgs == null || typeArgs.Count != 0);
+ if (typeArgs != null) {
+ wr.Write("<{0}>", Util.Comma(",", typeArgs, ty => ty.ToString()));
+ }
+ }
+
+ public void PrintDatatype(DatatypeDecl dt, int indent) {
+ Contract.Requires(dt != null);
+ Indent(indent);
+ PrintClassMethodHelper(dt is IndDatatypeDecl ? "datatype" : "codatatype", dt.Attributes, dt.Name, dt.TypeArgs);
+ wr.Write(" =");
+ string sep = "";
+ foreach (DatatypeCtor ctor in dt.Ctors) {
+ wr.Write(sep);
+ PrintClassMethodHelper("", ctor.Attributes, ctor.Name, new List<TypeParameter>());
+ if (ctor.Formals.Count != 0) {
+ PrintFormals(ctor.Formals);
+ }
+ sep = " |";
+ }
+ wr.WriteLine();
+ }
+
+ /// <summary>
+ /// Prints a space before each attribute.
+ /// </summary>
+ public void PrintAttributes(Attributes a) {
+ if (a != null) {
+ PrintAttributes(a.Prev);
+ wr.Write(" ");
+ PrintOneAttribute(a);
+ }
+ }
+ public void PrintOneAttribute(Attributes a, string nameSubstitution = null) {
+ Contract.Requires(a != null);
+ var name = nameSubstitution ?? a.Name;
+ var usAttribute = name.StartsWith("_");
+ wr.Write("{1}{{:{0}", name, usAttribute ? "/*" : "");
+ if (a.Args != null) {
+ PrintAttributeArgs(a.Args, false);
+ }
+ wr.Write("}}{0}", usAttribute ? "*/" : "");
+ }
+
+ public void PrintAttributeArgs(List<Expression> args, bool isFollowedBySemicolon) {
+ Contract.Requires(args != null);
+ string prefix = " ";
+ foreach (var arg in args) {
+ Contract.Assert(arg != null);
+ wr.Write(prefix);
+ prefix = ", ";
+ PrintExpression(arg, isFollowedBySemicolon);
+ }
+ }
+
+ public void PrintField(Field field, int indent) {
+ Contract.Requires(field != null);
+ Indent(indent);
+ if (field.IsGhost) {
+ wr.Write("ghost ");
+ }
+ wr.Write("var");
+ PrintAttributes(field.Attributes);
+ wr.Write(" {0}: ", field.Name);
+ PrintType(field.Type);
+ if (field.IsUserMutable) {
+ // nothing more to say
+ } else if (field.IsMutable) {
+ wr.Write(" // non-assignable");
+ } else {
+ wr.Write(" // immutable");
+ }
+ wr.WriteLine();
+ }
+
+ public void PrintFunction(Function f, int indent, bool printSignatureOnly) {
+ Contract.Requires(f != null);
+
+ if (PrintModeSkipFunctionOrMethod(f.IsGhost, f.Attributes, f.Name)) { return; }
+ var isPredicate = f is Predicate || f is PrefixPredicate;
+ Indent(indent);
+ string k = isPredicate ? "predicate" : f is InductivePredicate ? "inductive predicate" : f is CoPredicate ? "copredicate" : "function";
+ if (f.IsProtected) { k = "protected " + k; }
+ if (f.HasStaticKeyword) { k = "static " + k; }
+ if (!f.IsGhost) { k += " method"; }
+ PrintClassMethodHelper(k, f.Attributes, f.Name, f.TypeArgs);
+ if (f.SignatureIsOmitted) {
+ wr.WriteLine(" ...");
+ } else {
+ PrintFormals(f.Formals, f.Name);
+ if (!isPredicate) {
+ wr.Write(": ");
+ PrintType(f.ResultType);
+ }
+ wr.WriteLine();
+ }
+
+ int ind = indent + IndentAmount;
+ PrintSpec("requires", f.Req, ind);
+ PrintFrameSpecLine("reads", f.Reads, ind, null);
+ PrintSpec("ensures", f.Ens, ind);
+ PrintDecreasesSpec(f.Decreases, ind);
+ if (f.Body != null && !printSignatureOnly) {
+ Indent(indent);
+ wr.WriteLine("{");
+ PrintExtendedExpr(f.Body, ind, true, false);
+ Indent(indent);
+ wr.WriteLine("}");
+ }
+ }
+
+ // ----------------------------- PrintMethod -----------------------------
+
+ const int IndentAmount = 2; // The amount of indent for each new scope
+ const string BunchaSpaces = " ";
+ void Indent(int amount)
+ {
+ Contract.Requires(0 <= amount);
+
+ while (0 < amount) {
+ wr.Write(BunchaSpaces.Substring(0, amount));
+ amount -= BunchaSpaces.Length;
+ }
+ }
+
+ private bool PrintModeSkipFunctionOrMethod(bool IsGhost, Attributes attributes, string name)
+ {
+ if (printMode == DafnyOptions.PrintModes.NoGhost && IsGhost)
+ { return true; }
+ if (printMode == DafnyOptions.PrintModes.NoIncludes || printMode == DafnyOptions.PrintModes.NoGhost)
+ {
+ bool verify = true;
+ if (Attributes.ContainsBool(attributes, "verify", ref verify) && !verify)
+ { return true; }
+ if (name.Contains("INTERNAL") || name.StartsWith("reveal_"))
+ { return true; }
+ }
+ return false;
+ }
+
+ private bool PrintModeSkipGeneral(Bpl.IToken tok, string fileBeingPrinted)
+ {
+ return (printMode == DafnyOptions.PrintModes.NoIncludes || printMode == DafnyOptions.PrintModes.NoGhost)
+ && (tok.filename != null && fileBeingPrinted != null && Path.GetFullPath(tok.filename) != fileBeingPrinted);
+ }
+
+ public void PrintMethod(Method method, int indent, bool printSignatureOnly) {
+ Contract.Requires(method != null);
+
+ if (PrintModeSkipFunctionOrMethod(method.IsGhost, method.Attributes, method.Name)) { return; }
+ Indent(indent);
+ string k = method is Constructor ? "constructor" :
+ method is InductiveLemma ? "inductive lemma" :
+ method is CoLemma ? "colemma" :
+ method is Lemma ? "lemma" :
+ "method";
+ if (method.HasStaticKeyword) { k = "static " + k; }
+ if (method.IsGhost && !(method is Lemma) && !(method is FixpointLemma)) { k = "ghost " + k; }
+ string nm = method is Constructor && !((Constructor)method).HasName ? "" : method.Name;
+ PrintClassMethodHelper(k, method.Attributes, nm, method.TypeArgs);
+ if (method.SignatureIsOmitted) {
+ wr.WriteLine(" ...");
+ } else {
+ PrintFormals(method.Ins, method.Name);
+ if (method.Outs.Count != 0) {
+ if (method.Ins.Count + method.Outs.Count <= 3) {
+ wr.Write(" returns ");
+ } else {
+ wr.WriteLine();
+ Indent(indent + 2 * IndentAmount);
+ wr.Write("returns ");
+ }
+ PrintFormals(method.Outs);
+ }
+ wr.WriteLine();
+ }
+
+ int ind = indent + IndentAmount;
+ PrintSpec("requires", method.Req, ind);
+ if (method.Mod.Expressions != null)
+ {
+ PrintFrameSpecLine("modifies", method.Mod.Expressions, ind, method.Mod.HasAttributes() ? method.Mod.Attributes : null);
+ }
+ PrintSpec("ensures", method.Ens, ind);
+ PrintDecreasesSpec(method.Decreases, ind);
+
+ if (method.Body != null && !printSignatureOnly) {
+ Indent(indent);
+ PrintStatement(method.Body, indent);
+ wr.WriteLine();
+ }
+ }
+
+ internal void PrintFormals(List<Formal> ff, string name = null) {
+ Contract.Requires(ff != null);
+ if (name != null && name.EndsWith("#")) {
+ wr.Write("[");
+ PrintFormal(ff[0]);
+ wr.Write("]");
+ ff = new List<Formal>(ff.Skip(1));
+ }
+ wr.Write("(");
+ string sep = "";
+ foreach (Formal f in ff) {
+ Contract.Assert(f != null);
+ wr.Write(sep);
+ sep = ", ";
+ PrintFormal(f);
+ }
+ wr.Write(")");
+ }
+
+ void PrintFormal(Formal f) {
+ Contract.Requires(f != null);
+ if (f.IsGhost) {
+ wr.Write("ghost ");
+ }
+ if (f.HasName) {
+ wr.Write("{0}: ", f.DisplayName);
+ }
+ PrintType(f.Type);
+ }
+
+ internal void PrintSpec(string kind, List<Expression> ee, int indent) {
+ Contract.Requires(kind != null);
+ Contract.Requires(ee != null);
+ foreach (Expression e in ee) {
+ Contract.Assert(e != null);
+ Indent(indent);
+ wr.Write("{0} ", kind);
+ PrintExpression(e, true);
+ wr.WriteLine();
+ }
+ }
+
+ internal void PrintDecreasesSpec(Specification<Expression> decs, int indent, bool newLine = true) {
+ Contract.Requires(decs != null);
+ if (printMode == DafnyOptions.PrintModes.NoGhost) { return; }
+ if (decs.Expressions != null && decs.Expressions.Count != 0) {
+ Indent(indent);
+ wr.Write("decreases");
+ if (decs.HasAttributes())
+ {
+ PrintAttributes(decs.Attributes);
+ }
+ wr.Write(" ");
+ PrintExpressionList(decs.Expressions, true);
+ if (newLine) {
+ wr.WriteLine();
+ } else {
+ wr.Write(" ");
+ }
+ }
+ }
+
+ internal void PrintFrameSpecLine(string kind, List<FrameExpression/*!*/> ee, int indent, Attributes attrs, bool newLine = true) {
+ Contract.Requires(kind != null);
+ Contract.Requires(cce.NonNullElements(ee));
+ if (ee != null && ee.Count != 0) {
+ Indent(indent);
+ wr.Write("{0}", kind);
+ if (attrs != null) {
+ PrintAttributes(attrs);
+ }
+ wr.Write(" ");
+ PrintFrameExpressionList(ee);
+ if (newLine) {
+ wr.WriteLine();
+ } else {
+ wr.Write(" ");
+ }
+ }
+ }
+
+ internal void PrintSpec(string kind, List<MaybeFreeExpression> ee, int indent, bool newLine = true) {
+ Contract.Requires(kind != null);
+ Contract.Requires(ee != null);
+ if (printMode == DafnyOptions.PrintModes.NoGhost) { return; }
+ foreach (MaybeFreeExpression e in ee)
+ {
+ Contract.Assert(e != null);
+ Indent(indent);
+ wr.Write("{0}{1}", e.IsFree ? "free " : "", kind);
+
+ if (e.HasAttributes())
+ {
+ PrintAttributes(e.Attributes);
+ }
+
+ wr.Write(" ");
+ PrintExpression(e.E, true);
+ if (newLine) {
+ wr.WriteLine();
+ } else {
+ wr.Write(" ");
+ }
+ }
+ }
+
+ // ----------------------------- PrintType -----------------------------
+
+ public void PrintType(Type ty) {
+ Contract.Requires(ty != null);
+ wr.Write(ty.ToString());
+ }
+
+ public void PrintType(string prefix, Type ty) {
+ Contract.Requires(prefix != null);
+ Contract.Requires(ty != null);
+ string s = ty.ToString();
+ if (s != "?") {
+ wr.Write("{0}{1}", prefix, s);
+ }
+ }
+
+ string EqualitySupportSuffix(TypeParameter.EqualitySupportValue es) {
+ if (es == TypeParameter.EqualitySupportValue.Required ||
+ (es == TypeParameter.EqualitySupportValue.InferredRequired && DafnyOptions.O.DafnyPrintResolvedFile != null)) {
+ return "(==)";
+ } else {
+ return "";
+ }
+ }
+
+ // ----------------------------- PrintStatement -----------------------------
+
+ /// <summary>
+ /// Prints from the current position of the current line.
+ /// If the statement requires several lines, subsequent lines are indented at "indent".
+ /// No newline is printed after the statement.
+ /// </summary>
+ public void PrintStatement(Statement stmt, int indent) {
+ Contract.Requires(stmt != null);
+
+ if (stmt.IsGhost && printMode == DafnyOptions.PrintModes.NoGhost) { return; }
+ for (LList<Label> label = stmt.Labels; label != null; label = label.Next) {
+ if (label.Data.Name != null) {
+ wr.WriteLine("label {0}:", label.Data.Name);
+ Indent(indent);
+ }
+ }
+
+ if (stmt is PredicateStmt) {
+ if (printMode == DafnyOptions.PrintModes.NoGhost) { return; }
+ Expression expr = ((PredicateStmt)stmt).Expr;
+ wr.Write(stmt is AssertStmt ? "assert" : "assume");
+ if (stmt.Attributes != null) {
+ PrintAttributes(stmt.Attributes);
+ }
+ wr.Write(" ");
+ PrintExpression(expr, true);
+ wr.Write(";");
+
+ } else if (stmt is PrintStmt) {
+ PrintStmt s = (PrintStmt)stmt;
+ wr.Write("print");
+ PrintAttributeArgs(s.Args, true);
+ wr.Write(";");
+
+ } else if (stmt is BreakStmt) {
+ BreakStmt s = (BreakStmt)stmt;
+ if (s.TargetLabel != null) {
+ wr.Write("break {0};", s.TargetLabel);
+ } else {
+ string sep = "";
+ for (int i = 0; i < s.BreakCount; i++) {
+ wr.Write("{0}break", sep);
+ sep = " ";
+ }
+ wr.Write(";");
+ }
+
+ } else if (stmt is ProduceStmt) {
+ var s = (ProduceStmt) stmt;
+ wr.Write(s is YieldStmt ? "yield" : "return");
+ if (s.rhss != null) {
+ var sep = " ";
+ foreach (var rhs in s.rhss) {
+ wr.Write(sep);
+ PrintRhs(rhs);
+ sep = ", ";
+ }
+ }
+ wr.Write(";");
+
+ } else if (stmt is AssignStmt) {
+ AssignStmt s = (AssignStmt)stmt;
+ PrintExpression(s.Lhs, true);
+ wr.Write(" := ");
+ PrintRhs(s.Rhs);
+ wr.Write(";");
+
+ } else if (stmt is BlockStmt) {
+ wr.WriteLine("{");
+ int ind = indent + IndentAmount;
+ foreach (Statement s in ((BlockStmt)stmt).Body) {
+ Indent(ind);
+ PrintStatement(s, ind);
+ wr.WriteLine();
+ }
+ Indent(indent);
+ wr.Write("}");
+
+ } else if (stmt is IfStmt) {
+ IfStmt s = (IfStmt)stmt;
+ PrintIfStatement(indent, s, false);
+
+ } else if (stmt is AlternativeStmt) {
+ var s = (AlternativeStmt)stmt;
+ wr.WriteLine("if {");
+ PrintAlternatives(indent, s.Alternatives);
+ Indent(indent);
+ wr.Write("}");
+
+ } else if (stmt is WhileStmt) {
+ WhileStmt s = (WhileStmt)stmt;
+ PrintWhileStatement(indent, s, false, false);
+
+ } else if (stmt is AlternativeLoopStmt) {
+ var s = (AlternativeLoopStmt)stmt;
+ wr.WriteLine("while");
+ PrintSpec("invariant", s.Invariants, indent + IndentAmount);
+ PrintDecreasesSpec(s.Decreases, indent + IndentAmount);
+
+ Indent(indent);
+ wr.WriteLine("{");
+ PrintAlternatives(indent, s.Alternatives);
+ Indent(indent);
+ wr.Write("}");
+
+ } else if (stmt is ForallStmt) {
var s = (ForallStmt)stmt;
- wr.Write("forall");
- if (s.BoundVars.Count != 0) {
- wr.Write(" ");
- PrintQuantifierDomain(s.BoundVars, s.Attributes, s.Range);
- }
- if (s.Ens.Count == 0) {
- wr.Write(" ");
- } else {
- wr.WriteLine();
- PrintSpec("ensures", s.Ens, indent + IndentAmount, s.Body != null);
- Indent(indent);
- }
- if (s.Body != null) {
- PrintStatement(s.Body, indent);
- }
-
- } else if (stmt is ModifyStmt) {
- var s = (ModifyStmt)stmt;
- PrintModifyStmt(indent, s, false);
-
- } else if (stmt is CalcStmt) {
- CalcStmt s = (CalcStmt)stmt;
- if (printMode == DafnyOptions.PrintModes.NoGhost) { return; } // Calcs don't get a "ghost" attribute, but they are.
- wr.Write("calc ");
- if (!s.Op.Equals(CalcStmt.DefaultOp)) {
- PrintCalcOp(s.Op);
- wr.Write(" ");
- }
- wr.WriteLine("{");
- int lineInd = indent + IndentAmount;
- int lineCount = s.Lines.Count == 0 ? 0 : s.Lines.Count - 1; // if nonempty, .Lines always contains a duplicated last line
- // The number of op/hints is commonly one less than the number of lines, but
- // it can also equal the number of lines for empty calc's and for calc's with
- // a dangling hint.
- int hintCount = s.Lines.Count != 0 && s.Hints.Last().Body.Count == 0 ? lineCount - 1 : lineCount;
- for (var i = 0; i < lineCount; i++) {
- var e = s.Lines[i];
- var op = s.StepOps[i];
- var h = s.Hints[i];
- // print the line
- Indent(lineInd);
- PrintExpression(e, true, lineInd);
- wr.WriteLine(";");
- if (i == hintCount) {
- break;
+ if (s.ForallExpressions != null) {
+ foreach (var expr in s.ForallExpressions) {
+ PrintExpression(expr, false, " ensures ");
}
- // print the operator, if any
- if (!s.Op.Equals(op)) {
- Indent(indent); // this lines up with the "calc"
- PrintCalcOp(op);
- wr.WriteLine();
- }
- // print the hints
- foreach (var st in h.Body) {
- Indent(lineInd);
- PrintStatement(st, lineInd);
- wr.WriteLine();
- }
- }
- Indent(indent);
- wr.Write("}");
-
- } else if (stmt is MatchStmt) {
- MatchStmt s = (MatchStmt)stmt;
- wr.Write("match ");
- PrintExpression(s.Source, false);
- if (s.UsesOptionalBraces) {
- wr.Write(" {");
- }
- int caseInd = indent + (s.UsesOptionalBraces ? IndentAmount : 0);
- foreach (MatchCaseStmt mc in s.Cases) {
- wr.WriteLine();
- Indent(caseInd);
- wr.Write("case {0}", mc.Id);
- if (mc.Arguments.Count != 0) {
- string sep = "(";
- foreach (BoundVar bv in mc.Arguments) {
- wr.Write("{0}{1}", sep, bv.DisplayName);
- if (bv.Type is NonProxyType) {
- wr.Write(": {0}", bv.Type);
- }
- sep = ", ";
- }
- wr.Write(")");
+ } else {
+ wr.Write("forall");
+ if (s.BoundVars.Count != 0) {
+ wr.Write(" ");
+ PrintQuantifierDomain(s.BoundVars, s.Attributes, s.Range);
}
- wr.Write(" =>");
- foreach (Statement bs in mc.Body) {
+ if (s.Ens.Count == 0) {
+ wr.Write(" ");
+ } else {
wr.WriteLine();
- Indent(caseInd + IndentAmount);
- PrintStatement(bs, caseInd + IndentAmount);
+ PrintSpec("ensures", s.Ens, indent + IndentAmount, s.Body != null);
+ Indent(indent);
}
- }
- if (s.UsesOptionalBraces) {
- wr.WriteLine();
- Indent(indent);
- wr.Write("}");
- }
-
- } else if (stmt is ConcreteUpdateStatement) {
- var s = (ConcreteUpdateStatement)stmt;
- string sep = "";
- foreach (var lhs in s.Lhss) {
- wr.Write(sep);
- PrintExpression(lhs, true);
- sep = ", ";
- }
- PrintUpdateRHS(s);
- wr.Write(";");
-
- } else if (stmt is VarDeclStmt) {
- var s = (VarDeclStmt)stmt;
- if (s.Locals.Exists(v => v.IsGhost) && printMode == DafnyOptions.PrintModes.NoGhost) { return; }
- if (s.Locals.Exists(v => v.IsGhost)) {
- wr.Write("ghost ");
- }
+ }
+ if (s.Body != null) {
+ PrintStatement(s.Body, indent);
+ }
+
+ } else if (stmt is ModifyStmt) {
+ var s = (ModifyStmt)stmt;
+ PrintModifyStmt(indent, s, false);
+
+ } else if (stmt is CalcStmt) {
+ CalcStmt s = (CalcStmt)stmt;
+ if (printMode == DafnyOptions.PrintModes.NoGhost) { return; } // Calcs don't get a "ghost" attribute, but they are.
+ wr.Write("calc ");
+ if (!s.Op.Equals(CalcStmt.DefaultOp)) {
+ PrintCalcOp(s.Op);
+ wr.Write(" ");
+ }
+ wr.WriteLine("{");
+ int lineInd = indent + IndentAmount;
+ int lineCount = s.Lines.Count == 0 ? 0 : s.Lines.Count - 1; // if nonempty, .Lines always contains a duplicated last line
+ // The number of op/hints is commonly one less than the number of lines, but
+ // it can also equal the number of lines for empty calc's and for calc's with
+ // a dangling hint.
+ int hintCount = s.Lines.Count != 0 && s.Hints.Last().Body.Count == 0 ? lineCount - 1 : lineCount;
+ for (var i = 0; i < lineCount; i++) {
+ var e = s.Lines[i];
+ var op = s.StepOps[i];
+ var h = s.Hints[i];
+ // print the line
+ Indent(lineInd);
+ PrintExpression(e, true, lineInd);
+ wr.WriteLine(";");
+ if (i == hintCount) {
+ break;
+ }
+ // print the operator, if any
+ if (!s.Op.Equals(op)) {
+ Indent(indent); // this lines up with the "calc"
+ PrintCalcOp(op);
+ wr.WriteLine();
+ }
+ // print the hints
+ foreach (var st in h.Body) {
+ Indent(lineInd);
+ PrintStatement(st, lineInd);
+ wr.WriteLine();
+ }
+ }
+ Indent(indent);
+ wr.Write("}");
+
+ } else if (stmt is MatchStmt) {
+ MatchStmt s = (MatchStmt)stmt;
+ wr.Write("match ");
+ PrintExpression(s.Source, false);
+ if (s.UsesOptionalBraces) {
+ wr.Write(" {");
+ }
+ int caseInd = indent + (s.UsesOptionalBraces ? IndentAmount : 0);
+ foreach (MatchCaseStmt mc in s.Cases) {
+ wr.WriteLine();
+ Indent(caseInd);
+ wr.Write("case {0}", mc.Id);
+ PrintMatchCaseArgument(mc);
+ wr.Write(" =>");
+ foreach (Statement bs in mc.Body) {
+ wr.WriteLine();
+ Indent(caseInd + IndentAmount);
+ PrintStatement(bs, caseInd + IndentAmount);
+ }
+ }
+ if (s.UsesOptionalBraces) {
+ wr.WriteLine();
+ Indent(indent);
+ wr.Write("}");
+ }
+
+ } else if (stmt is ConcreteUpdateStatement) {
+ var s = (ConcreteUpdateStatement)stmt;
+ string sep = "";
+ foreach (var lhs in s.Lhss) {
+ wr.Write(sep);
+ PrintExpression(lhs, true);
+ sep = ", ";
+ }
+ PrintUpdateRHS(s);
+ wr.Write(";");
+
+ } else if (stmt is VarDeclStmt) {
+ var s = (VarDeclStmt)stmt;
+ if (s.Locals.Exists(v => v.IsGhost) && printMode == DafnyOptions.PrintModes.NoGhost) { return; }
+ if (s.Locals.Exists(v => v.IsGhost)) {
+ wr.Write("ghost ");
+ }
+ wr.Write("var");
+ string sep = "";
+ foreach (var local in s.Locals) {
+ wr.Write(sep);
+ if (local.Attributes != null) {
+ PrintAttributes(local.Attributes);
+ }
+ wr.Write(" {0}", local.DisplayName);
+ PrintType(": ", local.OptionalType);
+ sep = ",";
+ }
+ if (s.Update != null) {
+ PrintUpdateRHS(s.Update);
+ }
+ wr.Write(";");
+
+ } else if (stmt is LetStmt) {
+ var s = (LetStmt)stmt;
wr.Write("var");
string sep = "";
- foreach (var local in s.Locals) {
- wr.Write(sep);
- if (local.Attributes != null) {
- PrintAttributes(local.Attributes);
- }
- wr.Write(" {0}", local.DisplayName);
- PrintType(": ", local.OptionalType);
- sep = ",";
- }
- if (s.Update != null) {
- PrintUpdateRHS(s.Update);
- }
- wr.Write(";");
-
- } else if (stmt is SkeletonStatement) {
- var s = (SkeletonStatement)stmt;
- if (s.S == null) {
- wr.Write("...;");
- } else if (s.S is AssertStmt) {
- Contract.Assert(s.ConditionOmitted);
- wr.Write("assert ...;");
- } else if (s.S is AssumeStmt) {
- Contract.Assert(s.ConditionOmitted);
- wr.Write("assume ...;");
- } else if (s.S is IfStmt) {
- PrintIfStatement(indent, (IfStmt)s.S, s.ConditionOmitted);
- } else if (s.S is WhileStmt) {
- PrintWhileStatement(indent, (WhileStmt)s.S, s.ConditionOmitted, s.BodyOmitted);
- } else if (s.S is ModifyStmt) {
- PrintModifyStmt(indent, (ModifyStmt)s.S, true);
- } else {
- Contract.Assert(false); throw new cce.UnreachableException(); // unexpected skeleton statement
- }
-
- } else {
- Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement
- }
- }
-
- private void PrintModifyStmt(int indent, ModifyStmt s, bool omitFrame) {
- Contract.Requires(0 <= indent);
- Contract.Requires(s != null);
- Contract.Requires(!omitFrame || s.Mod.Expressions.Count == 0);
-
- wr.Write("modify");
- PrintAttributes(s.Mod.Attributes);
- wr.Write(" ");
- if (omitFrame) {
- wr.Write("...");
- } else {
- PrintFrameExpressionList(s.Mod.Expressions);
- }
- if (s.Body != null) {
- // There's a possible syntactic ambiguity, namely if the frame is empty (more precisely,
- // if s.Mod.Expressions.Count is 0). Since the statement was parsed at some point, this
- // situation can occur only if the modify statement inherited its frame by refinement
- // and we're printing the post-resolve AST. In this special case, print an explicit
- // empty set as the frame.
- if (s.Mod.Expressions.Count == 0) {
- wr.Write(" {}");
- }
- wr.Write(" ");
- PrintStatement(s.Body, indent);
- } else {
- wr.Write(";");
- }
- }
-
- /// <summary>
- /// Does not print LHS
- /// </summary>
- void PrintUpdateRHS(ConcreteUpdateStatement s) {
- Contract.Requires(s != null);
- if (s is UpdateStmt) {
- var update = (UpdateStmt)s;
- if (update.Lhss.Count != 0) {
- wr.Write(" := ");
- }
- var sep = "";
- foreach (var rhs in update.Rhss) {
+ foreach (var lhs in s.LHSs) {
wr.Write(sep);
- PrintRhs(rhs);
+ PrintCasePattern(lhs);
sep = ", ";
}
- } else if (s is AssignSuchThatStmt) {
- var update = (AssignSuchThatStmt)s;
- wr.Write(" :| ");
- if (update.AssumeToken != null) {
- wr.Write("assume ");
- }
- PrintExpression(update.Expr, true);
- } else {
- Contract.Assert(s == null); // otherwise, unknown type
- }
- }
-
- void PrintIfStatement(int indent, IfStmt s, bool omitGuard) {
- while (true) {
- if (omitGuard) {
- wr.Write("if ... ");
- } else {
- wr.Write("if ");
- PrintGuard(s.Guard);
- wr.Write(" ");
- }
- PrintStatement(s.Thn, indent);
- if (s.Els == null) {
- break;
- }
+ wr.Write(" := ");
+ PrintExpressionList(s.RHSs, true);
+ wr.WriteLine(";");
+
+ } else if (stmt is SkeletonStatement) {
+ var s = (SkeletonStatement)stmt;
+ if (s.S == null) {
+ wr.Write("...;");
+ } else if (s.S is AssertStmt) {
+ Contract.Assert(s.ConditionOmitted);
+ wr.Write("assert ...;");
+ } else if (s.S is AssumeStmt) {
+ Contract.Assert(s.ConditionOmitted);
+ wr.Write("assume ...;");
+ } else if (s.S is IfStmt) {
+ PrintIfStatement(indent, (IfStmt)s.S, s.ConditionOmitted);
+ } else if (s.S is WhileStmt) {
+ PrintWhileStatement(indent, (WhileStmt)s.S, s.ConditionOmitted, s.BodyOmitted);
+ } else if (s.S is ModifyStmt) {
+ PrintModifyStmt(indent, (ModifyStmt)s.S, true);
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected skeleton statement
+ }
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement
+ }
+ }
+
+ private void PrintModifyStmt(int indent, ModifyStmt s, bool omitFrame) {
+ Contract.Requires(0 <= indent);
+ Contract.Requires(s != null);
+ Contract.Requires(!omitFrame || s.Mod.Expressions.Count == 0);
+
+ wr.Write("modify");
+ PrintAttributes(s.Mod.Attributes);
+ wr.Write(" ");
+ if (omitFrame) {
+ wr.Write("...");
+ } else {
+ PrintFrameExpressionList(s.Mod.Expressions);
+ }
+ if (s.Body != null) {
+ // There's a possible syntactic ambiguity, namely if the frame is empty (more precisely,
+ // if s.Mod.Expressions.Count is 0). Since the statement was parsed at some point, this
+ // situation can occur only if the modify statement inherited its frame by refinement
+ // and we're printing the post-resolve AST. In this special case, print an explicit
+ // empty set as the frame.
+ if (s.Mod.Expressions.Count == 0) {
+ wr.Write(" {}");
+ }
+ wr.Write(" ");
+ PrintStatement(s.Body, indent);
+ } else {
+ wr.Write(";");
+ }
+ }
+
+ /// <summary>
+ /// Does not print LHS
+ /// </summary>
+ void PrintUpdateRHS(ConcreteUpdateStatement s) {
+ Contract.Requires(s != null);
+ if (s is UpdateStmt) {
+ var update = (UpdateStmt)s;
+ if (update.Lhss.Count != 0) {
+ wr.Write(" := ");
+ }
+ var sep = "";
+ foreach (var rhs in update.Rhss) {
+ wr.Write(sep);
+ PrintRhs(rhs);
+ sep = ", ";
+ }
+ } else if (s is AssignSuchThatStmt) {
+ var update = (AssignSuchThatStmt)s;
+ wr.Write(" :| ");
+ if (update.AssumeToken != null) {
+ wr.Write("assume ");
+ }
+ PrintExpression(update.Expr, true);
+ } else {
+ Contract.Assert(s == null); // otherwise, unknown type
+ }
+ }
+
+ void PrintIfStatement(int indent, IfStmt s, bool omitGuard) {
+ if (omitGuard) {
+ wr.Write("if ... ");
+ } else {
+ wr.Write("if ");
+ PrintGuard(s.IsExistentialGuard, s.Guard);
+ wr.Write(" ");
+ }
+ PrintStatement(s.Thn, indent);
+ if (s.Els != null) {
wr.Write(" else ");
- if (s.Els is IfStmt) {
- s = (IfStmt)s.Els;
- } else {
- PrintStatement(s.Els, indent);
- break;
- }
- }
- }
-
- void PrintWhileStatement(int indent, WhileStmt s, bool omitGuard, bool omitBody) {
- Contract.Requires(0 <= indent);
- if (omitGuard) {
- wr.WriteLine("while ...");
- } else {
- wr.Write("while ");
- PrintGuard(s.Guard);
- wr.WriteLine();
- }
-
- PrintSpec("invariant", s.Invariants, indent + IndentAmount, s.Body != null || omitBody || (s.Decreases.Expressions != null && s.Decreases.Expressions.Count != 0) || (s.Mod.Expressions != null && s.Mod.Expressions.Count != 0));
- PrintDecreasesSpec(s.Decreases, indent + IndentAmount, s.Body != null || omitBody || (s.Mod.Expressions != null && s.Mod.Expressions.Count != 0));
- if (s.Mod.Expressions != null) {
- PrintFrameSpecLine("modifies", s.Mod.Expressions, indent + IndentAmount, s.Mod.HasAttributes() ? s.Mod.Attributes : null, s.Body != null || omitBody);
- }
- Indent(indent);
- if (omitBody) {
- wr.WriteLine("...;");
- } else if (s.Body != null) {
- PrintStatement(s.Body, indent);
- }
- }
-
- void PrintAlternatives(int indent, List<GuardedAlternative> alternatives) {
- int caseInd = indent + IndentAmount;
- foreach (var alternative in alternatives) {
- Indent(caseInd);
+ PrintStatement(s.Els, indent);
+ }
+ }
+
+ void PrintWhileStatement(int indent, WhileStmt s, bool omitGuard, bool omitBody) {
+ Contract.Requires(0 <= indent);
+ if (omitGuard) {
+ wr.WriteLine("while ...");
+ } else {
+ wr.Write("while ");
+ PrintGuard(false, s.Guard);
+ wr.WriteLine();
+ }
+
+ PrintSpec("invariant", s.Invariants, indent + IndentAmount, s.Body != null || omitBody || (s.Decreases.Expressions != null && s.Decreases.Expressions.Count != 0) || (s.Mod.Expressions != null && s.Mod.Expressions.Count != 0));
+ PrintDecreasesSpec(s.Decreases, indent + IndentAmount, s.Body != null || omitBody || (s.Mod.Expressions != null && s.Mod.Expressions.Count != 0));
+ if (s.Mod.Expressions != null) {
+ PrintFrameSpecLine("modifies", s.Mod.Expressions, indent + IndentAmount, s.Mod.HasAttributes() ? s.Mod.Attributes : null, s.Body != null || omitBody);
+ }
+ Indent(indent);
+ if (omitBody) {
+ wr.WriteLine("...;");
+ } else if (s.Body != null) {
+ PrintStatement(s.Body, indent);
+ }
+ }
+
+ void PrintAlternatives(int indent, List<GuardedAlternative> alternatives) {
+ int caseInd = indent + IndentAmount;
+ foreach (var alternative in alternatives) {
+ Indent(caseInd);
wr.Write("case ");
- PrintExpression(alternative.Guard, false);
- wr.WriteLine(" =>");
- foreach (Statement s in alternative.Body) {
- Indent(caseInd + IndentAmount);
- PrintStatement(s, caseInd + IndentAmount);
- wr.WriteLine();
- }
- }
- }
-
- void PrintRhs(AssignmentRhs rhs) {
- Contract.Requires(rhs != null);
- if (rhs is ExprRhs) {
- PrintExpression(((ExprRhs)rhs).Expr, true);
- } else if (rhs is HavocRhs) {
- wr.Write("*");
- } else if (rhs is TypeRhs) {
- TypeRhs t = (TypeRhs)rhs;
- wr.Write("new ");
- if (t.ArrayDimensions != null) {
- PrintType(t.EType);
- string s = "[";
- foreach (Expression dim in t.ArrayDimensions) {
- Contract.Assume(dim != null);
- wr.Write(s);
- PrintExpression(dim, false);
- s = ", ";
- }
- wr.Write("]");
- } else if (t.Arguments == null) {
- PrintType(t.EType);
- } else {
- PrintType(t.Path);
- wr.Write("(");
- PrintExpressionList(t.Arguments, false);
- wr.Write(")");
- }
- } else {
- Contract.Assert(false); throw new cce.UnreachableException(); // unexpected RHS
- }
-
- if (rhs.HasAttributes())
- {
- PrintAttributes(rhs.Attributes);
- }
- }
-
- void PrintGuard(Expression guard) {
- if (guard == null) {
- wr.Write("*");
- } else {
- PrintExpression(guard, false);
- }
- }
-
- void PrintCalcOp(CalcStmt.CalcOp op) {
- Contract.Requires(op != null);
- wr.Write(op.ToString());
- if (op is CalcStmt.TernaryCalcOp) {
- wr.Write("[");
- PrintExpression(((CalcStmt.TernaryCalcOp) op).Index, false);
- wr.Write("]");
- }
- }
-
- // ----------------------------- PrintExpression -----------------------------
-
- /// <summary>
- /// PrintExtendedExpr prints an expression, but formats top-level if-then-else and match expressions across several lines.
- /// Its intended use is thus to print the body of a function.
- /// </summary>
- public void PrintExtendedExpr(Expression expr, int indent, bool isRightmost, bool endWithCloseParen) {
- Contract.Requires(expr != null);
- if (expr is ITEExpr) {
- Indent(indent);
- while (true) {
- var ite = (ITEExpr)expr;
- wr.Write("if ");
- PrintExpression(ite.Test, false);
- wr.WriteLine(" then");
- PrintExtendedExpr(ite.Thn, indent + IndentAmount, true, false);
- expr = ite.Els;
- if (expr is ITEExpr) {
- Indent(indent); wr.Write("else ");
- } else {
- Indent(indent); wr.WriteLine("else");
- Indent(indent + IndentAmount);
- PrintExpression(expr, isRightmost, false);
- wr.WriteLine(endWithCloseParen ? ")" : "");
- return;
- }
- }
- } else if (expr is MatchExpr) {
- var e = (MatchExpr)expr;
- Indent(indent);
- var parensNeeded = !isRightmost && !e.UsesOptionalBraces;
- if (parensNeeded) { wr.Write("("); }
- wr.Write("match ");
- PrintExpression(e.Source, isRightmost && e.Cases.Count == 0, false);
- if (e.UsesOptionalBraces) { wr.WriteLine(" {"); }
- else if (parensNeeded && e.Cases.Count == 0) { wr.WriteLine(")"); }
- else { wr.WriteLine(); }
- int i = 0;
- int ind = indent + (e.UsesOptionalBraces ? IndentAmount : 0);
- foreach (var mc in e.Cases) {
- bool isLastCase = i == e.Cases.Count - 1;
- Indent(ind);
- wr.Write("case {0}", mc.Id);
- if (mc.Arguments.Count != 0) {
- string sep = "(";
- foreach (BoundVar bv in mc.Arguments) {
- wr.Write("{0}{1}", sep, bv.DisplayName);
- if (bv.Type is NonProxyType) {
- wr.Write(": {0}", bv.Type);
- }
- sep = ", ";
- }
- wr.Write(")");
- }
- wr.WriteLine(" =>");
- PrintExtendedExpr(mc.Body, ind + IndentAmount, isLastCase, isLastCase && (parensNeeded || endWithCloseParen));
- i++;
- }
- if (e.UsesOptionalBraces) {
- Indent(indent);
- wr.WriteLine("}");
- }
- } else if (expr is LetExpr) {
- var e = (LetExpr)expr;
- Indent(indent);
- wr.Write("var ");
- string sep = "";
- foreach (var lhs in e.LHSs) {
- wr.Write(sep);
- PrintCasePattern(lhs);
- sep = ", ";
- }
- if (e.Exact) {
- wr.Write(" := ");
+ if (alternative.IsExistentialGuard) {
+ var exists = (ExistsExpr)alternative.Guard;
+ PrintExistentialGuard(exists);
} else {
- wr.Write(" :| ");
- }
- PrintExpressionList(e.RHSs, true);
- wr.WriteLine(";");
- PrintExtendedExpr(e.Body, indent, isRightmost, endWithCloseParen);
-
- } else if (expr is ParensExpression) {
- PrintExtendedExpr(((ParensExpression)expr).E, indent, isRightmost, endWithCloseParen);
- } else {
- Indent(indent);
- PrintExpression(expr, false, indent);
- wr.WriteLine(endWithCloseParen ? ")" : "");
- }
+ PrintExpression(alternative.Guard, false);
+ }
+ wr.WriteLine(" =>");
+ foreach (Statement s in alternative.Body) {
+ Indent(caseInd + IndentAmount);
+ PrintStatement(s, caseInd + IndentAmount);
+ wr.WriteLine();
+ }
+ }
+ }
+
+ void PrintRhs(AssignmentRhs rhs) {
+ Contract.Requires(rhs != null);
+ if (rhs is ExprRhs) {
+ PrintExpression(((ExprRhs)rhs).Expr, true);
+ } else if (rhs is HavocRhs) {
+ wr.Write("*");
+ } else if (rhs is TypeRhs) {
+ TypeRhs t = (TypeRhs)rhs;
+ wr.Write("new ");
+ if (t.ArrayDimensions != null) {
+ PrintType(t.EType);
+ string s = "[";
+ foreach (Expression dim in t.ArrayDimensions) {
+ Contract.Assume(dim != null);
+ wr.Write(s);
+ PrintExpression(dim, false);
+ s = ", ";
+ }
+ wr.Write("]");
+ } else if (t.Arguments == null) {
+ PrintType(t.EType);
+ } else {
+ PrintType(t.Path);
+ wr.Write("(");
+ PrintExpressionList(t.Arguments, false);
+ wr.Write(")");
+ }
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected RHS
+ }
+
+ if (rhs.HasAttributes())
+ {
+ PrintAttributes(rhs.Attributes);
+ }
+ }
+
+ void PrintGuard(bool isExistentialGuard, Expression guard) {
+ Contract.Requires(!isExistentialGuard || (guard is ExistsExpr && ((ExistsExpr)guard).Range == null));
+ if (guard == null) {
+ wr.Write("*");
+ } else if (isExistentialGuard) {
+ var exists = (ExistsExpr)guard;
+ PrintExistentialGuard(exists);
+ } else {
+ PrintExpression(guard, false);
+ }
}
- public void PrintExpression(Expression expr, bool isFollowedBySemicolon) {
- Contract.Requires(expr != null);
- PrintExpr(expr, 0, false, true, isFollowedBySemicolon, -1);
+ void PrintExistentialGuard(ExistsExpr guard) {
+ Contract.Requires(guard != null);
+ Contract.Requires(guard.Range == null);
+ PrintQuantifierDomain(guard.BoundVars, guard.Attributes, null);
+ wr.Write(" :| ");
+ PrintExpression(guard.Term, false);
+ }
+
+ void PrintCalcOp(CalcStmt.CalcOp op) {
+ Contract.Requires(op != null);
+ wr.Write(op.ToString());
+ if (op is CalcStmt.TernaryCalcOp) {
+ wr.Write("[");
+ PrintExpression(((CalcStmt.TernaryCalcOp) op).Index, false);
+ wr.Write("]");
+ }
+ }
+
+ // ----------------------------- PrintExpression -----------------------------
+
+ /// <summary>
+ /// PrintExtendedExpr prints an expression, but formats top-level if-then-else and match expressions across several lines.
+ /// Its intended use is thus to print the body of a function.
+ /// </summary>
+ public void PrintExtendedExpr(Expression expr, int indent, bool isRightmost, bool endWithCloseParen) {
+ Contract.Requires(expr != null);
+ if (expr is ITEExpr) {
+ Indent(indent);
+ while (true) {
+ var ite = (ITEExpr)expr;
+ wr.Write("if ");
+ PrintExpression(ite.Test, false);
+ wr.WriteLine(" then");
+ PrintExtendedExpr(ite.Thn, indent + IndentAmount, true, false);
+ expr = ite.Els;
+ if (expr is ITEExpr) {
+ Indent(indent); wr.Write("else ");
+ } else {
+ Indent(indent); wr.WriteLine("else");
+ Indent(indent + IndentAmount);
+ PrintExpression(expr, isRightmost, false);
+ wr.WriteLine(endWithCloseParen ? ")" : "");
+ return;
+ }
+ }
+ } else if (expr is MatchExpr) {
+ var e = (MatchExpr)expr;
+ Indent(indent);
+ var parensNeeded = !isRightmost && !e.UsesOptionalBraces;
+ if (parensNeeded) { wr.Write("("); }
+ wr.Write("match ");
+ PrintExpression(e.Source, isRightmost && e.Cases.Count == 0, false);
+ if (e.UsesOptionalBraces) { wr.WriteLine(" {"); }
+ else if (parensNeeded && e.Cases.Count == 0) { wr.WriteLine(")"); }
+ else { wr.WriteLine(); }
+ int i = 0;
+ int ind = indent + (e.UsesOptionalBraces ? IndentAmount : 0);
+ foreach (var mc in e.Cases) {
+ bool isLastCase = i == e.Cases.Count - 1;
+ Indent(ind);
+ wr.Write("case {0}", mc.Id);
+ PrintMatchCaseArgument(mc);
+ wr.WriteLine(" =>");
+ PrintExtendedExpr(mc.Body, ind + IndentAmount, isLastCase, isLastCase && (parensNeeded || endWithCloseParen));
+ i++;
+ }
+ if (e.UsesOptionalBraces) {
+ Indent(indent);
+ wr.WriteLine("}");
+ }
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ Indent(indent);
+ wr.Write("var ");
+ string sep = "";
+ foreach (var lhs in e.LHSs) {
+ wr.Write(sep);
+ PrintCasePattern(lhs);
+ sep = ", ";
+ }
+ if (e.Exact) {
+ wr.Write(" := ");
+ } else {
+ wr.Write(" :| ");
+ }
+ PrintExpressionList(e.RHSs, true);
+ wr.WriteLine(";");
+ PrintExtendedExpr(e.Body, indent, isRightmost, endWithCloseParen);
+
+ } else if (expr is ParensExpression) {
+ PrintExtendedExpr(((ParensExpression)expr).E, indent, isRightmost, endWithCloseParen);
+ } else {
+ Indent(indent);
+ PrintExpression(expr, false, indent);
+ wr.WriteLine(endWithCloseParen ? ")" : "");
+ }
+ }
+
+ public void PrintMatchCaseArgument(MatchCase mc) {
+ if (mc.Arguments != null) {
+ if (mc.Arguments.Count != 0) {
+ string sep = "(";
+ foreach (BoundVar bv in mc.Arguments) {
+ wr.Write("{0}{1}", sep, bv.DisplayName);
+ if (bv.Type is NonProxyType) {
+ wr.Write(": {0}", bv.Type);
+ }
+ sep = ", ";
+ }
+ wr.Write(")");
+ }
+ } else {
+ Contract.Assert(mc.CasePatterns != null);
+ if (mc.CasePatterns.Count != 0) {
+ string sep = "(";
+ foreach (var cp in mc.CasePatterns) {
+ wr.Write(sep);
+ PrintCasePattern(cp);
+ sep = ", ";
+ }
+ wr.Write(")");
+ }
+ }
+ }
+
+ public void PrintExpression(Expression expr, bool isFollowedBySemicolon) {
+ Contract.Requires(expr != null);
+ PrintExpr(expr, 0, false, true, isFollowedBySemicolon, -1);
+ }
+
+ public void PrintExpression(Expression expr, bool isRightmost, bool isFollowedBySemicolon) {
+ Contract.Requires(expr != null);
+ PrintExpr(expr, 0, false, isRightmost, isFollowedBySemicolon, -1);
+ }
+
+ /// <summary>
+ /// An indent of -1 means print the entire expression on one line.
+ /// </summary>
+ public void PrintExpression(Expression expr, bool isFollowedBySemicolon, int indent) {
+ Contract.Requires(expr != null);
+ PrintExpr(expr, 0, false, true, isFollowedBySemicolon, indent);
}
- public void PrintExpression(Expression expr, bool isRightmost, bool isFollowedBySemicolon) {
+ public void PrintExpression(Expression expr, bool isFollowedBySemicolon, string keyword) {
Contract.Requires(expr != null);
- PrintExpr(expr, 0, false, isRightmost, isFollowedBySemicolon, -1);
- }
-
- /// <summary>
- /// An indent of -1 means print the entire expression on one line.
- /// </summary>
- public void PrintExpression(Expression expr, bool isFollowedBySemicolon, int indent) {
- Contract.Requires(expr != null);
- PrintExpr(expr, 0, false, true, isFollowedBySemicolon, indent);
- }
-
- /// <summary>
- /// An indent of -1 means print the entire expression on one line.
- /// </summary>
- void PrintExpr(Expression expr, int contextBindingStrength, bool fragileContext, bool isRightmost, bool isFollowedBySemicolon, int indent, int resolv_count = 2)
- {
- Contract.Requires(-1 <= indent);
- Contract.Requires(expr != null);
-
- /* When debugging:
- if (resolv_count > 0 && expr.Resolved != null) {
- PrintExpr(expr.Resolved, contextBindingStrength, fragileContext, isRightmost, isFollowedBySemicolon, indent, resolv_count - 1);
- return;
- }
- */
-
- if (expr is StaticReceiverExpr) {
- StaticReceiverExpr e = (StaticReceiverExpr)expr;
- wr.Write(e.Type);
- } else if (expr is LiteralExpr) {
- LiteralExpr e = (LiteralExpr)expr;
- if (e.Value == null) {
- wr.Write("null");
- } else if (e.Value is bool) {
- wr.Write((bool)e.Value ? "true" : "false");
- } else if (e is CharLiteralExpr) {
- wr.Write("'{0}'", (string)e.Value);
- } else if (e is StringLiteralExpr) {
- var str = (StringLiteralExpr)e;
- wr.Write("{0}\"{1}\"", str.IsVerbatim ? "@" : "", (string)e.Value);
- } else if (e.Value is Basetypes.BigDec) {
- Basetypes.BigDec dec = (Basetypes.BigDec)e.Value;
- wr.Write((dec.Mantissa >= 0) ? "" : "-");
- string s = BigInteger.Abs(dec.Mantissa).ToString();
- int digits = s.Length;
- if (dec.Exponent >= 0) {
- wr.Write("{0}{1}.0", s, new string('0', dec.Exponent));
- } else {
- int exp = -dec.Exponent;
- if (exp < digits) {
- int intDigits = digits - exp;
- int fracDigits = digits - intDigits;
- wr.Write("{0}.{1}", s.Substring(0, intDigits), s.Substring(intDigits, fracDigits));
- } else {
- int fracDigits = digits;
- wr.Write("0.{0}{1}", new string('0', exp - fracDigits), s.Substring(0, fracDigits));
- }
- }
- } else {
- wr.Write((BigInteger)e.Value);
- }
-
- } else if (expr is ThisExpr) {
- wr.Write("this");
-
- } else if (expr is IdentifierExpr) {
- wr.Write(((IdentifierExpr)expr).Name);
-
- } else if (expr is DatatypeValue) {
- var dtv = (DatatypeValue)expr;
- bool printParens;
- if (dtv.MemberName == BuiltIns.TupleTypeCtorName) {
- // we're looking at a tuple, whose printed constructor name is essentially the empty string
- printParens = true;
- } else {
- wr.Write("{0}.{1}", dtv.DatatypeName, dtv.MemberName);
- printParens = dtv.Arguments.Count != 0;
- }
- if (printParens) {
- wr.Write("(");
- PrintExpressionList(dtv.Arguments, false);
- wr.Write(")");
- }
-
- } else if (expr is DisplayExpression) {
- DisplayExpression e = (DisplayExpression)expr;
- if (e is MultiSetDisplayExpr) wr.Write("multiset");
- wr.Write(e is SetDisplayExpr || e is MultiSetDisplayExpr ? "{" : "[");
- PrintExpressionList(e.Elements, false);
- wr.Write(e is SetDisplayExpr || e is MultiSetDisplayExpr ? "}" : "]");
-
- } else if (expr is MapDisplayExpr) {
- MapDisplayExpr e = (MapDisplayExpr)expr;
- wr.Write(e.Finite ? "map" : "imap");
- wr.Write("[");
- PrintExpressionPairList(e.Elements);
- wr.Write("]");
-
- } else if (expr is NameSegment) {
- var e = (NameSegment)expr;
- wr.Write(e.Name);
- PrintTypeInstantiation(e.OptTypeArguments);
-
- } else if (expr is ExprDotName) {
- var e = (ExprDotName)expr;
- // determine if parens are needed
- int opBindingStrength = 0x70;
- bool parensNeeded = !(e.Lhs is ImplicitThisExpr) &&
- opBindingStrength < contextBindingStrength ||
- (fragileContext && opBindingStrength == contextBindingStrength);
-
- if (parensNeeded) { wr.Write("("); }
- if (!(e.Lhs is ImplicitThisExpr)) {
- PrintExpr(e.Lhs, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, -1);
- wr.Write(".");
- }
- wr.Write(e.SuffixName);
- PrintTypeInstantiation(e.OptTypeArguments);
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is ApplySuffix) {
- var e = (ApplySuffix)expr;
- // determine if parens are needed
- int opBindingStrength = 0x70;
- bool parensNeeded = !(e.Lhs is ImplicitThisExpr) &&
- opBindingStrength < contextBindingStrength ||
- (fragileContext && opBindingStrength == contextBindingStrength);
-
- if (parensNeeded) { wr.Write("("); }
- if (ParensMayMatter(e.Lhs)) {
- wr.Write("(");
- PrintExpression(e.Lhs, false);
- wr.Write(")");
+ PrintExpr(expr, 0, false, true, isFollowedBySemicolon, -1, keyword);
+ }
+
+ private bool ParensNeeded(int opBindingStrength, int contextBindingStrength, bool fragileContext) {
+ return opBindingStrength < contextBindingStrength ||
+ (fragileContext && opBindingStrength == contextBindingStrength);
+ }
+
+ /// <summary>
+ /// An indent of -1 means print the entire expression on one line.
+ /// </summary>
+ void PrintExpr(Expression expr, int contextBindingStrength, bool fragileContext, bool isRightmost, bool isFollowedBySemicolon, int indent, string keyword = null, int resolv_count = 2 )
+ {
+ Contract.Requires(-1 <= indent);
+ Contract.Requires(expr != null);
+
+ /* When debugging:
+ if (resolv_count > 0 && expr.Resolved != null) {
+ PrintExpr(expr.Resolved, contextBindingStrength, fragileContext, isRightmost, isFollowedBySemicolon, indent, resolv_count - 1);
+ return;
+ }
+ */
+
+ if (expr is StaticReceiverExpr) {
+ StaticReceiverExpr e = (StaticReceiverExpr)expr;
+ wr.Write(e.Type);
+ } else if (expr is LiteralExpr) {
+ LiteralExpr e = (LiteralExpr)expr;
+ if (e.Value == null) {
+ wr.Write("null");
+ } else if (e.Value is bool) {
+ wr.Write((bool)e.Value ? "true" : "false");
+ } else if (e is CharLiteralExpr) {
+ wr.Write("'{0}'", (string)e.Value);
+ } else if (e is StringLiteralExpr) {
+ var str = (StringLiteralExpr)e;
+ wr.Write("{0}\"{1}\"", str.IsVerbatim ? "@" : "", (string)e.Value);
+ } else if (e.Value is Basetypes.BigDec) {
+ Basetypes.BigDec dec = (Basetypes.BigDec)e.Value;
+ wr.Write((dec.Mantissa >= 0) ? "" : "-");
+ string s = BigInteger.Abs(dec.Mantissa).ToString();
+ int digits = s.Length;
+ if (dec.Exponent >= 0) {
+ wr.Write("{0}{1}.0", s, new string('0', dec.Exponent));
+ } else {
+ int exp = -dec.Exponent;
+ if (exp < digits) {
+ int intDigits = digits - exp;
+ int fracDigits = digits - intDigits;
+ wr.Write("{0}.{1}", s.Substring(0, intDigits), s.Substring(intDigits, fracDigits));
+ } else {
+ int fracDigits = digits;
+ wr.Write("0.{0}{1}", new string('0', exp - fracDigits), s.Substring(0, fracDigits));
+ }
+ }
+ } else {
+ wr.Write((BigInteger)e.Value);
+ }
+
+ } else if (expr is ThisExpr) {
+ wr.Write("this");
+
+ } else if (expr is IdentifierExpr) {
+ wr.Write(((IdentifierExpr)expr).Name);
+
+ } else if (expr is DatatypeValue) {
+ var dtv = (DatatypeValue)expr;
+ bool printParens;
+ if (dtv.MemberName == BuiltIns.TupleTypeCtorName) {
+ // we're looking at a tuple, whose printed constructor name is essentially the empty string
+ printParens = true;
+ } else {
+ wr.Write("{0}.{1}", dtv.DatatypeName, dtv.MemberName);
+ printParens = dtv.Arguments.Count != 0;
+ }
+ if (printParens) {
+ wr.Write("(");
+ PrintExpressionList(dtv.Arguments, false);
+ wr.Write(")");
+ }
+
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ if (e is MultiSetDisplayExpr) {
+ wr.Write("multiset");
+ } else if (e is SetDisplayExpr && !((SetDisplayExpr)e).Finite) {
+ wr.Write("iset");
+ }
+ wr.Write(e is SetDisplayExpr || e is MultiSetDisplayExpr ? "{" : "[");
+ PrintExpressionList(e.Elements, false);
+ wr.Write(e is SetDisplayExpr || e is MultiSetDisplayExpr ? "}" : "]");
+
+ } else if (expr is MapDisplayExpr) {
+ MapDisplayExpr e = (MapDisplayExpr)expr;
+ wr.Write(e.Finite ? "map" : "imap");
+ wr.Write("[");
+ PrintExpressionPairList(e.Elements);
+ wr.Write("]");
+
+ } else if (expr is NameSegment) {
+ var e = (NameSegment)expr;
+ wr.Write(e.Name);
+ PrintTypeInstantiation(e.OptTypeArguments);
+
+ } else if (expr is ExprDotName) {
+ var e = (ExprDotName)expr;
+ // determine if parens are needed
+ int opBindingStrength = 0x70;
+ bool parensNeeded = !e.Lhs.IsImplicit && // KRML: I think that this never holds
+ ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext);
+
+ if (parensNeeded) { wr.Write("("); }
+ if (!e.Lhs.IsImplicit) {
+ PrintExpr(e.Lhs, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, -1, keyword);
+ wr.Write(".");
+ }
+ wr.Write(e.SuffixName);
+ PrintTypeInstantiation(e.OptTypeArguments);
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is ApplySuffix) {
+ var e = (ApplySuffix)expr;
+ // determine if parens are needed
+ int opBindingStrength = 0x70;
+ bool parensNeeded = !e.Lhs.IsImplicit && // KRML: I think that this never holds
+ ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext);
+
+ if (parensNeeded) { wr.Write("("); }
+ if (ParensMayMatter(e.Lhs)) {
+ wr.Write("(");
+ PrintExpression(e.Lhs, false);
+ wr.Write(")");
} else {
- PrintExpr(e.Lhs, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, -1);
- }
- wr.Write("(");
- PrintExpressionList(e.Args, false);
- wr.Write(")");
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is MemberSelectExpr) {
- MemberSelectExpr e = (MemberSelectExpr)expr;
- // determine if parens are needed
- int opBindingStrength = 0x70;
- bool parensNeeded = !(e.Obj is ImplicitThisExpr) &&
- opBindingStrength < contextBindingStrength ||
- (fragileContext && opBindingStrength == contextBindingStrength);
-
- if (parensNeeded) { wr.Write("("); }
- if (!(e.Obj is ImplicitThisExpr)) {
- PrintExpr(e.Obj, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, -1);
- wr.Write(".");
- }
- wr.Write(e.MemberName);
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is SeqSelectExpr) {
- SeqSelectExpr e = (SeqSelectExpr)expr;
- // determine if parens are needed
- int opBindingStrength = 0x70;
- bool parensNeeded = opBindingStrength < contextBindingStrength ||
- (fragileContext && opBindingStrength == contextBindingStrength);
-
+ PrintExpr(e.Lhs, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, -1, keyword);
+ }
+ wr.Write("(");
+ PrintExpressionList(e.Args, false);
+ wr.Write(")");
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is MemberSelectExpr) {
+ MemberSelectExpr e = (MemberSelectExpr)expr;
+ // determine if parens are needed
+ int opBindingStrength = 0x70;
+ bool parensNeeded = !e.Obj.IsImplicit &&
+ ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext);
+
+ if (parensNeeded) { wr.Write("("); }
+ if (!(e.Obj.IsImplicit)) {
+ PrintExpr(e.Obj, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, -1, keyword);
+ wr.Write(".");
+ }
+ wr.Write(e.MemberName);
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ // determine if parens are needed
+ int opBindingStrength = 0x70;
+ bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext);
+
if (parensNeeded) { wr.Write("("); }
- PrintExpr(e.Seq, 0x00, false, false, !parensNeeded && isFollowedBySemicolon, indent); // BOGUS: fix me
- wr.Write("[");
- if (e.SelectOne) {
- Contract.Assert( e.E0 != null);
- PrintExpression(e.E0, false);
- } else {
- if (e.E0 != null) {
- PrintExpression(e.E0, false);
- }
- wr.Write(e.E0 != null && e.E1 != null ? " .. " : "..");
- if (e.E1 != null) {
- PrintExpression(e.E1, false);
- }
- }
- wr.Write("]");
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is MultiSelectExpr) {
- MultiSelectExpr e = (MultiSelectExpr)expr;
- // determine if parens are needed
- int opBindingStrength = 0x70;
- bool parensNeeded = opBindingStrength < contextBindingStrength ||
- (fragileContext && opBindingStrength == contextBindingStrength);
-
+ PrintExpr(e.Seq, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, indent, keyword);
+ wr.Write("[");
+ if (e.SelectOne) {
+ Contract.Assert( e.E0 != null);
+ PrintExpression(e.E0, false);
+ } else {
+ if (e.E0 != null) {
+ PrintExpression(e.E0, false);
+ }
+ wr.Write(e.E0 != null && e.E1 != null ? " .. " : "..");
+ if (e.E1 != null) {
+ PrintExpression(e.E1, false);
+ }
+ }
+ wr.Write("]");
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is MultiSelectExpr) {
+ MultiSelectExpr e = (MultiSelectExpr)expr;
+ // determine if parens are needed
+ int opBindingStrength = 0x70;
+ bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext);
+
if (parensNeeded) { wr.Write("("); }
- PrintExpr(e.Array, 0x00, false, false, !parensNeeded && isFollowedBySemicolon, indent); // BOGUS: fix me
- string prefix = "[";
- foreach (Expression idx in e.Indices) {
- Contract.Assert(idx != null);
- wr.Write(prefix);
- PrintExpression(idx, false);
- prefix = ", ";
- }
- wr.Write("]");
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is SeqUpdateExpr) {
- SeqUpdateExpr e = (SeqUpdateExpr)expr;
- if (e.ResolvedUpdateExpr != null)
- {
- PrintExpr(e.ResolvedUpdateExpr, contextBindingStrength, fragileContext, isRightmost, isFollowedBySemicolon, indent);
- }
- else
+ PrintExpr(e.Array, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, indent, keyword);
+ string prefix = "[";
+ foreach (Expression idx in e.Indices) {
+ Contract.Assert(idx != null);
+ wr.Write(prefix);
+ PrintExpression(idx, false);
+ prefix = ", ";
+ }
+ wr.Write("]");
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is SeqUpdateExpr) {
+ SeqUpdateExpr e = (SeqUpdateExpr)expr;
+ if (e.ResolvedUpdateExpr != null)
{
- // determine if parens are needed
- int opBindingStrength = 0x70;
- bool parensNeeded = opBindingStrength < contextBindingStrength ||
- (fragileContext && opBindingStrength == contextBindingStrength);
-
+ PrintExpr(e.ResolvedUpdateExpr, contextBindingStrength, fragileContext, isRightmost, isFollowedBySemicolon, indent, keyword);
+ }
+ else
+ {
+ // determine if parens are needed
+ int opBindingStrength = 0x70;
+ bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext);
+
if (parensNeeded) { wr.Write("("); }
- PrintExpr(e.Seq, 00, false, false, !parensNeeded && isFollowedBySemicolon, indent); // BOGUS: fix me
- wr.Write("[");
- PrintExpression(e.Index, false);
- wr.Write(" := ");
- PrintExpression(e.Value, false);
- wr.Write("]");
- if (parensNeeded) { wr.Write(")"); }
+ PrintExpr(e.Seq, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, indent, keyword);
+ wr.Write("[");
+ PrintExpression(e.Index, false);
+ wr.Write(" := ");
+ PrintExpression(e.Value, false);
+ wr.Write("]");
+ if (parensNeeded) { wr.Write(")"); }
}
- } else if (expr is ApplyExpr) {
- var e = (ApplyExpr)expr;
- // determine if parens are needed
- int opBindingStrength = 0x70;
- bool parensNeeded =
- opBindingStrength < contextBindingStrength ||
- (fragileContext && opBindingStrength == contextBindingStrength);
-
- if (parensNeeded) { wr.Write("("); }
- PrintExpr(e.Function, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, -1);
- wr.Write("(");
- PrintExpressionList(e.Args, false);
- wr.Write(")");
-
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is FunctionCallExpr) {
- var e = (FunctionCallExpr)expr;
+ } else if (expr is DatatypeUpdateExpr) {
+ var e = (DatatypeUpdateExpr)expr;
// determine if parens are needed
int opBindingStrength = 0x70;
- bool parensNeeded = !(e.Receiver is ImplicitThisExpr) &&
- opBindingStrength < contextBindingStrength ||
- (fragileContext && opBindingStrength == contextBindingStrength);
+ bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext);
if (parensNeeded) { wr.Write("("); }
- if (!(e.Receiver is ImplicitThisExpr)) {
- PrintExpr(e.Receiver, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, -1);
- wr.Write(".");
- }
- wr.Write(e.Name);
- /* When debugging, this is nice to have:
- if (e.TypeArgumentSubstitutions.Count > 0) {
- wr.Write("[");
- wr.Write(Util.Comma(",", e.TypeArgumentSubstitutions, kv => kv.Key.FullName() + "->" + kv.Value));
- wr.Write("]");
- }
- */
- if (e.OpenParen == null && e.Args.Count == 0) {
- } else {
- PrintActualArguments(e.Args, e.Name);
+ PrintExpr(e.Root, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, indent, keyword);
+ wr.Write(".(");
+ var sep = "";
+ foreach (var update in e.Updates) {
+ wr.Write("{0}{1} := ", sep, update.Item2);
+ PrintExpression(update.Item3, false);
+ sep = ", ";
}
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is OldExpr) {
- wr.Write("old(");
- PrintExpression(((OldExpr)expr).E, false);
wr.Write(")");
+ if (parensNeeded) { wr.Write(")"); }
- } else if (expr is MultiSetFormingExpr) {
- wr.Write("multiset(");
- PrintExpression(((MultiSetFormingExpr)expr).E, false);
- wr.Write(")");
-
- } else if (expr is UnaryOpExpr) {
- var e = (UnaryOpExpr)expr;
- if (e.Op == UnaryOpExpr.Opcode.Cardinality) {
- wr.Write("|");
- PrintExpression(e.E, false);
- wr.Write("|");
- } else if (e.Op == UnaryOpExpr.Opcode.Fresh) {
- wr.Write("fresh(");
- PrintExpression(e.E, false);
- wr.Write(")");
- } else {
- // Prefix operator.
- // determine if parens are needed
- string op;
- int opBindingStrength;
- switch (e.Op) {
- case UnaryOpExpr.Opcode.Not:
- op = "!"; opBindingStrength = 0x60; break;
- default:
- Contract.Assert(false); throw new cce.UnreachableException(); // unexpected unary opcode
- }
- bool parensNeeded = opBindingStrength < contextBindingStrength ||
- (fragileContext && opBindingStrength == contextBindingStrength);
-
- bool containsNestedNot = e.E is ParensExpression &&
- ((ParensExpression)e.E).E is UnaryExpr &&
- ((UnaryOpExpr)((ParensExpression)e.E).E).Op == UnaryOpExpr.Opcode.Not;
+ } else if (expr is ApplyExpr) {
+ var e = (ApplyExpr)expr;
+ // determine if parens are needed
+ int opBindingStrength = 0x70;
+ bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext);
+
+ if (parensNeeded) { wr.Write("("); }
- if (parensNeeded) { wr.Write("("); }
+ PrintExpr(e.Function, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, -1, keyword);
+ wr.Write("(");
+ PrintExpressionList(e.Args, false);
+ wr.Write(")");
+
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is FunctionCallExpr) {
+ var e = (FunctionCallExpr)expr;
+ // determine if parens are needed
+ int opBindingStrength = 0x70;
+ bool parensNeeded = !(e.Receiver.IsImplicit) &&
+ ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext);
+
+ if (parensNeeded) { wr.Write("("); }
+ if (!e.Receiver.IsImplicit) {
+ PrintExpr(e.Receiver, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, -1, keyword);
+ wr.Write(".");
+ }
+ wr.Write(e.Name);
+ /* When debugging, this is nice to have:
+ if (e.TypeArgumentSubstitutions.Count > 0) {
+ wr.Write("[");
+ wr.Write(Util.Comma(",", e.TypeArgumentSubstitutions, kv => kv.Key.FullName() + "->" + kv.Value));
+ wr.Write("]");
+ }
+ */
+ if (e.OpenParen == null && e.Args.Count == 0) {
+ } else {
+ PrintActualArguments(e.Args, e.Name);
+ }
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is OldExpr) {
+ wr.Write("old(");
+ PrintExpression(((OldExpr)expr).E, false);
+ wr.Write(")");
+
+ } else if (expr is MultiSetFormingExpr) {
+ wr.Write("multiset(");
+ PrintExpression(((MultiSetFormingExpr)expr).E, false);
+ wr.Write(")");
+
+ } else if (expr is UnaryOpExpr) {
+ var e = (UnaryOpExpr)expr;
+ if (e.Op == UnaryOpExpr.Opcode.Cardinality) {
+ wr.Write("|");
+ PrintExpression(e.E, false);
+ wr.Write("|");
+ } else if (e.Op == UnaryOpExpr.Opcode.Fresh) {
+ wr.Write("fresh(");
+ PrintExpression(e.E, false);
+ wr.Write(")");
+ } else {
+ // Prefix operator.
+ // determine if parens are needed
+ string op;
+ int opBindingStrength;
+ switch (e.Op) {
+ case UnaryOpExpr.Opcode.Not:
+ op = "!"; opBindingStrength = 0x60; break;
+ default:
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected unary opcode
+ }
+ bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext);
+
+ bool containsNestedNot = e.E is ParensExpression &&
+ ((ParensExpression)e.E).E is UnaryExpr &&
+ ((UnaryOpExpr)((ParensExpression)e.E).E).Op == UnaryOpExpr.Opcode.Not;
+
+ if (parensNeeded) { wr.Write("("); }
wr.Write(op);
- PrintExpr(e.E, opBindingStrength, containsNestedNot, parensNeeded || isRightmost, !parensNeeded && isFollowedBySemicolon, -1);
- if (parensNeeded) { wr.Write(")"); }
- }
-
- } else if (expr is ConversionExpr) {
- var e = (ConversionExpr)expr;
- PrintType(e.ToType);
- wr.Write("(");
- PrintExpression(e.E, false);
- wr.Write(")");
-
- } else if (expr is BinaryExpr) {
- BinaryExpr e = (BinaryExpr)expr;
- // determine if parens are needed
- int opBindingStrength;
- bool fragileLeftContext = false; // false means "allow same binding power on left without parens"
- bool fragileRightContext = false; // false means "allow same binding power on right without parens"
- switch (e.Op)
- {
- case BinaryExpr.Opcode.Add:
- opBindingStrength = 0x40; break;
- case BinaryExpr.Opcode.Sub:
- opBindingStrength = 0x40; fragileRightContext = true; break;
- case BinaryExpr.Opcode.Mul:
- opBindingStrength = 0x50; break;
- case BinaryExpr.Opcode.Div:
- case BinaryExpr.Opcode.Mod:
- opBindingStrength = 0x50; fragileRightContext = true; break;
- case BinaryExpr.Opcode.Eq:
- case BinaryExpr.Opcode.Neq:
- case BinaryExpr.Opcode.Gt:
- case BinaryExpr.Opcode.Ge:
- case BinaryExpr.Opcode.Lt:
- case BinaryExpr.Opcode.Le:
- case BinaryExpr.Opcode.Disjoint:
- case BinaryExpr.Opcode.In:
- case BinaryExpr.Opcode.NotIn:
- opBindingStrength = 0x30; fragileLeftContext = fragileRightContext = true; break;
- case BinaryExpr.Opcode.And:
- opBindingStrength = 0x20; break;
- case BinaryExpr.Opcode.Or:
- opBindingStrength = 0x21; break;
- case BinaryExpr.Opcode.Imp:
- opBindingStrength = 0x10; fragileLeftContext = true; break;
- case BinaryExpr.Opcode.Exp:
- opBindingStrength = 0x11; fragileRightContext = true; break;
- case BinaryExpr.Opcode.Iff:
- opBindingStrength = 0x08; break;
- default:
- Contract.Assert(false); throw new cce.UnreachableException(); // unexpected binary operator
- }
- int opBS = opBindingStrength & 0xF8;
- int ctxtBS = contextBindingStrength & 0xF8;
- bool parensNeeded = opBS < ctxtBS ||
- (opBS == ctxtBS && (opBindingStrength != contextBindingStrength || fragileContext));
-
- string op = BinaryExpr.OpcodeString(e.Op);
- if (parensNeeded) { wr.Write("("); }
- var sem = !parensNeeded && isFollowedBySemicolon;
+ PrintExpr(e.E, opBindingStrength, containsNestedNot, parensNeeded || isRightmost, !parensNeeded && isFollowedBySemicolon, -1, keyword);
+ if (parensNeeded) { wr.Write(")"); }
+ }
+
+ } else if (expr is ConversionExpr) {
+ var e = (ConversionExpr)expr;
+ PrintType(e.ToType);
+ wr.Write("(");
+ PrintExpression(e.E, false);
+ wr.Write(")");
+
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ // determine if parens are needed
+ int opBindingStrength;
+ bool fragileLeftContext = false; // false means "allow same binding power on left without parens"
+ bool fragileRightContext = false; // false means "allow same binding power on right without parens"
+ switch (e.Op)
+ {
+ case BinaryExpr.Opcode.Add:
+ opBindingStrength = 0x40; break;
+ case BinaryExpr.Opcode.Sub:
+ opBindingStrength = 0x40; fragileRightContext = true; break;
+ case BinaryExpr.Opcode.Mul:
+ opBindingStrength = 0x50; break;
+ case BinaryExpr.Opcode.Div:
+ case BinaryExpr.Opcode.Mod:
+ opBindingStrength = 0x50; fragileRightContext = true; break;
+ case BinaryExpr.Opcode.Eq:
+ case BinaryExpr.Opcode.Neq:
+ case BinaryExpr.Opcode.Gt:
+ case BinaryExpr.Opcode.Ge:
+ case BinaryExpr.Opcode.Lt:
+ case BinaryExpr.Opcode.Le:
+ case BinaryExpr.Opcode.Disjoint:
+ case BinaryExpr.Opcode.In:
+ case BinaryExpr.Opcode.NotIn:
+ opBindingStrength = 0x30; fragileLeftContext = fragileRightContext = true; break;
+ case BinaryExpr.Opcode.And:
+ opBindingStrength = 0x20; break;
+ case BinaryExpr.Opcode.Or:
+ opBindingStrength = 0x21; break;
+ case BinaryExpr.Opcode.Imp:
+ opBindingStrength = 0x10; fragileLeftContext = true; break;
+ case BinaryExpr.Opcode.Exp:
+ opBindingStrength = 0x11; fragileRightContext = true; break;
+ case BinaryExpr.Opcode.Iff:
+ opBindingStrength = 0x08; break;
+ default:
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected binary operator
+ }
+ int opBS = opBindingStrength & 0xF8;
+ int ctxtBS = contextBindingStrength & 0xF8;
+ bool parensNeeded = opBS < ctxtBS ||
+ (opBS == ctxtBS && (opBindingStrength != contextBindingStrength || fragileContext));
+
+ string op = BinaryExpr.OpcodeString(e.Op);
+ if (parensNeeded) { wr.Write("("); }
+ var sem = !parensNeeded && isFollowedBySemicolon;
if (0 <= indent && e.Op == BinaryExpr.Opcode.And) {
- PrintExpr(e.E0, opBindingStrength, fragileLeftContext, false, sem, indent);
- wr.WriteLine(" {0}", op);
+ PrintExpr(e.E0, opBindingStrength, fragileLeftContext, false, sem, indent, keyword);
+ wr.WriteLine(" {0}", op);
Indent(indent);
- PrintExpr(e.E1, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, sem, indent);
+ PrintExpr(e.E1, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, sem, indent, keyword);
} else if (0 <= indent && e.Op == BinaryExpr.Opcode.Imp) {
- PrintExpr(e.E0, opBindingStrength, fragileLeftContext, false, sem, indent);
- wr.WriteLine(" {0}", op);
- int ind = indent + IndentAmount;
+ PrintExpr(e.E0, opBindingStrength, fragileLeftContext, false, sem, indent, keyword);
+ wr.WriteLine(" {0}", op);
+ int ind = indent + IndentAmount;
Indent(ind);
- PrintExpr(e.E1, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, sem, ind);
+ PrintExpr(e.E1, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, sem, ind, keyword);
} else if (0 <= indent && e.Op == BinaryExpr.Opcode.Exp) {
- PrintExpr(e.E1, opBindingStrength, fragileLeftContext, false, sem, indent);
- wr.WriteLine(" {0}", op);
- int ind = indent + IndentAmount;
+ PrintExpr(e.E1, opBindingStrength, fragileLeftContext, false, sem, indent, keyword);
+ wr.WriteLine(" {0}", op);
+ int ind = indent + IndentAmount;
Indent(ind);
- PrintExpr(e.E0, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, sem, ind);
- } else if (e.Op == BinaryExpr.Opcode.Exp) {
- PrintExpr(e.E1, opBindingStrength, fragileLeftContext, false, sem, -1);
+ PrintExpr(e.E0, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, sem, ind, keyword);
+ } else if (e.Op == BinaryExpr.Opcode.Exp) {
+ PrintExpr(e.E1, opBindingStrength, fragileLeftContext, false, sem, -1, keyword);
wr.Write(" {0} ", op);
- PrintExpr(e.E0, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, sem, -1);
+ PrintExpr(e.E0, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, sem, -1, keyword);
} else {
- PrintExpr(e.E0, opBindingStrength, fragileLeftContext, false, sem, -1);
+ PrintExpr(e.E0, opBindingStrength, fragileLeftContext, false, sem, -1, keyword);
wr.Write(" {0} ", op);
- PrintExpr(e.E1, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, sem, -1);
- }
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is TernaryExpr) {
- var e = (TernaryExpr)expr;
- switch (e.Op) {
- case TernaryExpr.Opcode.PrefixEqOp:
- case TernaryExpr.Opcode.PrefixNeqOp:
- var opBindingStrength = 0x30;
- var fragileLeftContext = true;
- var fragileRightContext = true;
-
- int opBS = opBindingStrength & 0xF8;
- int ctxtBS = contextBindingStrength & 0xF8;
- bool parensNeeded = opBS < ctxtBS ||
- (opBS == ctxtBS && (opBindingStrength != contextBindingStrength || fragileContext));
-
- if (parensNeeded) { wr.Write("("); }
+ PrintExpr(e.E1, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, sem, -1, keyword);
+ }
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is TernaryExpr) {
+ var e = (TernaryExpr)expr;
+ switch (e.Op) {
+ case TernaryExpr.Opcode.PrefixEqOp:
+ case TernaryExpr.Opcode.PrefixNeqOp:
+ var opBindingStrength = 0x30;
+ var fragileLeftContext = true;
+ var fragileRightContext = true;
+
+ int opBS = opBindingStrength & 0xF8;
+ int ctxtBS = contextBindingStrength & 0xF8;
+ bool parensNeeded = opBS < ctxtBS ||
+ (opBS == ctxtBS && (opBindingStrength != contextBindingStrength || fragileContext));
+
+ if (parensNeeded) { wr.Write("("); }
var sem = !parensNeeded && isFollowedBySemicolon;
- PrintExpr(e.E1, opBindingStrength, fragileLeftContext, false, sem, -1);
- wr.Write(" {0}#[", e.Op == TernaryExpr.Opcode.PrefixEqOp ? "==" : "!=");
- PrintExpression(e.E0, false);
+ PrintExpr(e.E1, opBindingStrength, fragileLeftContext, false, sem, -1, keyword);
+ wr.Write(" {0}#[", e.Op == TernaryExpr.Opcode.PrefixEqOp ? "==" : "!=");
+ PrintExpression(e.E0, false);
wr.Write("] ");
- PrintExpr(e.E2, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, sem, -1);
- if (parensNeeded) { wr.Write(")"); }
- break;
- default:
- Contract.Assert(false); // unexpected ternary operator
- break;
- }
-
- } else if (expr is ChainingExpression) {
- var e = (ChainingExpression)expr;
- // determine if parens are needed
- int opBindingStrength = 0x30;
- int opBS = opBindingStrength & 0xF8;
- int ctxtBS = contextBindingStrength & 0xF8;
- bool parensNeeded = opBS < ctxtBS ||
- (opBS == ctxtBS && (opBindingStrength != contextBindingStrength || fragileContext));
-
- if (parensNeeded) { wr.Write("("); }
+ PrintExpr(e.E2, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, sem, -1, keyword);
+ if (parensNeeded) { wr.Write(")"); }
+ break;
+ default:
+ Contract.Assert(false); // unexpected ternary operator
+ break;
+ }
+
+ } else if (expr is ChainingExpression) {
+ var e = (ChainingExpression)expr;
+ // determine if parens are needed
+ int opBindingStrength = 0x30;
+ int opBS = opBindingStrength & 0xF8;
+ int ctxtBS = contextBindingStrength & 0xF8;
+ bool parensNeeded = opBS < ctxtBS ||
+ (opBS == ctxtBS && (opBindingStrength != contextBindingStrength || fragileContext));
+
+ if (parensNeeded) { wr.Write("("); }
var sem = !parensNeeded && isFollowedBySemicolon;
- PrintExpr(e.Operands[0], opBindingStrength, true, false, sem, -1);
- for (int i = 0; i < e.Operators.Count; i++) {
- string op = BinaryExpr.OpcodeString(e.Operators[i]);
- if (e.PrefixLimits[i] == null) {
- wr.Write(" {0} ", op);
- } else {
- wr.Write(" {0}#[", op);
- PrintExpression(e.PrefixLimits[i], false);
- wr.Write("] ");
+ PrintExpr(e.Operands[0], opBindingStrength, true, false, sem, -1, keyword);
+ for (int i = 0; i < e.Operators.Count; i++) {
+ string op = BinaryExpr.OpcodeString(e.Operators[i]);
+ if (e.PrefixLimits[i] == null) {
+ wr.Write(" {0} ", op);
+ } else {
+ wr.Write(" {0}#[", op);
+ PrintExpression(e.PrefixLimits[i], false);
+ wr.Write("] ");
}
- PrintExpr(e.Operands[i+1], opBindingStrength, true, i == e.Operators.Count - 1 && (parensNeeded || isRightmost), sem, -1);
- }
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is LetExpr) {
- var e = (LetExpr)expr;
- bool parensNeeded = !isRightmost;
- if (parensNeeded) { wr.Write("("); }
- wr.Write("var ");
- string sep = "";
- foreach (var lhs in e.LHSs) {
- wr.Write(sep);
- PrintCasePattern(lhs);
- sep = ", ";
- }
- if (e.Exact) {
- wr.Write(" := ");
- } else {
- wr.Write(" :| ");
- }
- PrintExpressionList(e.RHSs, true);
- wr.Write("; ");
- PrintExpression(e.Body, !parensNeeded && isFollowedBySemicolon);
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is QuantifierExpr) {
- QuantifierExpr e = (QuantifierExpr)expr;
- bool parensNeeded = !isRightmost;
- if (parensNeeded) { wr.Write("("); }
- wr.Write(e is ForallExpr ? "forall" : "exists");
- PrintTypeParams(e.TypeArgs); // new!
- wr.Write(" ");
+ PrintExpr(e.Operands[i + 1], opBindingStrength, true, i == e.Operators.Count - 1 && (parensNeeded || isRightmost), sem, -1, keyword);
+ }
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ bool parensNeeded = !isRightmost;
+ if (parensNeeded) { wr.Write("("); }
+ wr.Write("var ");
+ string sep = "";
+ foreach (var lhs in e.LHSs) {
+ wr.Write(sep);
+ PrintCasePattern(lhs);
+ sep = ", ";
+ }
+ if (e.Exact) {
+ wr.Write(" := ");
+ } else {
+ wr.Write(" :| ");
+ }
+ PrintExpressionList(e.RHSs, true);
+ wr.Write("; ");
+ PrintExpression(e.Body, !parensNeeded && isFollowedBySemicolon);
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is QuantifierExpr) {
+ QuantifierExpr e = (QuantifierExpr)expr;
+
+ if (DafnyOptions.O.DafnyPrintResolvedFile != null && e.SplitQuantifier != null) {
+ PrintExpr(e.SplitQuantifierExpression, contextBindingStrength, fragileContext, isRightmost, isFollowedBySemicolon, indent, keyword, resolv_count);
+ return;
+ }
+
+ bool parensNeeded = !isRightmost;
+ if (parensNeeded) { wr.Write("("); }
+ wr.Write(e is ForallExpr ? "forall" : "exists");
+ PrintTypeParams(e.TypeArgs); // new!
+ wr.Write(" ");
PrintQuantifierDomain(e.BoundVars, e.Attributes, e.Range);
- wr.Write(" :: ");
- if (0 <= indent) {
- int ind = indent + IndentAmount;
- wr.WriteLine();
- Indent(ind);
- PrintExpression(e.Term, !parensNeeded && isFollowedBySemicolon, ind);
- } else {
- PrintExpression(e.Term, !parensNeeded && isFollowedBySemicolon);
- }
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is NamedExpr) {
- var e = (NamedExpr)expr;
- wr.Write("expr {0}: ", e.Name);
- PrintExpression(e.Body, isFollowedBySemicolon);
-
- } else if (expr is SetComprehension) {
- var e = (SetComprehension)expr;
- bool parensNeeded = !isRightmost;
- if (parensNeeded) { wr.Write("("); }
- wr.Write("set ");
- string sep = "";
- foreach (BoundVar bv in e.BoundVars) {
- wr.Write("{0}{1}", sep, bv.DisplayName);
- sep = ", ";
- PrintType(": ", bv.Type);
- }
- PrintAttributes(e.Attributes);
- wr.Write(" | ");
- PrintExpression(e.Range, !parensNeeded && isFollowedBySemicolon);
- if (!e.TermIsImplicit) {
- wr.Write(" :: ");
- PrintExpression(e.Term, !parensNeeded && isFollowedBySemicolon);
- }
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is MapComprehension) {
- var e = (MapComprehension)expr;
- bool parensNeeded = !isRightmost;
- if (parensNeeded) { wr.Write("("); }
- wr.Write(e.Finite ? "map " : "imap ");
- string sep = "";
- foreach (BoundVar bv in e.BoundVars) {
- wr.Write("{0}{1}", sep, bv.DisplayName);
- sep = ", ";
- PrintType(": ", bv.Type);
- }
- PrintAttributes(e.Attributes);
- wr.Write(" | ");
- PrintExpression(e.Range, !parensNeeded && isFollowedBySemicolon);
- wr.Write(" :: ");
- PrintExpression(e.Term, !parensNeeded && isFollowedBySemicolon);
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is LambdaExpr) {
- var e = (LambdaExpr)expr;
- wr.Write("(");
- wr.Write(Util.Comma(e.BoundVars, bv => bv.DisplayName + ":" + bv.Type));
- wr.Write(")");
- if (e.Range != null) {
- wr.Write(" requires ");
- PrintExpression(e.Range, false);
- }
- foreach (var read in e.Reads) {
- wr.Write(" reads ");
- PrintExpression(read.E, false);
- }
- wr.Write(e.OneShot ? " -> " : " => ");
- PrintExpression(e.Body, isFollowedBySemicolon);
-
- } else if (expr is WildcardExpr) {
- wr.Write("*");
-
- } else if (expr is StmtExpr) {
- var e = (StmtExpr)expr;
- bool parensNeeded;
- if (e.S is AssertStmt || e.S is AssumeStmt || e.S is CalcStmt) {
- parensNeeded = !isRightmost;
- } else {
- parensNeeded = !isRightmost || isFollowedBySemicolon;
- }
- if (parensNeeded) { wr.Write("("); }
- int ind = indent < 0 ? IndentAmount : indent; // if the expression was to be printed on one line, instead print the .S part at indentation IndentAmount (not pretty, but something)
- PrintStatement(e.S, ind);
- wr.Write(" ");
- PrintExpression(e.E, !parensNeeded && isFollowedBySemicolon);
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is ITEExpr) {
- ITEExpr ite = (ITEExpr)expr;
- bool parensNeeded = !isRightmost;
- if (parensNeeded) { wr.Write("("); }
- wr.Write("if ");
- PrintExpression(ite.Test, false);
- wr.Write(" then ");
- PrintExpression(ite.Thn, false);
- wr.Write(" else ");
- PrintExpression(ite.Els, !parensNeeded && isFollowedBySemicolon);
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is ParensExpression) {
- var e = (ParensExpression)expr;
+ string s = keyword ?? " :: ";
+ wr.Write("{0}", s);
+ if (0 <= indent) {
+ int ind = indent + IndentAmount;
+ wr.WriteLine();
+ Indent(ind);
+ PrintExpression(e.Term, !parensNeeded && isFollowedBySemicolon, ind);
+ } else {
+ PrintExpression(e.Term, !parensNeeded && isFollowedBySemicolon);
+ }
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is NamedExpr) {
+ var e = (NamedExpr)expr;
+ wr.Write("expr {0}: ", e.Name);
+ PrintExpression(e.Body, isFollowedBySemicolon);
+
+ } else if (expr is SetComprehension) {
+ var e = (SetComprehension)expr;
+ bool parensNeeded = !isRightmost;
+ if (parensNeeded) { wr.Write("("); }
+ wr.Write("set ");
+ string sep = "";
+ foreach (BoundVar bv in e.BoundVars) {
+ wr.Write("{0}{1}", sep, bv.DisplayName);
+ sep = ", ";
+ PrintType(": ", bv.Type);
+ }
+ PrintAttributes(e.Attributes);
+ wr.Write(" | ");
+ PrintExpression(e.Range, !parensNeeded && isFollowedBySemicolon);
+ if (!e.TermIsImplicit) {
+ wr.Write(" :: ");
+ PrintExpression(e.Term, !parensNeeded && isFollowedBySemicolon);
+ }
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is MapComprehension) {
+ var e = (MapComprehension)expr;
+ bool parensNeeded = !isRightmost;
+ if (parensNeeded) { wr.Write("("); }
+ wr.Write(e.Finite ? "map " : "imap ");
+ string sep = "";
+ foreach (BoundVar bv in e.BoundVars) {
+ wr.Write("{0}{1}", sep, bv.DisplayName);
+ sep = ", ";
+ PrintType(": ", bv.Type);
+ }
+ PrintAttributes(e.Attributes);
+ wr.Write(" | ");
+ PrintExpression(e.Range, !parensNeeded && isFollowedBySemicolon);
+ wr.Write(" :: ");
+ PrintExpression(e.Term, !parensNeeded && isFollowedBySemicolon);
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is LambdaExpr) {
+ var e = (LambdaExpr)expr;
+ bool parensNeeded = !isRightmost;
+ if (parensNeeded) { wr.Write("("); }
+ var skipSignatureParens = e.BoundVars.Count == 1 && e.BoundVars[0].Type is InferredTypeProxy;
+ if (!skipSignatureParens) { wr.Write("("); }
+ wr.Write(Util.Comma(", ", e.BoundVars, bv => bv.DisplayName + (bv.Type is InferredTypeProxy ? "" : ": " + bv.Type)));
+ if (!skipSignatureParens) { wr.Write(")"); }
+ if (e.Range != null) {
+ wr.Write(" requires ");
+ PrintExpression(e.Range, false);
+ }
+ foreach (var read in e.Reads) {
+ wr.Write(" reads ");
+ PrintExpression(read.E, false);
+ }
+ wr.Write(e.OneShot ? " -> " : " => ");
+ PrintExpression(e.Body, isFollowedBySemicolon);
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is WildcardExpr) {
+ wr.Write("*");
+
+ } else if (expr is StmtExpr) {
+ var e = (StmtExpr)expr;
+ bool parensNeeded;
+ if (e.S is AssertStmt || e.S is AssumeStmt || e.S is CalcStmt) {
+ parensNeeded = !isRightmost;
+ } else {
+ parensNeeded = !isRightmost || isFollowedBySemicolon;
+ }
+ if (parensNeeded) { wr.Write("("); }
+ int ind = indent < 0 ? IndentAmount : indent; // if the expression was to be printed on one line, instead print the .S part at indentation IndentAmount (not pretty, but something)
+ PrintStatement(e.S, ind);
+ wr.Write(" ");
+ PrintExpression(e.E, !parensNeeded && isFollowedBySemicolon);
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is ITEExpr) {
+ ITEExpr ite = (ITEExpr)expr;
+ bool parensNeeded = !isRightmost;
+ if (parensNeeded) { wr.Write("("); }
+ wr.Write("if ");
+ PrintExpression(ite.Test, false);
+ wr.Write(" then ");
+ PrintExpression(ite.Thn, false);
+ wr.Write(" else ");
+ PrintExpression(ite.Els, !parensNeeded && isFollowedBySemicolon);
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is ParensExpression) {
+ var e = (ParensExpression)expr;
// printing of parentheses is done optimally, not according to the parentheses in the given program
- PrintExpr(e.E, contextBindingStrength, fragileContext, isRightmost, isFollowedBySemicolon, indent);
-
- } else if (expr is NegationExpression) {
- var e = (NegationExpression)expr;
- string op = "-";
- int opBindingStrength = 0x60;
- bool parensNeeded = opBindingStrength < contextBindingStrength ||
- (fragileContext && opBindingStrength == contextBindingStrength);
-
- bool containsNestedNegation = e.E is ParensExpression && ((ParensExpression)e.E).E is NegationExpression;
-
- if (parensNeeded) { wr.Write("("); }
+ PrintExpr(e.E, contextBindingStrength, fragileContext, isRightmost, isFollowedBySemicolon, indent, keyword);
+
+ } else if (expr is NegationExpression) {
+ var e = (NegationExpression)expr;
+ string op = "-";
+ int opBindingStrength = 0x60;
+ bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext);
+
+ bool containsNestedNegation = e.E is ParensExpression && ((ParensExpression)e.E).E is NegationExpression;
+
+ if (parensNeeded) { wr.Write("("); }
wr.Write(op);
- PrintExpr(e.E, opBindingStrength, containsNestedNegation, parensNeeded || isRightmost, !parensNeeded && isFollowedBySemicolon, -1);
- if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is MatchExpr) {
- var e = (MatchExpr)expr;
- var parensNeeded = !isRightmost && !e.UsesOptionalBraces;
- if (parensNeeded) { wr.Write("("); }
- wr.Write("match ");
- PrintExpression(e.Source, isRightmost && e.Cases.Count == 0, !parensNeeded && isFollowedBySemicolon);
- if (e.UsesOptionalBraces) { wr.Write(" {"); }
- int i = 0;
- foreach (var mc in e.Cases) {
- bool isLastCase = i == e.Cases.Count - 1;
- wr.Write(" case {0}", mc.Id);
- if (mc.Arguments.Count != 0) {
- string sep = "(";
- foreach (BoundVar bv in mc.Arguments) {
- wr.Write("{0}{1}", sep, bv.DisplayName);
- sep = ", ";
- }
- wr.Write(")");
- }
- wr.Write(" => ");
- PrintExpression(mc.Body, isRightmost && isLastCase, !parensNeeded && isFollowedBySemicolon);
- i++;
- }
- if (e.UsesOptionalBraces) { wr.Write(" }"); }
- else if (parensNeeded) { wr.Write(")"); }
-
- } else if (expr is BoxingCastExpr) {
- // this is not expected for a parsed program, but we may be called for /trace purposes in the translator
+ PrintExpr(e.E, opBindingStrength, containsNestedNegation, parensNeeded || isRightmost, !parensNeeded && isFollowedBySemicolon, -1, keyword);
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is MatchExpr) {
+ var e = (MatchExpr)expr;
+ var parensNeeded = !isRightmost && !e.UsesOptionalBraces;
+ if (parensNeeded) { wr.Write("("); }
+ wr.Write("match ");
+ PrintExpression(e.Source, isRightmost && e.Cases.Count == 0, !parensNeeded && isFollowedBySemicolon);
+ if (e.UsesOptionalBraces) { wr.Write(" {"); }
+ int i = 0;
+ foreach (var mc in e.Cases) {
+ bool isLastCase = i == e.Cases.Count - 1;
+ wr.Write(" case {0}", mc.Id);
+ PrintMatchCaseArgument(mc);
+ wr.Write(" => ");
+ PrintExpression(mc.Body, isRightmost && isLastCase, !parensNeeded && isFollowedBySemicolon);
+ i++;
+ }
+ if (e.UsesOptionalBraces) { wr.Write(" }"); }
+ else if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is BoxingCastExpr) {
+ // this is not expected for a parsed program, but we may be called for /trace purposes in the translator
var e = (BoxingCastExpr)expr;
- PrintExpr(e.E, contextBindingStrength, fragileContext, isRightmost, isFollowedBySemicolon, indent);
- } else if (expr is Translator.BoogieWrapper) {
+ PrintExpr(e.E, contextBindingStrength, fragileContext, isRightmost, isFollowedBySemicolon, indent, keyword);
+ } else if (expr is Translator.BoogieWrapper) {
wr.Write("[BoogieWrapper]"); // this is somewhat unexpected, but we can get here if the /trace switch is used, so it seems best to cover this case here
+ } else if (expr is Translator.BoogieFunctionCall) {
+ wr.Write("[BoogieFunctionCall]"); // this prevents debugger watch window crash
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
- }
- }
-
- bool ParensMayMatter(Expression expr) {
- Contract.Requires(expr != null);
- int parenPairs = 0;
- for (; expr is ParensExpression; parenPairs++) {
- expr = ((ParensExpression)expr).E;
- }
- // If the program were resolved, we could be more precise than the following (in particular, looking
- // to see if expr denotes a MemberSelectExpr of a member that is a Function.
- return parenPairs != 0 && (expr is NameSegment || expr is ExprDotName);
- }
-
- void PrintCasePattern(CasePattern pat) {
- Contract.Requires(pat != null);
- var v = pat.Var;
- if (v != null) {
- wr.Write(v.DisplayName);
- if (v.Type is NonProxyType || DafnyOptions.O.DafnyPrintResolvedFile != null) {
- PrintType(": ", v.Type);
- }
- } else {
- wr.Write(pat.Id);
- if (pat.Arguments != null) {
- wr.Write("(");
- var sep = "";
- foreach (var arg in pat.Arguments) {
- wr.Write(sep);
- PrintCasePattern(arg);
- sep = ", ";
- }
- wr.Write(")");
- }
- }
- }
-
- private void PrintQuantifierDomain(List<BoundVar> boundVars, Attributes attrs, Expression range) {
- Contract.Requires(boundVars != null);
- string sep = "";
- foreach (BoundVar bv in boundVars) {
- wr.Write("{0}{1}", sep, bv.DisplayName);
- PrintType(": ", bv.Type);
- sep = ", ";
- }
- PrintAttributes(attrs);
- if (range != null) {
- wr.Write(" | ");
- PrintExpression(range, false);
- }
- }
-
- void PrintActualArguments(List<Expression> args, string name) {
- Contract.Requires(args != null);
- Contract.Requires(name != null);
- if (name.EndsWith("#")) {
- wr.Write("[");
- PrintExpression(args[0], false);
- wr.Write("]");
- args = new List<Expression>(args.Skip(1));
- }
- wr.Write("(");
- PrintExpressionList(args, false);
- wr.Write(")");
- }
-
- void PrintExpressionList(List<Expression> exprs, bool isFollowedBySemicolon) {
- Contract.Requires(exprs != null);
- string sep = "";
- foreach (Expression e in exprs) {
- Contract.Assert(e != null);
- wr.Write(sep);
- sep = ", ";
- PrintExpression(e, isFollowedBySemicolon);
- }
- }
- void PrintExpressionPairList(List<ExpressionPair> exprs) {
- Contract.Requires(exprs != null);
- string sep = "";
- foreach (ExpressionPair p in exprs) {
- Contract.Assert(p != null);
- wr.Write(sep);
- sep = ", ";
- PrintExpression(p.A, false);
- wr.Write(":=");
- PrintExpression(p.B, false);
- }
- }
-
- void PrintFrameExpressionList(List<FrameExpression/*!*/>/*!*/ fexprs) {
- Contract.Requires(fexprs != null);
- string sep = "";
- foreach (FrameExpression fe in fexprs) {
- Contract.Assert(fe != null);
- wr.Write(sep);
- sep = ", ";
- PrintExpression(fe.E, true);
- if (fe.FieldName != null) {
- wr.Write("`{0}", fe.FieldName);
- }
- }
- }
- }
-}
+ }
+ }
+
+ bool ParensMayMatter(Expression expr) {
+ Contract.Requires(expr != null);
+ int parenPairs = 0;
+ for (; expr is ParensExpression; parenPairs++) {
+ expr = ((ParensExpression)expr).E;
+ }
+ // If the program were resolved, we could be more precise than the following (in particular, looking
+ // to see if expr denotes a MemberSelectExpr of a member that is a Function.
+ return parenPairs != 0 && (expr is NameSegment || expr is ExprDotName);
+ }
+
+ void PrintCasePattern(CasePattern pat) {
+ Contract.Requires(pat != null);
+ var v = pat.Var;
+ if (v != null) {
+ wr.Write(v.DisplayName);
+ if (v.Type is NonProxyType || DafnyOptions.O.DafnyPrintResolvedFile != null) {
+ PrintType(": ", v.Type);
+ }
+ } else {
+ wr.Write(pat.Id);
+ if (pat.Arguments != null) {
+ wr.Write("(");
+ var sep = "";
+ foreach (var arg in pat.Arguments) {
+ wr.Write(sep);
+ PrintCasePattern(arg);
+ sep = ", ";
+ }
+ wr.Write(")");
+ }
+ }
+ }
+
+ private void PrintQuantifierDomain(List<BoundVar> boundVars, Attributes attrs, Expression range) {
+ Contract.Requires(boundVars != null);
+ string sep = "";
+ foreach (BoundVar bv in boundVars) {
+ wr.Write("{0}{1}", sep, bv.DisplayName);
+ PrintType(": ", bv.Type);
+ sep = ", ";
+ }
+ PrintAttributes(attrs);
+ if (range != null) {
+ wr.Write(" | ");
+ PrintExpression(range, false);
+ }
+ }
+
+ void PrintActualArguments(List<Expression> args, string name) {
+ Contract.Requires(args != null);
+ Contract.Requires(name != null);
+ if (name.EndsWith("#")) {
+ wr.Write("[");
+ PrintExpression(args[0], false);
+ wr.Write("]");
+ args = new List<Expression>(args.Skip(1));
+ }
+ wr.Write("(");
+ PrintExpressionList(args, false);
+ wr.Write(")");
+ }
+
+ void PrintExpressionList(List<Expression> exprs, bool isFollowedBySemicolon) {
+ Contract.Requires(exprs != null);
+ string sep = "";
+ foreach (Expression e in exprs) {
+ Contract.Assert(e != null);
+ wr.Write(sep);
+ sep = ", ";
+ PrintExpression(e, isFollowedBySemicolon);
+ }
+ }
+ void PrintExpressionPairList(List<ExpressionPair> exprs) {
+ Contract.Requires(exprs != null);
+ string sep = "";
+ foreach (ExpressionPair p in exprs) {
+ Contract.Assert(p != null);
+ wr.Write(sep);
+ sep = ", ";
+ PrintExpression(p.A, false);
+ wr.Write(":=");
+ PrintExpression(p.B, false);
+ }
+ }
+
+ void PrintFrameExpressionList(List<FrameExpression/*!*/>/*!*/ fexprs) {
+ Contract.Requires(fexprs != null);
+ string sep = "";
+ foreach (FrameExpression fe in fexprs) {
+ Contract.Assert(fe != null);
+ wr.Write(sep);
+ sep = ", ";
+ PrintExpression(fe.E, true);
+ if (fe.FieldName != null) {
+ wr.Write("`{0}", fe.FieldName);
+ }
+ }
+ }
+ }
+}
diff --git a/Source/Dafny/RefinementTransformer.cs b/Source/Dafny/RefinementTransformer.cs
index d819652d..a243ad53 100644
--- a/Source/Dafny/RefinementTransformer.cs
+++ b/Source/Dafny/RefinementTransformer.cs
@@ -52,16 +52,19 @@ namespace Microsoft.Dafny
public class RefinementTransformer : IRewriter
{
- ResolutionErrorReporter reporter;
- Action<AdditionalInformation> additionalInformationReporter;
Cloner rawCloner; // This cloner just gives exactly the same thing back.
RefinementCloner refinementCloner; // This cloner wraps things in a RefinementTransformer
+
Program program;
- public RefinementTransformer(ResolutionErrorReporter reporter, Action<AdditionalInformation> additionalInformationReporter, Program p) {
- Contract.Requires(reporter != null);
- this.reporter = reporter;
- this.additionalInformationReporter = additionalInformationReporter;
+
+ public RefinementTransformer(ErrorReporter reporter)
+ : base(reporter) {
rawCloner = new Cloner();
+ }
+
+ public RefinementTransformer(Program p)
+ : this(p.reporter) {
+ Contract.Requires(p != null);
program = p;
}
@@ -71,27 +74,47 @@ namespace Microsoft.Dafny
private Method currentMethod;
public ModuleSignature RefinedSig; // the intention is to use this field only after a successful PreResolve
- void ReportAdditionalInformation(IToken token, string text, int length)
- {
- Contract.Requires(token != null);
- Contract.Requires(text != null);
- Contract.Requires(0 <= length);
- if (additionalInformationReporter != null) {
- additionalInformationReporter(new AdditionalInformation { Token = token, Text = text, Length = length });
- }
- }
-
- public void PreResolve(ModuleDefinition m) {
+ internal override void PreResolve(ModuleDefinition m) {
if (m.RefinementBaseRoot != null) {
if (Resolver.ResolvePath(m.RefinementBaseRoot, m.RefinementBaseName, out RefinedSig, reporter)) {
if (RefinedSig.ModuleDef != null) {
m.RefinementBase = RefinedSig.ModuleDef;
+ if (m.IsExclusiveRefinement) {
+ if (null == m.RefinementBase.ExclusiveRefinement) {
+ m.RefinementBase.ExclusiveRefinement = m;
+ } else {
+ reporter.Error(MessageSource.RefinementTransformer,
+ m.tok,
+ "no more than one exclusive refinement may exist for a given module.");
+ }
+ }
+ // check that the openess in the imports between refinement and its base matches
+ List<TopLevelDecl> declarations = m.TopLevelDecls;
+ List<TopLevelDecl> baseDeclarations = m.RefinementBase.TopLevelDecls;
+ foreach (var im in declarations) {
+ if (im is ModuleDecl) {
+ ModuleDecl mdecl = (ModuleDecl)im;
+ //find the matching import from the base
+ foreach (var bim in baseDeclarations) {
+ if (bim is ModuleDecl && ((ModuleDecl)bim).Name.Equals(mdecl.Name)) {
+ if (mdecl.Opened != ((ModuleDecl)bim).Opened) {
+ string message = mdecl.Opened ?
+ "{0} in {1} cannot be imported with \"opened\" because it does not match the corresponding import in the refinement base {2} " :
+ "{0} in {1} must be imported with \"opened\" to match the corresponding import in its refinement base {2}.";
+ reporter.Error(MessageSource.RefinementTransformer,m.tok, message, im.Name, m.Name, m.RefinementBase.Name);
+ }
+ break;
+ }
+ }
+ }
+ }
+
PreResolveWorker(m);
} else {
- reporter.Error(m.RefinementBaseName[0], "module ({0}) named as refinement base is not a literal module or simple reference to a literal module", Util.Comma(".", m.RefinementBaseName, x => x.val));
+ reporter.Error(MessageSource.RefinementTransformer, m.RefinementBaseName[0], "module ({0}) named as refinement base is not a literal module or simple reference to a literal module", Util.Comma(".", m.RefinementBaseName, x => x.val));
}
} else {
- reporter.Error(m.RefinementBaseName[0], "module ({0}) named as refinement base does not exist", Util.Comma(".", m.RefinementBaseName, x => x.val));
+ reporter.Error(MessageSource.RefinementTransformer, m.RefinementBaseName[0], "module ({0}) named as refinement base does not exist", Util.Comma(".", m.RefinementBaseName, x => x.val));
}
}
}
@@ -118,99 +141,117 @@ namespace Microsoft.Dafny
}
// Merge the declarations of prev into the declarations of m
+ List<string> processedDecl = new List<string>();
foreach (var d in prev.TopLevelDecls) {
int index;
+ processedDecl.Add(d.Name);
if (!declaredNames.TryGetValue(d.Name, out index)) {
m.TopLevelDecls.Add(refinementCloner.CloneDeclaration(d, m));
} else {
var nw = m.TopLevelDecls[index];
- if (d is ModuleDecl) {
- if (!(nw is ModuleDecl)) {
- reporter.Error(nw, "a module ({0}) must refine another module", nw.Name);
- } else if (!(d is ModuleFacadeDecl)) {
- reporter.Error(nw, "a module ({0}) can only refine a module facade", nw.Name);
- } else {
- ModuleSignature original = ((ModuleFacadeDecl)d).OriginalSignature;
- ModuleSignature derived = null;
- if (nw is AliasModuleDecl) {
- derived = ((AliasModuleDecl)nw).Signature;
- } else if (nw is ModuleFacadeDecl) {
- derived = ((ModuleFacadeDecl)nw).Signature;
- } else {
- reporter.Error(nw, "a module ({0}) can only be refined by an alias module or a module facade", d.Name);
+ MergeTopLevelDecls(m, nw, d, index);
+ }
+ }
+
+ // Merge the imports of prev
+ var prevTopLevelDecls = RefinedSig.TopLevels.Values;
+ foreach (var d in prevTopLevelDecls) {
+ int index;
+ if (!processedDecl.Contains(d.Name) && (declaredNames.TryGetValue(d.Name, out index))) {
+ // if it is redefined, we need to merge them.
+ var nw = m.TopLevelDecls[index];
+ MergeTopLevelDecls(m, nw, d, index);
+ }
+ }
+ m.RefinementBaseSig = RefinedSig;
+
+ Contract.Assert(moduleUnderConstruction == m); // this should be as it was set earlier in this method
+ }
+
+ private void MergeTopLevelDecls(ModuleDefinition m, TopLevelDecl nw, TopLevelDecl d, int index) {
+ if (d is ModuleDecl) {
+ if (!(nw is ModuleDecl)) {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a module ({0}) must refine another module", nw.Name);
+ } else if (!(d is ModuleFacadeDecl)) {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a module ({0}) can only refine a module facade", nw.Name);
+ } else {
+ ModuleSignature original = ((ModuleFacadeDecl)d).OriginalSignature;
+ ModuleSignature derived = null;
+ if (nw is AliasModuleDecl) {
+ derived = ((AliasModuleDecl)nw).Signature;
+ } else if (nw is ModuleFacadeDecl) {
+ derived = ((ModuleFacadeDecl)nw).Signature;
+ } else {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a module ({0}) can only be refined by an alias module or a module facade", d.Name);
+ }
+ if (derived != null) {
+ // check that the new module refines the previous declaration
+ if (!CheckIsRefinement(derived, original))
+ reporter.Error(MessageSource.RefinementTransformer, nw.tok, "a module ({0}) can only be replaced by a refinement of the original module", d.Name);
+ }
+ }
+ } else if (d is OpaqueTypeDecl) {
+ if (nw is ModuleDecl) {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a module ({0}) must refine another module", nw.Name);
+ } else {
+ bool dDemandsEqualitySupport = ((OpaqueTypeDecl)d).MustSupportEquality;
+ if (nw is OpaqueTypeDecl) {
+ if (dDemandsEqualitySupport != ((OpaqueTypeDecl)nw).MustSupportEquality) {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "type declaration '{0}' is not allowed to change the requirement of supporting equality", nw.Name);
+ }
+ if (nw.TypeArgs.Count != d.TypeArgs.Count) {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "type '{0}' is not allowed to change its number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count);
+ }
+ } else if (dDemandsEqualitySupport) {
+ if (nw is ClassDecl) {
+ // fine, as long as "nw" takes the right number of type parameters
+ if (nw.TypeArgs.Count != d.TypeArgs.Count) {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "opaque type '{0}' is not allowed to be replaced by a class that takes a different number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count);
}
- if (derived != null) {
- // check that the new module refines the previous declaration
- if (!CheckIsRefinement(derived, original))
- reporter.Error(nw.tok, "a module ({0}) can only be replaced by a refinement of the original module", d.Name);
+ } else if (nw is NewtypeDecl) {
+ // fine, as long as "nw" does not take any type parameters
+ if (nw.TypeArgs.Count != 0) {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "opaque type '{0}', which has {1} type argument{2}, is not allowed to be replaced by a newtype, which takes none", nw.Name, d.TypeArgs.Count, d.TypeArgs.Count == 1 ? "" : "s");
}
- }
- } else if (d is OpaqueTypeDecl) {
- if (nw is ModuleDecl) {
- reporter.Error(nw, "a module ({0}) must refine another module", nw.Name);
+ } else if (nw is CoDatatypeDecl) {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a type declaration that requires equality support cannot be replaced by a codatatype");
} else {
- bool dDemandsEqualitySupport = ((OpaqueTypeDecl)d).MustSupportEquality;
- if (nw is OpaqueTypeDecl) {
- if (dDemandsEqualitySupport != ((OpaqueTypeDecl)nw).MustSupportEquality) {
- reporter.Error(nw, "type declaration '{0}' is not allowed to change the requirement of supporting equality", nw.Name);
- }
- if (nw.TypeArgs.Count != d.TypeArgs.Count) {
- reporter.Error(nw, "type '{0}' is not allowed to change its number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count);
- }
- } else if (dDemandsEqualitySupport) {
- if (nw is ClassDecl) {
- // fine, as long as "nw" takes the right number of type parameters
- if (nw.TypeArgs.Count != d.TypeArgs.Count) {
- reporter.Error(nw, "opaque type '{0}' is not allowed to be replaced by a class that takes a different number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count);
- }
- } else if (nw is NewtypeDecl) {
- // fine, as long as "nw" does not take any type parameters
- if (nw.TypeArgs.Count != 0) {
- reporter.Error(nw, "opaque type '{0}', which has {1} type argument{2}, is not allowed to be replaced by a newtype, which takes none", nw.Name, d.TypeArgs.Count, d.TypeArgs.Count == 1 ? "" : "s");
- }
- } else if (nw is CoDatatypeDecl) {
- reporter.Error(nw, "a type declaration that requires equality support cannot be replaced by a codatatype");
- } else {
- Contract.Assert(nw is IndDatatypeDecl || nw is TypeSynonymDecl);
- if (nw.TypeArgs.Count != d.TypeArgs.Count) {
- reporter.Error(nw, "opaque type '{0}' is not allowed to be replaced by a type that takes a different number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count);
- } else {
- // Here, we need to figure out if the new type supports equality. But we won't know about that until resolution has
- // taken place, so we defer it until the PostResolve phase.
- var udt = UserDefinedType.FromTopLevelDecl(nw.tok, nw);
- postTasks.Enqueue(() => {
- if (!udt.SupportsEquality) {
- reporter.Error(udt.tok, "type '{0}' is used to refine an opaque type with equality support, but '{0}' does not support equality", udt.Name);
- }
- });
+ Contract.Assert(nw is IndDatatypeDecl || nw is TypeSynonymDecl);
+ if (nw.TypeArgs.Count != d.TypeArgs.Count) {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "opaque type '{0}' is not allowed to be replaced by a type that takes a different number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count);
+ } else {
+ // Here, we need to figure out if the new type supports equality. But we won't know about that until resolution has
+ // taken place, so we defer it until the PostResolve phase.
+ var udt = UserDefinedType.FromTopLevelDecl(nw.tok, nw);
+ postTasks.Enqueue(() => {
+ if (!udt.SupportsEquality) {
+ reporter.Error(MessageSource.RefinementTransformer, udt.tok, "type '{0}' is used to refine an opaque type with equality support, but '{0}' does not support equality", udt.Name);
}
- }
- } else if (d.TypeArgs.Count != nw.TypeArgs.Count) {
- reporter.Error(nw, "opaque type '{0}' is not allowed to be replaced by a type that takes a different number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count);
+ });
}
}
- } else if (nw is OpaqueTypeDecl) {
- reporter.Error(nw, "an opaque type declaration ({0}) in a refining module cannot replace a more specific type declaration in the refinement base", nw.Name);
- } else if (nw is DatatypeDecl) {
- reporter.Error(nw, "a datatype declaration ({0}) in a refinement module can only replace an opaque type declaration", nw.Name);
- } else if (nw is IteratorDecl) {
- if (d is IteratorDecl) {
- m.TopLevelDecls[index] = MergeIterator((IteratorDecl)nw, (IteratorDecl)d);
- } else {
- reporter.Error(nw, "an iterator declaration ({0}) is a refining module cannot replace a different kind of declaration in the refinement base", nw.Name);
- }
- } else {
- Contract.Assert(nw is ClassDecl);
- if (d is DatatypeDecl) {
- reporter.Error(nw, "a class declaration ({0}) in a refining module cannot replace a different kind of declaration in the refinement base", nw.Name);
- } else {
- m.TopLevelDecls[index] = MergeClass((ClassDecl)nw, (ClassDecl)d);
- }
+ } else if (d.TypeArgs.Count != nw.TypeArgs.Count) {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "opaque type '{0}' is not allowed to be replaced by a type that takes a different number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count);
}
}
+ } else if (nw is OpaqueTypeDecl) {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "an opaque type declaration ({0}) in a refining module cannot replace a more specific type declaration in the refinement base", nw.Name);
+ } else if (nw is DatatypeDecl) {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a datatype declaration ({0}) in a refinement module can only replace an opaque type declaration", nw.Name);
+ } else if (nw is IteratorDecl) {
+ if (d is IteratorDecl) {
+ m.TopLevelDecls[index] = MergeIterator((IteratorDecl)nw, (IteratorDecl)d);
+ } else {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "an iterator declaration ({0}) is a refining module cannot replace a different kind of declaration in the refinement base", nw.Name);
+ }
+ } else {
+ Contract.Assert(nw is ClassDecl);
+ if (d is DatatypeDecl) {
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a class declaration ({0}) in a refining module cannot replace a different kind of declaration in the refinement base", nw.Name);
+ } else {
+ m.TopLevelDecls[index] = MergeClass((ClassDecl)nw, (ClassDecl)d);
+ }
}
-
- Contract.Assert(moduleUnderConstruction == m); // this should be as it was set earlier in this method
}
public bool CheckIsRefinement(ModuleSignature derived, ModuleSignature original) {
@@ -229,42 +270,42 @@ namespace Microsoft.Dafny
// Second, we need to determine whether the specifications will be compatible
// (i.e. substitutable), by translating to Boogie.
- var errorCount = reporter.ErrorCount;
+ var errorCount = reporter.Count(ErrorLevel.Error);
foreach (var kv in original.TopLevels) {
var d = kv.Value;
TopLevelDecl nw;
if (derived.TopLevels.TryGetValue(kv.Key, out nw)) {
if (d is ModuleDecl) {
if (!(nw is ModuleDecl)) {
- reporter.Error(nw, "a module ({0}) must refine another module", nw.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a module ({0}) must refine another module", nw.Name);
} else {
CheckIsRefinement(((ModuleDecl)nw).Signature, ((ModuleDecl)d).Signature);
}
} else if (d is OpaqueTypeDecl) {
if (nw is ModuleDecl) {
- reporter.Error(nw, "a module ({0}) must refine another module", nw.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a module ({0}) must refine another module", nw.Name);
} else {
bool dDemandsEqualitySupport = ((OpaqueTypeDecl)d).MustSupportEquality;
if (nw is OpaqueTypeDecl) {
if (dDemandsEqualitySupport != ((OpaqueTypeDecl)nw).MustSupportEquality) {
- reporter.Error(nw, "type declaration '{0}' is not allowed to change the requirement of supporting equality", nw.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "type declaration '{0}' is not allowed to change the requirement of supporting equality", nw.Name);
}
} else if (dDemandsEqualitySupport) {
if (nw is ClassDecl) {
// fine, as long as "nw" does not take any type parameters
if (nw.TypeArgs.Count != 0) {
- reporter.Error(nw, "opaque type '{0}' is not allowed to be replaced by a class that takes type parameters", nw.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "opaque type '{0}' is not allowed to be replaced by a class that takes type parameters", nw.Name);
}
} else if (nw is CoDatatypeDecl) {
- reporter.Error(nw, "a type declaration that requires equality support cannot be replaced by a codatatype");
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a type declaration that requires equality support cannot be replaced by a codatatype");
} else {
Contract.Assert(nw is IndDatatypeDecl);
if (nw.TypeArgs.Count != 0) {
- reporter.Error(nw, "opaque type '{0}' is not allowed to be replaced by a datatype that takes type parameters", nw.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "opaque type '{0}' is not allowed to be replaced by a datatype that takes type parameters", nw.Name);
} else {
var udt = new UserDefinedType(nw.tok, nw.Name, nw, new List<Type>());
if (!(udt.SupportsEquality)) {
- reporter.Error(nw.tok, "datatype '{0}' is used to refine an opaque type with equality support, but '{0}' does not support equality", nw.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nw.tok, "datatype '{0}' is used to refine an opaque type with equality support, but '{0}' does not support equality", nw.Name);
}
}
}
@@ -273,18 +314,18 @@ namespace Microsoft.Dafny
} else if (d is DatatypeDecl) {
if (nw is DatatypeDecl) {
if (d is IndDatatypeDecl && !(nw is IndDatatypeDecl)) {
- reporter.Error(nw, "a datatype ({0}) must be replaced by a datatype, not a codatatype", d.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a datatype ({0}) must be replaced by a datatype, not a codatatype", d.Name);
} else if (d is CoDatatypeDecl && !(nw is CoDatatypeDecl)) {
- reporter.Error(nw, "a codatatype ({0}) must be replaced by a codatatype, not a datatype", d.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a codatatype ({0}) must be replaced by a codatatype, not a datatype", d.Name);
}
// check constructors, formals, etc.
CheckDatatypesAreRefinements((DatatypeDecl)d, (DatatypeDecl)nw);
} else {
- reporter.Error(nw, "a {0} ({1}) must be refined by a {0}", d is IndDatatypeDecl ? "datatype" : "codatatype", d.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a {0} ({1}) must be refined by a {0}", d is IndDatatypeDecl ? "datatype" : "codatatype", d.Name);
}
} else if (d is ClassDecl) {
if (!(nw is ClassDecl)) {
- reporter.Error(nw, "a class declaration ({0}) must be refined by another class declaration", nw.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a class declaration ({0}) must be refined by another class declaration", nw.Name);
} else {
CheckClassesAreRefinements((ClassDecl)nw, (ClassDecl)d);
}
@@ -292,15 +333,15 @@ namespace Microsoft.Dafny
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected toplevel
}
} else {
- reporter.Error(d, "declaration {0} must have a matching declaration in the refining module", d.Name);
+ reporter.Error(MessageSource.RefinementTransformer, d, "declaration {0} must have a matching declaration in the refining module", d.Name);
}
}
- return errorCount == reporter.ErrorCount;
+ return errorCount == reporter.Count(ErrorLevel.Error);
}
private void CheckClassesAreRefinements(ClassDecl nw, ClassDecl d) {
if (nw.TypeArgs.Count != d.TypeArgs.Count) {
- reporter.Error(nw, "a refining class ({0}) must have the same number of type parameters", nw.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a refining class ({0}) must have the same number of type parameters", nw.Name);
} else {
var map = new Dictionary<string, MemberDecl>();
foreach (var mem in nw.Members) {
@@ -310,33 +351,33 @@ namespace Microsoft.Dafny
MemberDecl newMem;
if (map.TryGetValue(m.Name, out newMem)) {
if (m.HasStaticKeyword != newMem.HasStaticKeyword) {
- reporter.Error(newMem, "member {0} must {1}", m.Name, m.HasStaticKeyword ? "be static" : "not be static");
+ reporter.Error(MessageSource.RefinementTransformer, newMem, "member {0} must {1}", m.Name, m.HasStaticKeyword ? "be static" : "not be static");
}
if (m is Field) {
if (newMem is Field) {
var newField = (Field)newMem;
if (!ResolvedTypesAreTheSame(newField.Type, ((Field)m).Type))
- reporter.Error(newMem, "field must be refined by a field with the same type (got {0}, expected {1})", newField.Type, ((Field)m).Type);
+ reporter.Error(MessageSource.RefinementTransformer, newMem, "field must be refined by a field with the same type (got {0}, expected {1})", newField.Type, ((Field)m).Type);
if (m.IsGhost || !newField.IsGhost)
- reporter.Error(newField, "a field re-declaration ({0}) must be to ghostify the field", newField.Name, nw.Name);
+ reporter.Error(MessageSource.RefinementTransformer, newField, "a field re-declaration ({0}) must be to ghostify the field", newField.Name, nw.Name);
} else {
- reporter.Error(newMem, "a field declaration ({1}) must be replaced by a field in the refinement base (not {0})", newMem.Name, nw.Name);
+ reporter.Error(MessageSource.RefinementTransformer, newMem, "a field declaration ({1}) must be replaced by a field in the refinement base (not {0})", newMem.Name, nw.Name);
}
} else if (m is Method) {
if (newMem is Method) {
CheckMethodsAreRefinements((Method)newMem, (Method)m);
} else {
- reporter.Error(newMem, "method must be refined by a method");
+ reporter.Error(MessageSource.RefinementTransformer, newMem, "method must be refined by a method");
}
} else if (m is Function) {
if (newMem is Function) {
CheckFunctionsAreRefinements((Function)newMem, (Function)m);
} else {
- reporter.Error(newMem, "{0} must be refined by a {0}", m.WhatKind);
+ reporter.Error(MessageSource.RefinementTransformer, newMem, "{0} must be refined by a {0}", m.WhatKind);
}
}
} else {
- reporter.Error(nw is DefaultClassDecl ? nw.Module.tok : nw.tok, "refining {0} must have member {1}", nw is DefaultClassDecl ? "module" : "class", m.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nw is DefaultClassDecl ? nw.Module.tok : nw.tok, "refining {0} must have member {1}", nw is DefaultClassDecl ? "module" : "class", m.Name);
}
}
}
@@ -349,17 +390,17 @@ namespace Microsoft.Dafny
Contract.Requires(thing != null);
Contract.Requires(parameterKind != null);
if (old.Count != nw.Count) {
- reporter.Error(tok, "{0} '{1}' is declared with a different number of {2} ({3} instead of {4}) than the corresponding {0} in the module it refines", thing, name, parameterKind, nw.Count, old.Count);
+ reporter.Error(MessageSource.RefinementTransformer, tok, "{0} '{1}' is declared with a different number of {2} ({3} instead of {4}) than the corresponding {0} in the module it refines", thing, name, parameterKind, nw.Count, old.Count);
} else {
for (int i = 0; i < old.Count; i++) {
var o = old[i];
var n = nw[i];
if (!o.IsGhost && n.IsGhost) {
- reporter.Error(n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from non-ghost to ghost", parameterKind, n.Name, thing, name);
+ reporter.Error(MessageSource.RefinementTransformer, n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from non-ghost to ghost", parameterKind, n.Name, thing, name);
} else if (o.IsGhost && !n.IsGhost) {
- reporter.Error(n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from ghost to non-ghost", parameterKind, n.Name, thing, name);
+ reporter.Error(MessageSource.RefinementTransformer, n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from ghost to non-ghost", parameterKind, n.Name, thing, name);
} else if (!ResolvedTypesAreTheSame(o.Type, n.Type)) {
- reporter.Error(n.tok, "the type of {0} '{1}' is different from the type of the same {0} in the corresponding {2} in the module it refines ('{3}' instead of '{4}')", parameterKind, n.Name, thing, n.Type, o.Type);
+ reporter.Error(MessageSource.RefinementTransformer, n.tok, "the type of {0} '{1}' is different from the type of the same {0} in the corresponding {2} in the module it refines ('{3}' instead of '{4}')", parameterKind, n.Name, thing, n.Type, o.Type);
}
}
}
@@ -373,22 +414,22 @@ namespace Microsoft.Dafny
private void CheckFunctionsAreRefinements(Function nw, Function f) {
if (f is Predicate) {
if (!(nw is Predicate)) {
- reporter.Error(nw, "a predicate declaration ({0}) can only be refined by a predicate", nw.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a predicate declaration ({0}) can only be refined by a predicate", nw.Name);
} else {
CheckAgreement_TypeParameters(nw.tok, f.TypeArgs, nw.TypeArgs, nw.Name, "predicate", false);
CheckAgreementResolvedParameters(nw.tok, f.Formals, nw.Formals, nw.Name, "predicate", "parameter");
}
} else if (f is FixpointPredicate) {
- reporter.Error(nw, "refinement of {0}s is not supported", f.WhatKind);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "refinement of {0}s is not supported", f.WhatKind);
} else {
// f is a plain Function
if (nw is Predicate || nw is FixpointPredicate) {
- reporter.Error(nw, "a {0} declaration ({1}) can only be refined by a function or function method", nw.IsGhost ? "function" : "function method", nw.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "a {0} declaration ({1}) can only be refined by a function or function method", nw.IsGhost ? "function" : "function method", nw.Name);
} else {
CheckAgreement_TypeParameters(nw.tok, f.TypeArgs, nw.TypeArgs, nw.Name, "function", false);
CheckAgreementResolvedParameters(nw.tok, f.Formals, nw.Formals, nw.Name, "function", "parameter");
if (!ResolvedTypesAreTheSame(nw.ResultType, f.ResultType)) {
- reporter.Error(nw, "the result type of function '{0}' ({1}) differs from the result type of the corresponding function in the module it refines ({2})", nw.Name, nw.ResultType, f.ResultType);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "the result type of function '{0}' ({1}) differs from the result type of the corresponding function in the module it refines ({2})", nw.Name, nw.ResultType, f.ResultType);
}
}
}
@@ -399,7 +440,7 @@ namespace Microsoft.Dafny
private void CheckDatatypesAreRefinements(DatatypeDecl dd, DatatypeDecl nn) {
CheckAgreement_TypeParameters(nn.tok, dd.TypeArgs, nn.TypeArgs, dd.Name, "datatype", false);
if (dd.Ctors.Count != nn.Ctors.Count) {
- reporter.Error(nn.tok, "a refining datatype must have the same number of constructors");
+ reporter.Error(MessageSource.RefinementTransformer, nn.tok, "a refining datatype must have the same number of constructors");
} else {
var map = new Dictionary<string, DatatypeCtor>();
foreach (var ctor in nn.Ctors) {
@@ -409,21 +450,21 @@ namespace Microsoft.Dafny
DatatypeCtor newCtor;
if (map.TryGetValue(ctor.Name, out newCtor)) {
if (newCtor.Formals.Count != ctor.Formals.Count) {
- reporter.Error(newCtor, "the constructor ({0}) must have the same number of formals as in the refined module", newCtor.Name);
+ reporter.Error(MessageSource.RefinementTransformer, newCtor, "the constructor ({0}) must have the same number of formals as in the refined module", newCtor.Name);
} else {
for (int i = 0; i < newCtor.Formals.Count; i++) {
var a = ctor.Formals[i]; var b = newCtor.Formals[i];
if (a.HasName) {
if (!b.HasName || a.Name != b.Name)
- reporter.Error(b, "formal argument {0} in constructor {1} does not have the same name as in the refined module (should be {2})", i, ctor.Name, a.Name);
+ reporter.Error(MessageSource.RefinementTransformer, b, "formal argument {0} in constructor {1} does not have the same name as in the refined module (should be {2})", i, ctor.Name, a.Name);
}
if (!ResolvedTypesAreTheSame(a.Type, b.Type)) {
- reporter.Error(b, "formal argument {0} in constructor {1} does not have the same type as in the refined module (should be {2}, not {3})", i, ctor.Name, a.Type.ToString(), b.Type.ToString());
+ reporter.Error(MessageSource.RefinementTransformer, b, "formal argument {0} in constructor {1} does not have the same type as in the refined module (should be {2}, not {3})", i, ctor.Name, a.Type.ToString(), b.Type.ToString());
}
}
}
} else {
- reporter.Error(nn, "the constructor {0} must be present in the refining datatype", ctor.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nn, "the constructor {0} must be present in the refining datatype", ctor.Name);
}
}
}
@@ -452,7 +493,8 @@ namespace Microsoft.Dafny
} else if (prev is ObjectType) {
return next is ObjectType;
} else if (prev is SetType) {
- return next is SetType && ResolvedTypesAreTheSame(((SetType)prev).Arg, ((SetType)next).Arg);
+ return next is SetType && ((SetType)prev).Finite == ((SetType)next).Finite &&
+ ResolvedTypesAreTheSame(((SetType)prev).Arg, ((SetType)next).Arg);
} else if (prev is MultiSetType) {
return next is MultiSetType && ResolvedTypesAreTheSame(((MultiSetType)prev).Arg, ((MultiSetType)next).Arg);
} else if (prev is MapType) {
@@ -491,7 +533,8 @@ namespace Microsoft.Dafny
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
}
}
- public void PostResolve(ModuleDefinition m) {
+
+ internal override void PostResolve(ModuleDefinition m) {
if (m == moduleUnderConstruction) {
while (this.postTasks.Count != 0) {
var a = postTasks.Dequeue();
@@ -502,8 +545,7 @@ namespace Microsoft.Dafny
}
moduleUnderConstruction = null;
}
- public void PostCyclicityResolve(ModuleDefinition m) {
- }
+
Function CloneFunction(IToken tok, Function f, bool isGhost, List<Expression> moreEnsures, Expression moreBody, Expression replacementBody, bool checkPrevPostconditions, Attributes moreAttributes) {
Contract.Requires(tok != null);
Contract.Requires(moreBody == null || f is Predicate);
@@ -544,16 +586,16 @@ namespace Microsoft.Dafny
if (f is Predicate) {
return new Predicate(tok, f.Name, f.HasStaticKeyword, f.IsProtected, isGhost, tps, formals,
- req, reads, ens, decreases, body, bodyOrigin, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null);
+ req, reads, ens, decreases, body, bodyOrigin, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null, f);
} else if (f is InductivePredicate) {
return new InductivePredicate(tok, f.Name, f.HasStaticKeyword, f.IsProtected, tps, formals,
- req, reads, ens, body, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null);
+ req, reads, ens, body, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null, f);
} else if (f is CoPredicate) {
return new CoPredicate(tok, f.Name, f.HasStaticKeyword, f.IsProtected, tps, formals,
- req, reads, ens, body, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null);
+ req, reads, ens, body, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null, f);
} else {
return new Function(tok, f.Name, f.HasStaticKeyword, f.IsProtected, isGhost, tps, formals, refinementCloner.CloneType(f.ResultType),
- req, reads, ens, decreases, body, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null);
+ req, reads, ens, decreases, body, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null, f);
}
}
@@ -578,19 +620,19 @@ namespace Microsoft.Dafny
var body = newBody ?? refinementCloner.CloneBlockStmt(m.Body);
if (m is Constructor) {
return new Constructor(new RefinementToken(m.tok, moduleUnderConstruction), m.Name, tps, ins,
- req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null);
+ req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null, m);
} else if (m is InductiveLemma) {
return new InductiveLemma(new RefinementToken(m.tok, moduleUnderConstruction), m.Name, m.HasStaticKeyword, tps, ins, m.Outs.ConvertAll(refinementCloner.CloneFormal),
- req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null);
+ req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null, m);
} else if (m is CoLemma) {
return new CoLemma(new RefinementToken(m.tok, moduleUnderConstruction), m.Name, m.HasStaticKeyword, tps, ins, m.Outs.ConvertAll(refinementCloner.CloneFormal),
- req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null);
+ req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null, m);
} else if (m is Lemma) {
return new Lemma(new RefinementToken(m.tok, moduleUnderConstruction), m.Name, m.HasStaticKeyword, tps, ins, m.Outs.ConvertAll(refinementCloner.CloneFormal),
- req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null);
+ req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null, m);
} else {
return new Method(new RefinementToken(m.tok, moduleUnderConstruction), m.Name, m.HasStaticKeyword, m.IsGhost, tps, ins, m.Outs.ConvertAll(refinementCloner.CloneFormal),
- req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null);
+ req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null, m);
}
}
@@ -601,26 +643,26 @@ namespace Microsoft.Dafny
Contract.Requires(prev != null);
if (nw.Requires.Count != 0) {
- reporter.Error(nw.Requires[0].E.tok, "a refining iterator is not allowed to add preconditions");
+ reporter.Error(MessageSource.RefinementTransformer, nw.Requires[0].E.tok, "a refining iterator is not allowed to add preconditions");
}
if (nw.YieldRequires.Count != 0) {
- reporter.Error(nw.YieldRequires[0].E.tok, "a refining iterator is not allowed to add yield preconditions");
+ reporter.Error(MessageSource.RefinementTransformer, nw.YieldRequires[0].E.tok, "a refining iterator is not allowed to add yield preconditions");
}
if (nw.Reads.Expressions.Count != 0) {
- reporter.Error(nw.Reads.Expressions[0].E.tok, "a refining iterator is not allowed to extend the reads clause");
+ reporter.Error(MessageSource.RefinementTransformer, nw.Reads.Expressions[0].E.tok, "a refining iterator is not allowed to extend the reads clause");
}
if (nw.Modifies.Expressions.Count != 0) {
- reporter.Error(nw.Modifies.Expressions[0].E.tok, "a refining iterator is not allowed to extend the modifies clause");
+ reporter.Error(MessageSource.RefinementTransformer, nw.Modifies.Expressions[0].E.tok, "a refining iterator is not allowed to extend the modifies clause");
}
if (nw.Decreases.Expressions.Count != 0) {
- reporter.Error(nw.Decreases.Expressions[0].tok, "a refining iterator is not allowed to extend the decreases clause");
+ reporter.Error(MessageSource.RefinementTransformer, nw.Decreases.Expressions[0].tok, "a refining iterator is not allowed to extend the decreases clause");
}
if (nw.SignatureIsOmitted) {
Contract.Assert(nw.TypeArgs.Count == 0);
Contract.Assert(nw.Ins.Count == 0);
Contract.Assert(nw.Outs.Count == 0);
- ReportAdditionalInformation(nw.SignatureEllipsis, Printer.IteratorSignatureToString(prev), 3);
+ reporter.Info(MessageSource.RefinementTransformer, nw.SignatureEllipsis, Printer.IteratorSignatureToString(prev));
} else {
CheckAgreement_TypeParameters(nw.tok, prev.TypeArgs, nw.TypeArgs, nw.Name, "iterator");
CheckAgreement_Parameters(nw.tok, prev.Ins, nw.Ins, nw.Name, "iterator", "in-parameter");
@@ -684,9 +726,9 @@ namespace Microsoft.Dafny
if (nwMember is Field) {
if (member is Field && TypesAreSyntacticallyEqual(((Field)nwMember).Type, ((Field)member).Type)) {
if (member.IsGhost || !nwMember.IsGhost)
- reporter.Error(nwMember, "a field re-declaration ({0}) must be to ghostify the field", nwMember.Name, nw.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nwMember, "a field re-declaration ({0}) must be to ghostify the field", nwMember.Name, nw.Name);
} else {
- reporter.Error(nwMember, "a field declaration ({0}) in a refining class ({1}) must replace a field in the refinement base", nwMember.Name, nw.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nwMember, "a field declaration ({0}) in a refining class ({1}) must replace a field in the refinement base", nwMember.Name, nw.Name);
}
nwMember.RefinementBase = member;
@@ -699,38 +741,38 @@ namespace Microsoft.Dafny
(isPredicate && !(member is Predicate)) ||
(isIndPredicate && !(member is InductivePredicate)) ||
(isCoPredicate && !(member is CoPredicate))) {
- reporter.Error(nwMember, "a {0} declaration ({1}) can only refine a {0}", f.WhatKind, nwMember.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nwMember, "a {0} declaration ({1}) can only refine a {0}", f.WhatKind, nwMember.Name);
} else if (f.IsProtected != ((Function)member).IsProtected) {
- reporter.Error(f, "a {0} in a refinement module must be declared 'protected' if and only if the refined {0} is", f.WhatKind);
+ reporter.Error(MessageSource.RefinementTransformer, f, "a {0} in a refinement module must be declared 'protected' if and only if the refined {0} is", f.WhatKind);
} else {
var prevFunction = (Function)member;
if (f.Req.Count != 0) {
- reporter.Error(f.Req[0].tok, "a refining {0} is not allowed to add preconditions", f.WhatKind);
+ reporter.Error(MessageSource.RefinementTransformer, f.Req[0].tok, "a refining {0} is not allowed to add preconditions", f.WhatKind);
}
if (f.Reads.Count != 0) {
- reporter.Error(f.Reads[0].E.tok, "a refining {0} is not allowed to extend the reads clause", f.WhatKind);
+ reporter.Error(MessageSource.RefinementTransformer, f.Reads[0].E.tok, "a refining {0} is not allowed to extend the reads clause", f.WhatKind);
}
if (f.Decreases.Expressions.Count != 0) {
- reporter.Error(f.Decreases.Expressions[0].tok, "decreases clause on refining {0} not supported", f.WhatKind);
+ reporter.Error(MessageSource.RefinementTransformer, f.Decreases.Expressions[0].tok, "decreases clause on refining {0} not supported", f.WhatKind);
}
if (prevFunction.HasStaticKeyword != f.HasStaticKeyword) {
- reporter.Error(f, "a function in a refining module cannot be changed from static to non-static or vice versa: {0}", f.Name);
+ reporter.Error(MessageSource.RefinementTransformer, f, "a function in a refining module cannot be changed from static to non-static or vice versa: {0}", f.Name);
}
if (!prevFunction.IsGhost && f.IsGhost) {
- reporter.Error(f, "a function method cannot be changed into a (ghost) function in a refining module: {0}", f.Name);
+ reporter.Error(MessageSource.RefinementTransformer, f, "a function method cannot be changed into a (ghost) function in a refining module: {0}", f.Name);
} else if (prevFunction.IsGhost && !f.IsGhost && prevFunction.Body != null) {
- reporter.Error(f, "a function can be changed into a function method in a refining module only if the function has not yet been given a body: {0}", f.Name);
+ reporter.Error(MessageSource.RefinementTransformer, f, "a function can be changed into a function method in a refining module only if the function has not yet been given a body: {0}", f.Name);
}
if (f.SignatureIsOmitted) {
Contract.Assert(f.TypeArgs.Count == 0);
Contract.Assert(f.Formals.Count == 0);
- ReportAdditionalInformation(f.SignatureEllipsis, Printer.FunctionSignatureToString(prevFunction), 3);
+ reporter.Info(MessageSource.RefinementTransformer, f.SignatureEllipsis, Printer.FunctionSignatureToString(prevFunction));
} else {
CheckAgreement_TypeParameters(f.tok, prevFunction.TypeArgs, f.TypeArgs, f.Name, "function");
CheckAgreement_Parameters(f.tok, prevFunction.Formals, f.Formals, f.Name, "function", "parameter");
if (!TypesAreSyntacticallyEqual(prevFunction.ResultType, f.ResultType)) {
- reporter.Error(f, "the result type of function '{0}' ({1}) differs from the result type of the corresponding function in the module it refines ({2})", f.Name, f.ResultType, prevFunction.ResultType);
+ reporter.Error(MessageSource.RefinementTransformer, f, "the result type of function '{0}' ({1}) differs from the result type of the corresponding function in the module it refines ({2})", f.Name, f.ResultType, prevFunction.ResultType);
}
}
@@ -742,9 +784,9 @@ namespace Microsoft.Dafny
if (isPredicate && f.IsProtected) {
moreBody = f.Body;
} else if (isPredicate) {
- reporter.Error(nwMember, "a refining predicate is not allowed to extend/change the body unless it is declared 'protected'");
+ reporter.Error(MessageSource.RefinementTransformer, nwMember, "a refining predicate is not allowed to extend/change the body unless it is declared 'protected'");
} else {
- reporter.Error(nwMember, "a refining function is not allowed to extend/change the body");
+ reporter.Error(MessageSource.RefinementTransformer, nwMember, "a refining function is not allowed to extend/change the body");
}
}
var newF = CloneFunction(f.tok, prevFunction, f.IsGhost, f.Ens, moreBody, replacementBody, prevFunction.Body == null, f.Attributes);
@@ -755,14 +797,14 @@ namespace Microsoft.Dafny
} else {
var m = (Method)nwMember;
if (!(member is Method)) {
- reporter.Error(nwMember, "a method declaration ({0}) can only refine a method", nwMember.Name);
+ reporter.Error(MessageSource.RefinementTransformer, nwMember, "a method declaration ({0}) can only refine a method", nwMember.Name);
} else {
var prevMethod = (Method)member;
if (m.Req.Count != 0) {
- reporter.Error(m.Req[0].E.tok, "a refining method is not allowed to add preconditions");
+ reporter.Error(MessageSource.RefinementTransformer, m.Req[0].E.tok, "a refining method is not allowed to add preconditions");
}
if (m.Mod.Expressions.Count != 0) {
- reporter.Error(m.Mod.Expressions[0].E.tok, "a refining method is not allowed to extend the modifies clause");
+ reporter.Error(MessageSource.RefinementTransformer, m.Mod.Expressions[0].E.tok, "a refining method is not allowed to extend the modifies clause");
}
// If the previous method was not specified with "decreases *", then the new method is not allowed to provide any "decreases" clause.
// Any "decreases *" clause is not inherited, so if the previous method was specified with "decreases *", then the new method needs
@@ -775,23 +817,23 @@ namespace Microsoft.Dafny
} else {
if (!Contract.Exists(prevMethod.Decreases.Expressions, e => e is WildcardExpr)) {
// If the previous loop was not specified with "decreases *", then the new loop is not allowed to provide any "decreases" clause.
- reporter.Error(m.Decreases.Expressions[0].tok, "decreases clause on refining method not supported, unless the refined method was specified with 'decreases *'");
+ reporter.Error(MessageSource.RefinementTransformer, m.Decreases.Expressions[0].tok, "decreases clause on refining method not supported, unless the refined method was specified with 'decreases *'");
}
decreases = m.Decreases;
}
if (prevMethod.HasStaticKeyword != m.HasStaticKeyword) {
- reporter.Error(m, "a method in a refining module cannot be changed from static to non-static or vice versa: {0}", m.Name);
+ reporter.Error(MessageSource.RefinementTransformer, m, "a method in a refining module cannot be changed from static to non-static or vice versa: {0}", m.Name);
}
if (prevMethod.IsGhost && !m.IsGhost) {
- reporter.Error(m, "a method cannot be changed into a ghost method in a refining module: {0}", m.Name);
+ reporter.Error(MessageSource.RefinementTransformer, m, "a method cannot be changed into a ghost method in a refining module: {0}", m.Name);
} else if (!prevMethod.IsGhost && m.IsGhost) {
- reporter.Error(m, "a ghost method cannot be changed into a non-ghost method in a refining module: {0}", m.Name);
+ reporter.Error(MessageSource.RefinementTransformer, m, "a ghost method cannot be changed into a non-ghost method in a refining module: {0}", m.Name);
}
if (m.SignatureIsOmitted) {
Contract.Assert(m.TypeArgs.Count == 0);
Contract.Assert(m.Ins.Count == 0);
Contract.Assert(m.Outs.Count == 0);
- ReportAdditionalInformation(m.SignatureEllipsis, Printer.MethodSignatureToString(prevMethod), 3);
+ reporter.Info(MessageSource.RefinementTransformer, m.SignatureEllipsis, Printer.MethodSignatureToString(prevMethod));
} else {
CheckAgreement_TypeParameters(m.tok, prevMethod.TypeArgs, m.TypeArgs, m.Name, "method");
CheckAgreement_Parameters(m.tok, prevMethod.Ins, m.Ins, m.Name, "method", "in-parameter");
@@ -823,13 +865,13 @@ namespace Microsoft.Dafny
Contract.Requires(name != null);
Contract.Requires(thing != null);
if (old.Count != nw.Count) {
- reporter.Error(tok, "{0} '{1}' is declared with a different number of type parameters ({2} instead of {3}) than the corresponding {0} in the module it refines", thing, name, nw.Count, old.Count);
+ reporter.Error(MessageSource.RefinementTransformer, tok, "{0} '{1}' is declared with a different number of type parameters ({2} instead of {3}) than the corresponding {0} in the module it refines", thing, name, nw.Count, old.Count);
} else {
for (int i = 0; i < old.Count; i++) {
var o = old[i];
var n = nw[i];
if (o.Name != n.Name && checkNames) { // if checkNames is false, then just treat the parameters positionally.
- reporter.Error(n.tok, "type parameters are not allowed to be renamed from the names given in the {0} in the module being refined (expected '{1}', found '{2}')", thing, o.Name, n.Name);
+ reporter.Error(MessageSource.RefinementTransformer, n.tok, "type parameters are not allowed to be renamed from the names given in the {0} in the module being refined (expected '{1}', found '{2}')", thing, o.Name, n.Name);
} else {
// This explains what we want to do and why:
// switch (o.EqualitySupport) {
@@ -849,7 +891,7 @@ namespace Microsoft.Dafny
// }
// Here's how we actually compute it:
if (o.EqualitySupport != TypeParameter.EqualitySupportValue.InferredRequired && o.EqualitySupport != n.EqualitySupport) {
- reporter.Error(n.tok, "type parameter '{0}' is not allowed to change the requirement of supporting equality", n.Name);
+ reporter.Error(MessageSource.RefinementTransformer, n.tok, "type parameter '{0}' is not allowed to change the requirement of supporting equality", n.Name);
}
}
}
@@ -862,7 +904,7 @@ namespace Microsoft.Dafny
CheckOverrideResolvedParameters(nw.tok, f.Formals, nw.Formals, nw.Name, "function", "parameter");
if (!ResolvedTypesAreTheSame(nw.ResultType, f.ResultType))
{
- reporter.Error(nw, "the result type of function '{0}' ({1}) differs from the result type of the corresponding function in the module it overrides ({2})", nw.Name, nw.ResultType, f.ResultType);
+ reporter.Error(MessageSource.RefinementTransformer, nw, "the result type of function '{0}' ({1}) differs from the result type of the corresponding function in the module it overrides ({2})", nw.Name, nw.ResultType, f.ResultType);
}
}
@@ -882,7 +924,7 @@ namespace Microsoft.Dafny
Contract.Requires(thing != null);
if (old.Count != nw.Count)
{
- reporter.Error(tok, "{0} '{1}' is declared with a different number of type parameters ({2} instead of {3}) than the corresponding {0} in the module it overrides", thing, name, nw.Count, old.Count);
+ reporter.Error(MessageSource.RefinementTransformer, tok, "{0} '{1}' is declared with a different number of type parameters ({2} instead of {3}) than the corresponding {0} in the module it overrides", thing, name, nw.Count, old.Count);
}
else
{
@@ -892,14 +934,14 @@ namespace Microsoft.Dafny
var n = nw[i];
if (o.Name != n.Name && checkNames)
{ // if checkNames is false, then just treat the parameters positionally.
- reporter.Error(n.tok, "type parameters are not allowed to be renamed from the names given in the {0} in the module being overriden (expected '{1}', found '{2}')", thing, o.Name, n.Name);
+ reporter.Error(MessageSource.RefinementTransformer, n.tok, "type parameters are not allowed to be renamed from the names given in the {0} in the module being overriden (expected '{1}', found '{2}')", thing, o.Name, n.Name);
}
else
{
// Here's how we actually compute it:
if (o.EqualitySupport != TypeParameter.EqualitySupportValue.InferredRequired && o.EqualitySupport != n.EqualitySupport)
{
- reporter.Error(n.tok, "type parameter '{0}' is not allowed to change the requirement of supporting equality", n.Name);
+ reporter.Error(MessageSource.RefinementTransformer, n.tok, "type parameter '{0}' is not allowed to change the requirement of supporting equality", n.Name);
}
}
}
@@ -916,7 +958,7 @@ namespace Microsoft.Dafny
Contract.Requires(parameterKind != null);
if (old.Count != nw.Count)
{
- reporter.Error(tok, "{0} '{1}' is declared with a different number of {2} ({3} instead of {4}) than the corresponding {0} in the module it overrides", thing, name, parameterKind, nw.Count, old.Count);
+ reporter.Error(MessageSource.RefinementTransformer, tok, "{0} '{1}' is declared with a different number of {2} ({3} instead of {4}) than the corresponding {0} in the module it overrides", thing, name, parameterKind, nw.Count, old.Count);
}
else
{
@@ -926,15 +968,15 @@ namespace Microsoft.Dafny
var n = nw[i];
if (!o.IsGhost && n.IsGhost)
{
- reporter.Error(n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it overrides, from non-ghost to ghost", parameterKind, n.Name, thing, name);
+ reporter.Error(MessageSource.RefinementTransformer, n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it overrides, from non-ghost to ghost", parameterKind, n.Name, thing, name);
}
else if (o.IsGhost && !n.IsGhost)
{
- reporter.Error(n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it overrides, from ghost to non-ghost", parameterKind, n.Name, thing, name);
+ reporter.Error(MessageSource.RefinementTransformer, n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it overrides, from ghost to non-ghost", parameterKind, n.Name, thing, name);
}
else if (!ResolvedTypesAreTheSame(o.Type, n.Type))
{
- reporter.Error(n.tok, "the type of {0} '{1}' is different from the type of the same {0} in the corresponding {2} in the module it overrides ('{3}' instead of '{4}')", parameterKind, n.Name, thing, n.Type, o.Type);
+ reporter.Error(MessageSource.RefinementTransformer, n.tok, "the type of {0} '{1}' is different from the type of the same {0} in the corresponding {2} in the module it overrides ('{3}' instead of '{4}')", parameterKind, n.Name, thing, n.Type, o.Type);
}
}
}
@@ -948,19 +990,19 @@ namespace Microsoft.Dafny
Contract.Requires(thing != null);
Contract.Requires(parameterKind != null);
if (old.Count != nw.Count) {
- reporter.Error(tok, "{0} '{1}' is declared with a different number of {2} ({3} instead of {4}) than the corresponding {0} in the module it refines", thing, name, parameterKind, nw.Count, old.Count);
+ reporter.Error(MessageSource.RefinementTransformer, tok, "{0} '{1}' is declared with a different number of {2} ({3} instead of {4}) than the corresponding {0} in the module it refines", thing, name, parameterKind, nw.Count, old.Count);
} else {
for (int i = 0; i < old.Count; i++) {
var o = old[i];
var n = nw[i];
if (o.Name != n.Name) {
- reporter.Error(n.tok, "there is a difference in name of {0} {1} ('{2}' versus '{3}') of {4} {5} compared to corresponding {4} in the module it refines", parameterKind, i, n.Name, o.Name, thing, name);
+ reporter.Error(MessageSource.RefinementTransformer, n.tok, "there is a difference in name of {0} {1} ('{2}' versus '{3}') of {4} {5} compared to corresponding {4} in the module it refines", parameterKind, i, n.Name, o.Name, thing, name);
} else if (!o.IsGhost && n.IsGhost) {
- reporter.Error(n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from non-ghost to ghost", parameterKind, n.Name, thing, name);
+ reporter.Error(MessageSource.RefinementTransformer, n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from non-ghost to ghost", parameterKind, n.Name, thing, name);
} else if (o.IsGhost && !n.IsGhost) {
- reporter.Error(n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from ghost to non-ghost", parameterKind, n.Name, thing, name);
+ reporter.Error(MessageSource.RefinementTransformer, n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from ghost to non-ghost", parameterKind, n.Name, thing, name);
} else if (!TypesAreSyntacticallyEqual(o.Type, n.Type)) {
- reporter.Error(n.tok, "the type of {0} '{1}' is different from the type of the same {0} in the corresponding {2} in the module it refines ('{3}' instead of '{4}')", parameterKind, n.Name, thing, n.Type, o.Type);
+ reporter.Error(MessageSource.RefinementTransformer, n.tok, "the type of {0} '{1}' is different from the type of the same {0} in the corresponding {2} in the module it refines ('{3}' instead of '{4}')", parameterKind, n.Name, thing, n.Type, o.Type);
}
}
}
@@ -986,7 +1028,7 @@ namespace Microsoft.Dafny
} else if (((SkeletonStatement)cur).S == null) {
// the "..." matches the empty statement sequence
} else {
- reporter.Error(cur.Tok, "skeleton statement does not match old statement");
+ reporter.Error(MessageSource.RefinementTransformer, cur.Tok, "skeleton statement does not match old statement");
}
i++;
} else {
@@ -1040,7 +1082,7 @@ namespace Microsoft.Dafny
Contract.Assert(c.NameReplacements.Count == c.ExprReplacements.Count);
for (int k = 0; k < c.NameReplacements.Count; k++) {
if (subExprs.ContainsKey(c.NameReplacements[k].val)) {
- reporter.Error(c.NameReplacements[k], "replacement definition must contain at most one definition for a given label");
+ reporter.Error(MessageSource.RefinementTransformer, c.NameReplacements[k], "replacement definition must contain at most one definition for a given label");
} else subExprs.Add(c.NameReplacements[k].val, c.ExprReplacements[k]);
}
subber = new SubstitutionCloner(subExprs, rawCloner);
@@ -1061,12 +1103,12 @@ namespace Microsoft.Dafny
oldS = oldStmt.Body[j];
}
if (hoverTextA.Length != 0) {
- ReportAdditionalInformation(c.Tok, hoverTextA, 3);
+ reporter.Info(MessageSource.RefinementTransformer, c.Tok, hoverTextA);
}
if (subber != null && subber.SubstitutionsMade.Count < subber.Exprs.Count) {
foreach (var s in subber.SubstitutionsMade)
subber.Exprs.Remove(s);
- reporter.Error(c.Tok, "could not find labeled expression(s): " + Util.Comma(", ", subber.Exprs.Keys, x => x));
+ reporter.Error(MessageSource.RefinementTransformer, c.Tok, "could not find labeled expression(s): " + Util.Comma(", ", subber.Exprs.Keys, x => x));
}
}
i++;
@@ -1076,7 +1118,7 @@ namespace Microsoft.Dafny
Contract.Assert(c.ConditionOmitted);
var oldAssume = oldS as PredicateStmt;
if (oldAssume == null) {
- reporter.Error(cur.Tok, "assert template does not match inherited statement");
+ reporter.Error(MessageSource.RefinementTransformer, cur.Tok, "assert template does not match inherited statement");
i++;
} else {
// Clone the expression, but among the new assert's attributes, indicate
@@ -1087,7 +1129,7 @@ namespace Microsoft.Dafny
var attrs = refinementCloner.MergeAttributes(oldAssume.Attributes, skel.Attributes);
body.Add(new AssertStmt(new Translator.ForceCheckToken(skel.Tok), new Translator.ForceCheckToken(skel.EndTok),
e, new Attributes("prependAssertToken", new List<Expression>(), attrs)));
- ReportAdditionalInformation(c.ConditionEllipsis, "assume->assert: " + Printer.ExprToString(e), 3);
+ reporter.Info(MessageSource.RefinementTransformer, c.ConditionEllipsis, "assume->assert: " + Printer.ExprToString(e));
i++; j++;
}
@@ -1096,13 +1138,13 @@ namespace Microsoft.Dafny
Contract.Assert(c.ConditionOmitted);
var oldAssume = oldS as AssumeStmt;
if (oldAssume == null) {
- reporter.Error(cur.Tok, "assume template does not match inherited statement");
+ reporter.Error(MessageSource.RefinementTransformer, cur.Tok, "assume template does not match inherited statement");
i++;
} else {
var e = refinementCloner.CloneExpr(oldAssume.Expr);
var attrs = refinementCloner.MergeAttributes(oldAssume.Attributes, skel.Attributes);
body.Add(new AssumeStmt(skel.Tok, skel.EndTok, e, attrs));
- ReportAdditionalInformation(c.ConditionEllipsis, Printer.ExprToString(e), 3);
+ reporter.Info(MessageSource.RefinementTransformer, c.ConditionEllipsis, Printer.ExprToString(e));
i++; j++;
}
@@ -1111,15 +1153,15 @@ namespace Microsoft.Dafny
Contract.Assert(c.ConditionOmitted);
var oldIf = oldS as IfStmt;
if (oldIf == null) {
- reporter.Error(cur.Tok, "if-statement template does not match inherited statement");
+ reporter.Error(MessageSource.RefinementTransformer, cur.Tok, "if-statement template does not match inherited statement");
i++;
} else {
var resultingThen = MergeBlockStmt(skel.Thn, oldIf.Thn);
var resultingElse = MergeElse(skel.Els, oldIf.Els);
var e = refinementCloner.CloneExpr(oldIf.Guard);
- var r = new IfStmt(skel.Tok, skel.EndTok, e, resultingThen, resultingElse);
+ var r = new IfStmt(skel.Tok, skel.EndTok, oldIf.IsExistentialGuard, e, resultingThen, resultingElse);
body.Add(r);
- ReportAdditionalInformation(c.ConditionEllipsis, Printer.GuardToString(e), 3);
+ reporter.Info(MessageSource.RefinementTransformer, c.ConditionEllipsis, Printer.GuardToString(oldIf.IsExistentialGuard, e));
i++; j++;
}
@@ -1127,16 +1169,16 @@ namespace Microsoft.Dafny
var skel = (WhileStmt)S;
var oldWhile = oldS as WhileStmt;
if (oldWhile == null) {
- reporter.Error(cur.Tok, "while-statement template does not match inherited statement");
+ reporter.Error(MessageSource.RefinementTransformer, cur.Tok, "while-statement template does not match inherited statement");
i++;
} else {
Expression guard;
if (c.ConditionOmitted) {
guard = refinementCloner.CloneExpr(oldWhile.Guard);
- ReportAdditionalInformation(c.ConditionEllipsis, Printer.GuardToString(oldWhile.Guard), 3);
+ reporter.Info(MessageSource.RefinementTransformer, c.ConditionEllipsis, Printer.GuardToString(false, oldWhile.Guard));
} else {
if (oldWhile.Guard != null) {
- reporter.Error(skel.Guard.tok, "a skeleton while statement with a guard can only replace a while statement with a non-deterministic guard");
+ reporter.Error(MessageSource.RefinementTransformer, skel.Guard.tok, "a skeleton while statement with a guard can only replace a while statement with a non-deterministic guard");
}
guard = skel.Guard;
}
@@ -1152,7 +1194,7 @@ namespace Microsoft.Dafny
Contract.Assert(c.ConditionOmitted);
var oldModifyStmt = oldS as ModifyStmt;
if (oldModifyStmt == null) {
- reporter.Error(cur.Tok, "modify template does not match inherited statement");
+ reporter.Error(MessageSource.RefinementTransformer, cur.Tok, "modify template does not match inherited statement");
i++;
} else {
var mod = refinementCloner.CloneSpecFrameExpr(oldModifyStmt.Mod);
@@ -1162,13 +1204,13 @@ namespace Microsoft.Dafny
} else if (oldModifyStmt.Body == null) {
mbody = skel.Body;
} else if (skel.Body == null) {
- reporter.Error(cur.Tok, "modify template must have a body if the inherited modify statement does");
+ reporter.Error(MessageSource.RefinementTransformer, cur.Tok, "modify template must have a body if the inherited modify statement does");
mbody = null;
} else {
mbody = MergeBlockStmt(skel.Body, oldModifyStmt.Body);
}
body.Add(new ModifyStmt(skel.Tok, skel.EndTok, mod.Expressions, mod.Attributes, mbody));
- ReportAdditionalInformation(c.ConditionEllipsis, Printer.FrameExprListToString(mod.Expressions), 3);
+ reporter.Info(MessageSource.RefinementTransformer, c.ConditionEllipsis, Printer.FrameExprListToString(mod.Expressions));
i++; j++;
}
@@ -1284,7 +1326,7 @@ namespace Microsoft.Dafny
var cNew = (IfStmt)cur;
var cOld = oldS as IfStmt;
if (cOld != null && cOld.Guard == null) {
- var r = new IfStmt(cNew.Tok, cNew.EndTok, cNew.Guard, MergeBlockStmt(cNew.Thn, cOld.Thn), MergeElse(cNew.Els, cOld.Els));
+ var r = new IfStmt(cNew.Tok, cNew.EndTok, cNew.IsExistentialGuard, cNew.Guard, MergeBlockStmt(cNew.Thn, cOld.Thn), MergeElse(cNew.Els, cOld.Els));
body.Add(r);
i++; j++;
} else {
@@ -1331,7 +1373,7 @@ namespace Microsoft.Dafny
sep = "\n";
}
if (hoverText.Length != 0) {
- ReportAdditionalInformation(skeleton.EndTok, hoverText, 3);
+ reporter.Info(MessageSource.RefinementTransformer, skeleton.EndTok, hoverText);
}
return new BlockStmt(skeleton.Tok, skeleton.EndTok, body);
}
@@ -1437,7 +1479,7 @@ namespace Microsoft.Dafny
} else {
if (!Contract.Exists(cOld.Decreases.Expressions, e => e is WildcardExpr)) {
// If the previous loop was not specified with "decreases *", then the new loop is not allowed to provide any "decreases" clause.
- reporter.Error(cNew.Decreases.Expressions[0].tok, "a refining loop can provide a decreases clause only if the loop being refined was declared with 'decreases *'");
+ reporter.Error(MessageSource.RefinementTransformer, cNew.Decreases.Expressions[0].tok, "a refining loop can provide a decreases clause only if the loop being refined was declared with 'decreases *'");
}
decr = cNew.Decreases;
}
@@ -1479,9 +1521,9 @@ namespace Microsoft.Dafny
void MergeAddStatement(Statement s, List<Statement> stmtList) {
Contract.Requires(s != null);
Contract.Requires(stmtList != null);
- var prevErrorCount = reporter.ErrorCount;
+ var prevErrorCount = reporter.Count(ErrorLevel.Error);
CheckIsOkayNewStatement(s, new Stack<string>(), 0);
- if (reporter.ErrorCount == prevErrorCount) {
+ if (reporter.Count(ErrorLevel.Error) == prevErrorCount) {
stmtList.Add(s);
}
}
@@ -1498,33 +1540,30 @@ namespace Microsoft.Dafny
labels.Push(n.Data.Name);
}
if (s is SkeletonStatement) {
- reporter.Error(s, "skeleton statement may not be used here; it does not have a matching statement in what is being replaced");
+ reporter.Error(MessageSource.RefinementTransformer, s, "skeleton statement may not be used here; it does not have a matching statement in what is being replaced");
} else if (s is ReturnStmt) {
// allow return statements, but make note of that this requires verifying the postcondition
((ReturnStmt)s).ReverifyPost = true;
} else if (s is YieldStmt) {
- reporter.Error(s, "yield statements are not allowed in skeletons");
+ reporter.Error(MessageSource.RefinementTransformer, s, "yield statements are not allowed in skeletons");
} else if (s is BreakStmt) {
var b = (BreakStmt)s;
if (b.TargetLabel != null ? !labels.Contains(b.TargetLabel) : loopLevels < b.BreakCount) {
- reporter.Error(s, "break statement in skeleton is not allowed to break outside the skeleton fragment");
+ reporter.Error(MessageSource.RefinementTransformer, s, "break statement in skeleton is not allowed to break outside the skeleton fragment");
}
} else if (s is AssignStmt) {
// TODO: To be a refinement automatically (that is, without any further verification), only variables and fields defined
// in this module are allowed. This needs to be checked. If the LHS refers to an l-value that was not declared within
// this module, then either an error should be reported or the Translator needs to know to translate new proof obligations.
var a = (AssignStmt)s;
- reporter.Error(a.Tok, "cannot have assignment statement");
+ reporter.Error(MessageSource.RefinementTransformer, a.Tok, "cannot have assignment statement");
} else if (s is ConcreteUpdateStatement) {
postTasks.Enqueue(() =>
{
- CheckIsOkayUpdateStmt((ConcreteUpdateStatement)s, moduleUnderConstruction, reporter);
+ CheckIsOkayUpdateStmt((ConcreteUpdateStatement)s, moduleUnderConstruction);
});
} else if (s is CallStmt) {
- reporter.Error(s.Tok, "cannot have call statement");
- } else if (s is ForallStmt) {
- if (((ForallStmt)s).Kind == ForallStmt.ParBodyKind.Assign) // allow Proof and Call (as neither touch any existing state)
- reporter.Error(s.Tok, "cannot have forall statement");
+ reporter.Error(MessageSource.RefinementTransformer, s.Tok, "cannot have call statement");
} else {
if (s is WhileStmt || s is AlternativeLoopStmt) {
loopLevels++;
@@ -1540,7 +1579,7 @@ namespace Microsoft.Dafny
}
// Checks that statement stmt, defined in the constructed module m, is a refinement of skip in the parent module
- void CheckIsOkayUpdateStmt(ConcreteUpdateStatement stmt, ModuleDefinition m, ResolutionErrorReporter reporter) {
+ void CheckIsOkayUpdateStmt(ConcreteUpdateStatement stmt, ModuleDefinition m) {
foreach (var lhs in stmt.Lhss) {
var l = lhs.Resolved;
if (l is IdentifierExpr) {
@@ -1548,23 +1587,23 @@ namespace Microsoft.Dafny
Contract.Assert(ident.Var is LocalVariable || ident.Var is Formal); // LHS identifier expressions must be locals or out parameters (ie. formals)
if ((ident.Var is LocalVariable && RefinementToken.IsInherited(((LocalVariable)ident.Var).Tok, m)) || ident.Var is Formal) {
// for some reason, formals are not considered to be inherited.
- reporter.Error(l.tok, "refinement method cannot assign to variable defined in parent module ('{0}')", ident.Var.Name);
+ reporter.Error(MessageSource.RefinementTransformer, l.tok, "refinement method cannot assign to variable defined in parent module ('{0}')", ident.Var.Name);
}
} else if (l is MemberSelectExpr) {
var member = ((MemberSelectExpr)l).Member;
if (RefinementToken.IsInherited(member.tok, m)) {
- reporter.Error(l.tok, "refinement method cannot assign to a field defined in parent module ('{0}')", member.Name);
+ reporter.Error(MessageSource.RefinementTransformer, l.tok, "refinement method cannot assign to a field defined in parent module ('{0}')", member.Name);
}
} else {
// must be an array element
- reporter.Error(l.tok, "new assignments in a refinement method can only assign to state that the module defines (which never includes array elements)");
+ reporter.Error(MessageSource.RefinementTransformer, l.tok, "new assignments in a refinement method can only assign to state that the module defines (which never includes array elements)");
}
}
if (stmt is UpdateStmt) {
var s = (UpdateStmt)stmt;
foreach (var rhs in s.Rhss) {
if (rhs.CanAffectPreviouslyKnownExpressions) {
- reporter.Error(rhs.Tok, "assignment RHS in refinement method is not allowed to affect previously defined state");
+ reporter.Error(MessageSource.RefinementTransformer, rhs.Tok, "assignment RHS in refinement method is not allowed to affect previously defined state");
}
}
}
diff --git a/Source/Dafny/Reporting.cs b/Source/Dafny/Reporting.cs
new file mode 100644
index 00000000..b36efc55
--- /dev/null
+++ b/Source/Dafny/Reporting.cs
@@ -0,0 +1,160 @@
+using Microsoft.Boogie;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Dafny {
+ public enum ErrorLevel {
+ Info, Warning, Error
+ }
+
+ public enum MessageSource {
+ Parser, Resolver, Translator, Rewriter, Other,
+ RefinementTransformer,
+ Cloner
+ }
+
+ public struct ErrorMessage {
+ public IToken token;
+ public string message;
+ public MessageSource source;
+ }
+
+ public abstract class ErrorReporter {
+ public bool ErrorsOnly { get; set; }
+ public Dictionary<ErrorLevel, List<ErrorMessage>> AllMessages { get; private set; }
+
+ protected ErrorReporter() {
+ ErrorsOnly = false;
+ AllMessages = new Dictionary<ErrorLevel, List<ErrorMessage>>();
+ AllMessages[ErrorLevel.Error] = new List<ErrorMessage>();
+ AllMessages[ErrorLevel.Warning] = new List<ErrorMessage>();
+ AllMessages[ErrorLevel.Info] = new List<ErrorMessage>();
+ }
+
+ // This is the only thing that needs to be overriden
+ public virtual bool Message(MessageSource source, ErrorLevel level, IToken tok, string msg) {
+ bool discard = (ErrorsOnly && level != ErrorLevel.Error) || // Discard non-errors if ErrorsOnly is set
+ (tok is TokenWrapper && !(tok is NestedToken) && !(tok is RefinementToken)); // Discard wrapped tokens, except for nested and refinement
+ if (!discard) {
+ AllMessages[level].Add(new ErrorMessage { token = tok, message = msg });
+ }
+ return !discard;
+ }
+
+ public int Count(ErrorLevel level) {
+ return AllMessages[level].Count;
+ }
+
+ public void Error(MessageSource source, IToken tok, string msg) {
+ Contract.Requires(tok != null);
+ Contract.Requires(msg != null);
+ Message(source, ErrorLevel.Error, tok, msg);
+ }
+
+ // This method required by the Parser
+ internal void Error(MessageSource source, string filename, int line, int col, string msg) {
+ var tok = new Token(line, col);
+ tok.filename = filename;
+ Error(source, tok, msg);
+ }
+
+ public void Error(MessageSource source, IToken tok, string msg, params object[] args) {
+ Contract.Requires(tok != null);
+ Contract.Requires(msg != null);
+ Error(source, tok, String.Format(msg, args));
+ }
+
+ public void Error(MessageSource source, Declaration d, string msg, params object[] args) {
+ Contract.Requires(d != null);
+ Contract.Requires(msg != null);
+ Error(source, d.tok, msg, args);
+ }
+
+ public void Error(MessageSource source, Statement s, string msg, params object[] args) {
+ Contract.Requires(s != null);
+ Contract.Requires(msg != null);
+ Error(source, s.Tok, msg, args);
+ }
+
+ public void Error(MessageSource source, NonglobalVariable v, string msg, params object[] args) {
+ Contract.Requires(v != null);
+ Contract.Requires(msg != null);
+ Error(source, v.tok, msg, args);
+ }
+
+ public void Error(MessageSource source, Expression e, string msg, params object[] args) {
+ Contract.Requires(e != null);
+ Contract.Requires(msg != null);
+ Error(source, e.tok, msg, args);
+ }
+
+ public void Warning(MessageSource source, IToken tok, string msg) {
+ Contract.Requires(tok != null);
+ Contract.Requires(msg != null);
+ Message(source, ErrorLevel.Warning, tok, msg);
+ }
+
+ public void Warning(MessageSource source, IToken tok, string msg, params object[] args) {
+ Contract.Requires(tok != null);
+ Contract.Requires(msg != null);
+ Warning(source, tok, String.Format(msg, args));
+ }
+
+ public void Info(MessageSource source, IToken tok, string msg) {
+ Contract.Requires(tok != null);
+ Contract.Requires(msg != null);
+ Message(source, ErrorLevel.Info, tok, msg);
+ }
+
+ public void Info(MessageSource source, IToken tok, string msg, params object[] args) {
+ Contract.Requires(tok != null);
+ Contract.Requires(msg != null);
+ Info(source, tok, String.Format(msg, args));
+ }
+
+ public static string ErrorToString(ErrorLevel header, IToken tok, string msg) {
+ return ErrorToString_Internal(": " + header.ToString(), tok.filename, tok.line, tok.col, ": " + msg);
+ }
+
+ public static string ErrorToString(ErrorLevel header, string filename, int oneBasedLine, int oneBasedColumn, string msg) {
+ return ErrorToString_Internal(": " + header.ToString(), filename, oneBasedLine, oneBasedColumn, ": " + msg);
+ }
+
+ public static string ErrorToString_Internal(string header, string filename, int oneBasedLine, int oneBasedColumn, string msg) {
+ return String.Format("{0}({1},{2}){3}{4}", filename, oneBasedLine, oneBasedColumn - 1, header, msg ?? "");
+ }
+ }
+
+ public class ConsoleErrorReporter : ErrorReporter {
+ private ConsoleColor ColorForLevel(ErrorLevel level) {
+ switch (level) {
+ case ErrorLevel.Error:
+ return ConsoleColor.Red;
+ case ErrorLevel.Warning:
+ return ConsoleColor.Yellow;
+ case ErrorLevel.Info:
+ return ConsoleColor.Green;
+ default:
+ throw new cce.UnreachableException();
+ }
+ }
+
+ public override bool Message(MessageSource source, ErrorLevel level, IToken tok, string msg) {
+ if (base.Message(source, level, tok, msg) && ((DafnyOptions.O != null && DafnyOptions.O.PrintTooltips) || level != ErrorLevel.Info)) {
+ // Extra indent added to make it easier to distinguish multiline error messages for clients that rely on the CLI
+ msg = msg.Replace(Environment.NewLine, Environment.NewLine + " ");
+
+ ConsoleColor previousColor = Console.ForegroundColor;
+ Console.ForegroundColor = ColorForLevel(level);
+ Console.WriteLine(ErrorToString(level, tok, msg));
+ Console.ForegroundColor = previousColor;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+}
diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs
index f7ec135b..b6e783b4 100644
--- a/Source/Dafny/Resolver.cs
+++ b/Source/Dafny/Resolver.cs
@@ -12,67 +12,11 @@ using Microsoft.Boogie;
namespace Microsoft.Dafny
{
- public class ResolutionErrorReporter
- {
- public int ErrorCount = 0;
-
- /// <summary>
- /// This method is virtual, because it is overridden in the VSX plug-in for Dafny.
- /// </summary>
- public virtual void Error(IToken tok, string msg, params object[] args) {
- Contract.Requires(tok != null);
- Contract.Requires(msg != null);
- ConsoleColor col = Console.ForegroundColor;
- Console.ForegroundColor = ConsoleColor.Red;
- Console.WriteLine("{0}({1},{2}): Error: {3}",
- DafnyOptions.Clo.UseBaseNameForFileName ? System.IO.Path.GetFileName(tok.filename) : tok.filename, tok.line, tok.col - 1,
- string.Format(msg, args));
- Console.ForegroundColor = col;
- ErrorCount++;
- }
- public void Error(Declaration d, string msg, params object[] args) {
- Contract.Requires(d != null);
- Contract.Requires(msg != null);
- Error(d.tok, msg, args);
- }
- public void Error(Statement s, string msg, params object[] args) {
- Contract.Requires(s != null);
- Contract.Requires(msg != null);
- Error(s.Tok, msg, args);
- }
- public void Error(NonglobalVariable v, string msg, params object[] args) {
- Contract.Requires(v != null);
- Contract.Requires(msg != null);
- Error(v.tok, msg, args);
- }
- public void Error(Expression e, string msg, params object[] args) {
- Contract.Requires(e != null);
- Contract.Requires(msg != null);
- Error(e.tok, msg, args);
- }
- public void Warning(IToken tok, string msg, params object[] args) {
- Contract.Requires(tok != null);
- Contract.Requires(msg != null);
- ConsoleColor col = Console.ForegroundColor;
- Console.ForegroundColor = ConsoleColor.Yellow;
- Console.WriteLine("{0}({1},{2}): Warning: {3}",
- DafnyOptions.Clo.UseBaseNameForFileName ? System.IO.Path.GetFileName(tok.filename) : tok.filename, tok.line, tok.col - 1,
- string.Format(msg, args));
- Console.ForegroundColor = col;
- }
- }
-
- public struct AdditionalInformation
- {
- public IToken Token;
- public string Text;
- public int Length;
- }
-
- public class Resolver : ResolutionErrorReporter
+ public class Resolver
{
readonly BuiltIns builtIns;
+ readonly ErrorReporter reporter;
ModuleSignature moduleInfo = null;
FreshIdGenerator defaultTempVarIdGenerator;
@@ -91,18 +35,6 @@ namespace Microsoft.Dafny
return defaultTempVarIdGenerator.FreshId(prefix);
}
- public Action<AdditionalInformation> AdditionalInformationReporter;
-
- internal void ReportAdditionalInformation(IToken token, string text, int length)
- {
- Contract.Requires(token != null);
- Contract.Requires(text != null);
- Contract.Requires(0 <= length);
- if (AdditionalInformationReporter != null) {
- AdditionalInformationReporter(new AdditionalInformation { Token = token, Text = text, Length = length });
- }
- }
-
interface IAmbiguousThing<Thing>
{
/// <summary>
@@ -251,6 +183,7 @@ namespace Microsoft.Dafny
public Resolver(Program prog) {
Contract.Requires(prog != null);
builtIns = prog.BuiltIns;
+ reporter = prog.reporter;
// Populate the members of the basic types
var trunc = new SpecialField(Token.NoToken, "Trunc", "ToBigInteger()", "", "", false, false, false, Type.Int, null);
basicTypeMembers[(int)BasicTypeVariety.Real].Add(trunc.Name, trunc);
@@ -264,22 +197,49 @@ namespace Microsoft.Dafny
Contract.Invariant(cce.NonNullDictionaryAndValues(datatypeCtors) && Contract.ForAll(datatypeCtors.Values, v => cce.NonNullDictionaryAndValues(v)));
}
+ /// <summary>
+ /// Check that now two modules that are being compiled have the same CompileName.
+ ///
+ /// This could happen if they are given the same name using the 'extern' declaration modifier.
+ /// </summary>
+ /// <param name="prog">The Dafny program being compiled.</param>
+ void CheckDupModuleNames(Program prog)
+ {
+ // Check that none of the modules have the same CompileName.
+ Dictionary<string, ModuleDefinition> compileNameMap = new Dictionary<string, ModuleDefinition>();
+ foreach (ModuleDefinition m in prog.CompileModules) {
+ if (m.IsAbstract) {
+ // the purpose of an abstract module is to skip compilation
+ continue;
+ }
+ string compileName = m.CompileName;
+ ModuleDefinition priorModDef;
+ if (compileNameMap.TryGetValue(compileName, out priorModDef)) {
+ reporter.Error(MessageSource.Resolver, m.tok,
+ "Modules '{0}' and '{1}' both have CompileName '{2}'.",
+ priorModDef.tok.val, m.tok.val, compileName);
+ }
+ else {
+ compileNameMap.Add(compileName, m);
+ }
+ }
+ }
public void ResolveProgram(Program prog) {
Contract.Requires(prog != null);
- var origErrorCount = ErrorCount;
+ var origErrorCount = reporter.Count(ErrorLevel.Error); //TODO: This is used further below, but not in the >0 comparisons in the next few lines. Is that right?
var bindings = new ModuleBindings(null);
var b = BindModuleNames(prog.DefaultModuleDef, bindings);
bindings.BindName("_module", prog.DefaultModule, b);
- if (ErrorCount > 0) { return; } // if there were errors, then the implict ModuleBindings data structure invariant
+ if (reporter.Count(ErrorLevel.Error) > 0) { return; } // if there were errors, then the implict ModuleBindings data structure invariant
// is violated, so Processing dependencies will not succeed.
ProcessDependencies(prog.DefaultModule, b, dependencies);
// check for cycles in the import graph
List<ModuleDecl> cycle = dependencies.TryFindCycle();
if (cycle != null) {
var cy = Util.Comma(" -> ", cycle, m => m.Name);
- Error(cycle[0], "module definition contains a cycle (note: parent modules implicitly depend on submodules): {0}", cy);
+ reporter.Error(MessageSource.Resolver, cycle[0], "module definition contains a cycle (note: parent modules implicitly depend on submodules): {0}", cy);
}
- if (ErrorCount > 0) { return; } // give up on trying to resolve anything else
+ if (reporter.Count(ErrorLevel.Error) > 0) { return; } // give up on trying to resolve anything else
// fill in module heights
List<ModuleDecl> sortedDecls = dependencies.TopologicallySortedComponents();
@@ -295,16 +255,51 @@ namespace Microsoft.Dafny
}
var rewriters = new List<IRewriter>();
- var refinementTransformer = new RefinementTransformer(this, AdditionalInformationReporter, prog);
+ var refinementTransformer = new RefinementTransformer(prog);
rewriters.Add(refinementTransformer);
- rewriters.Add(new AutoContractsRewriter());
- var opaqueRewriter = new OpaqueFunctionRewriter(this);
- rewriters.Add(new AutoReqFunctionRewriter(this, opaqueRewriter));
+ rewriters.Add(new AutoContractsRewriter(reporter));
+ var opaqueRewriter = new OpaqueFunctionRewriter(this.reporter);
+ rewriters.Add(new AutoReqFunctionRewriter(this.reporter, opaqueRewriter));
rewriters.Add(opaqueRewriter);
- rewriters.Add(new TimeLimitRewriter());
+ rewriters.Add(new TimeLimitRewriter(reporter));
+ rewriters.Add(new ForallStmtRewriter(reporter));
+
+ if (DafnyOptions.O.AutoTriggers) {
+ rewriters.Add(new QuantifierSplittingRewriter(reporter));
+ rewriters.Add(new TriggerGeneratingRewriter(reporter));
+ }
+
+ rewriters.Add(new InductionRewriter(reporter));
systemNameInfo = RegisterTopLevelDecls(prog.BuiltIns.SystemModule, false);
prog.CompileModules.Add(prog.BuiltIns.SystemModule);
+
+ // first, we need to detect which top-level modules have exclusive refinement relationships.
+ foreach (ModuleDecl decl in sortedDecls) {
+ if (decl is LiteralModuleDecl) {
+ var literalDecl = (LiteralModuleDecl)decl;
+ var m = literalDecl.ModuleDef;
+ if (m.RefinementBaseRoot != null) {
+ if (m.IsExclusiveRefinement) {
+ foreach (var d in sortedDecls) {
+ // refinement dependencies won't be later in the sorted module list than the one we're looking at.
+ if (Object.ReferenceEquals(d, decl)) {
+ break;
+ }
+ if (d is LiteralModuleDecl) {
+ var ld = (LiteralModuleDecl)d;
+ // currently, only exclusive refinements of top-level modules are supported.
+ if (string.Equals(m.RefinementBaseName[0].val, m.RefinementBaseRoot.Name, StringComparison.InvariantCulture)
+ && string.Equals(m.RefinementBaseName[0].val, ld.ModuleDef.Name, StringComparison.InvariantCulture)) {
+ ld.ModuleDef.ExclusiveRefinementCount += 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
foreach (var decl in sortedDecls) {
if (decl is LiteralModuleDecl) {
// The declaration is a literal module, so it has members and such that we need
@@ -318,7 +313,7 @@ namespace Microsoft.Dafny
var literalDecl = (LiteralModuleDecl)decl;
var m = literalDecl.ModuleDef;
- var errorCount = ErrorCount;
+ var errorCount = reporter.Count(ErrorLevel.Error);
foreach (var r in rewriters) {
r.PreResolve(m);
}
@@ -327,18 +322,21 @@ namespace Microsoft.Dafny
literalDecl.Signature.Refines = refinementTransformer.RefinedSig;
var sig = literalDecl.Signature;
// set up environment
- var preResolveErrorCount = ErrorCount;
+ var preResolveErrorCount = reporter.Count(ErrorLevel.Error);
ResolveModuleDefinition(m, sig);
+ ResolveModuleExport(literalDecl, sig);
foreach (var r in rewriters) {
- if (ErrorCount != preResolveErrorCount) {
+ if (reporter.Count(ErrorLevel.Error) != preResolveErrorCount) {
break;
}
r.PostResolve(m);
}
- if (ErrorCount == errorCount && !m.IsAbstract) {
+ if (reporter.Count(ErrorLevel.Error) == errorCount && !m.IsAbstract) {
// compilation should only proceed if everything is good, including the signature (which preResolveErrorCount does not include);
Contract.Assert(!useCompileSignatures);
useCompileSignatures = true; // set Resolver-global flag to indicate that Signatures should be followed to their CompiledSignature
+ var oldErrorsOnly = reporter.ErrorsOnly;
+ reporter.ErrorsOnly = true; // turn off warning reporting for the clone
var nw = new Cloner().CloneModuleDefinition(m, m.CompileName + "_Compile");
var compileSig = RegisterTopLevelDecls(nw, true);
compileSig.Refines = refinementTransformer.RefinedSig;
@@ -346,12 +344,13 @@ namespace Microsoft.Dafny
ResolveModuleDefinition(nw, compileSig);
prog.CompileModules.Add(nw);
useCompileSignatures = false; // reset the flag
+ reporter.ErrorsOnly = oldErrorsOnly;
}
} else if (decl is AliasModuleDecl) {
var alias = (AliasModuleDecl)decl;
// resolve the path
ModuleSignature p;
- if (ResolvePath(alias.Root, alias.Path, out p, this)) {
+ if (ResolvePath(alias.Root, alias.Path, out p, reporter)) {
alias.Signature = p;
} else {
alias.Signature = new ModuleSignature(); // there was an error, give it a valid but empty signature
@@ -359,29 +358,41 @@ namespace Microsoft.Dafny
} else if (decl is ModuleFacadeDecl) {
var abs = (ModuleFacadeDecl)decl;
ModuleSignature p;
- if (ResolvePath(abs.Root, abs.Path, out p, this)) {
- abs.Signature = MakeAbstractSignature(p, abs.FullCompileName, abs.Height, prog.Modules);
+ if (ResolvePath(abs.Root, abs.Path, out p, reporter)) {
abs.OriginalSignature = p;
- ModuleSignature compileSig;
- if (abs.CompilePath != null) {
- if (ResolvePath(abs.CompileRoot, abs.CompilePath, out compileSig, this)) {
- if (refinementTransformer.CheckIsRefinement(compileSig, p)) {
- abs.Signature.CompileSignature = compileSig;
- } else {
- Error(abs.CompilePath[0],
- "module " + Util.Comma(".", abs.CompilePath, x => x.val) + " must be a refinement of " + Util.Comma(".", abs.Path, x => x.val));
+ // ModuleDefinition.ExclusiveRefinement may not be set at this point but ExclusiveRefinementCount will be.
+ if (0 == abs.Root.Signature.ModuleDef.ExclusiveRefinementCount) {
+ abs.Signature = MakeAbstractSignature(p, abs.FullCompileName, abs.Height, prog.Modules);
+ ModuleSignature compileSig;
+ if (abs.CompilePath != null) {
+ if (ResolvePath(abs.CompileRoot, abs.CompilePath, out compileSig, reporter)) {
+ if (refinementTransformer.CheckIsRefinement(compileSig, p)) {
+ abs.Signature.CompileSignature = compileSig;
+ } else {
+ reporter.Error(MessageSource.Resolver,
+ abs.CompilePath[0],
+ "module " + Util.Comma(".", abs.CompilePath, x => x.val) + " must be a refinement of "
+ + Util.Comma(".", abs.Path, x => x.val));
+ }
+ abs.Signature.IsAbstract = compileSig.IsAbstract;
+ // always keep the ghost information, to supress a spurious error message when the compile module isn't actually a refinement
}
- abs.Signature.IsGhost = compileSig.IsGhost;
- // always keep the ghost information, to supress a spurious error message when the compile module isn't actually a refinement
}
+ } else {
+ abs.Signature = p;
}
} else {
abs.Signature = new ModuleSignature(); // there was an error, give it a valid but empty signature
}
+ } else if (decl is ModuleExportDecl) {
+ ModuleExportDecl export = (ModuleExportDecl)decl;
+ export.Signature = new ModuleSignature();
+ export.Signature.IsAbstract = false;
+ export.Signature.ModuleDef = null;
} else { Contract.Assert(false); }
Contract.Assert(decl.Signature != null);
}
- if (ErrorCount != origErrorCount) {
+ if (reporter.Count(ErrorLevel.Error) != origErrorCount) {
// do nothing else
return;
}
@@ -437,7 +448,7 @@ namespace Microsoft.Dafny
}
foreach (var module in prog.Modules) {
foreach (var iter in ModuleDefinition.AllIteratorDecls(module.TopLevelDecls)) {
- ReportAdditionalInformation(iter.tok, Printer.IteratorClassToString(iter), iter.Name.Length);
+ reporter.Info(MessageSource.Resolver, iter.tok, Printer.IteratorClassToString(iter));
}
}
// fill in other additional information
@@ -455,6 +466,34 @@ namespace Microsoft.Dafny
}
}
}
+
+ // Determine, for each function, whether someone tries to adjust its fuel parameter
+ foreach (var module in prog.Modules) {
+ CheckForFuelAdjustments(module.tok, module.Attributes, module);
+ foreach (var clbl in ModuleDefinition.AllItersAndCallables(module.TopLevelDecls)) {
+ Statement body = null;
+ if (clbl is Method) {
+ body = ((Method)clbl).Body;
+ CheckForFuelAdjustments(clbl.Tok,((Method)clbl).Attributes, module);
+ } else if (clbl is IteratorDecl) {
+ body = ((IteratorDecl)clbl).Body;
+ CheckForFuelAdjustments(clbl.Tok, ((IteratorDecl)clbl).Attributes, module);
+ } else if (clbl is Function) {
+ CheckForFuelAdjustments(clbl.Tok, ((Function)clbl).Attributes, module);
+ var c = new FuelAdjustment_Visitor(this);
+ var bodyExpr = ((Function)clbl).Body;
+ if (bodyExpr != null) {
+ c.Visit(bodyExpr, new FuelAdjustment_Context(module));
+ }
+ }
+ if (body != null) {
+ var c = new FuelAdjustment_Visitor(this);
+ c.Visit(body, new FuelAdjustment_Context(module));
+ }
+ }
+ }
+
+ CheckDupModuleNames(prog);
}
void FillInDefaultDecreasesClauses(Program prog)
@@ -487,17 +526,10 @@ namespace Microsoft.Dafny
showIt = ((Method)m).IsRecursive;
}
if (showIt) {
- s += "decreases ";
- if (m.Decreases.Expressions.Count != 0) {
- string sep = "";
- foreach (var d in m.Decreases.Expressions) {
- s += sep + Printer.ExprToString(d);
- sep = ", ";
- }
- }
+ s += "decreases " + Util.Comma(", ", m.Decreases.Expressions, Printer.ExprToString);
// Note, in the following line, we use the location information for "clbl", not "m". These
// are the same, except in the case where "clbl" is a CoLemma and "m" is a prefix lemma.
- ReportAdditionalInformation(clbl.Tok, s, clbl.Tok.val.Length);
+ reporter.Info(MessageSource.Resolver, clbl.Tok, s);
}
}
}
@@ -520,7 +552,7 @@ namespace Microsoft.Dafny
var decr = clbl.Decreases.Expressions;
if (DafnyOptions.O.Dafnycc) {
if (decr.Count > 1) {
- Error(decr[1].tok, "In dafnycc mode, only one decreases expression is allowed");
+ reporter.Error(MessageSource.Resolver, decr[1].tok, "In dafnycc mode, only one decreases expression is allowed");
}
// In dafnycc mode, only consider first argument
if (decr.Count == 0 && clbl.Ins.Count > 0) {
@@ -597,18 +629,18 @@ namespace Microsoft.Dafny
bvars.Add(oVar);
return
- new SetComprehension(e.tok, bvars,
+ new SetComprehension(e.tok, true, bvars,
new BinaryExpr(e.tok, BinaryExpr.Opcode.In, obj,
new ApplyExpr(e.tok, e, bexprs)
{
- Type = new SetType(new ObjectType())
+ Type = new SetType(true, new ObjectType())
})
{
ResolvedOp = BinaryExpr.ResolvedOpcode.InSet,
Type = Type.Bool
}, obj, null)
{
- Type = new SetType(new ObjectType())
+ Type = new SetType(true, new ObjectType())
};
} else {
return e;
@@ -646,8 +678,8 @@ namespace Microsoft.Dafny
var sInE = new BinaryExpr(e.tok, BinaryExpr.Opcode.In, bvIE, e);
sInE.ResolvedOp = BinaryExpr.ResolvedOpcode.InSeq; // resolve here
sInE.Type = Type.Bool; // resolve here
- var s = new SetComprehension(e.tok, new List<BoundVar>() { bv }, sInE, bvIE, null);
- s.Type = new SetType(new ObjectType()); // resolve here
+ var s = new SetComprehension(e.tok, true, new List<BoundVar>() { bv }, sInE, bvIE, null);
+ s.Type = new SetType(true, new ObjectType()); // resolve here
sets.Add(s);
} else {
// e is already a set
@@ -657,20 +689,20 @@ namespace Microsoft.Dafny
}
}
if (singletons != null) {
- Expression display = new SetDisplayExpr(singletons[0].tok, singletons);
- display.Type = new SetType(new ObjectType()); // resolve here
+ Expression display = new SetDisplayExpr(singletons[0].tok, true, singletons);
+ display.Type = new SetType(true, new ObjectType()); // resolve here
sets.Add(display);
}
if (sets.Count == 0) {
- Expression emptyset = new SetDisplayExpr(Token.NoToken, new List<Expression>());
- emptyset.Type = new SetType(new ObjectType()); // resolve here
+ Expression emptyset = new SetDisplayExpr(Token.NoToken, true, new List<Expression>());
+ emptyset.Type = new SetType(true, new ObjectType()); // resolve here
return emptyset;
} else {
Expression s = sets[0];
for (int i = 1; i < sets.Count; i++) {
BinaryExpr union = new BinaryExpr(s.tok, BinaryExpr.Opcode.Add, s, sets[i]);
union.ResolvedOp = BinaryExpr.ResolvedOpcode.Union; // resolve here
- union.Type = new SetType(new ObjectType()); // resolve here
+ union.Type = new SetType(true, new ObjectType()); // resolve here
s = union;
}
return s;
@@ -678,18 +710,117 @@ namespace Microsoft.Dafny
}
private void ResolveModuleDefinition(ModuleDefinition m, ModuleSignature sig) {
+ Contract.Requires(AllTypeConstraints.Count == 0);
+ Contract.Ensures(AllTypeConstraints.Count == 0);
moduleInfo = MergeSignature(sig, systemNameInfo);
// resolve
var datatypeDependencies = new Graph<IndDatatypeDecl>();
var codatatypeDependencies = new Graph<CoDatatypeDecl>();
- int prevErrorCount = ErrorCount;
- ResolveAttributes(m.Attributes, new ResolveOpts(new NoContext(m.Module), false));
+ int prevErrorCount = reporter.Count(ErrorLevel.Error);
ResolveTopLevelDecls_Signatures(m, m.TopLevelDecls, datatypeDependencies, codatatypeDependencies);
- if (ErrorCount == prevErrorCount) {
- ResolveTopLevelDecls_Meat(m.TopLevelDecls, datatypeDependencies, codatatypeDependencies);
+ Contract.Assert(AllTypeConstraints.Count == 0); // signature resolution does not add any type constraints
+ ResolveAttributes(m.Attributes, new ResolveOpts(new NoContext(m.Module), false)); // Must follow ResolveTopLevelDecls_Signatures, in case attributes refer to members
+ SolveAllTypeConstraints(); // solve any type constraints entailed by the attributes
+ if (reporter.Count(ErrorLevel.Error) == prevErrorCount) {
+ ResolveTopLevelDecls_Core(m.TopLevelDecls, datatypeDependencies, codatatypeDependencies);
+ }
+ }
+
+ // Resolve the exports and detect cycles.
+ private void ResolveModuleExport(LiteralModuleDecl literalDecl, ModuleSignature sig) {
+ ModuleDefinition m = literalDecl.ModuleDef;
+ literalDecl.DefaultExport = sig;
+ Graph<ModuleExportDecl> exportDependencies = new Graph<ModuleExportDecl>();
+ foreach (TopLevelDecl toplevel in m.TopLevelDecls) {
+ if (toplevel is ModuleExportDecl) {
+ ModuleExportDecl d = (ModuleExportDecl) toplevel;
+ exportDependencies.AddVertex(d);
+ foreach (string s in d.Extends) {
+ TopLevelDecl top;
+ if (sig.TopLevels.TryGetValue(s, out top) && top is ModuleExportDecl) {
+ ModuleExportDecl extend = (ModuleExportDecl) top;
+ d.ExtendDecls.Add(extend);
+ exportDependencies.AddEdge(d, extend);
+ } else {
+ reporter.Error(MessageSource.Resolver, m.tok, s + " must be an export of " + m.Name + " to be extended");
+ }
+ }
+ foreach (ExportSignature export in d.Exports) {
+ // check to see if it is a datatype or a member or
+ // static function or method in the enclosing module or its imports
+ TopLevelDecl decl;
+ MemberDecl member;
+ string name = export.Name;
+ if (sig.TopLevels.TryGetValue(name, out decl)) {
+ // Member of the enclosing module
+ export.Decl = decl;
+ } else if (sig.StaticMembers.TryGetValue(name, out member)) {
+ export.Decl = member;
+ } else {
+ reporter.Error(MessageSource.Resolver, d.tok, name + " must be a member of " + m.Name + " to be exported");
+ }
+ }
+ }
}
- }
+ // detect cycles in the extend
+ var cycle = exportDependencies.TryFindCycle();
+ if (cycle != null) {
+ var cy = Util.Comma(" -> ", cycle, c => c.Name);
+ reporter.Error(MessageSource.Resolver, cycle[0], "module export contains a cycle: {0}", cy);
+ return;
+ }
+
+ // fill in the exports for the extends.
+ List<ModuleExportDecl> sortedDecls = exportDependencies.TopologicallySortedComponents();
+ ModuleExportDecl defaultExport = null;
+ foreach (ModuleExportDecl decl in sortedDecls) {
+ if (decl.IsDefault) {
+ if (defaultExport == null) {
+ defaultExport = decl;
+ } else {
+ reporter.Error(MessageSource.Resolver, m.tok, "more than one default export declared in module {0}", m.Name);
+ }
+ }
+ // fill in export signature
+ ModuleSignature signature = decl.Signature;
+ foreach (ModuleExportDecl extend in decl.ExtendDecls) {
+ ModuleSignature s = extend.Signature;
+ foreach (var kv in s.TopLevels) {
+ if (!signature.TopLevels.ContainsKey(kv.Key)) {
+ signature.TopLevels.Add(kv.Key, kv.Value);
+ }
+ }
+ foreach (var kv in s.Ctors) {
+ if (!signature.Ctors.ContainsKey(kv.Key)) {
+ signature.Ctors.Add(kv.Key, kv.Value);
+ }
+ }
+ foreach (var kv in s.StaticMembers) {
+ if (!signature.StaticMembers.ContainsKey(kv.Key)) {
+ signature.StaticMembers.Add(kv.Key, kv.Value);
+ }
+ }
+ }
+ foreach (ExportSignature export in decl.Exports) {
+ if (export.Decl is TopLevelDecl) {
+ signature.TopLevels.Add(export.Name, (TopLevelDecl)export.Decl);
+ } else if (export.Decl is MemberDecl) {
+ signature.StaticMembers.Add(export.Name, (MemberDecl)export.Decl);
+ }
+ }
+ }
+
+ // set the default export if it exists
+ if (defaultExport != null) {
+ literalDecl.DefaultExport = defaultExport.Signature;
+ } else {
+ // if there is at least one exported view, but no exported view marked as default, then defaultExport should be null.
+ if (sortedDecls.Count > 0) {
+ literalDecl.DefaultExport = null;
+ }
+ }
+ }
public class ModuleBindings
{
@@ -744,17 +875,17 @@ namespace Microsoft.Dafny
var subdecl = (LiteralModuleDecl)tld;
var subBindings = BindModuleNames(subdecl.ModuleDef, bindings);
if (!bindings.BindName(subdecl.Name, subdecl, subBindings)) {
- Error(subdecl.tok, "Duplicate module name: {0}", subdecl.Name);
+ reporter.Error(MessageSource.Resolver, subdecl.tok, "Duplicate module name: {0}", subdecl.Name);
}
} else if (tld is ModuleFacadeDecl) {
var subdecl = (ModuleFacadeDecl)tld;
if (!bindings.BindName(subdecl.Name, subdecl, null)) {
- Error(subdecl.tok, "Duplicate module name: {0}", subdecl.Name);
+ reporter.Error(MessageSource.Resolver, subdecl.tok, "Duplicate module name: {0}", subdecl.Name);
}
} else if (tld is AliasModuleDecl) {
var subdecl = (AliasModuleDecl)tld;
if (!bindings.BindName(subdecl.Name, subdecl, null)) {
- Error(subdecl.tok, "Duplicate module name: {0}", subdecl.Name);
+ reporter.Error(MessageSource.Resolver, subdecl.tok, "Duplicate module name: {0}", subdecl.Name);
}
}
}
@@ -765,9 +896,9 @@ namespace Microsoft.Dafny
if (m.RefinementBaseName != null) {
ModuleDecl other;
if (!bindings.TryLookup(m.RefinementBaseName[0], out other)) {
- Error(m.RefinementBaseName[0], "module {0} named as refinement base does not exist", m.RefinementBaseName[0].val);
+ reporter.Error(MessageSource.Resolver, m.RefinementBaseName[0], "module {0} named as refinement base does not exist", m.RefinementBaseName[0].val);
} else if (other is LiteralModuleDecl && ((LiteralModuleDecl)other).ModuleDef == m) {
- Error(m.RefinementBaseName[0], "module cannot refine itself: {0}", m.RefinementBaseName[0].val);
+ reporter.Error(MessageSource.Resolver, m.RefinementBaseName[0], "module cannot refine itself: {0}", m.RefinementBaseName[0].val);
} else {
Contract.Assert(other != null); // follows from postcondition of TryGetValue
dependencies.AddEdge(decl, other);
@@ -791,7 +922,7 @@ namespace Microsoft.Dafny
var alias = moduleDecl as AliasModuleDecl;
ModuleDecl root;
if (!bindings.TryLookupIgnore(alias.Path[0], out root, alias))
- Error(alias.tok, ModuleNotFoundErrorMessage(0, alias.Path));
+ reporter.Error(MessageSource.Resolver, alias.tok, ModuleNotFoundErrorMessage(0, alias.Path));
else {
dependencies.AddEdge(moduleDecl, root);
alias.Root = root;
@@ -800,14 +931,14 @@ namespace Microsoft.Dafny
var abs = moduleDecl as ModuleFacadeDecl;
ModuleDecl root;
if (!bindings.TryLookup(abs.Path[0], out root))
- Error(abs.tok, ModuleNotFoundErrorMessage(0, abs.Path));
+ reporter.Error(MessageSource.Resolver, abs.tok, ModuleNotFoundErrorMessage(0, abs.Path));
else {
dependencies.AddEdge(moduleDecl, root);
abs.Root = root;
}
if (abs.CompilePath != null) {
if (!bindings.TryLookup(abs.CompilePath[0], out root))
- Error(abs.tok, ModuleNotFoundErrorMessage(0, abs.CompilePath));
+ reporter.Error(MessageSource.Resolver, abs.tok, ModuleNotFoundErrorMessage(0, abs.CompilePath));
else {
dependencies.AddEdge(moduleDecl, root);
abs.CompileRoot = root;
@@ -842,37 +973,76 @@ namespace Microsoft.Dafny
foreach (var kv in m.StaticMembers) {
info.StaticMembers[kv.Key] = kv.Value;
}
- info.IsGhost = m.IsGhost;
+ info.IsAbstract = m.IsAbstract;
return info;
}
ModuleSignature RegisterTopLevelDecls(ModuleDefinition moduleDef, bool useImports) {
Contract.Requires(moduleDef != null);
var sig = new ModuleSignature();
sig.ModuleDef = moduleDef;
- sig.IsGhost = moduleDef.IsAbstract;
+ sig.IsAbstract = moduleDef.IsAbstract;
List<TopLevelDecl> declarations = moduleDef.TopLevelDecls;
- if (useImports) {
- // First go through and add anything from the opened imports
- foreach (var im in declarations) {
- if (im is ModuleDecl && ((ModuleDecl)im).Opened) {
- var s = GetSignature(((ModuleDecl)im).Signature);
+ // First go through and add anything from the opened imports
+ foreach (var im in declarations) {
+ if (im is ModuleDecl && ((ModuleDecl)im).Opened) {
+ var s = GetSignature(((ModuleDecl)im).Signature);
+
+ if (useImports || DafnyOptions.O.IronDafny) {
// classes:
foreach (var kv in s.TopLevels) {
- TopLevelDecl d;
- if (sig.TopLevels.TryGetValue(kv.Key, out d)) {
- sig.TopLevels[kv.Key] = AmbiguousTopLevelDecl.Create(moduleDef, d, kv.Value);
- } else {
- sig.TopLevels.Add(kv.Key, kv.Value);
+ // IronDafny: we need to pull the members of the opened module's _default class in so that they can be merged.
+ if (useImports || string.Equals(kv.Key, "_default", StringComparison.InvariantCulture)) {
+ TopLevelDecl d;
+ if (sig.TopLevels.TryGetValue(kv.Key, out d)) {
+ bool resolved = false;
+ if (DafnyOptions.O.IronDafny) {
+ // sometimes, we need to compare two type synonyms in order to come up with a decision regarding substitution.
+ var aliased1 = Object.ReferenceEquals(kv.Value, d);
+ if (!aliased1) {
+ var a = d;
+ while (a.ExclusiveRefinement != null) {
+ a = a.ExclusiveRefinement;
+ }
+ var b = kv.Value;
+ while (b.ExclusiveRefinement != null) {
+ b = b.ExclusiveRefinement;
+ }
+ if (a is TypeSynonymDecl && b is TypeSynonymDecl) {
+ aliased1 = UnifyTypes(((TypeSynonymDecl)a).Rhs, ((TypeSynonymDecl)b).Rhs);
+ } else {
+ aliased1 = Object.ReferenceEquals(a, b);
+ }
+ }
+ if (aliased1 ||
+ Object.ReferenceEquals(kv.Value.ClonedFrom, d) ||
+ Object.ReferenceEquals(d.ClonedFrom, kv.Value) ||
+ Object.ReferenceEquals(kv.Value.ExclusiveRefinement, d)) {
+ sig.TopLevels[kv.Key] = kv.Value;
+ resolved = true;
+ }
+ }
+ if (!resolved) {
+ sig.TopLevels[kv.Key] = AmbiguousTopLevelDecl.Create(moduleDef, d, kv.Value);
+ }
+ } else {
+ sig.TopLevels.Add(kv.Key, kv.Value);
+ }
}
}
+ }
+
+ if (useImports) {
// constructors:
foreach (var kv in s.Ctors) {
Tuple<DatatypeCtor, bool> pair;
if (sig.Ctors.TryGetValue(kv.Key, out pair)) {
// The same ctor can be imported from two different imports (e.g "diamond" imports), in which case,
// they are not duplicates.
- if (kv.Value.Item1 != pair.Item1) {
+ if (!Object.ReferenceEquals(kv.Value.Item1, pair.Item1) &&
+ (!DafnyOptions.O.IronDafny ||
+ (!Object.ReferenceEquals(kv.Value.Item1.ClonedFrom, pair.Item1) &&
+ !Object.ReferenceEquals(kv.Value.Item1, pair.Item1.ClonedFrom)))) {
// mark it as a duplicate
sig.Ctors[kv.Key] = new Tuple<DatatypeCtor, bool>(pair.Item1, true);
}
@@ -881,19 +1051,74 @@ namespace Microsoft.Dafny
sig.Ctors.Add(kv.Key, kv.Value);
}
}
+ }
+
+ if (useImports || DafnyOptions.O.IronDafny) {
// static members:
foreach (var kv in s.StaticMembers) {
MemberDecl md;
if (sig.StaticMembers.TryGetValue(kv.Key, out md)) {
+ var resolved = false;
+ if (DafnyOptions.O.IronDafny) {
+ var aliased0 = Object.ReferenceEquals(kv.Value, md) || Object.ReferenceEquals(kv.Value.ClonedFrom, md) || Object.ReferenceEquals(md.ClonedFrom, kv.Value);
+ var aliased1 = aliased0;
+ if (!aliased0) {
+ var a = kv.Value.EnclosingClass;
+ while (a != null &&
+ (a.ExclusiveRefinement != null || a.ClonedFrom != null)) {
+ if (a.ClonedFrom != null) {
+ a = (TopLevelDecl)a.ClonedFrom;
+ } else {
+ Contract.Assert(a.ExclusiveRefinement != null);
+ a = a.ExclusiveRefinement;
+ }
+ }
+ var b = md.EnclosingClass;
+ while (b != null &&
+ (b.ExclusiveRefinement != null || b.ClonedFrom != null)) {
+ if (b.ClonedFrom != null) {
+ b = (TopLevelDecl)b.ClonedFrom;
+ } else {
+ Contract.Assert(b.ExclusiveRefinement != null);
+ b = b.ExclusiveRefinement;
+ }
+ }
+ aliased1 = Object.ReferenceEquals(a, b);
+ }
+ if (aliased0 || aliased1) {
+ if (kv.Value.EnclosingClass != null &&
+ md.EnclosingClass != null &&
+ md.EnclosingClass.ExclusiveRefinement != null &&
+ !Object.ReferenceEquals(
+ kv.Value.EnclosingClass.ExclusiveRefinement,
+ md.EnclosingClass)) {
+ sig.StaticMembers[kv.Key] = kv.Value;
+ }
+ resolved = true;
+ }
+ }
+ if (!resolved) {
sig.StaticMembers[kv.Key] = AmbiguousMemberDecl.Create(moduleDef, md, kv.Value);
+ }
} else {
// add new
sig.StaticMembers.Add(kv.Key, kv.Value);
}
- }
+ }
}
}
}
+
+ // second go through and overriding anything from the opened imports with the ones from the refinementBase
+ if (useImports && moduleDef.RefinementBaseSig != null) {
+ foreach (var kv in moduleDef.RefinementBaseSig.TopLevels) {
+ sig.TopLevels[kv.Key] = kv.Value;
+ }
+ foreach (var kv in moduleDef.RefinementBaseSig.StaticMembers) {
+ sig.StaticMembers[kv.Key] = kv.Value;
+ }
+ }
+
// This is solely used to detect duplicates amongst the various e
Dictionary<string, TopLevelDecl> toplevels = new Dictionary<string, TopLevelDecl>();
// Now add the things present
@@ -901,7 +1126,7 @@ namespace Microsoft.Dafny
Contract.Assert(d != null);
// register the class/datatype/module name
if (toplevels.ContainsKey(d.Name)) {
- Error(d, "Duplicate name of top-level declaration: {0}", d.Name);
+ reporter.Error(MessageSource.Resolver, d, "Duplicate name of top-level declaration: {0}", d.Name);
} else {
toplevels[d.Name] = d;
sig.TopLevels[d.Name] = d;
@@ -925,7 +1150,7 @@ namespace Microsoft.Dafny
// First, register the iterator's in- and out-parameters as readonly fields
foreach (var p in iter.Ins) {
if (members.ContainsKey(p.Name)) {
- Error(p, "Name of in-parameter is used by another member of the iterator: {0}", p.Name);
+ reporter.Error(MessageSource.Resolver, p, "Name of in-parameter is used by another member of the iterator: {0}", p.Name);
} else {
var field = new SpecialField(p.tok, p.Name, p.CompileName, "", "", p.IsGhost, false, false, p.Type, null);
field.EnclosingClass = iter; // resolve here
@@ -935,7 +1160,7 @@ namespace Microsoft.Dafny
}
foreach (var p in iter.Outs) {
if (members.ContainsKey(p.Name)) {
- Error(p, "Name of yield-parameter is used by another member of the iterator: {0}", p.Name);
+ reporter.Error(MessageSource.Resolver, p, "Name of yield-parameter is used by another member of the iterator: {0}", p.Name);
} else {
var field = new SpecialField(p.tok, p.Name, p.CompileName, "", "", p.IsGhost, true, true, p.Type, null);
field.EnclosingClass = iter; // resolve here
@@ -947,7 +1172,7 @@ namespace Microsoft.Dafny
foreach (var p in iter.Outs) {
var nm = p.Name + "s";
if (members.ContainsKey(nm)) {
- Error(p.tok, "Name of implicit yield-history variable '{0}' is already used by another member of the iterator", p.Name);
+ reporter.Error(MessageSource.Resolver, p.tok, "Name of implicit yield-history variable '{0}' is already used by another member of the iterator", p.Name);
} else {
var tp = new SeqType(p.Type.IsSubrangeType ? new IntType() : p.Type);
var field = new SpecialField(p.tok, nm, nm, "", "", true, true, false, tp, null);
@@ -961,9 +1186,9 @@ namespace Microsoft.Dafny
iter.Members.Add(f);
});
// add the additional special variables as fields
- iter.Member_Reads = new SpecialField(iter.tok, "_reads", "_reads", "", "", true, false, false, new SetType(new ObjectType()), null);
- iter.Member_Modifies = new SpecialField(iter.tok, "_modifies", "_modifies", "", "", true, false, false, new SetType(new ObjectType()), null);
- iter.Member_New = new SpecialField(iter.tok, "_new", "_new", "", "", true, true, true, new SetType(new ObjectType()), null);
+ iter.Member_Reads = new SpecialField(iter.tok, "_reads", "_reads", "", "", true, false, false, new SetType(true, new ObjectType()), null);
+ iter.Member_Modifies = new SpecialField(iter.tok, "_modifies", "_modifies", "", "", true, false, false, new SetType(true, new ObjectType()), null);
+ iter.Member_New = new SpecialField(iter.tok, "_new", "_new", "", "", true, true, true, new SetType(true, new ObjectType()), null);
foreach (var field in new List<Field>() { iter.Member_Reads, iter.Member_Modifies, iter.Member_New }) {
field.EnclosingClass = iter; // resolve here
members.Add(field.Name, field);
@@ -1019,7 +1244,7 @@ namespace Microsoft.Dafny
iter.Member_MoveNext = moveNext;
MemberDecl member;
if (members.TryGetValue(init.Name, out member)) {
- Error(member.tok, "member name '{0}' is already predefined for this iterator", init.Name);
+ reporter.Error(MessageSource.Resolver, member.tok, "member name '{0}' is already predefined for this iterator", init.Name);
} else {
members.Add(init.Name, init);
iter.Members.Add(init);
@@ -1027,13 +1252,13 @@ namespace Microsoft.Dafny
// If the name of the iterator is "Valid" or "MoveNext", one of the following will produce an error message. That
// error message may not be as clear as it could be, but the situation also seems unlikely to ever occur in practice.
if (members.TryGetValue("Valid", out member)) {
- Error(member.tok, "member name 'Valid' is already predefined for iterators");
+ reporter.Error(MessageSource.Resolver, member.tok, "member name 'Valid' is already predefined for iterators");
} else {
members.Add(valid.Name, valid);
iter.Members.Add(valid);
}
if (members.TryGetValue("MoveNext", out member)) {
- Error(member.tok, "member name 'MoveNext' is already predefined for iterators");
+ reporter.Error(MessageSource.Resolver, member.tok, "member name 'MoveNext' is already predefined for iterators");
} else {
members.Add(moveNext.Name, moveNext);
iter.Members.Add(moveNext);
@@ -1051,7 +1276,7 @@ namespace Microsoft.Dafny
members.Add(m.Name, m);
if (m is Constructor) {
if (cl is TraitDecl) {
- Error(m.tok, "a trait is not allowed to declare a constructor");
+ reporter.Error(MessageSource.Resolver, m.tok, "a trait is not allowed to declare a constructor");
} else {
cl.HasConstructor = true;
}
@@ -1107,9 +1332,9 @@ namespace Microsoft.Dafny
members.Add(extraName, extraMember);
}
} else if (m is Constructor && !((Constructor)m).HasName) {
- Error(m, "More than one anonymous constructor");
+ reporter.Error(MessageSource.Resolver, m, "More than one anonymous constructor");
} else {
- Error(m, "Duplicate member name: {0}", m.Name);
+ reporter.Error(MessageSource.Resolver, m, "Duplicate member name: {0}", m.Name);
}
}
if (cl.IsDefaultClass) {
@@ -1133,9 +1358,9 @@ namespace Microsoft.Dafny
foreach (DatatypeCtor ctor in dt.Ctors) {
if (ctor.Name.EndsWith("?")) {
- Error(ctor, "a datatype constructor name is not allowed to end with '?'");
+ reporter.Error(MessageSource.Resolver, ctor, "a datatype constructor name is not allowed to end with '?'");
} else if (ctors.ContainsKey(ctor.Name)) {
- Error(ctor, "Duplicate datatype constructor name: {0}", ctor.Name);
+ reporter.Error(MessageSource.Resolver, ctor, "Duplicate datatype constructor name: {0}", ctor.Name);
} else {
ctors.Add(ctor.Name, ctor);
@@ -1162,7 +1387,7 @@ namespace Microsoft.Dafny
foreach (var formal in ctor.Formals) {
bool nameError = false;
if (formal.HasName && members.ContainsKey(formal.Name)) {
- Error(ctor, "Name of deconstructor is used by another member of the datatype: {0}", formal.Name);
+ reporter.Error(MessageSource.Resolver, ctor, "Name of deconstructor is used by another member of the datatype: {0}", formal.Name);
nameError = true;
}
var dtor = new DatatypeDestructor(formal.tok, ctor, formal, formal.Name, "dtor_" + formal.CompileName, "", "", formal.IsGhost, formal.Type, null);
@@ -1179,15 +1404,17 @@ namespace Microsoft.Dafny
}
private ModuleSignature MakeAbstractSignature(ModuleSignature p, string Name, int Height, List<ModuleDefinition> mods) {
- var mod = new ModuleDefinition(Token.NoToken, Name + ".Abs", true, true, null, null, null, false);
+ var mod = new ModuleDefinition(Token.NoToken, Name + ".Abs", true, true, /*isExclusiveRefinement:*/ false, null, null, null, false);
+ mod.ClonedFrom = p.ModuleDef;
mod.Height = Height;
foreach (var kv in p.TopLevels) {
mod.TopLevelDecls.Add(CloneDeclaration(kv.Value, mod, mods, Name));
}
- var sig = RegisterTopLevelDecls(mod, false);
+ var sig = RegisterTopLevelDecls(mod, true);
sig.Refines = p.Refines;
sig.CompileSignature = p;
- sig.IsGhost = p.IsGhost;
+ sig.IsAbstract = p.IsAbstract;
+ sig.ExclusiveRefinement = p.ExclusiveRefinement;
mods.Add(mod);
ResolveModuleDefinition(mod, sig);
return sig;
@@ -1210,8 +1437,19 @@ namespace Microsoft.Dafny
}
- public static bool ResolvePath(ModuleDecl root, List<IToken> Path, out ModuleSignature p, ResolutionErrorReporter reporter) {
- Contract.Requires(reporter != null);
+ public static bool ResolvePath(ModuleDecl root, List<IToken> Path, out ModuleSignature p, ErrorReporter reporter) {
+ if (Path.Count == 1 && root is LiteralModuleDecl) {
+ // use the default export when the importing the root
+ LiteralModuleDecl decl = (LiteralModuleDecl)root;
+ p = decl.DefaultExport;
+ if (p == null) {
+ // no default view is specified.
+ reporter.Error(MessageSource.Resolver, decl.tok, "no default export declared in module: {0}", decl.Name);
+ return false;
+ }
+ return true;
+ }
+
p = root.Signature;
int i = 1;
while (i < Path.Count) {
@@ -1220,7 +1458,7 @@ namespace Microsoft.Dafny
p = pp;
i++;
} else {
- reporter.Error(Path[i], ModuleNotFoundErrorMessage(i, Path));
+ reporter.Error(MessageSource.Resolver, Path[i], ModuleNotFoundErrorMessage(i, Path));
break;
}
}
@@ -1241,6 +1479,14 @@ namespace Microsoft.Dafny
var typeRedirectionDependencies = new Graph<RedirectingTypeDecl>();
foreach (TopLevelDecl d in declarations) {
+ if (DafnyOptions.O.IronDafny && d.Module.IsExclusiveRefinement) {
+ var refinementOf =
+ def.RefinementBase.TopLevelDecls.Find(
+ i => String.Equals(i.Name, d.Name, StringComparison.InvariantCulture));
+ if (refinementOf != null && refinementOf.ExclusiveRefinement == null) {
+ refinementOf.ExclusiveRefinement = d;
+ }
+ }
Contract.Assert(d != null);
allTypeParameters.PushMarker();
ResolveTypeParameters(d.TypeArgs, true, d);
@@ -1271,11 +1517,15 @@ namespace Microsoft.Dafny
} else if (d is ModuleDecl) {
var decl = (ModuleDecl)d;
if (!def.IsAbstract) {
- if (decl.Signature.IsGhost)
+ if (decl.Signature.IsAbstract)
{
- if (!(def.IsDefaultModule)) // _module is allowed to contain abstract modules, but not be abstract itself. Note this presents a challenge to
+ if (// _module is allowed to contain abstract modules, but not be abstract itself. Note this presents a challenge to
// trusted verification, as toplevels can't be trusted if they invoke abstract module members.
- Error(d.tok, "an abstract module can only be imported into other abstract modules, not a concrete one.");
+ !def.IsDefaultModule
+ // [IronDafny] it's possbile for an abstract module to have an exclusive refinement, so it no longer makes sense to disallow this.
+ && !DafnyOptions.O.IronDafny)
+
+ reporter.Error(MessageSource.Resolver, d.tok, "an abstract module can only be imported into other abstract modules, not a concrete one.");
} else {
// physical modules are allowed everywhere
}
@@ -1301,16 +1551,7 @@ namespace Microsoft.Dafny
if (cycle != null) {
Contract.Assert(cycle.Count != 0);
var erste = cycle[0];
- Error(erste.Tok, "Cycle among redirecting types (newtypes, type synonyms): {0} -> {1}", Util.Comma(" -> ", cycle, syn => syn.Name), erste.Name);
- }
- }
-
- private readonly List<SetComprehension> needFiniteBoundsChecks_SetComprehension = new List<SetComprehension>();
- private readonly List<LetExpr> needFiniteBoundsChecks_LetSuchThatExpr = new List<LetExpr>();
- public int NFBC_Count {
- // provided just for the purpose of conveniently writing contracts for ResolveTopLevelDecl_Meat
- get {
- return needFiniteBoundsChecks_SetComprehension.Count + needFiniteBoundsChecks_LetSuchThatExpr.Count;
+ reporter.Error(MessageSource.Resolver, erste.Tok, "Cycle among redirecting types (newtypes, type synonyms): {0} -> {1}", Util.Comma(" -> ", cycle, syn => syn.Name), erste.Name);
}
}
@@ -1325,14 +1566,21 @@ namespace Microsoft.Dafny
new NativeType("long", Int64.MinValue, 0x8000000000000000, "L", false),
};
- public void ResolveTopLevelDecls_Meat(List<TopLevelDecl/*!*/>/*!*/ declarations, Graph<IndDatatypeDecl/*!*/>/*!*/ datatypeDependencies, Graph<CoDatatypeDecl/*!*/>/*!*/ codatatypeDependencies) {
+ public void ResolveTopLevelDecls_Core(List<TopLevelDecl/*!*/>/*!*/ declarations, Graph<IndDatatypeDecl/*!*/>/*!*/ datatypeDependencies, Graph<CoDatatypeDecl/*!*/>/*!*/ codatatypeDependencies) {
Contract.Requires(declarations != null);
Contract.Requires(cce.NonNullElements(datatypeDependencies));
Contract.Requires(cce.NonNullElements(codatatypeDependencies));
- Contract.Requires(NFBC_Count == 0);
- Contract.Ensures(NFBC_Count == 0);
+ Contract.Requires(AllTypeConstraints.Count == 0);
+ Contract.Ensures(AllTypeConstraints.Count == 0);
+
+ int prevErrorCount = reporter.Count(ErrorLevel.Error);
- int prevErrorCount = ErrorCount;
+ // ---------------------------------- Pass 0 ----------------------------------
+ // This pass resolves names, introduces (and may solve) type constraints, and
+ // builds the module's call graph.
+ // For 'newtype' declarations, it also checks that all types were fully
+ // determined.
+ // ----------------------------------------------------------------------------
// Resolve the meat of classes and iterators, the definitions of type synonyms, and the type parameters of all top-level type declarations
// First, resolve the newtype declarations and the constraint clauses, including filling in .ResolvedOp fields. This is needed for the
@@ -1347,7 +1595,7 @@ namespace Microsoft.Dafny
ResolveAttributes(d.Attributes, new ResolveOpts(new NoContext(d.Module), false));
// this check can be done only after it has been determined that the redirected types do not involve cycles
if (!dd.BaseType.IsNumericBased()) {
- Error(dd.tok, "newtypes must be based on some numeric type (got {0})", dd.BaseType);
+ reporter.Error(MessageSource.Resolver, dd.tok, "newtypes must be based on some numeric type (got {0})", dd.BaseType);
}
// type check the constraint, if any
if (dd.Var != null) {
@@ -1355,25 +1603,25 @@ namespace Microsoft.Dafny
Contract.Assert(dd.Constraint != null); // follows from NewtypeDecl invariant
scope.PushMarker();
var added = scope.Push(dd.Var.Name, dd.Var);
- Contract.Assert(added);
+ Contract.Assert(added == Scope<IVariable>.PushResult.Success);
ResolveType(dd.Var.tok, dd.Var.Type, dd, ResolveTypeOptionEnum.DontInfer, null);
- ResolveExpression(dd.Constraint, new ResolveOpts(dd, false, true));
+ ResolveExpression(dd.Constraint, new ResolveOpts(dd, false));
Contract.Assert(dd.Constraint.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(dd.Constraint.Type, Type.Bool)) {
- Error(dd.Constraint, "newtype constraint must be of type bool (instead got {0})", dd.Constraint.Type);
- }
+ ConstrainTypes(dd.Constraint.Type, Type.Bool, dd.Constraint, "newtype constraint must be of type bool (instead got {0})", dd.Constraint.Type);
+ SolveAllTypeConstraints();
if (!CheckTypeInference_Visitor.IsDetermined(dd.BaseType.NormalizeExpand())) {
- Error(dd.tok, "newtype's base type is not fully determined; add an explicit type for '{0}'", dd.Var.Name);
+ reporter.Error(MessageSource.Resolver, dd.tok, "newtype's base type is not fully determined; add an explicit type for '{0}'", dd.Var.Name);
}
- CheckTypeInference(dd.Constraint);
+ CheckTypeInference(dd.Constraint, dd);
scope.PopMarker();
}
}
}
// Now, we're ready for the other declarations.
foreach (TopLevelDecl d in declarations) {
+ Contract.Assert(AllTypeConstraints.Count == 0);
if (d is TraitDecl && d.TypeArgs.Count > 0) {
- Error(d, "sorry, traits with type parameters are not supported");
+ reporter.Error(MessageSource.Resolver, d, "sorry, traits with type parameters are not supported");
}
allTypeParameters.PushMarker();
ResolveTypeParameters(d.TypeArgs, false, d);
@@ -1392,156 +1640,86 @@ namespace Microsoft.Dafny
allTypeParameters.PopMarker();
}
- if (ErrorCount == prevErrorCount) {
- foreach (var e in needFiniteBoundsChecks_SetComprehension) {
- var missingBounds = new List<BoundVar>();
- CheckTypeInference(e.Range); // we need to resolve operators before the call to DiscoverBounds
- e.Bounds = DiscoverBounds(e.tok, e.BoundVars, e.Range, true, false, missingBounds);
- if (missingBounds.Count != 0) {
- e.MissingBounds = missingBounds;
- foreach (var bv in e.MissingBounds) {
- Error(e, "a set comprehension must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name);
- }
- }
- }
- foreach (var e in needFiniteBoundsChecks_LetSuchThatExpr) {
- Contract.Assert(!e.Exact); // only let-such-that expressions are ever added to the list
- Contract.Assert(e.RHSs.Count == 1); // if we got this far, the resolver will have checked this condition successfully
- var constraint = e.RHSs[0];
- var missingBounds = new List<IVariable>();
- CheckTypeInference(constraint); // we need to resolve operators before the call to DiscoverBounds
- var allBounds = DiscoverBoundsAux(e.tok, new List<IVariable>(e.BoundVars), constraint, true, true, true, missingBounds);
- if (missingBounds.Count != 0) {
- e.Constraint_MissingBounds = missingBounds;
- foreach (var bv in e.Constraint_MissingBounds) {
- Error(e, "a non-ghost let-such-that constraint must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name);
- }
- } else {
- e.Constraint_Bounds = new List<ComprehensionExpr.BoundedPool>();
- foreach (var pair in allBounds) {
- Contract.Assert(1 <= pair.Item2.Count);
- // TODO: The following could be improved by picking the bound that is most likely to give rise to an efficient compiled program
- e.Constraint_Bounds.Add(pair.Item2[0]);
- }
- }
- }
- }
- needFiniteBoundsChecks_SetComprehension.Clear();
- needFiniteBoundsChecks_LetSuchThatExpr.Clear();
+ // ---------------------------------- Pass 1 ----------------------------------
+ // This pass:
+ // * checks that type inference was able to determine all types
+ // * fills in the .ResolvedOp field of binary expressions
+ // * discovers bounds for:
+ // - forall statements
+ // - set comprehensions
+ // - map comprehensions
+ // - quantifier expressions
+ // - assign-such-that statements
+ // - compilable let-such-that expressions
+ // - newtype constraints
+ // For each statement body that it successfully typed, this pass also:
+ // * computes ghost interests
+ // * determines/checks tail-recursion.
+ // ----------------------------------------------------------------------------
Dictionary<string, NativeType> nativeTypeMap = new Dictionary<string, NativeType>();
foreach (var nativeType in NativeTypes) {
nativeTypeMap.Add(nativeType.Name, nativeType);
}
-
- if (ErrorCount == prevErrorCount) {
+ if (reporter.Count(ErrorLevel.Error) == prevErrorCount) {
// Check that type inference went well everywhere; this will also fill in the .ResolvedOp field in binary expressions
foreach (TopLevelDecl d in declarations) {
if (d is IteratorDecl) {
var iter = (IteratorDecl)d;
+ var prevErrCnt = reporter.Count(ErrorLevel.Error);
iter.Members.Iter(CheckTypeInference_Member);
+ if (prevErrCnt == reporter.Count(ErrorLevel.Error)) {
+ iter.SubExpressions.Iter(e => CheckExpression(e, this, iter));
+ }
if (iter.Body != null) {
- CheckTypeInference(iter.Body);
+ CheckTypeInference(iter.Body, iter);
+ if (prevErrCnt == reporter.Count(ErrorLevel.Error)) {
+ ComputeGhostInterest(iter.Body, false, iter);
+ CheckExpression(iter.Body, this, iter);
+ }
}
} else if (d is ClassDecl) {
var cl = (ClassDecl)d;
- cl.Members.Iter(CheckTypeInference_Member);
- } else if (d is NewtypeDecl) {
- var dd = (NewtypeDecl)d;
- bool? boolNativeType = null;
- NativeType stringNativeType = null;
- object nativeTypeAttr = true;
- bool hasNativeTypeAttr = Attributes.ContainsMatchingValue(dd.Attributes, "nativeType", ref nativeTypeAttr,
- new Attributes.MatchingValueOption[] {
- Attributes.MatchingValueOption.Empty,
- Attributes.MatchingValueOption.Bool,
- Attributes.MatchingValueOption.String },
- err => Error(dd, err));
- if (hasNativeTypeAttr) {
- if (nativeTypeAttr is bool) {
- boolNativeType = (bool)nativeTypeAttr;
- } else {
- string keyString = (string)nativeTypeAttr;
- if (nativeTypeMap.ContainsKey(keyString)) {
- stringNativeType = nativeTypeMap[keyString];
- } else {
- Error(dd, "Unsupported nativeType {0}", keyString);
+ foreach (var member in cl.Members) {
+ var prevErrCnt = reporter.Count(ErrorLevel.Error);
+ CheckTypeInference_Member(member);
+ if (prevErrCnt == reporter.Count(ErrorLevel.Error)) {
+ if (member is Method) {
+ var m = (Method)member;
+ if (m.Body != null) {
+ ComputeGhostInterest(m.Body, m.IsGhost, m);
+ CheckExpression(m.Body, this, m);
+ DetermineTailRecursion(m);
+ }
+ } else if (member is Function) {
+ var f = (Function)member;
+ if (!f.IsGhost && f.Body != null) {
+ CheckIsCompilable(f.Body);
+ }
+ DetermineTailRecursion(f);
+ }
+ if (prevErrCnt == reporter.Count(ErrorLevel.Error) && member is ICodeContext) {
+ member.SubExpressions.Iter(e => CheckExpression(e, this, (ICodeContext)member));
}
}
}
- if (stringNativeType != null || boolNativeType == true) {
- if (!dd.BaseType.IsNumericBased(Type.NumericPersuation.Int)) {
- Error(dd, "nativeType can only be used on integral types");
- }
- if (dd.Var == null) {
- Error(dd, "nativeType can only be used if newtype specifies a constraint");
- }
- }
+ } else if (d is NewtypeDecl) {
+ var dd = (NewtypeDecl)d;
if (dd.Var != null) {
Contract.Assert(dd.Constraint != null);
- CheckTypeInference(dd.Constraint);
-
- Func<Expression, BigInteger?> GetConst = null;
- GetConst = (Expression e) => {
- int m = 1;
- BinaryExpr bin = e as BinaryExpr;
- if (bin != null && bin.Op == BinaryExpr.Opcode.Sub && GetConst(bin.E0) == BigInteger.Zero) {
- m = -1;
- e = bin.E1;
- }
- LiteralExpr l = e as LiteralExpr;
- if (l != null && l.Value is BigInteger) {
- return m * (BigInteger)l.Value;
- }
- return null;
- };
- var missingBounds = new List<BoundVar>();
- var bounds = DiscoverBounds(dd.Constraint.tok, new List<BoundVar> { dd.Var }, dd.Constraint,
- true, true, missingBounds);
- List<NativeType> potentialNativeTypes =
- (stringNativeType != null) ? new List<NativeType> { stringNativeType } :
- (boolNativeType == false) ? new List<NativeType>() :
- NativeTypes;
- foreach (var nt in potentialNativeTypes) {
- if (missingBounds.Count == 0) {
- bool lowerOk = false;
- bool upperOk = false;
- foreach (var bound in bounds) {
- if (bound is ComprehensionExpr.IntBoundedPool) {
- var bnd = (ComprehensionExpr.IntBoundedPool)bound;
- if (bnd.LowerBound != null) {
- BigInteger? lower = GetConst(bnd.LowerBound);
- if (lower != null && nt.LowerBound <= lower) {
- lowerOk = true;
- }
- }
- if (bnd.UpperBound != null) {
- BigInteger? upper = GetConst(bnd.UpperBound);
- if (upper != null && upper <= nt.UpperBound) {
- upperOk = true;
- }
- }
- }
- }
- if (lowerOk && upperOk) {
- dd.NativeType = nt;
- break;
- }
- }
- }
- if (dd.NativeType == null && (boolNativeType == true || stringNativeType != null)) {
- Error(dd, "Dafny's heuristics cannot find a compatible native type. " +
- "Hint: try writing a newtype constraint of the form 'i:int | lowerBound <= i < upperBound && (...any additional constraints...)'");
- }
- if (dd.NativeType != null && stringNativeType == null) {
- ReportAdditionalInformation(dd.tok, "{:nativeType \"" + dd.NativeType.Name + "\"}", dd.tok.val.Length);
- }
+ CheckExpression(dd.Constraint, this, dd);
}
+ FigureOutNativeType(dd, nativeTypeMap);
}
}
}
- if (ErrorCount == prevErrorCount) {
+
+ // ---------------------------------- Pass 2 ----------------------------------
+ // This pass fills in various additional information.
+ // ----------------------------------------------------------------------------
+
+ if (reporter.Count(ErrorLevel.Error) == prevErrorCount) {
// fill in the postconditions and bodies of prefix lemmas
foreach (var com in ModuleDefinition.AllFixpointLemmas(declarations)) {
var prefixLemma = com.PrefixLemma;
@@ -1549,15 +1727,22 @@ namespace Microsoft.Dafny
continue; // something went wrong during registration of the prefix lemma (probably a duplicated fixpoint-lemma name)
}
var k = prefixLemma.Ins[0];
+ var focalPredicates = new HashSet<FixpointPredicate>();
if (com is CoLemma) {
// compute the postconditions of the prefix lemma
Contract.Assume(prefixLemma.Ens.Count == 0); // these are not supposed to have been filled in before
foreach (var p in com.Ens) {
var coConclusions = new HashSet<Expression>();
CollectFriendlyCallsInFixpointLemmaSpecification(p.E, true, coConclusions, true);
- var subst = new FixpointLemmaSpecificationSubstituter(coConclusions, new IdentifierExpr(k.tok, k.Name), this, true);
+ var subst = new FixpointLemmaSpecificationSubstituter(coConclusions, new IdentifierExpr(k.tok, k.Name), this.reporter, true);
var post = subst.CloneExpr(p.E);
prefixLemma.Ens.Add(new MaybeFreeExpression(post, p.IsFree));
+ foreach (var e in coConclusions) {
+ var fce = e as FunctionCallExpr;
+ if (fce != null) { // the other possibility is that "e" is a BinaryExpr
+ focalPredicates.Add((CoPredicate)fce.Function);
+ }
+ }
}
} else {
// compute the preconditions of the prefix lemma
@@ -1565,19 +1750,24 @@ namespace Microsoft.Dafny
foreach (var p in com.Req) {
var antecedents = new HashSet<Expression>();
CollectFriendlyCallsInFixpointLemmaSpecification(p.E, true, antecedents, false);
- var subst = new FixpointLemmaSpecificationSubstituter(antecedents, new IdentifierExpr(k.tok, k.Name), this, false);
+ var subst = new FixpointLemmaSpecificationSubstituter(antecedents, new IdentifierExpr(k.tok, k.Name), this.reporter, false);
var pre = subst.CloneExpr(p.E);
prefixLemma.Req.Add(new MaybeFreeExpression(pre, p.IsFree));
+ foreach (var e in antecedents) {
+ var fce = (FunctionCallExpr)e; // we expect "antecedents" to contain only FunctionCallExpr's
+ focalPredicates.Add((InductivePredicate)fce.Function);
+ }
}
}
+ reporter.Info(MessageSource.Resolver, com.tok, string.Format("{0} specialized for {1}", com.PrefixLemma.Name, Util.Comma(focalPredicates, p => p.Name)));
// Compute the statement body of the prefix lemma
Contract.Assume(prefixLemma.Body == null); // this is not supposed to have been filled in before
if (com.Body != null) {
var kMinusOne = new BinaryExpr(com.tok, BinaryExpr.Opcode.Sub, new IdentifierExpr(k.tok, k.Name), new LiteralExpr(com.tok, 1));
- var subst = new FixpointLemmaBodyCloner(com, kMinusOne, this);
+ var subst = new FixpointLemmaBodyCloner(com, kMinusOne, focalPredicates, this.reporter);
var mainBody = subst.CloneBlockStmt(com.Body);
var kPositive = new BinaryExpr(com.tok, BinaryExpr.Opcode.Lt, new LiteralExpr(com.tok, 0), new IdentifierExpr(k.tok, k.Name));
- var condBody = new IfStmt(com.BodyStartTok, mainBody.EndTok, kPositive, mainBody, null);
+ var condBody = new IfStmt(com.BodyStartTok, mainBody.EndTok, false, kPositive, mainBody, null);
prefixLemma.Body = new BlockStmt(com.tok, condBody.EndTok, new List<Statement>() { condBody });
}
// The prefix lemma now has all its components, so it's finally time we resolve it
@@ -1608,7 +1798,7 @@ namespace Microsoft.Dafny
}
}
- if (ErrorCount == prevErrorCount) { // because CheckCoCalls requires the given expression to have been successfully resolved
+ if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { // because CheckCoCalls requires the given expression to have been successfully resolved
// Perform the guardedness check on co-datatypes
foreach (var repr in ModuleDefinition.AllFunctionSCCs(declarations)) {
var module = repr.EnclosingModule;
@@ -1643,7 +1833,7 @@ namespace Microsoft.Dafny
foreach (var c in coCandidates) {
c.CandidateCall.CoCall = FunctionCallExpr.CoCallResolution.Yes;
c.EnclosingCoConstructor.IsCoCall = true;
- ReportAdditionalInformation(c.CandidateCall.tok, "co-recursive call", c.CandidateCall.Name.Length);
+ reporter.Info(MessageSource.Resolver, c.CandidateCall.tok, "co-recursive call");
}
// Finally, fill in the CoClusterTarget field
// Start by setting all the CoClusterTarget fields to CoRecursiveTargetAllTheWay.
@@ -1838,11 +2028,11 @@ namespace Microsoft.Dafny
// Check here for the presence of any 'ensures' clauses, which are not allowed (because we're not sure
// of their soundness)
if (fn.Ens.Count != 0) {
- Error(fn.Ens[0].tok, "a {0} is not allowed to declare any ensures clause", member.WhatKind);
+ reporter.Error(MessageSource.Resolver, fn.Ens[0].tok, "a {0} is not allowed to declare any ensures clause", member.WhatKind);
}
// Also check for 'reads' clauses
if (fn.Reads.Count != 0) {
- Error(fn.Reads[0].tok, "a {0} is not allowed to declare any reads clause", member.WhatKind); // (why?)
+ reporter.Error(MessageSource.Resolver, fn.Reads[0].tok, "a {0} is not allowed to declare any reads clause", member.WhatKind); // (why?)
}
if (fn.Body != null) {
FixpointPredicateChecks(fn.Body, fn, CallingPosition.Positive);
@@ -1858,13 +2048,229 @@ namespace Microsoft.Dafny
var dd = (NewtypeDecl)d;
if (dd.Module.CallGraph.GetSCCSize(dd) != 1) {
var cycle = Util.Comma(" -> ", dd.Module.CallGraph.GetSCC(dd), clbl => clbl.NameRelativeToModule);
- Error(dd.tok, "recursive dependency involving a newtype: " + cycle);
+ reporter.Error(MessageSource.Resolver, dd.tok, "recursive dependency involving a newtype: " + cycle);
}
}
}
}
}
+ private void FigureOutNativeType(NewtypeDecl dd, Dictionary<string, NativeType> nativeTypeMap) {
+ Contract.Requires(dd != null);
+ Contract.Requires(nativeTypeMap != null);
+ bool? boolNativeType = null;
+ NativeType stringNativeType = null;
+ object nativeTypeAttr = true;
+ bool hasNativeTypeAttr = Attributes.ContainsMatchingValue(dd.Attributes, "nativeType", ref nativeTypeAttr,
+ new Attributes.MatchingValueOption[] {
+ Attributes.MatchingValueOption.Empty,
+ Attributes.MatchingValueOption.Bool,
+ Attributes.MatchingValueOption.String },
+ err => reporter.Error(MessageSource.Resolver, dd, err));
+ if (hasNativeTypeAttr) {
+ if (nativeTypeAttr is bool) {
+ boolNativeType = (bool)nativeTypeAttr;
+ } else {
+ string keyString = (string)nativeTypeAttr;
+ if (nativeTypeMap.ContainsKey(keyString)) {
+ stringNativeType = nativeTypeMap[keyString];
+ } else {
+ reporter.Error(MessageSource.Resolver, dd, "Unsupported nativeType {0}", keyString);
+ }
+ }
+ }
+ if (stringNativeType != null || boolNativeType == true) {
+ if (!dd.BaseType.IsNumericBased(Type.NumericPersuation.Int)) {
+ reporter.Error(MessageSource.Resolver, dd, "nativeType can only be used on integral types");
+ }
+ if (dd.Var == null) {
+ reporter.Error(MessageSource.Resolver, dd, "nativeType can only be used if newtype specifies a constraint");
+ }
+ }
+ if (dd.Var != null) {
+ Func<Expression, BigInteger?> GetConst = null;
+ GetConst = (Expression e) => {
+ int m = 1;
+ BinaryExpr bin = e as BinaryExpr;
+ if (bin != null && bin.Op == BinaryExpr.Opcode.Sub && GetConst(bin.E0) == BigInteger.Zero) {
+ m = -1;
+ e = bin.E1;
+ }
+ LiteralExpr l = e as LiteralExpr;
+ if (l != null && l.Value is BigInteger) {
+ return m * (BigInteger)l.Value;
+ }
+ return null;
+ };
+ var bounds = DiscoverAllBounds_SingleVar(dd.Var, dd.Constraint);
+ List<NativeType> potentialNativeTypes =
+ (stringNativeType != null) ? new List<NativeType> { stringNativeType } :
+ (boolNativeType == false) ? new List<NativeType>() :
+ NativeTypes;
+ foreach (var nt in potentialNativeTypes) {
+ bool lowerOk = false;
+ bool upperOk = false;
+ foreach (var bound in bounds) {
+ if (bound is ComprehensionExpr.IntBoundedPool) {
+ var bnd = (ComprehensionExpr.IntBoundedPool)bound;
+ if (bnd.LowerBound != null) {
+ BigInteger? lower = GetConst(bnd.LowerBound);
+ if (lower != null && nt.LowerBound <= lower) {
+ lowerOk = true;
+ }
+ }
+ if (bnd.UpperBound != null) {
+ BigInteger? upper = GetConst(bnd.UpperBound);
+ if (upper != null && upper <= nt.UpperBound) {
+ upperOk = true;
+ }
+ }
+ }
+ }
+ if (lowerOk && upperOk) {
+ dd.NativeType = nt;
+ break;
+ }
+ }
+ if (dd.NativeType == null && (boolNativeType == true || stringNativeType != null)) {
+ reporter.Error(MessageSource.Resolver, dd, "Dafny's heuristics cannot find a compatible native type. " +
+ "Hint: try writing a newtype constraint of the form 'i:int | lowerBound <= i < upperBound && (...any additional constraints...)'");
+ }
+ if (dd.NativeType != null && stringNativeType == null) {
+ reporter.Info(MessageSource.Resolver, dd.tok, "{:nativeType \"" + dd.NativeType.Name + "\"}");
+ }
+ }
+ }
+
+ /// <summary>
+ /// Adds the type constraint ty==expected, eventually printing the error message "msg" if this is found to be
+ /// untenable. If this is immediately known not to be untenable, false is returned.
+ /// </summary>
+ private bool ConstrainTypes(Type ty, Type expected, TopLevelDecl declForToken, string msg, params object[] args) {
+ Contract.Requires(ty != null);
+ Contract.Requires(expected != null);
+ Contract.Requires(declForToken != null);
+ Contract.Requires(msg != null);
+ Contract.Requires(args != null);
+ return ConstrainTypes(ty, expected, declForToken.tok, msg, args);
+ }
+ /// <summary>
+ /// Adds the type constraint ty==expected, eventually printing the error message "msg" if this is found to be
+ /// untenable. If this is immediately known not to be untenable, false is returned.
+ /// </summary>
+ private bool ConstrainTypes(Type ty, Type expected, MemberDecl declForToken, string msg, params object[] args) {
+ Contract.Requires(ty != null);
+ Contract.Requires(expected != null);
+ Contract.Requires(declForToken != null);
+ Contract.Requires(msg != null);
+ Contract.Requires(args != null);
+ return ConstrainTypes(ty, expected, declForToken.tok, msg, args);
+ }
+ /// <summary>
+ /// Adds the type constraint ty==expected, eventually printing the error message "msg" if this is found to be
+ /// untenable. If this is immediately known not to be untenable, false is returned.
+ /// </summary>
+ private bool ConstrainTypes(Type ty, Type expected, Statement stmtForToken, string msg, params object[] args) {
+ Contract.Requires(ty != null);
+ Contract.Requires(expected != null);
+ Contract.Requires(stmtForToken != null);
+ Contract.Requires(msg != null);
+ Contract.Requires(args != null);
+ return ConstrainTypes(ty, expected, stmtForToken.Tok, msg, args);
+ }
+ /// <summary>
+ /// Adds the type constraint ty==expected, eventually printing the error message "msg" if this is found to be
+ /// untenable. If this is immediately known not to be untenable, false is returned.
+ /// </summary>
+ private bool ConstrainTypes(Type ty, Type expected, Expression exprForToken, string msg, params object[] args) {
+ Contract.Requires(ty != null);
+ Contract.Requires(expected != null);
+ Contract.Requires(exprForToken != null);
+ Contract.Requires(msg != null);
+ Contract.Requires(args != null);
+ return ConstrainTypes(ty, expected, exprForToken.tok, msg, args);
+ }
+ /// <summary>
+ /// Adds the type constraint ty==expected, eventually printing the error message "msg" if this is found to be
+ /// untenable. If this is immediately known not to be untenable, false is returned.
+ /// </summary>
+ private bool ConstrainTypes(Type ty, Type expected, IToken tok, string msg, params object[] args) {
+ Contract.Requires(ty != null);
+ Contract.Requires(expected != null);
+ Contract.Requires(tok != null);
+ Contract.Requires(msg != null);
+ Contract.Requires(args != null);
+#if LAZY_TYPE_CHECKING
+ var c = new TypeConstraint(ty, expected, tok, msg, args);
+ AllTypeConstraints.Add(c);
+ return true;
+#else
+ if (!UnifyTypes(ty, expected)) {
+ reporter.Error(MessageSource.Resolver, tok, msg, args);
+ return false;
+ }
+ return true;
+#endif
+ }
+
+ public List<TypeConstraint> AllTypeConstraints = new List<TypeConstraint>();
+
+ /// <summary>
+ /// Solves or simplifies as many type constraints as possible
+ /// </summary>
+ void PartiallySolveTypeConstraints() {
+ var remainingConstraints = new List<TypeConstraint>();
+ foreach (var constraint in AllTypeConstraints) {
+ if (!constraint.CheckTenable(this)) {
+ remainingConstraints.Add(constraint);
+ }
+ }
+ AllTypeConstraints = remainingConstraints;
+ }
+
+ /// <summary>
+ /// Attempts to fully solve all type constraints. Upon success, returns "true".
+ /// Upon failure, reports errors and returns "false".
+ /// Clears all constraints.
+ /// </summary>
+ bool SolveAllTypeConstraints() {
+ PartiallySolveTypeConstraints();
+ foreach (var constraint in AllTypeConstraints) {
+ constraint.ReportAsError(reporter);
+ }
+ var success = AllTypeConstraints.Count == 0;
+ AllTypeConstraints.Clear();
+ return success;
+ }
+
+ public class TypeConstraint
+ {
+ readonly Type Ty;
+ readonly Type Expected;
+ readonly IToken Tok;
+ readonly string Msg;
+ readonly object[] MsgArgs;
+ public TypeConstraint(Type ty, Type expected, IToken tok, string msg, object[] msgArgs) {
+ Ty = ty;
+ Expected = expected;
+ Tok = tok;
+ Msg = msg;
+ MsgArgs = msgArgs;
+ }
+ /// <summary>
+ /// Returns "true" if constraint is tenable, "false" otherwise.
+ /// </summary>
+ /// <returns></returns>
+ public bool CheckTenable(Resolver resolver) {
+ Contract.Requires(resolver != null);
+ return resolver.UnifyTypes(Ty, Expected);
+ }
+ public void ReportAsError(ErrorReporter reporter) {
+ Contract.Requires(reporter != null);
+ reporter.Error(MessageSource.Resolver, Tok, Msg, MsgArgs);
+ }
+ }
+
// ------------------------------------------------------------------------------------------------------
// ----- Visitors ---------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------
@@ -1876,45 +2282,14 @@ namespace Microsoft.Dafny
Contract.Requires(resolver != null);
this.resolver = resolver;
}
- public void Error(IToken tok, string msg, params object[] args) {
- Contract.Requires(tok != null);
- Contract.Requires(msg != null);
- Contract.Requires(args != null);
- resolver.Error(tok, msg, args);
- }
- public void Error(Expression expr, string msg, params object[] args) {
- Contract.Requires(expr != null);
- Contract.Requires(msg != null);
- Contract.Requires(args != null);
- Error(expr.tok, msg, args);
- }
}
abstract class ResolverTopDownVisitor<T> : TopDownVisitor<T>
{
- Resolver resolver;
+ protected Resolver resolver;
public ResolverTopDownVisitor(Resolver resolver) {
Contract.Requires(resolver != null);
this.resolver = resolver;
}
- protected void Error(IToken tok, string msg, params object[] args)
- {
- Contract.Requires(tok != null);
- Contract.Requires(msg != null);
- Contract.Requires(args != null);
- resolver.Error(tok, msg, args);
- }
- protected void Error(Expression expr, string msg, params object[] args)
- {
- Contract.Requires(expr != null);
- Contract.Requires(msg != null);
- Contract.Requires(args != null);
- Error(expr.tok, msg, args);
- }
- protected void ReportAdditionalInformation(IToken tok, string text, int length)
- {
- Contract.Requires(tok != null);
- resolver.ReportAdditionalInformation(tok, text, length);
- }
}
#endregion Visitors
@@ -1925,92 +2300,75 @@ namespace Microsoft.Dafny
private void CheckTypeInference_Member(MemberDecl member) {
if (member is Method) {
var m = (Method)member;
- m.Req.Iter(CheckTypeInference_MaybeFreeExpression);
- m.Ens.Iter(CheckTypeInference_MaybeFreeExpression);
- CheckTypeInference_Specification_FrameExpr(m.Mod);
- CheckTypeInference_Specification_Expr(m.Decreases);
+ m.Req.Iter(mfe => CheckTypeInference_MaybeFreeExpression(mfe, m));
+ m.Ens.Iter(mfe => CheckTypeInference_MaybeFreeExpression(mfe, m));
+ CheckTypeInference_Specification_FrameExpr(m.Mod, m);
+ CheckTypeInference_Specification_Expr(m.Decreases, m);
if (m.Body != null) {
- CheckTypeInference(m.Body);
- bool tail = true;
- bool hasTailRecursionPreference = Attributes.ContainsBool(m.Attributes, "tailrecursion", ref tail);
- if (hasTailRecursionPreference && !tail) {
- // the user specifically requested no tail recursion, so do nothing else
- } else if (hasTailRecursionPreference && tail && m.IsGhost) {
- Error(m.tok, "tail recursion can be specified only for methods that will be compiled, not for ghost methods");
- } else {
- var module = m.EnclosingClass.Module;
- var sccSize = module.CallGraph.GetSCCSize(m);
- if (hasTailRecursionPreference && 2 <= sccSize) {
- Error(m.tok, "sorry, tail-call optimizations are not supported for mutually recursive methods");
- } else if (hasTailRecursionPreference || sccSize == 1) {
- CallStmt tailCall = null;
- var status = CheckTailRecursive(m.Body.Body, m, ref tailCall, hasTailRecursionPreference);
- if (status != TailRecursionStatus.NotTailRecursive) {
- m.IsTailRecursive = true;
- if (tailCall != null) {
- // this means there was at least one recursive call
- ReportAdditionalInformation(m.tok, "tail recursive", m.Name.Length);
- }
- }
- }
- }
+ CheckTypeInference(m.Body, m);
}
} else if (member is Function) {
var f = (Function)member;
- var errorCount = ErrorCount;
- f.Req.Iter(CheckTypeInference);
- f.Ens.Iter(CheckTypeInference);
- f.Reads.Iter(fe => CheckTypeInference(fe.E));
- CheckTypeInference_Specification_Expr(f.Decreases);
+ var errorCount = reporter.Count(ErrorLevel.Error);
+ f.Req.Iter(e => CheckTypeInference(e, f));
+ f.Ens.Iter(e => CheckTypeInference(e, f));
+ f.Reads.Iter(fe => CheckTypeInference(fe.E, f));
+ CheckTypeInference_Specification_Expr(f.Decreases, f);
if (f.Body != null) {
- CheckTypeInference(f.Body);
- bool tail = true;
- if (Attributes.ContainsBool(f.Attributes, "tailrecursion", ref tail) && tail) {
- Error(f.tok, "sorry, tail-call functions are not supported");
- }
+ CheckTypeInference(f.Body, f);
}
- if (errorCount == ErrorCount && f is FixpointPredicate) {
+ if (errorCount == reporter.Count(ErrorLevel.Error) && f is FixpointPredicate) {
var cop = (FixpointPredicate)f;
CheckTypeInference_Member(cop.PrefixPredicate);
}
}
}
- private void CheckTypeInference_MaybeFreeExpression(MaybeFreeExpression mfe) {
+
+ private void CheckTypeInference_MaybeFreeExpression(MaybeFreeExpression mfe, ICodeContext codeContext) {
Contract.Requires(mfe != null);
+ Contract.Requires(codeContext != null);
foreach (var e in Attributes.SubExpressions(mfe.Attributes)) {
- CheckTypeInference(e);
+ CheckTypeInference(e, codeContext);
}
- CheckTypeInference(mfe.E);
+ CheckTypeInference(mfe.E, codeContext);
}
- private void CheckTypeInference_Specification_Expr(Specification<Expression> spec) {
+ private void CheckTypeInference_Specification_Expr(Specification<Expression> spec, ICodeContext codeContext) {
Contract.Requires(spec != null);
+ Contract.Requires(codeContext != null);
foreach (var e in Attributes.SubExpressions(spec.Attributes)) {
- CheckTypeInference(e);
+ CheckTypeInference(e, codeContext);
}
- spec.Expressions.Iter(CheckTypeInference);
+ spec.Expressions.Iter(e => CheckTypeInference(e, codeContext));
}
- private void CheckTypeInference_Specification_FrameExpr(Specification<FrameExpression> spec) {
+ private void CheckTypeInference_Specification_FrameExpr(Specification<FrameExpression> spec, ICodeContext codeContext) {
Contract.Requires(spec != null);
+ Contract.Requires(codeContext != null);
foreach (var e in Attributes.SubExpressions(spec.Attributes)) {
- CheckTypeInference(e);
+ CheckTypeInference(e, codeContext);
}
- spec.Expressions.Iter(fe => CheckTypeInference(fe.E));
+ spec.Expressions.Iter(fe => CheckTypeInference(fe.E, codeContext));
}
- void CheckTypeInference(Expression expr) {
+ void CheckTypeInference(Expression expr, ICodeContext codeContext) {
Contract.Requires(expr != null);
- var c = new CheckTypeInference_Visitor(this);
+ Contract.Requires(codeContext != null);
+ PartiallySolveTypeConstraints();
+ var c = new CheckTypeInference_Visitor(this, codeContext);
c.Visit(expr);
}
- void CheckTypeInference(Statement stmt) {
+ void CheckTypeInference(Statement stmt, ICodeContext codeContext) {
Contract.Requires(stmt != null);
- var c = new CheckTypeInference_Visitor(this);
+ Contract.Requires(codeContext != null);
+ var c = new CheckTypeInference_Visitor(this, codeContext);
c.Visit(stmt);
}
class CheckTypeInference_Visitor : ResolverBottomUpVisitor
{
- public CheckTypeInference_Visitor(Resolver resolver)
+ readonly ICodeContext codeContext;
+ public CheckTypeInference_Visitor(Resolver resolver, ICodeContext codeContext)
: base(resolver) {
Contract.Requires(resolver != null);
+ Contract.Requires(codeContext != null);
+ this.codeContext = codeContext;
}
protected override void VisitOneStmt(Statement stmt) {
if (stmt is VarDeclStmt) {
@@ -2018,28 +2376,109 @@ namespace Microsoft.Dafny
foreach (var local in s.Locals) {
CheckTypeIsDetermined(local.Tok, local.Type, "local variable");
}
+ } else if (stmt is LetStmt) {
+ var s = (LetStmt)stmt;
+ s.BoundVars.Iter(bv => CheckTypeIsDetermined(bv.tok, bv.Type, "bound variable"));
+
} else if (stmt is ForallStmt) {
var s = (ForallStmt)stmt;
s.BoundVars.Iter(bv => CheckTypeIsDetermined(bv.tok, bv.Type, "bound variable"));
+ List<BoundVar> missingBounds;
+ s.Bounds = DiscoverBestBounds_MultipleVars(s.BoundVars, s.Range, true, true, out missingBounds);
+
+ } else if (stmt is AssignSuchThatStmt) {
+ var s = (AssignSuchThatStmt)stmt;
+ if (s.AssumeToken == null) {
+ var varLhss = new List<IVariable>();
+ foreach (var lhs in s.Lhss) {
+ var ide = (IdentifierExpr)lhs.Resolved; // successful resolution implies all LHS's are IdentifierExpr's
+ varLhss.Add(ide.Var);
+ }
+ List<IVariable> missingBounds;
+ var bestBounds = DiscoverBestBounds_MultipleVars(varLhss, s.Expr, true, false, out missingBounds);
+ if (missingBounds.Count != 0) {
+ s.MissingBounds = missingBounds; // so that an error message can be produced during compilation
+ } else {
+ Contract.Assert(bestBounds != null);
+ s.Bounds = bestBounds;
+ }
+ }
+ } else if (stmt is CalcStmt) {
+ var s = (CalcStmt)stmt;
+ // The resolution of the calc statement builds up .Steps and .Result, which are of the form E0 OP E1, where
+ // E0 and E1 are expressions from .Lines. These additional expressions still need to have their .ResolvedOp
+ // fields filled in, so we visit them (but not their subexpressions) here.
+ foreach (var e in s.Steps) {
+ VisitOneExpr(e);
+ }
+ VisitOneExpr(s.Result);
}
}
protected override void VisitOneExpr(Expression expr) {
if (expr is ComprehensionExpr) {
var e = (ComprehensionExpr)expr;
- if (e != null) {
- foreach (var bv in e.BoundVars) {
- if (!IsDetermined(bv.Type.Normalize())) {
- Error(bv.tok, "type of bound variable '{0}' could not be determined; please specify the type explicitly",
- bv.Name);
+ foreach (var bv in e.BoundVars) {
+ if (!IsDetermined(bv.Type.Normalize())) {
+ resolver.reporter.Error(MessageSource.Resolver, bv.tok, "type of bound variable '{0}' could not be determined; please specify the type explicitly", bv.Name);
+ }
+ }
+ // apply bounds discovery to quantifiers, finite sets, and finite maps
+ string what = null;
+ Expression whereToLookForBounds = null;
+ bool polarity = true;
+ if (e is QuantifierExpr) {
+ what = "quantifier";
+ whereToLookForBounds = ((QuantifierExpr)e).LogicalBody();
+ polarity = e is ExistsExpr;
+ } else if (e is SetComprehension) {
+ what = "set comprehension";
+ whereToLookForBounds = e.Range;
+ } else if (e is MapComprehension) {
+ what = "map comprehension";
+ whereToLookForBounds = e.Range;
+ } else {
+ Contract.Assume(e is LambdaExpr); // otherwise, unexpected ComprehensionExpr
+ }
+ if (whereToLookForBounds != null) {
+ List<BoundVar> missingBounds;
+ e.Bounds = DiscoverBestBounds_MultipleVars(e.BoundVars, whereToLookForBounds, polarity, true, out missingBounds);
+ if (missingBounds.Count != 0) {
+ e.MissingBounds = missingBounds;
+
+ if ((e is SetComprehension && !((SetComprehension)e).Finite) || (e is MapComprehension && !((MapComprehension)e).Finite)) {
+ // a possibly infinite set/map has no restrictions on its range (unless it's used in a compilable context, which is checked later)
+ } else if (e is QuantifierExpr) {
+ // a quantifier has no restrictions on its range (unless it's used in a compilable context, which is checked later)
+ } else if (e is SetComprehension && e.Type.HasFinitePossibleValues) {
+ // This means the set is finite, regardless of if the Range is bounded. So, we don't give any error here.
+ // However, if this expression is used in a non-ghost context (which is not yet known at this stage of
+ // resolution), the resolver will generate an error about that later.
+ } else {
+ // we cannot be sure that the set/map really is finite
+ foreach (var bv in missingBounds) {
+ resolver.reporter.Error(MessageSource.Resolver, e, "a {0} must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{1}'", what, bv.Name);
+ }
+ }
+ }
+ if (codeContext is Function && e.Bounds != null) {
+ // functions are not allowed to depend on the set of allocated objects
+ Contract.Assert(e.Bounds.Count == e.BoundVars.Count);
+ for (int i = 0; i < e.Bounds.Count; i++) {
+ var bound = e.Bounds[i] as ComprehensionExpr.RefBoundedPool;
+ if (bound != null) {
+ var bv = e.BoundVars[i];
+ resolver.reporter.Error(MessageSource.Resolver, expr, "a {0} involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of '{1}'", what, bv.Name);
+ }
}
}
}
+
} else if (expr is MemberSelectExpr) {
var e = (MemberSelectExpr)expr;
if (e.Member is Function || e.Member is Method) {
foreach (var p in e.TypeApplication) {
if (!IsDetermined(p.Normalize())) {
- Error(e.tok, "type '{0}' to the {2} '{1}' is not determined", p, e.Member.Name, e.Member.WhatKind);
+ resolver.reporter.Error(MessageSource.Resolver, e.tok, "type '{0}' to the {2} '{1}' is not determined", p, e.Member.Name, e.Member.WhatKind);
}
}
}
@@ -2047,8 +2486,8 @@ namespace Microsoft.Dafny
var e = (FunctionCallExpr)expr;
foreach (var p in e.TypeArgumentSubstitutions) {
if (!IsDetermined(p.Value.Normalize())) {
- Error(e.tok, "type variable '{0}' in the function call to '{1}' could not be determined{2}", p.Key.Name, e.Name,
- (e.Name.Contains("reveal_") || e.Name.Contains("_FULL"))
+ resolver.reporter.Error(MessageSource.Resolver, e.tok, "type variable '{0}' in the function call to '{1}' could not be determined{2}", p.Key.Name, e.Name,
+ (e.Name.StartsWith("reveal_") || e.Name.EndsWith("_FULL"))
? ". If you are making an opaque function, make sure that the function can be called."
: ""
);
@@ -2059,7 +2498,7 @@ namespace Microsoft.Dafny
foreach (var p in e.LHSs) {
foreach (var x in p.Vars) {
if (!IsDetermined(x.Type.Normalize())) {
- Error(e.tok, "the type of the bound variable '{0}' could not be determined", x.Name);
+ resolver.reporter.Error(MessageSource.Resolver, e.tok, "the type of the bound variable '{0}' could not be determined", x.Name);
}
}
}
@@ -2097,7 +2536,8 @@ namespace Microsoft.Dafny
proxy.T = new ObjectType();
return true;
}
- return !(t is TypeProxy); // all other proxies indicate the type has not yet been determined
+ // all other proxies indicate the type has not yet been determined, provided their type parameters have been
+ return !(t is TypeProxy) && t.TypeArgs.All(tt => IsDetermined(tt.Normalize()));
}
ISet<TypeProxy> UnderspecifiedTypeProxies = new HashSet<TypeProxy>();
bool CheckTypeIsDetermined(IToken tok, Type t, string what) {
@@ -2126,7 +2566,7 @@ namespace Microsoft.Dafny
var proxy = (TypeProxy)t;
if (!UnderspecifiedTypeProxies.Contains(proxy)) {
// report an error for this TypeProxy only once
- Error(tok, "the type of this {0} is underspecified", what);
+ resolver.reporter.Error(MessageSource.Resolver, tok, "the type of this {0} is underspecified", what);
UnderspecifiedTypeProxies.Add(proxy);
}
return false;
@@ -2147,6 +2587,58 @@ namespace Microsoft.Dafny
#endregion CheckTypeInference
// ------------------------------------------------------------------------------------------------------
+ // ----- CheckExpression --------------------------------------------------------------------------------
+ // ------------------------------------------------------------------------------------------------------
+ #region CheckExpression
+ /// <summary>
+ /// This method computes ghost interests in the statement portion of StmtExpr's and
+ /// checks for hint restrictions in any CalcStmt.
+ /// </summary>
+ void CheckExpression(Expression expr, Resolver resolver, ICodeContext codeContext) {
+ Contract.Requires(expr != null);
+ Contract.Requires(resolver != null);
+ Contract.Requires(codeContext != null);
+ var v = new CheckExpression_Visitor(resolver, codeContext);
+ v.Visit(expr);
+ }
+ /// <summary>
+ /// This method computes ghost interests in the statement portion of StmtExpr's and
+ /// checks for hint restrictions in any CalcStmt.
+ /// </summary>
+ void CheckExpression(Statement stmt, Resolver resolver, ICodeContext codeContext) {
+ Contract.Requires(stmt != null);
+ Contract.Requires(resolver != null);
+ Contract.Requires(codeContext != null);
+ var v = new CheckExpression_Visitor(resolver, codeContext);
+ v.Visit(stmt);
+ }
+ class CheckExpression_Visitor : ResolverBottomUpVisitor
+ {
+ readonly ICodeContext CodeContext;
+ public CheckExpression_Visitor(Resolver resolver, ICodeContext codeContext)
+ : base(resolver) {
+ Contract.Requires(resolver != null);
+ Contract.Requires(codeContext != null);
+ CodeContext = codeContext;
+ }
+ protected override void VisitOneExpr(Expression expr) {
+ if (expr is StmtExpr) {
+ var e = (StmtExpr)expr;
+ resolver.ComputeGhostInterest(e.S, true, CodeContext);
+ }
+ }
+ protected override void VisitOneStmt(Statement stmt) {
+ if (stmt is CalcStmt) {
+ var s = (CalcStmt)stmt;
+ foreach (var h in s.Hints) {
+ resolver.CheckHintRestrictions(h, new HashSet<LocalVariable>());
+ }
+ }
+ }
+ }
+ #endregion
+
+ // ------------------------------------------------------------------------------------------------------
// ----- CheckTailRecursive -----------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------
#region CheckTailRecursive
@@ -2182,7 +2674,7 @@ namespace Microsoft.Dafny
if (status == TailRecursionStatus.TailCallSpent) {
// a tail call cannot be followed by non-ghost code
if (reportErrors) {
- Error(tailCall.Tok, "this recursive call is not recognized as being tail recursive, because it is followed by non-ghost code");
+ reporter.Error(MessageSource.Resolver, tailCall.Tok, "this recursive call is not recognized as being tail recursive, because it is followed by non-ghost code");
}
return TailRecursionStatus.NotTailRecursive;
}
@@ -2195,6 +2687,41 @@ namespace Microsoft.Dafny
return status;
}
+ void DetermineTailRecursion(Function f) {
+ Contract.Requires(f != null);
+ bool tail = true;
+ if (Attributes.ContainsBool(f.Attributes, "tailrecursion", ref tail) && tail) {
+ reporter.Error(MessageSource.Resolver, f.tok, "sorry, tail-call functions are not supported");
+ }
+ }
+
+ void DetermineTailRecursion(Method m) {
+ Contract.Requires(m != null);
+ bool tail = true;
+ bool hasTailRecursionPreference = Attributes.ContainsBool(m.Attributes, "tailrecursion", ref tail);
+ if (hasTailRecursionPreference && !tail) {
+ // the user specifically requested no tail recursion, so do nothing else
+ } else if (hasTailRecursionPreference && tail && m.IsGhost) {
+ reporter.Error(MessageSource.Resolver, m.tok, "tail recursion can be specified only for methods that will be compiled, not for ghost methods");
+ } else {
+ var module = m.EnclosingClass.Module;
+ var sccSize = module.CallGraph.GetSCCSize(m);
+ if (hasTailRecursionPreference && 2 <= sccSize) {
+ reporter.Error(MessageSource.Resolver, m.tok, "sorry, tail-call optimizations are not supported for mutually recursive methods");
+ } else if (hasTailRecursionPreference || sccSize == 1) {
+ CallStmt tailCall = null;
+ var status = CheckTailRecursive(m.Body.Body, m, ref tailCall, hasTailRecursionPreference);
+ if (status != TailRecursionStatus.NotTailRecursive) {
+ m.IsTailRecursive = true;
+ if (tailCall != null) {
+ // this means there was at least one recursive call
+ reporter.Info(MessageSource.Resolver, m.tok, "tail recursive");
+ }
+ }
+ }
+ }
+ }
+
/// <summary>
/// See CheckTailRecursive(List Statement, ...), including its description of "tailCall".
/// In the current implementation, "enclosingMethod" is not allowed to be a mutually recursive method.
@@ -2230,7 +2757,7 @@ namespace Microsoft.Dafny
// all is good
} else {
if (reportErrors) {
- Error(s.Tok, "the recursive call to '{0}' is not tail recursive because the actual out-parameter {1} is not the formal out-parameter '{2}'", s.Method.Name, i, formal.Name);
+ reporter.Error(MessageSource.Resolver, s.Tok, "the recursive call to '{0}' is not tail recursive because the actual out-parameter {1} is not the formal out-parameter '{2}'", s.Method.Name, i, formal.Name);
}
return TailRecursionStatus.NotTailRecursive;
}
@@ -2276,7 +2803,7 @@ namespace Microsoft.Dafny
if (status == TailRecursionStatus.NotTailRecursive) {
// an error has already been reported
} else if (reportErrors) {
- Error(tailCall.Tok, "a recursive call inside a loop is not recognized as being a tail call");
+ reporter.Error(MessageSource.Resolver, tailCall.Tok, "a recursive call inside a loop is not recognized as being a tail call");
}
return TailRecursionStatus.NotTailRecursive;
}
@@ -2288,7 +2815,7 @@ namespace Microsoft.Dafny
if (status == TailRecursionStatus.NotTailRecursive) {
// an error has already been reported
} else if (reportErrors) {
- Error(tailCall.Tok, "a recursive call inside a loop is not recognized as being a tail call");
+ reporter.Error(MessageSource.Resolver, tailCall.Tok, "a recursive call inside a loop is not recognized as being a tail call");
}
return TailRecursionStatus.NotTailRecursive;
}
@@ -2303,7 +2830,7 @@ namespace Microsoft.Dafny
if (status == TailRecursionStatus.NotTailRecursive) {
// an error has already been reported
} else if (reportErrors) {
- Error(tailCall.Tok, "a recursive call inside a forall statement is not a tail call");
+ reporter.Error(MessageSource.Resolver, tailCall.Tok, "a recursive call inside a forall statement is not a tail call");
}
return TailRecursionStatus.NotTailRecursive;
}
@@ -2328,6 +2855,7 @@ namespace Microsoft.Dafny
if (s.Update != null) {
return CheckTailRecursive(s.Update, enclosingMethod, ref tailCall, reportErrors);
}
+ } else if (stmt is LetStmt) {
} else {
Contract.Assert(false); // unexpected statement type
}
@@ -2336,6 +2864,56 @@ namespace Microsoft.Dafny
#endregion CheckTailRecursive
// ------------------------------------------------------------------------------------------------------
+ // ----- FuelAdjustmentChecks ---------------------------------------------------------------------------
+ // ------------------------------------------------------------------------------------------------------
+ #region FuelAdjustmentChecks
+
+ protected void CheckForFuelAdjustments(IToken tok, Attributes attrs, ModuleDefinition currentModule) {
+ List<List<Expression>> results = Attributes.FindAllExpressions(attrs, "fuel");
+
+ if (results != null) {
+ foreach (List<Expression> args in results) {
+ if (args != null && args.Count >= 2) {
+ // Try to extract the function from the first argument
+ MemberSelectExpr selectExpr = args[0].Resolved as MemberSelectExpr;
+ if (selectExpr != null) {
+ Function f = selectExpr.Member as Function;
+ if (f != null) {
+ f.IsFueled = true;
+ if (f.IsProtected && currentModule != f.EnclosingClass.Module) {
+ reporter.Error(MessageSource.Resolver, tok, "cannot adjust fuel for protected function {0} from another module", f.Name);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public class FuelAdjustment_Context
+ {
+ public ModuleDefinition currentModule;
+ public FuelAdjustment_Context(ModuleDefinition currentModule) {
+ this.currentModule = currentModule;
+ }
+ }
+
+ class FuelAdjustment_Visitor : ResolverTopDownVisitor<FuelAdjustment_Context>
+ {
+ public FuelAdjustment_Visitor(Resolver resolver)
+ : base(resolver) {
+ Contract.Requires(resolver != null);
+ }
+
+ protected override bool VisitOneStmt(Statement stmt, ref FuelAdjustment_Context st) {
+ resolver.CheckForFuelAdjustments(stmt.Tok, stmt.Attributes, st.currentModule);
+ return true;
+ }
+ }
+
+ #endregion FuelAdjustmentChecks
+
+ // ------------------------------------------------------------------------------------------------------
// ----- FixpointPredicateChecks ------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------
#region FixpointPredicateChecks
@@ -2409,6 +2987,7 @@ namespace Microsoft.Dafny
return false;
} else if (expr is QuantifierExpr) {
var e = (QuantifierExpr)expr;
+ Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution
var cpos = IsCoContext ? cp : Invert(cp);
if ((cpos == CallingPosition.Positive && e is ExistsExpr) || (cpos == CallingPosition.Negative && e is ForallExpr)) {
if (e.MissingBounds != null && e.MissingBounds.Count != 0) {
@@ -2450,7 +3029,7 @@ namespace Microsoft.Dafny
var article = context is InductivePredicate ? "an" : "a";
// we're looking at a recursive call
if (!(context is InductivePredicate ? e.Function is InductivePredicate : e.Function is CoPredicate)) {
- Error(e, "a recursive call from {0} {1} can go only to other {1}s", article, context.WhatKind);
+ resolver.reporter.Error(MessageSource.Resolver, e, "a recursive call from {0} {1} can go only to other {1}s", article, context.WhatKind);
} else if (cp != CallingPosition.Positive) {
var msg = string.Format("{0} {1} can be called recursively only in positive positions", article, context.WhatKind);
if (cp == CallingPosition.Neither) {
@@ -2459,10 +3038,10 @@ namespace Microsoft.Dafny
} else {
// the fixpoint-call is not inside an quantifier, so don't bother mentioning the part of existentials/universals in the error message
}
- Error(e, msg);
+ resolver.reporter.Error(MessageSource.Resolver, e, msg);
} else {
e.CoCall = FunctionCallExpr.CoCallResolution.Yes;
- ReportAdditionalInformation(e.tok, e.Function.Name + "#[_k - 1]", e.Function.Name.Length);
+ resolver.reporter.Info(MessageSource.Resolver, e.tok, e.Function.Name + "#[_k - 1]");
}
}
// do the sub-parts with cp := Neither
@@ -2477,7 +3056,7 @@ namespace Microsoft.Dafny
if (ModuleDefinition.InSameSCC(context, s.Method)) {
// we're looking at a recursive call
var article = context is InductivePredicate ? "an" : "a";
- Error(stmt.Tok, "a recursive call from {0} {1} can go only to other {1}s", article, context.WhatKind);
+ resolver.reporter.Error(MessageSource.Resolver, stmt.Tok, "a recursive call from {0} {1} can go only to other {1}s", article, context.WhatKind);
}
// do the sub-parts with the same "cp"
return true;
@@ -2518,7 +3097,7 @@ namespace Microsoft.Dafny
if (ModuleDefinition.InSameSCC(context, s.Method)) {
// we're looking at a recursive call (to a non-fixpoint-lemma)
var article = context is InductiveLemma ? "an" : "a";
- Error(s.Tok, "a recursive call from {0} {1} can go only to other {1}s and prefix lemmas", article, context.WhatKind);
+ resolver.reporter.Error(MessageSource.Resolver, s.Tok, "a recursive call from {0} {1} can go only to other {1}s and prefix lemmas", article, context.WhatKind);
}
}
}
@@ -2530,7 +3109,7 @@ namespace Microsoft.Dafny
// the call goes from a colemma context to a non-colemma callee
if (ModuleDefinition.InSameSCC(context, e.Function)) {
// we're looking at a recursive call (to a non-colemma)
- Error(e.tok, "a recursive call from a colemma can go only to other colemmas and prefix lemmas");
+ resolver.reporter.Error(MessageSource.Resolver, e.tok, "a recursive call from a colemma can go only to other colemmas and prefix lemmas");
}
}
}
@@ -2561,6 +3140,11 @@ namespace Microsoft.Dafny
foreach (var v in s.Locals) {
CheckEqualityTypes_Type(v.Tok, v.Type);
}
+ } else if (stmt is LetStmt) {
+ var s = (LetStmt)stmt;
+ foreach (var v in s.BoundVars) {
+ CheckEqualityTypes_Type(v.Tok, v.Type);
+ }
} else if (stmt is WhileStmt) {
var s = (WhileStmt)stmt;
// don't recurse on the specification parts, which are ghost
@@ -2589,7 +3173,7 @@ namespace Microsoft.Dafny
foreach (var formalTypeArg in s.Method.TypeArgs) {
var actualTypeArg = s.MethodSelect.TypeArgumentSubstitutions()[formalTypeArg];
if (formalTypeArg.MustSupportEquality && !actualTypeArg.SupportsEquality) {
- Error(s.Tok, "type parameter {0} ({1}) passed to method {2} must support equality (got {3}){4}", i, formalTypeArg.Name, s.Method.Name, actualTypeArg, TypeEqualityErrorMessageHint(actualTypeArg));
+ resolver.reporter.Error(MessageSource.Resolver, s.Tok, "type parameter {0} ({1}) passed to method {2} must support equality (got {3}){4}", i, formalTypeArg.Name, s.Method.Name, actualTypeArg, TypeEqualityErrorMessageHint(actualTypeArg));
}
i++;
}
@@ -2617,6 +3201,14 @@ namespace Microsoft.Dafny
foreach (var v in s.BoundVars) {
CheckEqualityTypes_Type(v.Tok, v.Type);
}
+ // do substatements and subexpressions, except attributes and ensures clauses, since they are not compiled
+ foreach (var ss in s.SubStatements) {
+ Visit(ss, st);
+ }
+ if (s.Range != null) {
+ Visit(s.Range, st);
+ }
+ return false; // we're done
}
return true;
}
@@ -2636,9 +3228,9 @@ namespace Microsoft.Dafny
} else if (e1 != null && e1.Arguments.Count == 0) {
// oh yeah!
} else if (!t0.SupportsEquality) {
- Error(e.E0, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0));
+ resolver.reporter.Error(MessageSource.Resolver, e.E0, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0));
} else if (!t1.SupportsEquality) {
- Error(e.E1, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t1, TypeEqualityErrorMessageHint(t1));
+ resolver.reporter.Error(MessageSource.Resolver, e.E1, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t1, TypeEqualityErrorMessageHint(t1));
}
break;
default:
@@ -2651,12 +3243,12 @@ namespace Microsoft.Dafny
case BinaryExpr.ResolvedOpcode.Prefix:
case BinaryExpr.ResolvedOpcode.ProperPrefix:
if (!t1.SupportsEquality) {
- Error(e.E1, "{0} can only be applied to expressions of sequence types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t1, TypeEqualityErrorMessageHint(t1));
+ resolver.reporter.Error(MessageSource.Resolver, e.E1, "{0} can only be applied to expressions of sequence types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t1, TypeEqualityErrorMessageHint(t1));
} else if (!t0.SupportsEquality) {
if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.InSet || e.ResolvedOp == BinaryExpr.ResolvedOpcode.NotInSeq) {
- Error(e.E0, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0));
+ resolver.reporter.Error(MessageSource.Resolver, e.E0, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0));
} else {
- Error(e.E0, "{0} can only be applied to expressions of sequence types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0));
+ resolver.reporter.Error(MessageSource.Resolver, e.E0, "{0} can only be applied to expressions of sequence types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0));
}
}
break;
@@ -2675,6 +3267,18 @@ namespace Microsoft.Dafny
foreach (var bv in e.BoundVars) {
CheckEqualityTypes_Type(bv.tok, bv.Type);
}
+ } else if (expr is MemberSelectExpr) {
+ var e = (MemberSelectExpr)expr;
+ if (e.Member is Function || e.Member is Method) {
+ var i = 0;
+ foreach (var tp in ((ICallable)e.Member).TypeArgs) {
+ var actualTp = e.TypeApplication[e.Member.EnclosingClass.TypeArgs.Count + i];
+ if (tp.MustSupportEquality && !actualTp.SupportsEquality) {
+ resolver.reporter.Error(MessageSource.Resolver, e.tok, "type parameter {0} ({1}) passed to {5} '{2}' must support equality (got {3}){4}", i, tp.Name, e.Member.Name, actualTp, TypeEqualityErrorMessageHint(actualTp), e.Member.WhatKind);
+ }
+ i++;
+ }
+ }
} else if (expr is FunctionCallExpr) {
var e = (FunctionCallExpr)expr;
Contract.Assert(e.Function.TypeArgs.Count <= e.TypeArgumentSubstitutions.Count);
@@ -2682,7 +3286,7 @@ namespace Microsoft.Dafny
foreach (var formalTypeArg in e.Function.TypeArgs) {
var actualTypeArg = e.TypeArgumentSubstitutions[formalTypeArg];
if (formalTypeArg.MustSupportEquality && !actualTypeArg.SupportsEquality) {
- Error(e.tok, "type parameter {0} ({1}) passed to function {2} must support equality (got {3}){4}", i, formalTypeArg.Name, e.Function.Name, actualTypeArg, TypeEqualityErrorMessageHint(actualTypeArg));
+ resolver.reporter.Error(MessageSource.Resolver, e.tok, "type parameter {0} ({1}) passed to function {2} must support equality (got {3}){4}", i, formalTypeArg.Name, e.Function.Name, actualTypeArg, TypeEqualityErrorMessageHint(actualTypeArg));
}
i++;
}
@@ -2697,7 +3301,7 @@ namespace Microsoft.Dafny
i++;
}
return false; // we've done what there is to be done
- } else if (expr is SetDisplayExpr || expr is MultiSetDisplayExpr || expr is MapDisplayExpr || expr is MultiSetFormingExpr) {
+ } else if (expr is SetDisplayExpr || expr is MultiSetDisplayExpr || expr is MapDisplayExpr || expr is MultiSetFormingExpr || expr is StaticReceiverExpr) {
// This catches other expressions whose type may potentially be illegal
CheckEqualityTypes_Type(expr.tok, expr.Type);
}
@@ -2711,26 +3315,24 @@ namespace Microsoft.Dafny
if (type is BasicType) {
// fine
} else if (type is SetType) {
- var argType = ((SetType)type).Arg;
+ var st = (SetType)type;
+ var argType = st.Arg;
if (!argType.SupportsEquality) {
- Error(tok, "set argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType));
+ resolver.reporter.Error(MessageSource.Resolver, tok, "{2}set argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType), st.Finite ? "" : "i");
}
CheckEqualityTypes_Type(tok, argType);
} else if (type is MultiSetType) {
var argType = ((MultiSetType)type).Arg;
if (!argType.SupportsEquality) {
- Error(tok, "multiset argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType));
+ resolver.reporter.Error(MessageSource.Resolver, tok, "multiset argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType));
}
CheckEqualityTypes_Type(tok, argType);
} else if (type is MapType) {
var mt = (MapType)type;
- if (!mt.Finite) {
- Error(tok, "imaps do not support equality: {0}", mt);
- }
if (!mt.Domain.SupportsEquality) {
- Error(tok, "map domain type must support equality (got {0}){1}", mt.Domain, TypeEqualityErrorMessageHint(mt.Domain));
+ resolver.reporter.Error(MessageSource.Resolver, tok, "{2}map domain type must support equality (got {0}){1}", mt.Domain, TypeEqualityErrorMessageHint(mt.Domain), mt.Finite ? "" : "i");
}
CheckEqualityTypes_Type(tok, mt.Domain);
CheckEqualityTypes_Type(tok, mt.Range);
@@ -2756,7 +3358,7 @@ namespace Microsoft.Dafny
foreach (var argType in udt.TypeArgs) {
var formalTypeArg = formalTypeArgs[i];
if (formalTypeArg.MustSupportEquality && !argType.SupportsEquality) {
- Error(tok, "type parameter {0} ({1}) passed to type {2} must support equality (got {3}){4}", i, formalTypeArg.Name, udt.ResolvedClass.Name, argType, TypeEqualityErrorMessageHint(argType));
+ resolver.reporter.Error(MessageSource.Resolver, tok, "type parameter {0} ({1}) passed to type {2} must support equality (got {3}){4}", i, formalTypeArg.Name, udt.ResolvedClass.Name, argType, TypeEqualityErrorMessageHint(argType));
}
CheckEqualityTypes_Type(tok, argType);
i++;
@@ -2799,6 +3401,321 @@ namespace Microsoft.Dafny
#endregion CheckEqualityTypes
// ------------------------------------------------------------------------------------------------------
+ // ----- ComputeGhostInterest ---------------------------------------------------------------------------
+ // ------------------------------------------------------------------------------------------------------
+ #region ComputeGhostInterest
+ public void ComputeGhostInterest(Statement stmt, bool mustBeErasable, ICodeContext codeContext) {
+ Contract.Requires(stmt != null);
+ Contract.Requires(codeContext != null);
+ var visitor = new GhostInterest_Visitor(codeContext, this);
+ visitor.Visit(stmt, mustBeErasable);
+ }
+ class GhostInterest_Visitor
+ {
+ readonly ICodeContext codeContext;
+ readonly Resolver resolver;
+ public GhostInterest_Visitor(ICodeContext codeContext, Resolver resolver) {
+ Contract.Requires(codeContext != null);
+ Contract.Requires(resolver != null);
+ this.codeContext = codeContext;
+ this.resolver = resolver;
+ }
+ protected void Error(Statement stmt, string msg, params object[] msgArgs) {
+ Contract.Requires(stmt != null);
+ Contract.Requires(msg != null);
+ Contract.Requires(msgArgs != null);
+ resolver.reporter.Error(MessageSource.Resolver, stmt, msg, msgArgs);
+ }
+ protected void Error(Expression expr, string msg, params object[] msgArgs) {
+ Contract.Requires(expr != null);
+ Contract.Requires(msg != null);
+ Contract.Requires(msgArgs != null);
+ resolver.reporter.Error(MessageSource.Resolver, expr, msg, msgArgs);
+ }
+ protected void Error(IToken tok, string msg, params object[] msgArgs) {
+ Contract.Requires(tok != null);
+ Contract.Requires(msg != null);
+ Contract.Requires(msgArgs != null);
+ resolver.reporter.Error(MessageSource.Resolver, tok, msg, msgArgs);
+ }
+ /// <summary>
+ /// This method does three things, in order:
+ /// 0. Sets .IsGhost to "true" if the statement is ghost. This often depends on some guard of the statement
+ /// (like the guard of an "if" statement) or the LHS of the statement (if it is an assignment).
+ /// Note, if "mustBeErasable", then the statement is already in a ghost context.
+ /// statement itself is ghost) or and the statement assigns to a non-ghost field
+ /// 1. Determines if the statement and all its subparts are legal under its computed .IsGhost setting.
+ /// 2. ``Upgrades'' .IsGhost to "true" if, after investigation of the substatements of the statement, it
+ /// turns out that the statement can be erased during compilation.
+ /// Notes:
+ /// * Both step (0) and step (2) sets the .IsGhost field. What step (0) does affects only the
+ /// rules of resolution, whereas step (2) makes a note for the later compilation phase.
+ /// * It is important to do step (0) before step (1)--that is, it is important to set the statement's ghost
+ /// status before descending into its sub-statements--because break statements look at the ghost status of
+ /// its enclosing statements.
+ /// * The method called by a StmtExpr must be ghost; however, this is checked elsewhere. For
+ /// this reason, it is not necessary to visit all subexpressions, unless the subexpression
+ /// matter for the ghost checking/recording of "stmt".
+ /// </summary>
+ public void Visit(Statement stmt, bool mustBeErasable) {
+ Contract.Requires(stmt != null);
+ Contract.Assume(!codeContext.IsGhost || mustBeErasable); // (this is really a precondition) codeContext.IsGhost ==> mustBeErasable
+
+ if (stmt is PredicateStmt) {
+ stmt.IsGhost = true;
+
+ } else if (stmt is PrintStmt) {
+ var s = (PrintStmt)stmt;
+ if (mustBeErasable) {
+ Error(stmt, "print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)");
+ } else {
+ s.Args.Iter(resolver.CheckIsCompilable);
+ }
+
+ } else if (stmt is BreakStmt) {
+ var s = (BreakStmt)stmt;
+ s.IsGhost = mustBeErasable;
+ if (s.IsGhost && !s.TargetStmt.IsGhost) {
+ var targetIsLoop = s.TargetStmt is WhileStmt || s.TargetStmt is AlternativeLoopStmt;
+ Error(stmt, "ghost-context break statement is not allowed to break out of non-ghost " + (targetIsLoop ? "loop" : "structure"));
+ }
+
+ } else if (stmt is ProduceStmt) {
+ var s = (ProduceStmt)stmt;
+ var kind = stmt is YieldStmt ? "yield" : "return";
+ if (mustBeErasable && !codeContext.IsGhost) {
+ Error(stmt, "{0} statement is not allowed in this context (because it is guarded by a specification-only expression)", kind);
+ }
+ if (s.hiddenUpdate != null) {
+ Visit(s.hiddenUpdate, mustBeErasable);
+ }
+
+ } else if (stmt is AssignSuchThatStmt) {
+ var s = (AssignSuchThatStmt)stmt;
+ s.IsGhost = mustBeErasable || s.AssumeToken != null || s.Lhss.Any(AssignStmt.LhsIsToGhost);
+ if (mustBeErasable && !codeContext.IsGhost) {
+ foreach (var lhs in s.Lhss) {
+ var gk = AssignStmt.LhsIsToGhost_Which(lhs);
+ if (gk != AssignStmt.NonGhostKind.IsGhost) {
+ Error(lhs, "cannot assign to {0} in a ghost context", AssignStmt.NonGhostKind_To_String(gk));
+ }
+ }
+ } else if (!mustBeErasable && s.AssumeToken == null && resolver.UsesSpecFeatures(s.Expr)) {
+ foreach (var lhs in s.Lhss) {
+ var gk = AssignStmt.LhsIsToGhost_Which(lhs);
+ if (gk != AssignStmt.NonGhostKind.IsGhost) {
+ Error(lhs, "{0} cannot be assigned a value that depends on a ghost", AssignStmt.NonGhostKind_To_String(gk));
+ }
+ }
+ }
+
+ } else if (stmt is UpdateStmt) {
+ var s = (UpdateStmt)stmt;
+ s.ResolvedStatements.Iter(ss => Visit(ss, mustBeErasable));
+ s.IsGhost = s.ResolvedStatements.All(ss => ss.IsGhost);
+
+ } else if (stmt is VarDeclStmt) {
+ var s = (VarDeclStmt)stmt;
+ if (mustBeErasable) {
+ foreach (var local in s.Locals) {
+ // a local variable in a specification-only context might as well be ghost
+ local.IsGhost = true;
+ }
+ }
+ s.IsGhost = (s.Update == null || s.Update.IsGhost) && s.Locals.All(v => v.IsGhost);
+ if (s.Update != null) {
+ Visit(s.Update, mustBeErasable);
+ }
+
+ } else if (stmt is LetStmt) {
+ var s = (LetStmt)stmt;
+ if (mustBeErasable) {
+ foreach (var bv in s.BoundVars) {
+ bv.IsGhost = true;
+ }
+ }
+ s.IsGhost = s.BoundVars.All(v => v.IsGhost);
+
+ } else if (stmt is AssignStmt) {
+ var s = (AssignStmt)stmt;
+ var lhs = s.Lhs.Resolved;
+ var gk = AssignStmt.LhsIsToGhost_Which(lhs);
+ if (gk == AssignStmt.NonGhostKind.IsGhost) {
+ s.IsGhost = true;
+ if (s.Rhs is TypeRhs) {
+ Error(s.Rhs.Tok, "'new' is not allowed in ghost contexts");
+ }
+ } else if (gk == AssignStmt.NonGhostKind.Variable && codeContext.IsGhost) {
+ // cool
+ } else if (mustBeErasable) {
+ Error(stmt, "Assignment to {0} is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)",
+ AssignStmt.NonGhostKind_To_String(gk));
+ } else if (s.Rhs is ExprRhs) {
+ var rhs = (ExprRhs)s.Rhs;
+ resolver.CheckIsCompilable(rhs.Expr);
+ } else if (s.Rhs is HavocRhs) {
+ // cool
+ } else {
+ var rhs = (TypeRhs)s.Rhs;
+ if (rhs.ArrayDimensions != null) {
+ foreach (var dim in rhs.ArrayDimensions) {
+ resolver.CheckIsCompilable(dim);
+ }
+ }
+ if (rhs.InitCall != null) {
+ foreach (var arg in rhs.InitCall.Args) {
+ resolver.CheckIsCompilable(arg);
+ }
+ }
+ }
+
+ } else if (stmt is CallStmt) {
+ var s = (CallStmt)stmt;
+ var callee = s.Method;
+ Contract.Assert(callee != null); // follows from the invariant of CallStmt
+ s.IsGhost = callee.IsGhost;
+ // check in-parameters
+ if (mustBeErasable) {
+ if (!s.IsGhost) {
+ Error(s, "only ghost methods can be called from this context");
+ }
+ } else {
+ resolver.CheckIsCompilable(s.Receiver);
+ int j;
+ if (!callee.IsGhost) {
+ j = 0;
+ foreach (var e in s.Args) {
+ Contract.Assume(j < callee.Ins.Count); // this should have already been checked by the resolver
+ if (!callee.Ins[j].IsGhost) {
+ resolver.CheckIsCompilable(e);
+ }
+ j++;
+ }
+ }
+ j = 0;
+ foreach (var e in s.Lhs) {
+ var resolvedLhs = e.Resolved;
+ if (callee.IsGhost || callee.Outs[j].IsGhost) {
+ // LHS must denote a ghost
+ if (resolvedLhs is IdentifierExpr) {
+ var ll = (IdentifierExpr)resolvedLhs;
+ if (!ll.Var.IsGhost) {
+ if (ll is AutoGhostIdentifierExpr && ll.Var is LocalVariable) {
+ // the variable was actually declared in this statement, so auto-declare it as ghost
+ ((LocalVariable)ll.Var).MakeGhost();
+ } else {
+ Error(s, "actual out-parameter {0} is required to be a ghost variable", j);
+ }
+ }
+ } else if (resolvedLhs is MemberSelectExpr) {
+ var ll = (MemberSelectExpr)resolvedLhs;
+ if (!ll.Member.IsGhost) {
+ Error(s, "actual out-parameter {0} is required to be a ghost field", j);
+ }
+ } else {
+ // this is an array update, and arrays are always non-ghost
+ Error(s, "actual out-parameter {0} is required to be a ghost variable", j);
+ }
+ }
+ j++;
+ }
+ }
+
+ } else if (stmt is BlockStmt) {
+ var s = (BlockStmt)stmt;
+ s.IsGhost = mustBeErasable; // set .IsGhost before descending into substatements (since substatements may do a 'break' out of this block)
+ s.Body.Iter(ss => Visit(ss, mustBeErasable));
+ s.IsGhost = s.IsGhost || s.Body.All(ss => ss.IsGhost); // mark the block statement as ghost if all its substatements are ghost
+
+ } else if (stmt is IfStmt) {
+ var s = (IfStmt)stmt;
+ s.IsGhost = mustBeErasable || (s.Guard != null && resolver.UsesSpecFeatures(s.Guard));
+ Visit(s.Thn, s.IsGhost);
+ if (s.Els != null) {
+ Visit(s.Els, s.IsGhost);
+ }
+ // if both branches were all ghost, then we can mark the enclosing statement as ghost as well
+ s.IsGhost = s.IsGhost || (s.Thn.IsGhost && (s.Els == null || s.Els.IsGhost));
+
+ } else if (stmt is AlternativeStmt) {
+ var s = (AlternativeStmt)stmt;
+ s.IsGhost = mustBeErasable || s.Alternatives.Exists(alt => resolver.UsesSpecFeatures(alt.Guard));
+ s.Alternatives.Iter(alt => alt.Body.Iter(ss => Visit(ss, s.IsGhost)));
+ s.IsGhost = s.IsGhost || s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost));
+
+ } else if (stmt is WhileStmt) {
+ var s = (WhileStmt)stmt;
+ s.IsGhost = mustBeErasable || (s.Guard != null && resolver.UsesSpecFeatures(s.Guard));
+ if (s.IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) {
+ Error(s, "'decreases *' is not allowed on ghost loops");
+ }
+ if (s.IsGhost && s.Mod.Expressions != null) {
+ s.Mod.Expressions.Iter(resolver.DisallowNonGhostFieldSpecifiers);
+ }
+ if (s.Body != null) {
+ Visit(s.Body, s.IsGhost);
+ }
+ s.IsGhost = s.IsGhost || s.Body == null || (!s.Decreases.Expressions.Exists(e => e is WildcardExpr) && s.Body.IsGhost);
+
+ } else if (stmt is AlternativeLoopStmt) {
+ var s = (AlternativeLoopStmt)stmt;
+ s.IsGhost = mustBeErasable || s.Alternatives.Exists(alt => resolver.UsesSpecFeatures(alt.Guard));
+ if (s.IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) {
+ Error(s, "'decreases *' is not allowed on ghost loops");
+ }
+ if (s.IsGhost && s.Mod.Expressions != null) {
+ s.Mod.Expressions.Iter(resolver.DisallowNonGhostFieldSpecifiers);
+ }
+ s.Alternatives.Iter(alt => alt.Body.Iter(ss => Visit(ss, s.IsGhost)));
+ s.IsGhost = s.IsGhost || (!s.Decreases.Expressions.Exists(e => e is WildcardExpr) && s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost)));
+
+ } else if (stmt is ForallStmt) {
+ var s = (ForallStmt)stmt;
+ s.IsGhost = mustBeErasable || s.Kind != ForallStmt.ParBodyKind.Assign || resolver.UsesSpecFeatures(s.Range);
+ if (s.Body != null) {
+ Visit(s.Body, s.IsGhost);
+ }
+ s.IsGhost = s.IsGhost || s.Body == null || s.Body.IsGhost;
+
+ } else if (stmt is ModifyStmt) {
+ var s = (ModifyStmt)stmt;
+ s.IsGhost = mustBeErasable;
+ if (s.IsGhost) {
+ s.Mod.Expressions.Iter(resolver.DisallowNonGhostFieldSpecifiers);
+ }
+ if (s.Body != null) {
+ Visit(s.Body, mustBeErasable);
+ }
+
+ } else if (stmt is CalcStmt) {
+ var s = (CalcStmt)stmt;
+ s.IsGhost = true;
+ foreach (var h in s.Hints) {
+ Visit(h, true);
+ }
+
+ } else if (stmt is MatchStmt) {
+ var s = (MatchStmt)stmt;
+ s.IsGhost = mustBeErasable || resolver.UsesSpecFeatures(s.Source);
+ s.Cases.Iter(kase => kase.Body.Iter(ss => Visit(ss, s.IsGhost)));
+ s.IsGhost = s.IsGhost || s.Cases.All(kase => kase.Body.All(ss => ss.IsGhost));
+
+ } else if (stmt is SkeletonStatement) {
+ var s = (SkeletonStatement)stmt;
+ s.IsGhost = mustBeErasable;
+ if (s.S != null) {
+ Visit(s.S, mustBeErasable);
+ s.IsGhost = s.IsGhost || s.S.IsGhost;
+ }
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException();
+ }
+ }
+ }
+ #endregion
+
+ // ------------------------------------------------------------------------------------------------------
// ----- FillInDefaultLoopDecreases ---------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------
#region FillInDefaultLoopDecreases
@@ -2845,10 +3762,10 @@ namespace Microsoft.Dafny
for (int i = 0; i < cs.Method.Ins.Count; i++) {
argsSubstMap.Add(cs.Method.Ins[i], cs.Args[i]);
}
- var substituter = new Translator.AlphaConverting_Substituter(cs.Receiver, argsSubstMap, new Dictionary<TypeParameter, Type>(), new Translator());
+ var substituter = new Translator.AlphaConverting_Substituter(cs.Receiver, argsSubstMap, new Dictionary<TypeParameter, Type>(), new Translator(resolver.reporter));
foreach (var ens in cs.Method.Ens) {
var p = substituter.Substitute(ens.E); // substitute the call's actuals for the method's formals
- resolver.ReportAdditionalInformation(s.Tok, "ensures " + Printer.ExprToString(p) + ";", s.Tok.val.Length);
+ resolver.reporter.Info(MessageSource.Resolver, s.Tok, "ensures " + Printer.ExprToString(p));
}
}
}
@@ -2912,7 +3829,6 @@ namespace Microsoft.Dafny
readonly Scope<IVariable>/*!*/ scope = new Scope<IVariable>();
Scope<Statement>/*!*/ labeledStatements = new Scope<Statement>();
List<Statement> loopStack = new List<Statement>(); // the enclosing loops (from which it is possible to break out)
- readonly Dictionary<Statement, bool> inSpecOnlyContext = new Dictionary<Statement, bool>(); // invariant: domain contain union of the domains of "labeledStatements" and "loopStack"
/// <summary>
/// This method resolves the types that have been given after the 'extends' keyword. Then, it populates
@@ -2926,26 +3842,26 @@ namespace Microsoft.Dafny
currentClass = cl;
if (cl.TraitsTyp.Count > 0 && cl.TypeArgs.Count > 0) {
- Error(cl.tok, "sorry, traits are currently supported only for classes that take no type arguments"); // TODO: do the work to remove this limitation
+ reporter.Error(MessageSource.Resolver, cl.tok, "sorry, traits are currently supported only for classes that take no type arguments"); // TODO: do the work to remove this limitation
}
// Resolve names of traits extended
foreach (var tt in cl.TraitsTyp) {
- var prevErrorCount = ErrorCount;
+ var prevErrorCount = reporter.Count(ErrorLevel.Error);
ResolveType(cl.tok, tt, new NoContext(cl.Module), ResolveTypeOptionEnum.DontInfer, null);
- if (ErrorCount == prevErrorCount) {
+ if (reporter.Count(ErrorLevel.Error) == prevErrorCount) {
var udt = tt as UserDefinedType;
if (udt != null && udt.ResolvedClass is TraitDecl) {
var trait = (TraitDecl)udt.ResolvedClass;
//disallowing inheritance in multi module case
if (cl.Module != trait.Module) {
- Error(udt.tok, "class '{0}' is in a different module than trait '{1}'. A class may only extend a trait in the same module.", cl.Name, trait.FullName);
+ reporter.Error(MessageSource.Resolver, udt.tok, "class '{0}' is in a different module than trait '{1}'. A class may only extend a trait in the same module.", cl.Name, trait.FullName);
} else {
// all is good
cl.TraitsObj.Add(trait);
}
} else {
- Error(udt != null ? udt.tok : cl.tok, "a class can only extend traits (found '{0}')", tt);
+ reporter.Error(MessageSource.Resolver, udt != null ? udt.tok : cl.tok, "a class can only extend traits (found '{0}')", tt);
}
}
}
@@ -2988,12 +3904,12 @@ namespace Microsoft.Dafny
} else if (member is Function) {
var f = (Function)member;
- var ec = ErrorCount;
+ var ec = reporter.Count(ErrorLevel.Error);
allTypeParameters.PushMarker();
ResolveTypeParameters(f.TypeArgs, true, f);
ResolveFunctionSignature(f);
allTypeParameters.PopMarker();
- if (f is FixpointPredicate && ec == ErrorCount) {
+ if (f is FixpointPredicate && ec == reporter.Count(ErrorLevel.Error)) {
var ff = ((FixpointPredicate)f).PrefixPredicate;
ff.EnclosingClass = cl;
allTypeParameters.PushMarker();
@@ -3004,13 +3920,13 @@ namespace Microsoft.Dafny
} else if (member is Method) {
var m = (Method)member;
- var ec = ErrorCount;
+ var ec = reporter.Count(ErrorLevel.Error);
allTypeParameters.PushMarker();
ResolveTypeParameters(m.TypeArgs, true, m);
ResolveMethodSignature(m);
allTypeParameters.PopMarker();
var com = m as FixpointLemma;
- if (com != null && com.PrefixLemma != null && ec == ErrorCount) {
+ if (com != null && com.PrefixLemma != null && ec == reporter.Count(ErrorLevel.Error)) {
var mm = com.PrefixLemma;
// resolve signature of the prefix lemma
mm.EnclosingClass = cl;
@@ -3031,7 +3947,7 @@ namespace Microsoft.Dafny
void InheritTraitMembers(ClassDecl cl) {
Contract.Requires(cl != null);
- var refinementTransformer = new RefinementTransformer(this, AdditionalInformationReporter, null);
+ var refinementTransformer = new RefinementTransformer(reporter);
//merging class members with parent members if any
var clMembers = classMembers[cl];
foreach (TraitDecl trait in cl.TraitsObj) {
@@ -3051,37 +3967,37 @@ namespace Microsoft.Dafny
} else if (traitMember is Function) {
var func = (Function)traitMember;
if (func.Body == null) {
- Error(cl.tok, "class '{0}' does not implement trait function '{1}.{2}'", cl.Name, trait.Name, traitMember.Name);
+ reporter.Error(MessageSource.Resolver, cl.tok, "class '{0}' does not implement trait function '{1}.{2}'", cl.Name, trait.Name, traitMember.Name);
} else if (!func.IsGhost && !func.IsStatic) {
cl.InheritedMembers.Add(func);
}
} else if (traitMember is Method) {
var method = (Method)traitMember;
if (method.Body == null) {
- Error(cl.tok, "class '{0}' does not implement trait method '{1}.{2}'", cl.Name, trait.Name, traitMember.Name);
+ reporter.Error(MessageSource.Resolver, cl.tok, "class '{0}' does not implement trait method '{1}.{2}'", cl.Name, trait.Name, traitMember.Name);
} else if (!method.IsGhost && !method.IsStatic) {
cl.InheritedMembers.Add(method);
}
}
} else if (clMember.EnclosingClass != cl) {
// The class inherits the member from two places
- Error(clMember.tok, "member name '{0}' in class '{1}' inherited from both traits '{2}' and '{3}'", traitMember.Name, cl.Name, clMember.EnclosingClass.Name, trait.Name);
+ reporter.Error(MessageSource.Resolver, clMember.tok, "member name '{0}' in class '{1}' inherited from both traits '{2}' and '{3}'", traitMember.Name, cl.Name, clMember.EnclosingClass.Name, trait.Name);
} else if (traitMember is Field) {
// The class is not allowed to do anything with the field other than silently inherit it.
if (clMember is Field) {
- Error(clMember.tok, "field '{0}' is inherited from trait '{1}' and is not allowed to be re-declared", traitMember.Name, trait.Name);
+ reporter.Error(MessageSource.Resolver, clMember.tok, "field '{0}' is inherited from trait '{1}' and is not allowed to be re-declared", traitMember.Name, trait.Name);
} else {
- Error(clMember.tok, "member name '{0}' in class '{1}' clashes with inherited field from trait '{2}'", traitMember.Name, cl.Name, trait.Name);
+ reporter.Error(MessageSource.Resolver, clMember.tok, "member name '{0}' in class '{1}' clashes with inherited field from trait '{2}'", traitMember.Name, cl.Name, trait.Name);
}
} else if (traitMember is Method) {
var traitMethod = (Method)traitMember;
if (traitMethod.Body != null) {
// The method was defined in the trait, so the class is not allowed to do anything with the method other than silently inherit it.
- Error(clMember.tok, "member '{0}' in class '{1}' overrides fully defined method inherited from trait '{2}'", clMember.Name, cl.Name, trait.Name);
+ reporter.Error(MessageSource.Resolver, clMember.tok, "member '{0}' in class '{1}' overrides fully defined method inherited from trait '{2}'", clMember.Name, cl.Name, trait.Name);
} else if (!(clMember is Method)) {
- Error(clMember.tok, "non-method member '{0}' overrides method '{1}' inherited from trait '{2}'", clMember.Name, traitMethod.Name, trait.Name);
+ reporter.Error(MessageSource.Resolver, clMember.tok, "non-method member '{0}' overrides method '{1}' inherited from trait '{2}'", clMember.Name, traitMethod.Name, trait.Name);
} else {
var classMethod = (Method)clMember;
classMethod.OverriddenMethod = traitMethod;
@@ -3093,7 +4009,7 @@ namespace Microsoft.Dafny
var traitMethodAllowsNonTermination = Contract.Exists(traitMethod.Decreases.Expressions, e => e is WildcardExpr);
var classMethodAllowsNonTermination = Contract.Exists(classMethod.Decreases.Expressions, e => e is WildcardExpr);
if (classMethodAllowsNonTermination && !traitMethodAllowsNonTermination) {
- Error(classMethod.tok, "not allowed to override a terminating method with a possibly non-terminating method ('{0}')", classMethod.Name);
+ reporter.Error(MessageSource.Resolver, classMethod.tok, "not allowed to override a terminating method with a possibly non-terminating method ('{0}')", classMethod.Name);
}
}
@@ -3101,9 +4017,9 @@ namespace Microsoft.Dafny
var traitFunction = (Function)traitMember;
if (traitFunction.Body != null) {
// The function was defined in the trait, so the class is not allowed to do anything with the function other than silently inherit it.
- Error(clMember.tok, "member '{0}' in class '{1}' overrides fully defined function inherited from trait '{2}'", clMember.Name, cl.Name, trait.Name);
+ reporter.Error(MessageSource.Resolver, clMember.tok, "member '{0}' in class '{1}' overrides fully defined function inherited from trait '{2}'", clMember.Name, cl.Name, trait.Name);
} else if (!(clMember is Function)) {
- Error(clMember.tok, "non-function member '{0}' overrides function '{1}' inherited from trait '{2}'", clMember.Name, traitFunction.Name, trait.Name);
+ reporter.Error(MessageSource.Resolver, clMember.tok, "non-function member '{0}' overrides function '{1}' inherited from trait '{2}'", clMember.Name, traitFunction.Name, trait.Name);
} else {
var classFunction = (Function)clMember;
classFunction.OverriddenFunction = traitFunction;
@@ -3136,12 +4052,12 @@ namespace Microsoft.Dafny
} else if (member is Function) {
var f = (Function)member;
- var ec = ErrorCount;
+ var ec = reporter.Count(ErrorLevel.Error);
allTypeParameters.PushMarker();
ResolveTypeParameters(f.TypeArgs, false, f);
ResolveFunction(f);
allTypeParameters.PopMarker();
- if (f is FixpointPredicate && ec == ErrorCount) {
+ if (f is FixpointPredicate && ec == reporter.Count(ErrorLevel.Error)) {
var ff = ((FixpointPredicate)f).PrefixPredicate;
allTypeParameters.PushMarker();
ResolveTypeParameters(ff.TypeArgs, false, ff);
@@ -3151,7 +4067,7 @@ namespace Microsoft.Dafny
} else if (member is Method) {
var m = (Method)member;
- var ec = ErrorCount;
+ var ec = reporter.Count(ErrorLevel.Error);
allTypeParameters.PushMarker();
ResolveTypeParameters(m.TypeArgs, false, m);
ResolveMethod(m);
@@ -3258,7 +4174,7 @@ namespace Microsoft.Dafny
// whatever is in scc-cleared now failed to pass the test
foreach (var dt in scc) {
if (dt.DefaultCtor == null) {
- Error(dt, "because of cyclic dependencies among constructor argument types, no instances of datatype '{0}' can be constructed", dt.Name);
+ reporter.Error(MessageSource.Resolver, dt, "because of cyclic dependencies among constructor argument types, no instances of datatype '{0}' can be constructed", dt.Name);
}
}
return;
@@ -3276,23 +4192,36 @@ namespace Microsoft.Dafny
Contract.Ensures(!Contract.Result<bool>() || dt.DefaultCtor != null);
// Stated differently, check that there is some constuctor where no argument type goes to the same stratum.
+ DatatypeCtor defaultCtor = null;
+ List<TypeParameter> lastTypeParametersUsed = null;
foreach (DatatypeCtor ctor in dt.Ctors) {
- var typeParametersUsed = new List<TypeParameter>();
+ List<TypeParameter> typeParametersUsed = new List<TypeParameter>();
foreach (Formal p in ctor.Formals) {
if (!CheckCanBeConstructed(p.Type, typeParametersUsed)) {
// the argument type (has a component which) is not yet known to be constructable
goto NEXT_OUTER_ITERATION;
}
}
- // this constructor satisfies the requirements, so the datatype is allowed
- dt.DefaultCtor = ctor;
+ // this constructor satisfies the requirements, check to see if it is a better fit than the
+ // one found so far. By "better" it means fewer type arguments. Between the ones with
+ // the same number of the type arguments, pick the one shows first.
+ if (defaultCtor == null || typeParametersUsed.Count < lastTypeParametersUsed.Count) {
+ defaultCtor = ctor;
+ lastTypeParametersUsed = typeParametersUsed;
+ }
+
+ NEXT_OUTER_ITERATION: { }
+ }
+
+ if (defaultCtor != null) {
+ dt.DefaultCtor = defaultCtor;
dt.TypeParametersUsedInConstructionByDefaultCtor = new bool[dt.TypeArgs.Count];
for (int i = 0; i < dt.TypeArgs.Count; i++) {
- dt.TypeParametersUsedInConstructionByDefaultCtor[i] = typeParametersUsed.Contains(dt.TypeArgs[i]);
+ dt.TypeParametersUsedInConstructionByDefaultCtor[i] = lastTypeParametersUsed.Contains(dt.TypeArgs[i]);
}
return true;
- NEXT_OUTER_ITERATION: { }
}
+
// no constructor satisfied the requirements, so this is an illegal datatype declaration
return false;
}
@@ -3339,7 +4268,8 @@ namespace Microsoft.Dafny
(anotherIndDt != null && anotherIndDt.EqualitySupport == IndDatatypeDecl.ES.Never) ||
arg.Type.IsCoDatatype ||
arg.Type.IsArrowType ||
- arg.Type.IsIMapType) {
+ arg.Type.IsIMapType ||
+ arg.Type.IsISetType) {
// arg.Type is known never to support equality
// So, go around the entire SCC and record what we learnt
foreach (var ddtt in scc) {
@@ -3422,26 +4352,17 @@ namespace Microsoft.Dafny
void ResolveAttributes(Attributes attrs, ResolveOpts opts) {
Contract.Requires(opts != null);
- Contract.Requires(opts.DontCareAboutCompilation); // attributes are never compiled
// order does not matter much for resolution, so resolve them in reverse order
- for (; attrs != null; attrs = attrs.Prev) {
- if (attrs.Args != null) {
- ResolveAttributeArgs(attrs.Args, opts, true);
- }
- }
- }
-
- void ResolveAttributeArgs(List<Expression> args, ResolveOpts opts, bool allowGhosts) {
- Contract.Requires(args != null);
- foreach (var arg in args) {
- Contract.Assert(arg != null);
- int prevErrors = ErrorCount;
- ResolveExpression(arg, opts);
- if (!allowGhosts) {
- CheckIsNonGhost(arg);
- }
- if (prevErrors == ErrorCount) {
- CheckTypeInference(arg);
+ foreach (var attr in attrs.AsEnumerable()) {
+ if (attr.Args != null) {
+ foreach (var arg in attr.Args) {
+ Contract.Assert(arg != null);
+ int prevErrors = reporter.Count(ErrorLevel.Error);
+ ResolveExpression(arg, opts);
+ if (prevErrors == reporter.Count(ErrorLevel.Error)) {
+ CheckTypeInference(arg, opts.codeContext);
+ }
+ }
}
}
}
@@ -3457,12 +4378,43 @@ namespace Microsoft.Dafny
tp.Parent = parent;
tp.PositionalIndex = index;
}
- if (!allTypeParameters.Push(tp.Name, tp) && emitErrors) {
- Error(tp, "Duplicate type-parameter name: {0}", tp.Name);
+ var r = allTypeParameters.Push(tp.Name, tp);
+ if (emitErrors) {
+ if (r == Scope<TypeParameter>.PushResult.Duplicate) {
+ reporter.Error(MessageSource.Resolver, tp, "Duplicate type-parameter name: {0}", tp.Name);
+ } else if (r == Scope<TypeParameter>.PushResult.Shadow) {
+ reporter.Warning(MessageSource.Resolver, tp.tok, "Shadowed type-parameter name: {0}", tp.Name);
+ }
}
}
}
+ void ScopePushAndReport(Scope<IVariable> scope, IVariable v, string kind) {
+ Contract.Requires(scope != null);
+ Contract.Requires(v != null);
+ Contract.Requires(kind != null);
+ ScopePushAndReport(scope, v.Name, v, v.Tok, kind);
+ }
+
+ void ScopePushAndReport<Thing>(Scope<Thing> scope, string name, Thing thing, IToken tok, string kind) where Thing : class {
+ Contract.Requires(scope != null);
+ Contract.Requires(name != null);
+ Contract.Requires(thing != null);
+ Contract.Requires(tok != null);
+ Contract.Requires(kind != null);
+ var r = scope.Push(name, thing);
+ switch (r) {
+ case Scope<Thing>.PushResult.Success:
+ break;
+ case Scope<Thing>.PushResult.Duplicate:
+ reporter.Error(MessageSource.Resolver, tok, "Duplicate {0} name: {1}", kind, name);
+ break;
+ case Scope<Thing>.PushResult.Shadow:
+ reporter.Warning(MessageSource.Resolver, tok, "Shadowed {0} name: {1}", kind, name);
+ break;
+ }
+ }
+
/// <summary>
/// Assumes type parameters have already been pushed
/// </summary>
@@ -3470,13 +4422,11 @@ namespace Microsoft.Dafny
Contract.Requires(f != null);
scope.PushMarker();
if (f.SignatureIsOmitted) {
- Error(f, "function signature can be omitted only in refining functions");
+ reporter.Error(MessageSource.Resolver, f, "function signature can be omitted only in refining functions");
}
var option = f.TypeArgs.Count == 0 ? new ResolveTypeOption(f) : new ResolveTypeOption(ResolveTypeOptionEnum.AllowPrefix);
foreach (Formal p in f.Formals) {
- if (!scope.Push(p.Name, p)) {
- Error(p, "Duplicate parameter name: {0}", p.Name);
- }
+ ScopePushAndReport(scope, p, "parameter");
ResolveType(p.tok, p.Type, f, option, f.TypeArgs);
}
ResolveType(f.tok, f.ResultType, f, option, f.TypeArgs);
@@ -3495,39 +4445,32 @@ namespace Microsoft.Dafny
foreach (Formal p in f.Formals) {
scope.Push(p.Name, p);
}
- ResolveAttributes(f.Attributes, new ResolveOpts(f, false, true));
+ ResolveAttributes(f.Attributes, new ResolveOpts(f, false));
foreach (Expression r in f.Req) {
- ResolveExpression(r, new ResolveOpts(f, false, true));
+ ResolveExpression(r, new ResolveOpts(f, false));
Contract.Assert(r.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(r.Type, Type.Bool)) {
- Error(r, "Precondition must be a boolean (got {0})", r.Type);
- }
+ ConstrainTypes(r.Type, Type.Bool, r, "Precondition must be a boolean (got {0})", r.Type);
}
foreach (FrameExpression fr in f.Reads) {
- ResolveFrameExpression(fr, true, f.IsGhost, f);
+ ResolveFrameExpression(fr, true, f);
}
foreach (Expression r in f.Ens) {
- ResolveExpression(r, new ResolveOpts(f, false, true)); // since this is a function, the postcondition is still a one-state predicate
+ ResolveExpression(r, new ResolveOpts(f, false)); // since this is a function, the postcondition is still a one-state predicate
Contract.Assert(r.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(r.Type, Type.Bool)) {
- Error(r, "Postcondition must be a boolean (got {0})", r.Type);
- }
+ ConstrainTypes(r.Type, Type.Bool, r, "Postcondition must be a boolean (got {0})", r.Type);
}
- ResolveAttributes(f.Decreases.Attributes, new ResolveOpts(f, false, true));
+ ResolveAttributes(f.Decreases.Attributes, new ResolveOpts(f, false));
foreach (Expression r in f.Decreases.Expressions) {
- ResolveExpression(r, new ResolveOpts(f, false, true));
+ ResolveExpression(r, new ResolveOpts(f, false));
// any type is fine
}
+ SolveAllTypeConstraints();
if (f.Body != null) {
- var prevErrorCount = ErrorCount;
+ var prevErrorCount = reporter.Count(ErrorLevel.Error);
ResolveExpression(f.Body, new ResolveOpts(f, false));
- if (!f.IsGhost && prevErrorCount == ErrorCount) {
- CheckIsNonGhost(f.Body);
- }
+ SolveAllTypeConstraints();
Contract.Assert(f.Body.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(f.Body.Type, f.ResultType)) {
- Error(f, "Function body type mismatch (expected {0}, got {1})", f.ResultType, f.Body.Type);
- }
+ ConstrainTypes(f.Body.Type, f.ResultType, f, "Function body type mismatch (expected {0}, got {1})", f.ResultType, f.Body.Type);
}
scope.PopMarker();
}
@@ -3535,14 +4478,11 @@ namespace Microsoft.Dafny
/// <summary>
///
/// </summary>
- /// <param name="fe"></param>
/// <param name="readsFrame">True indicates "reads", false indicates "modifies".</param>
- /// <param name="isGhostContext"></param>
- /// <param name="codeContext"></param>
- void ResolveFrameExpression(FrameExpression fe, bool readsFrame, bool isGhostContext, ICodeContext codeContext) {
+ void ResolveFrameExpression(FrameExpression fe, bool readsFrame, ICodeContext codeContext) {
Contract.Requires(fe != null);
Contract.Requires(codeContext != null);
- ResolveExpression(fe.E, new ResolveOpts(codeContext, false, true /* yes, this is ghost */));
+ ResolveExpression(fe.E, new ResolveOpts(codeContext, false));
Type t = fe.E.Type;
Contract.Assert(t != null); // follows from postcondition of ResolveExpression
var arrTy = t.AsArrowType;
@@ -3553,8 +4493,7 @@ namespace Microsoft.Dafny
if (collType != null) {
t = collType.Arg;
}
- if (!UnifyTypes(t, new ObjectType())) {
- Error(fe.E, "a {0}-clause expression must denote an object or a collection of objects (instead got {1})", readsFrame ? "reads" : "modifies", fe.E.Type);
+ if (!ConstrainTypes(t, new ObjectType(), fe.E, "a {0}-clause expression must denote an object or a collection of objects (instead got {1})", readsFrame ? "reads" : "modifies", fe.E.Type)) {
} else if (fe.FieldName != null) {
NonProxyType nptype;
MemberDecl member = ResolveMember(fe.E.tok, t, fe.FieldName, out nptype);
@@ -3562,9 +4501,7 @@ namespace Microsoft.Dafny
if (member == null) {
// error has already been reported by ResolveMember
} else if (!(member is Field)) {
- Error(fe.E, "member {0} in type {1} does not refer to a field", fe.FieldName, ctype.Name);
- } else if (!readsFrame && isGhostContext && !member.IsGhost) {
- Error(fe.E, "in a ghost context, only ghost fields can be mentioned as modifies frame targets ({0})", fe.FieldName);
+ reporter.Error(MessageSource.Resolver, fe.E, "member {0} in type {1} does not refer to a field", fe.FieldName, ctype.Name);
} else {
Contract.Assert(ctype != null && ctype.ResolvedClass != null); // follows from postcondition of ResolveMember
fe.Field = (Field)member;
@@ -3573,27 +4510,34 @@ namespace Microsoft.Dafny
}
/// <summary>
+ /// This method can be called even if the resolution of "fe" failed; in that case, this method will
+ /// not issue any error message.
+ /// </summary>
+ void DisallowNonGhostFieldSpecifiers(FrameExpression fe) {
+ Contract.Requires(fe != null);
+ if (fe.Field != null && !fe.Field.IsGhost) {
+ reporter.Error(MessageSource.Resolver, fe.E, "in a ghost context, only ghost fields can be mentioned as modifies frame targets ({0})", fe.FieldName);
+ }
+ }
+
+ /// <summary>
/// Assumes type parameters have already been pushed
/// </summary>
void ResolveMethodSignature(Method m) {
Contract.Requires(m != null);
scope.PushMarker();
if (m.SignatureIsOmitted) {
- Error(m, "method signature can be omitted only in refining methods");
+ reporter.Error(MessageSource.Resolver, m, "method signature can be omitted only in refining methods");
}
var option = m.TypeArgs.Count == 0 ? new ResolveTypeOption(m) : new ResolveTypeOption(ResolveTypeOptionEnum.AllowPrefix);
// resolve in-parameters
foreach (Formal p in m.Ins) {
- if (!scope.Push(p.Name, p)) {
- Error(p, "Duplicate parameter name: {0}", p.Name);
- }
+ ScopePushAndReport(scope, p, "parameter");
ResolveType(p.tok, p.Type, m, option, m.TypeArgs);
}
// resolve out-parameters
foreach (Formal p in m.Outs) {
- if (!scope.Push(p.Name, p)) {
- Error(p, "Duplicate parameter name: {0}", p.Name);
- }
+ ScopePushAndReport(scope, p, "parameter");
ResolveType(p.tok, p.Type, m, option, m.TypeArgs);
}
scope.PopMarker();
@@ -3620,34 +4564,31 @@ namespace Microsoft.Dafny
// Start resolving specification...
foreach (MaybeFreeExpression e in m.Req) {
- ResolveAttributes(e.Attributes, new ResolveOpts(m, false, true));
- ResolveExpression(e.E, new ResolveOpts(m, false, true));
+ ResolveAttributes(e.Attributes, new ResolveOpts(m, false));
+ ResolveExpression(e.E, new ResolveOpts(m, false));
Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.E.Type, Type.Bool)) {
- Error(e.E, "Precondition must be a boolean (got {0})", e.E.Type);
- }
+ ConstrainTypes(e.E.Type, Type.Bool, e.E, "Precondition must be a boolean (got {0})", e.E.Type);
}
- ResolveAttributes(m.Mod.Attributes, new ResolveOpts(m, false, true));
+ ResolveAttributes(m.Mod.Attributes, new ResolveOpts(m, false));
foreach (FrameExpression fe in m.Mod.Expressions) {
- ResolveFrameExpression(fe, false, m.IsGhost, m);
+ ResolveFrameExpression(fe, false, m);
if (m is Lemma || m is FixpointLemma) {
- Error(fe.tok, "{0}s are not allowed to have modifies clauses", m.WhatKind);
+ reporter.Error(MessageSource.Resolver, fe.tok, "{0}s are not allowed to have modifies clauses", m.WhatKind);
+ } else if (m.IsGhost) {
+ DisallowNonGhostFieldSpecifiers(fe);
}
}
- ResolveAttributes(m.Decreases.Attributes, new ResolveOpts(m, false, true));
+ ResolveAttributes(m.Decreases.Attributes, new ResolveOpts(m, false));
foreach (Expression e in m.Decreases.Expressions) {
- ResolveExpression(e, new ResolveOpts(m, false, true));
+ ResolveExpression(e, new ResolveOpts(m, false));
// any type is fine
- if (m.IsGhost && e is WildcardExpr) {
- Error(e, "'decreases *' is not allowed on ghost methods");
- }
}
// Add out-parameters to a new scope that will also include the outermost-level locals of the body
// Don't care about any duplication errors among the out-parameters, since they have already been reported
scope.PushMarker();
if (m is FixpointLemma && m.Outs.Count != 0) {
- Error(m.Outs[0].tok, "{0}s are not allowed to have out-parameters", m.WhatKind);
+ reporter.Error(MessageSource.Resolver, m.Outs[0].tok, "{0}s are not allowed to have out-parameters", m.WhatKind);
} else {
foreach (Formal p in m.Outs) {
scope.Push(p.Name, p);
@@ -3656,13 +4597,12 @@ namespace Microsoft.Dafny
// ... continue resolving specification
foreach (MaybeFreeExpression e in m.Ens) {
- ResolveAttributes(e.Attributes, new ResolveOpts(m, true, true));
- ResolveExpression(e.E, new ResolveOpts(m, true, true));
+ ResolveAttributes(e.Attributes, new ResolveOpts(m, true));
+ ResolveExpression(e.E, new ResolveOpts(m, true));
Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.E.Type, Type.Bool)) {
- Error(e.E, "Postcondition must be a boolean (got {0})", e.E.Type);
- }
+ ConstrainTypes(e.E.Type, Type.Bool, e.E, "Postcondition must be a boolean (got {0})", e.E.Type);
}
+ SolveAllTypeConstraints();
// Resolve body
if (m.Body != null) {
@@ -3673,12 +4613,12 @@ namespace Microsoft.Dafny
var k = com.PrefixLemma.Ins[0];
scope.Push(k.Name, k); // we expect no name conflict for _k
}
- var codeContext = m;
- ResolveBlockStatement(m.Body, m.IsGhost, codeContext);
+ ResolveBlockStatement(m.Body, m);
+ SolveAllTypeConstraints();
}
// attributes are allowed to mention both in- and out-parameters (including the implicit _k, for colemmas)
- ResolveAttributes(m.Attributes, new ResolveOpts(m, false, true));
+ ResolveAttributes(m.Attributes, new ResolveOpts(m, false));
scope.PopMarker(); // for the out-parameters and outermost-level locals
scope.PopMarker(); // for the in-parameters
@@ -3708,7 +4648,7 @@ namespace Microsoft.Dafny
Contract.Requires(iter != null);
scope.PushMarker();
if (iter.SignatureIsOmitted) {
- Error(iter, "iterator signature can be omitted only in refining methods");
+ reporter.Error(MessageSource.Resolver, iter, "iterator signature can be omitted only in refining methods");
}
var option = iter.TypeArgs.Count == 0 ? new ResolveTypeOption(iter) : new ResolveTypeOption(ResolveTypeOptionEnum.AllowPrefix);
// resolve the types of the parameters
@@ -3730,7 +4670,7 @@ namespace Microsoft.Dafny
Contract.Requires(currentClass == null);
Contract.Ensures(currentClass == null);
- var initialErrorCount = ErrorCount;
+ var initialErrorCount = reporter.Count(ErrorLevel.Error);
// Add in-parameters to the scope, but don't care about any duplication errors, since they have already been reported
scope.PushMarker();
@@ -3744,26 +4684,22 @@ namespace Microsoft.Dafny
Contract.Assert(iter.Decreases.Expressions.Count == iter.DecreasesFields.Count);
for (int i = 0; i < iter.Decreases.Expressions.Count; i++) {
var e = iter.Decreases.Expressions[i];
- ResolveExpression(e, new ResolveOpts(iter, false, true));
+ ResolveExpression(e, new ResolveOpts(iter, false));
// any type is fine, but associate this type with the corresponding _decreases<n> field
var d = iter.DecreasesFields[i];
- if (!UnifyTypes(d.Type, e.Type)) {
- // bummer, there was a use--and a bad use--of the field before, so this won't be the best of error messages
- Error(e, "type of field {0} is {1}, but has been constrained elsewhere to be of type {2}", d.Name, e.Type, d.Type);
- }
+ // If the following type constraint does not hold, then: Bummer, there was a use--and a bad use--of the field before, so this won't be the best of error messages
+ ConstrainTypes(d.Type, e.Type, e, "type of field {0} is {1}, but has been constrained elsewhere to be of type {2}", d.Name, e.Type, d.Type);
}
foreach (FrameExpression fe in iter.Reads.Expressions) {
- ResolveFrameExpression(fe, true, false, iter);
+ ResolveFrameExpression(fe, true, iter);
}
foreach (FrameExpression fe in iter.Modifies.Expressions) {
- ResolveFrameExpression(fe, false, false, iter);
+ ResolveFrameExpression(fe, false, iter);
}
foreach (MaybeFreeExpression e in iter.Requires) {
- ResolveExpression(e.E, new ResolveOpts(iter, false, true));
+ ResolveExpression(e.E, new ResolveOpts(iter, false));
Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.E.Type, Type.Bool)) {
- Error(e.E, "Precondition must be a boolean (got {0})", e.E.Type);
- }
+ ConstrainTypes(e.E.Type, Type.Bool, e.E, "Precondition must be a boolean (got {0})", e.E.Type);
}
scope.PopMarker(); // for the in-parameters
@@ -3775,34 +4711,28 @@ namespace Microsoft.Dafny
Contract.Assert(scope.AllowInstance);
foreach (MaybeFreeExpression e in iter.YieldRequires) {
- ResolveExpression(e.E, new ResolveOpts(iter, false, true));
+ ResolveExpression(e.E, new ResolveOpts(iter, false));
Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.E.Type, Type.Bool)) {
- Error(e.E, "Yield precondition must be a boolean (got {0})", e.E.Type);
- }
+ ConstrainTypes(e.E.Type, Type.Bool, e.E, "Yield precondition must be a boolean (got {0})", e.E.Type);
}
foreach (MaybeFreeExpression e in iter.YieldEnsures) {
- ResolveExpression(e.E, new ResolveOpts(iter, true, true));
+ ResolveExpression(e.E, new ResolveOpts(iter, true));
Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.E.Type, Type.Bool)) {
- Error(e.E, "Yield postcondition must be a boolean (got {0})", e.E.Type);
- }
+ ConstrainTypes(e.E.Type, Type.Bool, e.E, "Yield postcondition must be a boolean (got {0})", e.E.Type);
}
foreach (MaybeFreeExpression e in iter.Ensures) {
- ResolveExpression(e.E, new ResolveOpts(iter, true, true));
+ ResolveExpression(e.E, new ResolveOpts(iter, true));
Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.E.Type, Type.Bool)) {
- Error(e.E, "Postcondition must be a boolean (got {0})", e.E.Type);
- }
+ ConstrainTypes(e.E.Type, Type.Bool, e.E, "Postcondition must be a boolean (got {0})", e.E.Type);
}
- ResolveAttributes(iter.Attributes, new ResolveOpts(iter, false, true));
+ ResolveAttributes(iter.Attributes, new ResolveOpts(iter, false));
- var postSpecErrorCount = ErrorCount;
+ var postSpecErrorCount = reporter.Count(ErrorLevel.Error);
// Resolve body
if (iter.Body != null) {
- ResolveBlockStatement(iter.Body, false, iter);
+ ResolveBlockStatement(iter.Body, iter);
}
currentClass = null;
@@ -3840,10 +4770,10 @@ namespace Microsoft.Dafny
ens.Add(new MaybeFreeExpression(valid_call));
// ensures this._reads == old(ReadsClause);
var modSetSingletons = new List<Expression>();
- Expression frameSet = new SetDisplayExpr(iter.tok, modSetSingletons);
+ Expression frameSet = new SetDisplayExpr(iter.tok, true, modSetSingletons);
foreach (var fr in iter.Reads.Expressions) {
if (fr.FieldName != null) {
- Error(fr.tok, "sorry, a reads clause for an iterator is not allowed to designate specific fields");
+ reporter.Error(MessageSource.Resolver, fr.tok, "sorry, a reads clause for an iterator is not allowed to designate specific fields");
} else if (fr.E.Type.IsRefType) {
modSetSingletons.Add(fr.E);
} else {
@@ -3855,10 +4785,10 @@ namespace Microsoft.Dafny
new OldExpr(iter.tok, frameSet))));
// ensures this._modifies == old(ModifiesClause);
modSetSingletons = new List<Expression>();
- frameSet = new SetDisplayExpr(iter.tok, modSetSingletons);
+ frameSet = new SetDisplayExpr(iter.tok, true, modSetSingletons);
foreach (var fr in iter.Modifies.Expressions) {
if (fr.FieldName != null) {
- Error(fr.tok, "sorry, a modifies clause for an iterator is not allowed to designate specific fields");
+ reporter.Error(MessageSource.Resolver, fr.tok, "sorry, a modifies clause for an iterator is not allowed to designate specific fields");
} else if (fr.E.Type.IsRefType) {
modSetSingletons.Add(fr.E);
} else {
@@ -3871,7 +4801,7 @@ namespace Microsoft.Dafny
// ensures this._new == {};
ens.Add(new MaybeFreeExpression(new BinaryExpr(iter.tok, BinaryExpr.Opcode.Eq,
new MemberSelectExpr(iter.tok, new ThisExpr(iter.tok), "_new"),
- new SetDisplayExpr(iter.tok, new List<Expression>()))));
+ new SetDisplayExpr(iter.tok, true, new List<Expression>()))));
// ensures this._decreases0 == old(DecreasesClause[0]) && ...;
Contract.Assert(iter.Decreases.Expressions.Count == iter.DecreasesFields.Count);
for (int i = 0; i < iter.Decreases.Expressions.Count; i++) {
@@ -4044,7 +4974,7 @@ namespace Microsoft.Dafny
// nothing to resolve
} else if (type is MapType) {
var mt = (MapType)type;
- var errorCount = ErrorCount;
+ var errorCount = reporter.Count(ErrorLevel.Error);
int typeArgumentCount = 0;
if (mt.HasTypeArg()) {
ResolveType(tok, mt.Domain, context, option, defaultTypeArguments);
@@ -4065,19 +4995,19 @@ namespace Microsoft.Dafny
}
// defaults and auto have been applied; check if we now have the right number of arguments
if (2 != typeArgumentCount) {
- Error(tok, "Wrong number of type arguments ({0} instead of 2) passed to type: {1}", typeArgumentCount, mt.CollectionTypeName);
+ reporter.Error(MessageSource.Resolver, tok, "Wrong number of type arguments ({0} instead of 2) passed to type: {1}", typeArgumentCount, mt.CollectionTypeName);
// add proxy types, to make sure that MapType will have have a non-null Arg/Domain and Range
if (typeArgumentCount == 0) {
mt.SetTypeArg(new InferredTypeProxy());
}
mt.SetRangetype(new InferredTypeProxy());
}
- if (errorCount == ErrorCount && (mt.Domain.IsSubrangeType || mt.Range.IsSubrangeType)) {
- Error(tok, "sorry, cannot instantiate collection type with a subrange type");
+ if (errorCount == reporter.Count(ErrorLevel.Error) && (mt.Domain.IsSubrangeType || mt.Range.IsSubrangeType)) {
+ reporter.Error(MessageSource.Resolver, tok, "sorry, cannot instantiate collection type with a subrange type");
}
} else if (type is CollectionType) {
var t = (CollectionType)type;
- var errorCount = ErrorCount;
+ var errorCount = reporter.Count(ErrorLevel.Error);
if (t.HasTypeArg()) {
ResolveType(tok, t.Arg, context, option, defaultTypeArguments);
} else if (option.Opt != ResolveTypeOptionEnum.DontInfer) {
@@ -4090,13 +5020,13 @@ namespace Microsoft.Dafny
}
if (!t.HasTypeArg()) {
// defaults and auto have been applied; check if we now have the right number of arguments
- Error(tok, "Wrong number of type arguments (0 instead of 1) passed to type: {0}", t.CollectionTypeName);
+ reporter.Error(MessageSource.Resolver, tok, "Wrong number of type arguments (0 instead of 1) passed to type: {0}", t.CollectionTypeName);
// add a proxy type, to make sure that CollectionType will have have a non-null Arg
t.SetTypeArg(new InferredTypeProxy());
}
- if (errorCount == ErrorCount && t.Arg.IsSubrangeType) {
- Error(tok, "sorry, cannot instantiate collection type with a subrange type");
+ if (errorCount == reporter.Count(ErrorLevel.Error) && t.Arg.IsSubrangeType) {
+ reporter.Error(MessageSource.Resolver, tok, "sorry, cannot instantiate collection type with a subrange type");
}
} else if (type is UserDefinedType) {
@@ -4105,34 +5035,47 @@ namespace Microsoft.Dafny
// Apparently, this type has already been resolved
return null;
}
- var prevErrorCount = ErrorCount;
+ var prevErrorCount = reporter.Count(ErrorLevel.Error);
if (t.NamePath is ExprDotName) {
- var ret = ResolveDotSuffix_Type((ExprDotName)t.NamePath, new ResolveOpts(context, true, true), allowDanglingDotName, option, defaultTypeArguments);
+ var ret = ResolveDotSuffix_Type((ExprDotName)t.NamePath, new ResolveOpts(context, true), allowDanglingDotName, option, defaultTypeArguments);
if (ret != null) {
return ret;
}
} else {
var s = (NameSegment)t.NamePath;
- ResolveNameSegment_Type(s, new ResolveOpts(context, true, true), option, defaultTypeArguments);
+ ResolveNameSegment_Type(s, new ResolveOpts(context, true), option, defaultTypeArguments);
}
- if (ErrorCount == prevErrorCount) {
+ if (reporter.Count(ErrorLevel.Error) == prevErrorCount) {
var r = t.NamePath.Resolved as Resolver_IdentifierExpr;
if (r == null || !(r.Type is Resolver_IdentifierExpr.ResolverType_Type)) {
- Error(t.tok, "expected type");
+ reporter.Error(MessageSource.Resolver, t.tok, "expected type");
} else if (r.Type is Resolver_IdentifierExpr.ResolverType_Type && r.TypeParamDecl != null) {
t.ResolvedParam = r.TypeParamDecl;
} else if (r.Type is Resolver_IdentifierExpr.ResolverType_Type) {
var d = r.Decl;
if (d is OpaqueTypeDecl) {
- t.ResolvedParam = ((OpaqueTypeDecl)d).TheType; // resolve like a type parameter, and it may have type parameters if it's an opaque type
+ var dd = (OpaqueTypeDecl)d;
+ if (dd.Module.ClonedFrom != null && dd.Module.ClonedFrom.ExclusiveRefinement != null) {
+ t.ResolvedParam = ((OpaqueTypeDecl)dd.ClonedFrom).TheType;
+ t.ResolvedClass = d; // Store the decl, so the compiler will generate the fully qualified name
+ } else {
+ t.ResolvedParam = ((OpaqueTypeDecl)d).TheType;
+ // resolve like a type parameter, and it may have type parameters if it's an opaque type
+ t.ResolvedClass = d; // Store the decl, so the compiler will generate the fully qualified name
+ }
} else if (d is NewtypeDecl) {
var dd = (NewtypeDecl)d;
+ if (DafnyOptions.O.IronDafny) {
+ while (dd.ClonedFrom != null) {
+ dd = (NewtypeDecl)dd.ClonedFrom;
+ }
+ }
var caller = context as ICallable;
if (caller != null) {
caller.EnclosingModule.CallGraph.AddEdge(caller, dd);
if (caller == dd) {
// detect self-loops here, since they don't show up in the graph's SSC methods
- Error(dd.tok, "recursive dependency involving a newtype: {0} -> {0}", dd.Name);
+ reporter.Error(MessageSource.Resolver, dd.tok, "recursive dependency involving a newtype: {0} -> {0}", dd.Name);
}
}
t.ResolvedClass = dd;
@@ -4147,7 +5090,7 @@ namespace Microsoft.Dafny
}
// defaults and auto have been applied; check if we now have the right number of arguments
if (d.TypeArgs.Count != t.TypeArgs.Count) {
- Error(t.tok, "Wrong number of type arguments ({0} instead of {1}) passed to {2}: {3}", t.TypeArgs.Count, d.TypeArgs.Count, d.WhatKind, t.Name);
+ reporter.Error(MessageSource.Resolver, t.tok, "Wrong number of type arguments ({0} instead of {1}) passed to {2}: {3}", t.TypeArgs.Count, d.TypeArgs.Count, d.WhatKind, t.Name);
}
}
@@ -4233,7 +5176,8 @@ namespace Microsoft.Dafny
} else if (a is ObjectType) {
return b is ObjectType;
} else if (a is SetType) {
- return b is SetType && UnifyTypes(((SetType)a).Arg, ((SetType)b).Arg);
+ return b is SetType && ((SetType)a).Finite == ((SetType)b).Finite &&
+ UnifyTypes(((SetType)a).Arg, ((SetType)b).Arg);
} else if (a is MultiSetType) {
return b is MultiSetType && UnifyTypes(((MultiSetType)a).Arg, ((MultiSetType)b).Arg);
} else if (a is MapType) {
@@ -4241,13 +5185,60 @@ namespace Microsoft.Dafny
UnifyTypes(((MapType)a).Domain, ((MapType)b).Domain) && UnifyTypes(((MapType)a).Range, ((MapType)b).Range);
} else if (a is SeqType) {
return b is SeqType && UnifyTypes(((SeqType)a).Arg, ((SeqType)b).Arg);
- } else if (a is UserDefinedType) {
- if (!(b is UserDefinedType)) {
- return false;
+ } else if (a is UserDefinedType || b is UserDefinedType) {
+ if (!(a is UserDefinedType) && b is UserDefinedType) {
+ var x = a;
+ a = b;
+ b = x;
}
+
var aa = (UserDefinedType)a;
+ var rca = aa.ResolvedClass;
+ // traits are currently unfriendly to irondafny features.
+ if (DafnyOptions.O.IronDafny && !(rca is TraitDecl)) {
+ if (rca != null) {
+ while (rca.ClonedFrom != null || rca.ExclusiveRefinement != null) {
+ if (rca.ClonedFrom != null) {
+ rca = (TopLevelDecl)rca.ClonedFrom;
+ } else {
+ Contract.Assert(rca.ExclusiveRefinement != null);
+ rca = rca.ExclusiveRefinement;
+ }
+ }
+ }
+ }
+
+ if (!(b is UserDefinedType)) {
+ return DafnyOptions.O.IronDafny && rca is TypeSynonymDecl && UnifyTypes(((TypeSynonymDecl)rca).Rhs, b);
+ }
+
var bb = (UserDefinedType)b;
- if (aa.ResolvedClass != null && aa.ResolvedClass == bb.ResolvedClass) {
+ var rcb = bb.ResolvedClass;
+ // traits are currently unfriendly to irondafny features.
+ if (DafnyOptions.O.IronDafny && !(rca is TraitDecl) && !(rcb is TraitDecl)) {
+ if (rcb != null) {
+ while (rcb.ClonedFrom != null || rcb.ExclusiveRefinement != null) {
+ if (rcb.ClonedFrom != null) {
+ rcb = (TopLevelDecl)rcb.ClonedFrom;
+ } else {
+ Contract.Assert(rcb.ExclusiveRefinement != null);
+ rcb = rcb.ExclusiveRefinement;
+ }
+ }
+ }
+ if (rca is TypeSynonymDecl || rcb is TypeSynonymDecl) {
+ var aaa = a;
+ var bbb = b;
+ if (rca is TypeSynonymDecl) {
+ aaa = ((TypeSynonymDecl)rca).Rhs;
+ }
+ if (rcb is TypeSynonymDecl) {
+ bbb = ((TypeSynonymDecl)rcb).Rhs;
+ }
+ return UnifyTypes(aaa, bbb);
+ }
+ }
+ if (rca != null && Object.ReferenceEquals(rca, rcb)) {
// these are both resolved class/datatype types
Contract.Assert(aa.TypeArgs.Count == bb.TypeArgs.Count);
bool successSoFar = true;
@@ -4255,13 +5246,12 @@ namespace Microsoft.Dafny
successSoFar = UnifyTypes(aa.TypeArgs[i], bb.TypeArgs[i]);
}
return successSoFar;
- }
- else if ((bb.ResolvedClass is TraitDecl) && (aa.ResolvedClass is TraitDecl)) {
- return ((TraitDecl)bb.ResolvedClass).FullCompileName == ((TraitDecl)aa.ResolvedClass).FullCompileName;
- } else if ((bb.ResolvedClass is ClassDecl) && (aa.ResolvedClass is TraitDecl)) {
- return ((ClassDecl)bb.ResolvedClass).TraitsObj.Any(tr => tr.FullCompileName == ((TraitDecl)aa.ResolvedClass).FullCompileName);
- } else if ((aa.ResolvedClass is ClassDecl) && (bb.ResolvedClass is TraitDecl)) {
- return ((ClassDecl)aa.ResolvedClass).TraitsObj.Any(tr => tr.FullCompileName == ((TraitDecl)bb.ResolvedClass).FullCompileName);
+ } else if (rcb is TraitDecl && rca is TraitDecl) {
+ return rca == rcb;
+ } else if (rcb is ClassDecl && rca is TraitDecl) {
+ return ((ClassDecl)rcb).TraitsObj.Contains(rca);
+ } else if (rca is ClassDecl && rcb is TraitDecl) {
+ return ((ClassDecl)rca).TraitsObj.Contains(rcb);
} else if (aa.ResolvedParam != null && aa.ResolvedParam == bb.ResolvedParam) {
// type parameters
if (aa.TypeArgs.Count != bb.TypeArgs.Count) {
@@ -4279,6 +5269,10 @@ namespace Microsoft.Dafny
// something is wrong; either aa or bb wasn't properly resolved, or they don't unify
return false;
}
+ } else if (a is Resolver_IdentifierExpr.ResolverType_Type) {
+ return b is Resolver_IdentifierExpr.ResolverType_Type;
+ } else if (a is Resolver_IdentifierExpr.ResolverType_Module) {
+ return b is Resolver_IdentifierExpr.ResolverType_Module;
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
}
@@ -4354,7 +5348,9 @@ namespace Microsoft.Dafny
// fine
} else if (opProxy.AllowChar && t is CharType) {
// fine
- } else if (opProxy.AllowSetVarieties && (t is SetType || t is MultiSetType)) {
+ } else if (opProxy.AllowSetVarieties && ((t is SetType && ((SetType)t).Finite) || t is MultiSetType)) {
+ // fine
+ } else if (opProxy.AllowISet && (t is SetType && !((SetType)t).Finite)) {
// fine
} else if (opProxy.AllowSeq && t is SeqType) {
// fine
@@ -4365,7 +5361,7 @@ namespace Microsoft.Dafny
} else if (proxy is IndexableTypeProxy) {
var iProxy = (IndexableTypeProxy)proxy;
if (t is SeqType) {
- if (!UnifyTypes(iProxy.Domain, new OperationTypeProxy(true, false, false, false, false))) {
+ if (!UnifyTypes(iProxy.Domain, new OperationTypeProxy(true, false, false, false, false, false))) {
return false;
} else if (!UnifyTypes(iProxy.Range, ((SeqType)t).Arg)) {
return false;
@@ -4374,7 +5370,7 @@ namespace Microsoft.Dafny
}
} else if (iProxy.AllowArray && t.IsArrayType && t.AsArrayType.Dims == 1) {
Type elType = UserDefinedType.ArrayElementType(t);
- if (!UnifyTypes(iProxy.Domain, new OperationTypeProxy(true, false, false, false, false))) {
+ if (!UnifyTypes(iProxy.Domain, new OperationTypeProxy(true, false, false, false, false, false))) {
return false;
} else if (!UnifyTypes(iProxy.Range, elType)) {
return false;
@@ -4400,7 +5396,7 @@ namespace Microsoft.Dafny
} else if (t is MultiSetType) {
if (!UnifyTypes(iProxy.Domain, ((MultiSetType)t).Arg)) {
return false;
- } else if (!UnifyTypes(iProxy.Range, new OperationTypeProxy(true, false, false, false, false))) {
+ } else if (!UnifyTypes(iProxy.Range, new OperationTypeProxy(true, false, false, false, false, false))) {
return false;
} else if (!UnifyTypes(iProxy.Arg, iProxy.Domain)) {
return false;
@@ -4481,15 +5477,18 @@ namespace Microsoft.Dafny
} else if (a is CollectionTypeProxy) {
if (b is CollectionTypeProxy) {
- a.T = b;
- return UnifyTypes(((CollectionTypeProxy)a).Arg, ((CollectionTypeProxy)b).Arg);
+ var argUnificationSuccess = UnifyTypes(((CollectionTypeProxy)a).Arg, ((CollectionTypeProxy)b).Arg);
+ if (argUnificationSuccess) {
+ a.T = b;
+ }
+ return argUnificationSuccess;
} else if (b is OperationTypeProxy) {
var proxy = (OperationTypeProxy)b;
- if (proxy.AllowSeq && proxy.AllowSetVarieties) {
+ if (proxy.AllowSeq && proxy.AllowSetVarieties && proxy.AllowISet) {
b.T = a; // a is a stronger constraint than b
} else {
// a says set<T>,seq<T> and b says numeric,set; the intersection is set<T>
- var c = new SetType(((CollectionTypeProxy)a).Arg);
+ var c = new SetType(true, ((CollectionTypeProxy)a).Arg);
return AssignProxyAfterCycleCheck(a, c) && AssignProxyAfterCycleCheck(b, c);
}
return true;
@@ -4497,7 +5496,7 @@ namespace Microsoft.Dafny
var pa = (CollectionTypeProxy)a;
var ib = (IndexableTypeProxy)b;
// pa is:
- // set(Arg) or multiset(Arg) or seq(Arg) or map(Arg, anyRange) or imap(Arg, anyRange)
+ // set(Arg) or iset(Arg) or multiset(Arg) or seq(Arg) or map(Arg, anyRange) or imap(Arg, anyRange)
// ib is:
// multiset(Arg) or
// seq(Arg) or
@@ -4525,15 +5524,16 @@ namespace Microsoft.Dafny
var h = pa.AllowChar && pb.AllowChar;
var q = pa.AllowSeq && pb.AllowSeq;
var s = pa.AllowSetVarieties && pb.AllowSetVarieties;
- if (!i && !r && !h && !q && !s) {
+ var t = pa.AllowISet && pb.AllowISet;
+ if (!i && !r && !h && !q && !s && !t) {
// over-constrained
return false;
- } else if (i == pa.AllowInts && r == pa.AllowReals && h == pa.AllowChar && q == pa.AllowSeq && s == pa.AllowSetVarieties) {
+ } else if (i == pa.AllowInts && r == pa.AllowReals && h == pa.AllowChar && q == pa.AllowSeq && s == pa.AllowSetVarieties && t == pa.AllowISet) {
b.T = a; // a has the stronger requirement
- } else if (i == pb.AllowInts && r == pb.AllowReals && h == pb.AllowChar && q == pb.AllowSeq && s == pb.AllowSetVarieties) {
+ } else if (i == pb.AllowInts && r == pb.AllowReals && h == pb.AllowChar && q == pb.AllowSeq && s == pb.AllowSetVarieties && t == pb.AllowISet) {
a.T = b; // b has the stronger requirement
} else {
- Type c = !i && !r && h && !q && !s ? new CharType() : (Type)new OperationTypeProxy(i, r, h, q, s);
+ Type c = !i && !r && h && !q && !s && !t? new CharType() : (Type)new OperationTypeProxy(i, r, h, q, s, t);
// the calls to AssignProxyAfterCycleCheck are needed only when c is a non-proxy type, but it doesn't
// hurt to do them in both cases
return AssignProxyAfterCycleCheck(a, c) && AssignProxyAfterCycleCheck(b, c);
@@ -4604,48 +5604,35 @@ namespace Microsoft.Dafny
return at;
}
- /// <summary>
- /// "specContextOnly" means that the statement must be erasable, that is, it should be okay to omit it
- /// at run time. That means it must not have any side effects on non-ghost variables, for example.
- /// </summary>
- public void ResolveStatement(Statement stmt, bool specContextOnly, ICodeContext codeContext) {
+ public void ResolveStatement(Statement stmt, ICodeContext codeContext) {
Contract.Requires(stmt != null);
Contract.Requires(codeContext != null);
if (!(stmt is ForallStmt)) { // forall statements do their own attribute resolution below
- ResolveAttributes(stmt.Attributes, new ResolveOpts(codeContext, true, true));
+ ResolveAttributes(stmt.Attributes, new ResolveOpts(codeContext, true));
}
if (stmt is PredicateStmt) {
PredicateStmt s = (PredicateStmt)stmt;
- s.IsGhost = true;
- ResolveExpression(s.Expr, new ResolveOpts(codeContext, true, true));
+ ResolveExpression(s.Expr, new ResolveOpts(codeContext, true));
Contract.Assert(s.Expr.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(s.Expr.Type, Type.Bool)) {
- Error(s.Expr, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Expr.Type);
- }
+ ConstrainTypes(s.Expr.Type, Type.Bool, s.Expr, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Expr.Type);
} else if (stmt is PrintStmt) {
- PrintStmt s = (PrintStmt)stmt;
- ResolveAttributeArgs(s.Args, new ResolveOpts(codeContext, false, specContextOnly), false);
- if (specContextOnly) {
- Error(stmt, "print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)");
- }
+ var s = (PrintStmt)stmt;
+ var opts = new ResolveOpts(codeContext, false);
+ s.Args.Iter(e => ResolveExpression(e, opts));
} else if (stmt is BreakStmt) {
var s = (BreakStmt)stmt;
if (s.TargetLabel != null) {
Statement target = labeledStatements.Find(s.TargetLabel);
if (target == null) {
- Error(s, "break label is undefined or not in scope: {0}", s.TargetLabel);
+ reporter.Error(MessageSource.Resolver, s, "break label is undefined or not in scope: {0}", s.TargetLabel);
} else {
s.TargetStmt = target;
- bool targetIsLoop = target is WhileStmt || target is AlternativeLoopStmt;
- if (specContextOnly && !s.TargetStmt.IsGhost && !inSpecOnlyContext[s.TargetStmt]) {
- Error(stmt, "ghost-context break statement is not allowed to break out of non-ghost " + (targetIsLoop ? "loop" : "structure"));
- }
}
} else {
if (loopStack.Count < s.BreakCount) {
- Error(s, "trying to break out of more loop levels than there are enclosing loops");
+ reporter.Error(MessageSource.Resolver, s, "trying to break out of more loop levels than there are enclosing loops");
} else {
Statement target = loopStack[loopStack.Count - s.BreakCount];
if (target.Labels == null) {
@@ -4653,20 +5640,15 @@ namespace Microsoft.Dafny
target.Labels = new LList<Label>(new Label(target.Tok, null), null);
}
s.TargetStmt = target;
- if (specContextOnly && !target.IsGhost && !inSpecOnlyContext[target]) {
- Error(stmt, "ghost-context break statement is not allowed to break out of non-ghost loop");
- }
}
}
} else if (stmt is ProduceStmt) {
var kind = stmt is YieldStmt ? "yield" : "return";
if (stmt is YieldStmt && !(codeContext is IteratorDecl)) {
- Error(stmt, "yield statement is allowed only in iterators");
+ reporter.Error(MessageSource.Resolver, stmt, "yield statement is allowed only in iterators");
} else if (stmt is ReturnStmt && !(codeContext is Method)) {
- Error(stmt, "return statement is allowed only in method");
- } else if (specContextOnly && !codeContext.IsGhost) {
- Error(stmt, "{0} statement is not allowed in this context (because it is guarded by a specification-only expression)", kind);
+ reporter.Error(MessageSource.Resolver, stmt, "return statement is allowed only in method");
}
var s = (ProduceStmt)stmt;
if (s.rhss != null) {
@@ -4674,7 +5656,7 @@ namespace Microsoft.Dafny
if (cmc == null) {
// an error has already been reported above
} else if (cmc.Outs.Count != s.rhss.Count) {
- Error(s, "number of {2} parameters does not match declaration (found {0}, expected {1})", s.rhss.Count, cmc.Outs.Count, kind);
+ reporter.Error(MessageSource.Resolver, s, "number of {2} parameters does not match declaration (found {0}, expected {1})", s.rhss.Count, cmc.Outs.Count, kind);
} else {
Contract.Assert(s.rhss.Count > 0);
// Create a hidden update statement using the out-parameter formals, resolve the RHS, and check that the RHS is good.
@@ -4697,13 +5679,13 @@ namespace Microsoft.Dafny
}
s.hiddenUpdate = new UpdateStmt(s.Tok, s.EndTok, formals, s.rhss, true);
// resolving the update statement will check for return/yield statement specifics.
- ResolveStatement(s.hiddenUpdate, specContextOnly, codeContext);
+ ResolveStatement(s.hiddenUpdate, codeContext);
}
} else {// this is a regular return/yield statement.
s.hiddenUpdate = null;
}
} else if (stmt is ConcreteUpdateStatement) {
- ResolveConcreteUpdateStmt((ConcreteUpdateStatement)stmt, specContextOnly, codeContext);
+ ResolveConcreteUpdateStmt((ConcreteUpdateStatement)stmt, codeContext);
} else if (stmt is VarDeclStmt) {
var s = (VarDeclStmt)stmt;
// We have three cases.
@@ -4725,10 +5707,6 @@ namespace Microsoft.Dafny
foreach (var local in s.Locals) {
ResolveType(local.Tok, local.OptionalType, codeContext, ResolveTypeOptionEnum.InferTypeProxies, null);
local.type = local.OptionalType;
- if (specContextOnly) {
- // a local variable in a specification-only context might as well be ghost
- local.IsGhost = true;
- }
}
// Resolve the UpdateStmt, if any
if (s.Update is UpdateStmt) {
@@ -4743,64 +5721,76 @@ namespace Microsoft.Dafny
lhs.Type = local.Type;
}
// resolve the whole thing
- ResolveConcreteUpdateStmt(s.Update, specContextOnly, codeContext);
+ ResolveConcreteUpdateStmt(s.Update, codeContext);
}
// Add the locals to the scope
foreach (var local in s.Locals) {
- if (!scope.Push(local.Name, local)) {
- Error(local.Tok, "Duplicate local-variable name: {0}", local.Name);
- }
+ ScopePushAndReport(scope, local, "local-variable");
}
// With the new locals in scope, it's now time to resolve the attributes on all the locals
foreach (var local in s.Locals) {
- ResolveAttributes(local.Attributes, new ResolveOpts(codeContext, true, true));
+ ResolveAttributes(local.Attributes, new ResolveOpts(codeContext, true));
}
// Resolve the AssignSuchThatStmt, if any
if (s.Update is AssignSuchThatStmt) {
- ResolveConcreteUpdateStmt(s.Update, specContextOnly, codeContext);
+ ResolveConcreteUpdateStmt(s.Update, codeContext);
}
// Update the VarDeclStmt's ghost status according to its components
- if (!s.IsGhost) {
- s.IsGhost = (s.Update == null || s.Update.IsGhost) && s.Locals.All(v => v.IsGhost);
- }
foreach (var local in s.Locals)
{
if (Attributes.Contains(local.Attributes, "assumption"))
{
if (currentMethod == null)
{
- Error(local.Tok, "assumption variable can only be declared in a method");
- }
- if (!local.IsGhost)
- {
- Error(local.Tok, "assumption variable must be ghost");
+ reporter.Error(MessageSource.Resolver, local.Tok, "assumption variable can only be declared in a method");
}
if (!(local.Type.IsBoolType))
{
- Error(s, "assumption variable must be of type 'bool'");
+ reporter.Error(MessageSource.Resolver, local.Tok, "assumption variable must be of type 'bool'");
+ }
+ if (!local.IsGhost) {
+ reporter.Error(MessageSource.Resolver, local.Tok, "assumption variable must be ghost");
}
}
}
-
+ } else if (stmt is LetStmt) {
+ LetStmt s = (LetStmt)stmt;
+ foreach (var rhs in s.RHSs) {
+ ResolveExpression(rhs, new ResolveOpts(codeContext, true));
+ }
+ if (s.LHSs.Count != s.RHSs.Count) {
+ reporter.Error(MessageSource.Resolver, stmt, "let statement must have same number of LHSs (found {0}) as RHSs (found {1})", s.LHSs.Count, s.RHSs.Count);
+ }
+ var i = 0;
+ foreach (var lhs in s.LHSs) {
+ var rhsType = i < s.RHSs.Count ? s.RHSs[i].Type : new InferredTypeProxy();
+ ResolveCasePattern(lhs, rhsType, codeContext);
+ // Check for duplicate names now, because not until after resolving the case pattern do we know if identifiers inside it refer to bound variables or nullary constructors
+ var c = 0;
+ foreach (var bv in lhs.Vars) {
+ ScopePushAndReport(scope, bv, "local_variable");
+ c++;
+ }
+ if (c == 0) {
+ // Every identifier-looking thing in the pattern resolved to a constructor; that is, this LHS is a constant literal
+ reporter.Error(MessageSource.Resolver, lhs.tok, "LHS is a constant literal; to be legal, it must introduce at least one bound variable");
+ }
+ i++;
+ }
} else if (stmt is AssignStmt) {
AssignStmt s = (AssignStmt)stmt;
- int prevErrorCount = ErrorCount;
- ResolveExpression(s.Lhs, new ResolveOpts(codeContext, true, specContextOnly)); // allow ghosts for now, tighted up below
- bool lhsResolvedSuccessfully = ErrorCount == prevErrorCount;
+ int prevErrorCount = reporter.Count(ErrorLevel.Error);
+ ResolveExpression(s.Lhs, new ResolveOpts(codeContext, true)); // allow ghosts for now, tighted up below
+ bool lhsResolvedSuccessfully = reporter.Count(ErrorLevel.Error) == prevErrorCount;
Contract.Assert(s.Lhs.Type != null); // follows from postcondition of ResolveExpression
// check that LHS denotes a mutable variable or a field
- bool lvalueIsGhost = false;
var lhs = s.Lhs.Resolved;
if (lhs is IdentifierExpr) {
IVariable var = ((IdentifierExpr)lhs).Var;
if (var == null) {
// the LHS didn't resolve correctly; some error would already have been reported
} else {
- lvalueIsGhost = var.IsGhost || codeContext.IsGhost;
CheckIsLvalue(lhs, codeContext);
- if (!lvalueIsGhost && specContextOnly) {
- Error(stmt, "Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)");
- }
var localVar = var as LocalVariable;
if (localVar != null && currentMethod != null && Attributes.Contains(localVar.Attributes, "assumption"))
@@ -4818,25 +5808,13 @@ namespace Microsoft.Dafny
}
else
{
- Error(stmt, string.Format("there may be at most one assignment to an assumption variable, the RHS of which must match the expression \"{0} && <boolean expression>\"", localVar.Name));
+ reporter.Error(MessageSource.Resolver, stmt, string.Format("there may be at most one assignment to an assumption variable, the RHS of which must match the expression \"{0} && <boolean expression>\"", localVar.Name));
}
}
}
} else if (lhs is MemberSelectExpr) {
var fse = (MemberSelectExpr)lhs;
if (fse.Member != null) { // otherwise, an error was reported above
- lvalueIsGhost = fse.Member.IsGhost;
- if (!lvalueIsGhost) {
- if (specContextOnly) {
- Error(stmt, "Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)");
- } else {
- // It is now that we wish we would have resolved s.Lhs to not allow ghosts. Too late, so we do
- // the next best thing.
- if (lhsResolvedSuccessfully && UsesSpecFeatures(fse.Obj)) {
- Error(stmt, "Assignment to non-ghost field is not allowed to use specification-only expressions in the receiver");
- }
- }
- }
CheckIsLvalue(fse, codeContext);
}
} else if (lhs is SeqSelectExpr) {
@@ -4844,52 +5822,26 @@ namespace Microsoft.Dafny
// LHS is fine, provided the "sequence" is really an array
if (lhsResolvedSuccessfully) {
Contract.Assert(slhs.Seq.Type != null);
- if (specContextOnly) {
- Error(stmt, "Assignment to array element is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)");
- }
CheckIsLvalue(slhs, codeContext);
}
} else if (lhs is MultiSelectExpr) {
- if (specContextOnly) {
- Error(stmt, "Assignment to array element is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)");
- }
CheckIsLvalue(lhs, codeContext);
} else {
CheckIsLvalue(lhs, codeContext);
}
- s.IsGhost = lvalueIsGhost;
Type lhsType = s.Lhs.Type;
if (s.Rhs is ExprRhs) {
ExprRhs rr = (ExprRhs)s.Rhs;
- ResolveExpression(rr.Expr, new ResolveOpts(codeContext, true, specContextOnly));
- if (!lvalueIsGhost) {
- CheckIsNonGhost(rr.Expr);
- }
+ ResolveExpression(rr.Expr, new ResolveOpts(codeContext, true));
Contract.Assert(rr.Expr.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(lhsType, rr.Expr.Type)) {
- Error(stmt, "RHS (of type {0}) not assignable to LHS (of type {1})", rr.Expr.Type, lhsType);
- }
+ ConstrainTypes(lhsType, rr.Expr.Type, stmt, "RHS (of type {0}) not assignable to LHS (of type {1})", rr.Expr.Type, lhsType);
} else if (s.Rhs is TypeRhs) {
TypeRhs rr = (TypeRhs)s.Rhs;
- Type t = ResolveTypeRhs(rr, stmt, lvalueIsGhost, codeContext);
- if (!lvalueIsGhost) {
- if (rr.ArrayDimensions != null) {
- foreach (var dim in rr.ArrayDimensions) {
- CheckIsNonGhost(dim);
- }
- }
- if (rr.InitCall != null) {
- foreach (var arg in rr.InitCall.Args) {
- CheckIsNonGhost(arg);
- }
- }
- }
- if (!UnifyTypes(lhsType, t)) {
- Error(stmt, "type {0} is not assignable to LHS (of type {1})", t, lhsType);
- }
+ Type t = ResolveTypeRhs(rr, stmt, codeContext);
+ ConstrainTypes(lhsType, t, stmt, "type {0} is not assignable to LHS (of type {1})", t, lhsType);
} else if (s.Rhs is HavocRhs) {
// nothing else to do
} else {
@@ -4898,181 +5850,86 @@ namespace Microsoft.Dafny
} else if (stmt is CallStmt) {
CallStmt s = (CallStmt)stmt;
- ResolveCallStmt(s, specContextOnly, codeContext, null);
+ ResolveCallStmt(s, codeContext, null);
} else if (stmt is BlockStmt) {
var s = (BlockStmt)stmt;
scope.PushMarker();
- ResolveBlockStatement(s, specContextOnly, codeContext);
- if (!s.IsGhost) {
- s.IsGhost = s.Body.All(ss => ss.IsGhost); // mark the block statement as ghost if all its substatements are ghost
- }
+ ResolveBlockStatement(s, codeContext);
scope.PopMarker();
} else if (stmt is IfStmt) {
IfStmt s = (IfStmt)stmt;
- bool branchesAreSpecOnly = specContextOnly;
if (s.Guard != null) {
- int prevErrorCount = ErrorCount;
- ResolveExpression(s.Guard, new ResolveOpts(codeContext, true, specContextOnly));
+ ResolveExpression(s.Guard, new ResolveOpts(codeContext, true));
Contract.Assert(s.Guard.Type != null); // follows from postcondition of ResolveExpression
- bool successfullyResolved = ErrorCount == prevErrorCount;
- if (!UnifyTypes(s.Guard.Type, Type.Bool)) {
- Error(s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type);
- }
- if (!specContextOnly && successfullyResolved) {
- branchesAreSpecOnly = UsesSpecFeatures(s.Guard);
+ ConstrainTypes(s.Guard.Type, Type.Bool, s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type);
+ }
+
+ scope.PushMarker();
+ if (s.IsExistentialGuard) {
+ var exists = (ExistsExpr)s.Guard;
+ foreach (var v in exists.BoundVars) {
+ ScopePushAndReport(scope, v, "bound-variable");
}
}
- s.IsGhost = branchesAreSpecOnly;
- ResolveStatement(s.Thn, branchesAreSpecOnly, codeContext);
+ ResolveBlockStatement(s.Thn, codeContext);
+ scope.PopMarker();
+
if (s.Els != null) {
- ResolveStatement(s.Els, branchesAreSpecOnly, codeContext);
- }
- if (!s.IsGhost && s.Thn.IsGhost && (s.Els == null || s.Els.IsGhost)) {
- // mark the entire 'if' statement as ghost if its branches are ghost
- s.IsGhost = true;
+ ResolveStatement(s.Els, codeContext);
}
} else if (stmt is AlternativeStmt) {
var s = (AlternativeStmt)stmt;
- s.IsGhost = ResolveAlternatives(s.Alternatives, specContextOnly, null, codeContext);
- if (!s.IsGhost) {
- s.IsGhost = s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost));
- }
+ ResolveAlternatives(s.Alternatives, null, codeContext);
} else if (stmt is WhileStmt) {
WhileStmt s = (WhileStmt)stmt;
- bool bodyMustBeSpecOnly = specContextOnly;
var fvs = new HashSet<IVariable>();
- bool usesHeap = false, usesOldHeap = false;
- Type usesThis = null;
if (s.Guard != null) {
- int prevErrorCount = ErrorCount;
- ResolveExpression(s.Guard, new ResolveOpts(codeContext, true, specContextOnly));
+ ResolveExpression(s.Guard, new ResolveOpts(codeContext, true));
Contract.Assert(s.Guard.Type != null); // follows from postcondition of ResolveExpression
- bool successfullyResolved = ErrorCount == prevErrorCount;
- Translator.ComputeFreeVariables(s.Guard, fvs, ref usesHeap, ref usesOldHeap, ref usesThis, false);
- if (!UnifyTypes(s.Guard.Type, Type.Bool)) {
- Error(s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type);
- }
- if (!specContextOnly && successfullyResolved) {
- bodyMustBeSpecOnly = UsesSpecFeatures(s.Guard);
- }
- }
-
- foreach (MaybeFreeExpression inv in s.Invariants) {
- ResolveAttributes(inv.Attributes, new ResolveOpts(codeContext, true, true));
- ResolveExpression(inv.E, new ResolveOpts(codeContext, true, true));
- Contract.Assert(inv.E.Type != null); // follows from postcondition of ResolveExpression
- Translator.ComputeFreeVariables(inv.E, fvs, ref usesHeap, ref usesOldHeap, ref usesThis, false);
- if (!UnifyTypes(inv.E.Type, Type.Bool)) {
- Error(inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type);
- }
+ Translator.ComputeFreeVariables(s.Guard, fvs);
+ ConstrainTypes(s.Guard.Type, Type.Bool, s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type);
}
- ResolveAttributes(s.Decreases.Attributes, new ResolveOpts(codeContext, true, true));
- foreach (Expression e in s.Decreases.Expressions) {
- ResolveExpression(e, new ResolveOpts(codeContext, true, true));
- if (e is WildcardExpr) {
- if (bodyMustBeSpecOnly) {
- Error(e, "'decreases *' is not allowed on ghost loops");
- } else if (!codeContext.AllowsNontermination && !DafnyOptions.O.Dafnycc) {
- Error(e, "a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating");
- }
- }
- // any type is fine
- }
+ ResolveLoopSpecificationComponents(s.Invariants, s.Decreases, s.Mod, codeContext, fvs);
- if (s.Mod.Expressions != null) {
- ResolveAttributes(s.Mod.Attributes, new ResolveOpts(codeContext, true, true));
- foreach (FrameExpression fe in s.Mod.Expressions) {
- ResolveFrameExpression(fe, false, bodyMustBeSpecOnly, codeContext);
- Translator.ComputeFreeVariables(fe.E, fvs, ref usesHeap, ref usesOldHeap, ref usesThis, false);
- }
- }
- s.IsGhost = s.Body == null || bodyMustBeSpecOnly;
if (s.Body != null) {
loopStack.Add(s); // push
- if (s.Labels == null) { // otherwise, "s" is already in "inSpecOnlyContext" map
- inSpecOnlyContext.Add(s, specContextOnly);
- }
-
- ResolveStatement(s.Body, bodyMustBeSpecOnly, codeContext);
+ ResolveStatement(s.Body, codeContext);
loopStack.RemoveAt(loopStack.Count - 1); // pop
} else {
- string text = "havoc {";
- if (fvs.Count != 0) {
- string sep = "";
- foreach (var fv in fvs) {
- text += sep + fv.Name;
- sep = ", ";
- }
- }
- text += "};"; // always terminate with a semi-colon
- ReportAdditionalInformation(s.Tok, text, s.Tok.val.Length);
+ string text = "havoc {" + Util.Comma(", ", fvs, fv => fv.Name) + "};"; // always terminate with a semi-colon
+ reporter.Info(MessageSource.Resolver, s.Tok, text);
}
} else if (stmt is AlternativeLoopStmt) {
var s = (AlternativeLoopStmt)stmt;
- s.IsGhost = ResolveAlternatives(s.Alternatives, specContextOnly, s, codeContext);
- foreach (MaybeFreeExpression inv in s.Invariants) {
- ResolveExpression(inv.E, new ResolveOpts(codeContext, true, true));
- Contract.Assert(inv.E.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(inv.E.Type, Type.Bool)) {
- Error(inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type);
- }
- }
-
- foreach (Expression e in s.Decreases.Expressions) {
- ResolveExpression(e, new ResolveOpts(codeContext, true, true));
- if (e is WildcardExpr) {
- if (s.IsGhost) {
- Error(e, "'decreases *' is not allowed on ghost loops");
- } else if (!codeContext.AllowsNontermination && !DafnyOptions.O.Dafnycc) {
- Error(e, "a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating");
- }
- }
- // any type is fine
- }
+ ResolveAlternatives(s.Alternatives, s, codeContext);
+ ResolveLoopSpecificationComponents(s.Invariants, s.Decreases, s.Mod, codeContext, null);
} else if (stmt is ForallStmt) {
var s = (ForallStmt)stmt;
- int prevErrorCount = ErrorCount;
+ int prevErrorCount = reporter.Count(ErrorLevel.Error);
scope.PushMarker();
foreach (BoundVar v in s.BoundVars) {
- if (!scope.Push(v.Name, v)) {
- Error(v, "Duplicate bound-variable name: {0}", v.Name);
- }
+ ScopePushAndReport(scope, v, "local-variable");
ResolveType(v.tok, v.Type, codeContext, ResolveTypeOptionEnum.InferTypeProxies, null);
}
- ResolveExpression(s.Range, new ResolveOpts(codeContext, true, specContextOnly));
+ ResolveExpression(s.Range, new ResolveOpts(codeContext, true));
Contract.Assert(s.Range.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(s.Range.Type, Type.Bool)) {
- Error(stmt, "range restriction in forall statement must be of type bool (instead got {0})", s.Range.Type);
- }
+ ConstrainTypes(s.Range.Type, Type.Bool, stmt, "range restriction in forall statement must be of type bool (instead got {0})", s.Range.Type);
foreach (var ens in s.Ens) {
- ResolveExpression(ens.E, new ResolveOpts(codeContext, true, true));
+ ResolveExpression(ens.E, new ResolveOpts(codeContext, true));
Contract.Assert(ens.E.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(ens.E.Type, Type.Bool)) {
- Error(ens.E, "ensures condition is expected to be of type {0}, but is {1}", Type.Bool, ens.E.Type);
- }
+ ConstrainTypes(ens.E.Type, Type.Bool, ens.E, "ensures condition is expected to be of type {0}, but is {1}", Type.Bool, ens.E.Type);
}
// Since the range and postconditions are more likely to infer the types of the bound variables, resolve them
// first (above) and only then resolve the attributes (below).
- ResolveAttributes(s.Attributes, new ResolveOpts(codeContext, true, true));
-
- bool bodyMustBeSpecOnly = specContextOnly || (prevErrorCount == ErrorCount && UsesSpecFeatures(s.Range));
- if (!bodyMustBeSpecOnly && prevErrorCount == ErrorCount) {
- var missingBounds = new List<BoundVar>();
- CheckTypeInference(s.Range); // we need to resolve operators before the call to DiscoverBounds
- s.Bounds = DiscoverBounds(s.Tok, s.BoundVars, s.Range, true, false, missingBounds);
- if (missingBounds.Count != 0) {
- bodyMustBeSpecOnly = true;
- }
- }
- s.IsGhost = s.Body == null || bodyMustBeSpecOnly;
+ ResolveAttributes(s.Attributes, new ResolveOpts(codeContext, true));
if (s.Body != null) {
// clear the labels for the duration of checking the body, because break statements are not allowed to leave a forall statement
@@ -5080,13 +5937,13 @@ namespace Microsoft.Dafny
var prevLoopStack = loopStack;
labeledStatements = new Scope<Statement>();
loopStack = new List<Statement>();
- ResolveStatement(s.Body, bodyMustBeSpecOnly, codeContext);
+ ResolveStatement(s.Body, codeContext);
labeledStatements = prevLblStmts;
loopStack = prevLoopStack;
}
scope.PopMarker();
- if (prevErrorCount == ErrorCount) {
+ if (prevErrorCount == reporter.Count(ErrorLevel.Error)) {
// determine the Kind and run some additional checks on the body
if (s.Ens.Count != 0) {
// The only supported kind with ensures clauses is Proof.
@@ -5110,13 +5967,13 @@ namespace Microsoft.Dafny
// add the conclusion of the calc as a free postcondition
var result = ((CalcStmt)s0).Result;
s.Ens.Add(new MaybeFreeExpression(result, true));
- ReportAdditionalInformation(s.Tok, "ensures " + Printer.ExprToString(result) + ";", s.Tok.val.Length);
+ reporter.Info(MessageSource.Resolver, s.Tok, "ensures " + Printer.ExprToString(result));
} else {
s.Kind = ForallStmt.ParBodyKind.Proof;
if (s.Body is BlockStmt && ((BlockStmt)s.Body).Body.Count == 0) {
// an empty statement, so don't produce any warning
} else {
- Warning(s.Tok, "the conclusion of the body of this forall statement will not be known outside the forall statement; consider using an 'ensures' clause");
+ reporter.Warning(MessageSource.Resolver, s.Tok, "the conclusion of the body of this forall statement will not be known outside the forall statement; consider using an 'ensures' clause");
}
}
}
@@ -5127,34 +5984,30 @@ namespace Microsoft.Dafny
} else if (stmt is ModifyStmt) {
var s = (ModifyStmt)stmt;
- ResolveAttributes(s.Mod.Attributes, new ResolveOpts(codeContext, true, true));
+ ResolveAttributes(s.Mod.Attributes, new ResolveOpts(codeContext, true));
foreach (FrameExpression fe in s.Mod.Expressions) {
- // (yes, say "modifies", not "modify", in the next line -- it seems to give a more readable error message
- ResolveFrameExpression(fe, false, specContextOnly, codeContext);
+ ResolveFrameExpression(fe, false, codeContext);
}
if (s.Body != null) {
- ResolveBlockStatement(s.Body, specContextOnly, codeContext);
+ ResolveBlockStatement(s.Body, codeContext);
}
- s.IsGhost = specContextOnly;
} else if (stmt is CalcStmt) {
- var prevErrorCount = ErrorCount;
+ var prevErrorCount = reporter.Count(ErrorLevel.Error);
CalcStmt s = (CalcStmt)stmt;
- s.IsGhost = true;
if (s.Lines.Count > 0) {
var e0 = s.Lines.First();
- ResolveExpression(e0, new ResolveOpts(codeContext, true, true));
+ ResolveExpression(e0, new ResolveOpts(codeContext, true));
Contract.Assert(e0.Type != null); // follows from postcondition of ResolveExpression
for (int i = 1; i < s.Lines.Count; i++) {
- if (i < s.Lines.Count - 1 || prevErrorCount == ErrorCount) { // do not resolve the dummy step if there were errors, it might generate more errors
+ if (i < s.Lines.Count - 1 || prevErrorCount == reporter.Count(ErrorLevel.Error)) { // do not resolve the dummy step if there were errors, it might generate more errors
var e1 = s.Lines[i];
- ResolveExpression(e1, new ResolveOpts(codeContext, true, true));
+ ResolveExpression(e1, new ResolveOpts(codeContext, true));
Contract.Assert(e1.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e0.Type, e1.Type)) {
- Error(e1, "all lines in a calculation must have the same type (got {0} after {1})", e1.Type, e0.Type);
+ if (!ConstrainTypes(e0.Type, e1.Type, e1, "all lines in a calculation must have the same type (got {0} after {1})", e1.Type, e0.Type)) {
} else {
var step = s.StepOps[i - 1].StepExpr(e0, e1); // Use custom line operator
- ResolveExpression(step, new ResolveOpts(codeContext, true, true));
+ ResolveExpression(step, new ResolveOpts(codeContext, true));
s.Steps.Add(step);
}
e0 = e1;
@@ -5167,124 +6020,523 @@ namespace Microsoft.Dafny
labeledStatements = new Scope<Statement>();
loopStack = new List<Statement>();
foreach (var h in s.Hints) {
- ResolveStatement(h, true, codeContext);
- CheckHintRestrictions(h);
+ ResolveStatement(h, codeContext);
}
labeledStatements = prevLblStmts;
loopStack = prevLoopStack;
}
- if (prevErrorCount == ErrorCount && s.Lines.Count > 0) {
+ if (prevErrorCount == reporter.Count(ErrorLevel.Error) && s.Lines.Count > 0) {
// do not build Result from the lines if there were errors, as it might be ill-typed and produce unnecessary resolution errors
s.Result = s.ResultOp.StepExpr(s.Lines.First(), s.Lines.Last());
} else {
s.Result = CalcStmt.DefaultOp.StepExpr(Expression.CreateIntLiteral(s.Tok, 0), Expression.CreateIntLiteral(s.Tok, 0));
}
- ResolveExpression(s.Result, new ResolveOpts(codeContext, true, true));
+ ResolveExpression(s.Result, new ResolveOpts(codeContext, true));
Contract.Assert(s.Result != null);
- Contract.Assert(prevErrorCount != ErrorCount || s.Steps.Count == s.Hints.Count);
+ Contract.Assert(prevErrorCount != reporter.Count(ErrorLevel.Error) || s.Steps.Count == s.Hints.Count);
} else if (stmt is MatchStmt) {
- MatchStmt s = (MatchStmt)stmt;
- bool bodyIsSpecOnly = specContextOnly;
- int prevErrorCount = ErrorCount;
- ResolveExpression(s.Source, new ResolveOpts(codeContext, true, specContextOnly));
- Contract.Assert(s.Source.Type != null); // follows from postcondition of ResolveExpression
- bool successfullyResolved = ErrorCount == prevErrorCount;
- if (!specContextOnly && successfullyResolved) {
- bodyIsSpecOnly = UsesSpecFeatures(s.Source);
- }
- UserDefinedType sourceType = null;
- DatatypeDecl dtd = null;
- if (s.Source.Type.IsDatatype) {
- sourceType = (UserDefinedType)s.Source.Type.NormalizeExpand();
- dtd = cce.NonNull((DatatypeDecl)sourceType.ResolvedClass);
+ ResolveMatchStmt(stmt, codeContext);
+ } else if (stmt is SkeletonStatement) {
+ var s = (SkeletonStatement)stmt;
+ reporter.Error(MessageSource.Resolver, s.Tok, "skeleton statements are allowed only in refining methods");
+ // nevertheless, resolve the underlying statement; hey, why not
+ if (s.S != null) {
+ ResolveStatement(s.S, codeContext);
}
- var subst = new Dictionary<TypeParameter, Type>();
- Dictionary<string, DatatypeCtor> ctors;
- if (dtd == null) {
- Error(s.Source, "the type of the match source expression must be a datatype (instead found {0})", s.Source.Type);
- ctors = null;
- } else {
- Contract.Assert(sourceType != null); // dtd and sourceType are set together above
- ctors = datatypeCtors[dtd];
- Contract.Assert(ctors != null); // dtd should have been inserted into datatypeCtors during a previous resolution stage
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException();
+ }
+ }
+
+ private void ResolveLoopSpecificationComponents(List<MaybeFreeExpression> invariants, Specification<Expression> decreases, Specification<FrameExpression> modifies, ICodeContext codeContext, HashSet<IVariable> fvs) {
+ Contract.Requires(invariants != null);
+ Contract.Requires(decreases != null);
+ Contract.Requires(modifies != null);
+ Contract.Requires(codeContext != null);
- // build the type-parameter substitution map for this use of the datatype
- for (int i = 0; i < dtd.TypeArgs.Count; i++) {
- subst.Add(dtd.TypeArgs[i], sourceType.TypeArgs[i]);
+ foreach (MaybeFreeExpression inv in invariants) {
+ ResolveAttributes(inv.Attributes, new ResolveOpts(codeContext, true));
+ ResolveExpression(inv.E, new ResolveOpts(codeContext, true));
+ Contract.Assert(inv.E.Type != null); // follows from postcondition of ResolveExpression
+ if (fvs != null) {
+ Translator.ComputeFreeVariables(inv.E, fvs);
+ }
+ ConstrainTypes(inv.E.Type, Type.Bool, inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type);
+ }
+
+ ResolveAttributes(decreases.Attributes, new ResolveOpts(codeContext, true));
+ foreach (Expression e in decreases.Expressions) {
+ ResolveExpression(e, new ResolveOpts(codeContext, true));
+ if (e is WildcardExpr) {
+ if (!codeContext.AllowsNontermination && !DafnyOptions.O.Dafnycc) {
+ reporter.Error(MessageSource.Resolver, e, "a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating");
}
}
- s.IsGhost = bodyIsSpecOnly;
+ // any type is fine
+ }
+
+ ResolveAttributes(modifies.Attributes, new ResolveOpts(codeContext, true));
+ if (modifies.Expressions != null) {
+ foreach (FrameExpression fe in modifies.Expressions) {
+ ResolveFrameExpression(fe, false, codeContext);
+ if (fvs != null) {
+ Translator.ComputeFreeVariables(fe.E, fvs);
+ }
+ }
+ }
+ }
+
+ void ResolveMatchStmt(Statement stmt, ICodeContext codeContext) {
+ MatchStmt s = (MatchStmt)stmt;
+ DesugarMatchStmtWithTupleExpression(s);
- ISet<string> memberNamesUsed = new HashSet<string>();
- foreach (MatchCaseStmt mc in s.Cases) {
- DatatypeCtor ctor = null;
- if (ctors != null) {
- Contract.Assert(dtd != null);
- if (!ctors.TryGetValue(mc.Id, out ctor)) {
- Error(mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name);
+ ResolveExpression(s.Source, new ResolveOpts(codeContext, true));
+ Contract.Assert(s.Source.Type != null); // follows from postcondition of ResolveExpression
+ UserDefinedType sourceType = null;
+ DatatypeDecl dtd = null;
+ if (s.Source.Type.IsDatatype) {
+ sourceType = (UserDefinedType)s.Source.Type.NormalizeExpand();
+ dtd = cce.NonNull((DatatypeDecl)sourceType.ResolvedClass);
+ }
+ var subst = new Dictionary<TypeParameter, Type>();
+ Dictionary<string, DatatypeCtor> ctors;
+ if (dtd == null) {
+ reporter.Error(MessageSource.Resolver, s.Source, "the type of the match source expression must be a datatype (instead found {0})", s.Source.Type);
+ ctors = null;
+ } else {
+ Contract.Assert(sourceType != null); // dtd and sourceType are set together above
+ ctors = datatypeCtors[dtd];
+ Contract.Assert(ctors != null); // dtd should have been inserted into datatypeCtors during a previous resolution stage
+
+ // build the type-parameter substitution map for this use of the datatype
+ for (int i = 0; i < dtd.TypeArgs.Count; i++) {
+ subst.Add(dtd.TypeArgs[i], sourceType.TypeArgs[i]);
+ }
+ }
+
+ // convert CasePattern in MatchCaseExpr to BoundVar and flatten the MatchCaseExpr.
+ List<Tuple<CasePattern, BoundVar>> patternSubst = new List<Tuple<CasePattern, BoundVar>>();
+ if (dtd != null) {
+ DesugarMatchCaseStmt(s, dtd, patternSubst, codeContext);
+ }
+
+ ISet<string> memberNamesUsed = new HashSet<string>();
+ foreach (MatchCaseStmt mc in s.Cases) {
+ DatatypeCtor ctor = null;
+ if (ctors != null) {
+ Contract.Assert(dtd != null);
+ if (!ctors.TryGetValue(mc.Id, out ctor)) {
+ reporter.Error(MessageSource.Resolver, mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name);
+ } else {
+ Contract.Assert(ctor != null); // follows from postcondition of TryGetValue
+ mc.Ctor = ctor;
+ if (ctor.Formals.Count != mc.Arguments.Count) {
+ reporter.Error(MessageSource.Resolver, mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count);
+ }
+ if (memberNamesUsed.Contains(mc.Id)) {
+ reporter.Error(MessageSource.Resolver, mc.tok, "member {0} appears in more than one case", mc.Id);
} else {
- Contract.Assert(ctor != null); // follows from postcondition of TryGetValue
- mc.Ctor = ctor;
- if (ctor.Formals.Count != mc.Arguments.Count) {
- Error(mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count);
- }
- if (memberNamesUsed.Contains(mc.Id)) {
- Error(mc.tok, "member {0} appears in more than one case", mc.Id);
- } else {
- memberNamesUsed.Add(mc.Id); // add mc.Id to the set of names used
- }
+ memberNamesUsed.Add(mc.Id); // add mc.Id to the set of names used
}
}
- scope.PushMarker();
- int i = 0;
+ }
+ scope.PushMarker();
+ int i = 0;
+ if (mc.Arguments != null) {
foreach (BoundVar v in mc.Arguments) {
- if (!scope.Push(v.Name, v)) {
- Error(v, "Duplicate parameter name: {0}", v.Name);
- }
+ scope.Push(v.Name, v);
ResolveType(v.tok, v.Type, codeContext, ResolveTypeOptionEnum.InferTypeProxies, null);
if (ctor != null && i < ctor.Formals.Count) {
Formal formal = ctor.Formals[i];
Type st = SubstType(formal.Type, subst);
- if (!UnifyTypes(v.Type, st)) {
- Error(stmt, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st);
- }
+ ConstrainTypes(v.Type, st, stmt, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st);
v.IsGhost = formal.IsGhost;
+
+ // update the type of the boundvars in the MatchCaseToken
+ if (v.tok is MatchCaseToken) {
+ MatchCaseToken mt = (MatchCaseToken)v.tok;
+ foreach (Tuple<IToken, BoundVar, bool> entry in mt.varList) {
+ UnifyTypes(entry.Item2.Type, v.Type); // TODO: What if this unification fails? Can it? --KRML
+ }
+ }
}
i++;
}
+ }
+ foreach (Statement ss in mc.Body) {
+ ResolveStatement(ss, codeContext);
+ }
+ // substitute body to replace the case pat with v. This needs to happen
+ // after the body is resolved so we can scope the bv correctly.
+ if (patternSubst.Count > 0) {
+ MatchCaseExprSubstituteCloner cloner = new MatchCaseExprSubstituteCloner(patternSubst);
+ List<Statement> list = new List<Statement>();
foreach (Statement ss in mc.Body) {
- ResolveStatement(ss, bodyIsSpecOnly, codeContext);
+ Statement clone = cloner.CloneStmt(ss);
+ // resolve it again since we just cloned it.
+ ResolveStatement(clone, codeContext);
+ list.Add(clone);
}
- scope.PopMarker();
+ mc.UpdateBody(list);
}
- if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) {
- // We could complain about the syntactic omission of constructors:
- // Error(stmt, "match statement does not cover all constructors");
- // but instead we let the verifier do a semantic check.
- // So, for now, record the missing constructors:
- foreach (var ctr in dtd.Ctors) {
- if (!memberNamesUsed.Contains(ctr.Name)) {
- s.MissingCases.Add(ctr);
+
+ scope.PopMarker();
+ }
+ if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) {
+ // We could complain about the syntactic omission of constructors:
+ // reporter.Error(MessageSource.Resolver, stmt, "match statement does not cover all constructors");
+ // but instead we let the verifier do a semantic check.
+ // So, for now, record the missing constructors:
+ foreach (var ctr in dtd.Ctors) {
+ if (!memberNamesUsed.Contains(ctr.Name)) {
+ s.MissingCases.Add(ctr);
+ }
+ }
+ Contract.Assert(memberNamesUsed.Count + s.MissingCases.Count == dtd.Ctors.Count);
+ }
+ }
+
+ /*
+ * Convert
+ * match (x, y)
+ * case (Zero, _) => Zero
+ * case (Suc(_), Zero) => x
+ * case (Suc(a), Suc(b)) => minus(a, b)
+ * To:
+ * match x
+ * case Zero => match y (originalToken)
+ * case _ => zero
+ * case Suc(_) => match y (AutoGeneratedToken)
+ * case Zero => x
+ * case Suc(a) => match y (AutoGeneratedToken)
+ * case (b) => minus(a,b)
+ */
+ void DesugarMatchStmtWithTupleExpression(MatchStmt me) {
+ // (x, y) is treated as a 2-tuple constructor
+ if (me.Source is DatatypeValue) {
+ var e = (DatatypeValue)me.Source;
+ if (e.Arguments.Count < 1) {
+ reporter.Error(MessageSource.Resolver, me.Tok, "match source tuple needs at least 1 argument");
+ } else {
+ Expression source = e.Arguments[0];
+ List<MatchCaseStmt> cases = new List<MatchCaseStmt>();
+ // only keep the token for the first appearance, use autogenerated for the rest, otherwise more than one hovertext
+ // will show up in the IDE.
+ bool keepOrigToken = true;
+ foreach (MatchCaseStmt mc in me.Cases) {
+ if (mc.CasePatterns == null || mc.CasePatterns.Count != e.Arguments.Count) {
+ reporter.Error(MessageSource.Resolver, mc.tok, "case arguments count does not match source arguments count");
+ } else {
+ CasePattern cp = mc.CasePatterns[0];
+ List<CasePattern> patterns;
+ if (cp.Arguments != null) {
+ patterns = cp.Arguments;
+ } else {
+ patterns = new List<CasePattern>();
+ }
+
+ List<Statement> body = mc.Body;
+ for (int i = e.Arguments.Count; 1 <= --i; ) {
+ // others go into the body
+ body = CreateMatchCaseStmtBody(me.Tok, e.Arguments[i], mc.CasePatterns[i], body, keepOrigToken);
+ }
+ cases.Add(new MatchCaseStmt(cp.tok, cp.Id, patterns, body));
+ keepOrigToken = false;
}
}
- Contract.Assert(memberNamesUsed.Count + s.MissingCases.Count == dtd.Ctors.Count);
+ me.UpdateSource(source);
+ me.UpdateCases(cases);
}
- if (!s.IsGhost) {
- s.IsGhost = s.Cases.All(cs => cs.Body.All(ss => ss.IsGhost));
+ }
+ }
+
+ List<Statement> CreateMatchCaseStmtBody(Boogie.IToken tok, Expression source, CasePattern cp, List<Statement> body, bool keepToken) {
+ List<MatchCaseStmt> cases = new List<MatchCaseStmt>();
+ List<CasePattern> patterns;
+ if (cp.Var != null) {
+ var bv = cp.Var;
+ if (LocalVariable.HasWildcardName(bv)) {
+ return body;
+ } else {
+ patterns = new List<CasePattern>();
}
+ } else {
+ patterns = cp.Arguments;
+ }
+ cases.Add(new MatchCaseStmt(cp.tok, cp.Id, patterns, body));
+ if (!keepToken) {
+ AutoGeneratedTokenCloner cloner = new AutoGeneratedTokenCloner();
+ source = cloner.CloneExpr(source);
+ }
+ List<Statement> list = new List<Statement>();
+ // endTok??
+ list.Add(new MatchStmt(tok, tok, source, cases, false));
+ return list;
+ }
+
+
+ /*
+ * Convert
+ * match xs
+ * case Cons(y, Cons(z, zs)) => last(Cons(z, zs))
+ * case Cons(y, Nil) => y
+ * To
+ * match xs
+ * case Cons(y, ys) => match ys
+ * case Nil => y
+ * case Cons(z, zs) => last(ys)
+ */
+ void DesugarMatchCaseStmt(MatchStmt s, DatatypeDecl dtd, List<Tuple<CasePattern, BoundVar>> patterns, ICodeContext codeContext) {
+ Contract.Assert(dtd != null);
+ Dictionary<string, DatatypeCtor> ctors = datatypeCtors[dtd];
+ if (ctors == null) {
+ // there is no constructor, no need to desugar
+ return;
+ }
- } else if (stmt is SkeletonStatement) {
- var s = (SkeletonStatement)stmt;
- Error(s.Tok, "skeleton statements are allowed only in refining methods");
- // nevertheless, resolve the underlying statement; hey, why not
- if (s.S != null) {
- ResolveStatement(s.S, specContextOnly, codeContext);
+ Type type = new InferredTypeProxy();
+ string name = FreshTempVarName("_mc#", codeContext);
+ foreach (MatchCaseStmt mc in s.Cases) {
+ if (mc.Arguments != null) {
+ // already desugared. This happens during the second pass resolver after cloning.
+ Contract.Assert(mc.CasePatterns == null);
+ return;
+ }
+
+ BoundVar sourceVar = new BoundVar(new MatchCaseToken(s.Tok), name, type);
+ Contract.Assert(mc.Arguments == null);
+ Contract.Assert(mc.CasePatterns != null);
+ Contract.Assert(ctors != null);
+ DatatypeCtor ctor = null;
+
+ if (ctors.TryGetValue(mc.Id, out ctor)) {
+ scope.PushMarker();
+ foreach (CasePattern pat in mc.CasePatterns) {
+ FindDuplicateIdentifier(pat, ctors, true);
+ }
+ List<BoundVar> arguments = new List<BoundVar>();
+ foreach (CasePattern pat in mc.CasePatterns) {
+ if (pat.Var != null) {
+ BoundVar v = pat.Var;
+ arguments.Add(v);
+ } else {
+ DesugarMatchCasePattern(mc, pat, sourceVar);
+ patterns.Add(new Tuple<CasePattern, BoundVar>(pat, sourceVar));
+ arguments.Add(sourceVar);
+ }
+ }
+ mc.Arguments = arguments;
+ mc.CasePatterns = null;
+ scope.PopMarker();
+ }
+ }
+
+
+ List<MatchCaseStmt> newCases = new List<MatchCaseStmt>();
+
+ // need to consolidate the cases.
+ // Convert
+ // match xs
+ // case Cons(y, #mc#0) => match #mc#0
+ // case Cons((z, zs) => body
+ // case Cons(y, #mc#0) => match #mc#0
+ // case Nil => y
+ // into
+ // match xs
+ // case Cons(y, #mc#0) => match #mc#0
+ // case Cons((z, zs) => body
+ // case Nil => y
+ bool thingsChanged = false;
+ Dictionary<string, MatchCaseStmt> caseMap = new Dictionary<string, MatchCaseStmt>();
+ List<MatchCaseStmt> mcWithWildCard = new List<MatchCaseStmt>();
+ foreach (MatchCaseStmt mc in s.Cases) {
+ // check each CasePattern to see if it has wildcard.
+ if (CaseExprHasWildCard(mc)) {
+ mcWithWildCard.Add(mc);
+ } else {
+ thingsChanged |= CombineMatchCaseStmt(mc, newCases, caseMap, codeContext);
+ }
+ }
+
+ foreach (MatchCaseStmt mc in mcWithWildCard) {
+ // now process with cases with wildcard
+ thingsChanged |= CombineMatchCaseStmt(mc, newCases, caseMap, codeContext);
+ }
+
+ if (thingsChanged) {
+ s.UpdateCases(newCases);
+ }
+ }
+
+ void FindDuplicateIdentifier(CasePattern pat, Dictionary<string, DatatypeCtor> ctors, bool topLevel) {
+ Contract.Assert(ctors != null);
+ DatatypeCtor ctor = null;
+ // Find the constructor in the given datatype
+ // If what was parsed was just an identifier, we will interpret it as a datatype constructor, if possible
+ if (pat.Var == null || (pat.Var != null && pat.Var.Type is TypeProxy)) {
+ if (ctors.TryGetValue(pat.Id, out ctor)) {
+ pat.Ctor = ctor;
+ pat.Var = null;
+ }
+ }
+ if (pat.Var != null) {
+ BoundVar v = pat.Var;
+ if (topLevel) {
+ ScopePushAndReport(scope, v, "parameter");
+ } else {
+ // For cons(a, const(b, c)):
+ // this handles check to see if 'b' or 'c' is duplicate with 'a',
+ // the duplication check between 'b' and 'c' is handled in the desugared
+ // form (to avoid reporting the same error twice), that is why we don't
+ // push 'b' and 'c' onto the scope, only find.
+ if (scope.FindInCurrentScope(v.Name) != null) {
+ reporter.Error(MessageSource.Resolver, v, "Duplicate parameter name: {0}", v.Name);
+ }
}
} else {
- Contract.Assert(false); throw new cce.UnreachableException();
+ if (pat.Arguments != null) {
+ foreach (CasePattern cp in pat.Arguments) {
+ FindDuplicateIdentifier(cp, ctors, false);
+ }
+ }
+ }
+ }
+
+ void DesugarMatchCasePattern(MatchCaseStmt mc, CasePattern pat, BoundVar v) {
+ // convert
+ // case Cons(y, Cons(z, zs)) => body
+ // to
+ // case Cons(y, #mc#) => match #mc#
+ // case Cons(z, zs) => body
+
+ Expression source = new NameSegment(new AutoGeneratedToken(pat.tok), v.Name, null);
+ List<MatchCaseStmt> cases = new List<MatchCaseStmt>();
+ cases.Add(new MatchCaseStmt(pat.tok, pat.Id, pat.Arguments == null ? new List<CasePattern>() : pat.Arguments, mc.Body));
+ List<Statement> list = new List<Statement>();
+ // endTok??
+ list.Add(new MatchStmt(pat.tok, pat.tok, source, cases, false));
+ mc.UpdateBody(list);
+ }
+
+ bool CombineMatchCaseStmt(MatchCaseStmt mc, List<MatchCaseStmt> newCases, Dictionary<string, MatchCaseStmt> caseMap, ICodeContext codeContext) {
+ bool thingsChanged = false;
+ MatchCaseStmt old_mc;
+ if (caseMap.TryGetValue(mc.Id, out old_mc)) {
+ // already has a case with the same ctor, try to consolidate the body.
+ List<Statement> oldBody = old_mc.Body;
+ List<Statement> body = mc.Body;
+ if ((oldBody.Count == 1) && (oldBody[0] is MatchStmt)
+ && (body.Count == 1) && (body[0] is MatchStmt)) {
+ // both only have on statement and the statement is MatchStmt
+ if (SameMatchCaseStmt(old_mc, mc, codeContext)) {
+ MatchStmt old = (MatchStmt)old_mc.Body[0];
+ MatchStmt current = (MatchStmt)mc.Body[0];
+ foreach (MatchCaseStmt c in current.Cases) {
+ old.Cases.Add(c);
+ }
+ // add the token from mc to old_mc so the identifiers will show correctly in the IDE
+ List<BoundVar> arguments = new List<BoundVar>();
+ Contract.Assert(old_mc.Arguments.Count == mc.Arguments.Count);
+ for (int i = 0; i < old_mc.Arguments.Count; i++) {
+ var bv = old_mc.Arguments[i];
+ MatchCaseToken mcToken;
+ if (!(bv.tok is MatchCaseToken)) {
+ // create a MatchCaseToken
+ mcToken = new MatchCaseToken(bv.tok);
+ // clone the bv but with the MatchCaseToken
+ var bvNew = new BoundVar(mcToken, bv.Name, bv.Type);
+ bvNew.IsGhost = bv.IsGhost;
+ arguments.Add(bvNew);
+ } else {
+ mcToken = (MatchCaseToken)bv.tok;
+ arguments.Add(bv);
+ }
+ mcToken.AddVar(bv.tok, bv, true);
+ mcToken.AddVar(mc.Arguments[i].tok, mc.Arguments[i], true);
+ }
+ old_mc.Arguments = arguments;
+ thingsChanged = true;
+ }
+ } else {
+ // duplicate cases, do nothing for now. The error will be reported during resolving
+ }
+ } else {
+ // it is a new case.
+ newCases.Add(mc);
+ caseMap.Add(mc.Id, mc);
+ }
+ return thingsChanged;
+ }
+
+ bool SameMatchCaseStmt(MatchCaseStmt one, MatchCaseStmt other, ICodeContext codeContext) {
+ // this method is called after all the CasePattern in the match cases are converted
+ // into BoundVars.
+ Contract.Assert(one.CasePatterns == null && one.Arguments != null);
+ Contract.Assert(other.CasePatterns == null && other.Arguments != null);
+ // In order to combine the two match cases, the bodies need to be a MatchExpr and
+ // the arguments and the source of the body are the same.
+ // We do string equals since they should be in the same scope.
+ if (one.Arguments.Count != other.Arguments.Count) {
+ return false;
+ }
+ List<Statement> body1 = one.Body;
+ List<Statement> body2 = other.Body;
+ if ((body1.Count != 1) || (body2.Count != 1)) {
+ return false;
+ }
+ if (!(body1[0] is MatchStmt) || !(body2[0] is MatchStmt)) {
+ return false;
+ }
+ var source1 = ((MatchStmt)body1[0]).Source;
+ var source2 = ((MatchStmt)body2[0]).Source;
+ if (!(source1 is NameSegment) || !(source2 is NameSegment)) {
+ return false;
+ }
+ if (!((NameSegment)source1).Name.Equals(((NameSegment)source2).Name)) {
+ return false;
+ }
+ for (int i = 0; i < one.Arguments.Count; i++) {
+ BoundVar bv1 = one.Arguments[i];
+ BoundVar bv2 = other.Arguments[i];
+ if (!LocalVariable.HasWildcardName(bv1) && !LocalVariable.HasWildcardName(bv2)) {
+ if (!bv1.Name.Equals(bv2.Name)) {
+ // need to substitute bv2 with bv1 in the matchstmt body
+ // what if match body already has the bv?? need to make a new bv
+ Type type = new InferredTypeProxy();
+ string name = FreshTempVarName("_mc#", codeContext);
+ BoundVar bv = new BoundVar(new MatchCaseToken(one.tok), name, type);
+ ((MatchCaseToken)bv.tok).AddVar(bv1.tok, bv1, true);
+ ((MatchCaseToken)bv.tok).AddVar(bv2.tok, bv2, true);
+ SubstituteMatchCaseBoundVar(one, bv1, bv);
+ SubstituteMatchCaseBoundVar(other, bv2, bv);
+ }
+ }
+ }
+ return true;
+ }
+
+ void SubstituteMatchCaseBoundVar(MatchCaseStmt mc, BoundVar oldBv, BoundVar newBv) {
+ List<BoundVar> arguments = new List<BoundVar>();
+ for (int i = 0; i < mc.Arguments.Count; i++) {
+ BoundVar bv = mc.Arguments[i];
+ if (bv == oldBv) {
+ arguments.Add(newBv);
+ } else {
+ arguments.Add(bv);
+ }
}
+ mc.Arguments = arguments;
+
+ // substitue the oldBv with newBv in the body
+ MatchCaseExprSubstituteCloner cloner = new MatchCaseExprSubstituteCloner(oldBv, newBv);
+ List<Statement> list = new List<Statement>();
+ foreach (Statement ss in mc.Body) {
+ Statement clone = cloner.CloneStmt(ss);
+ list.Add(clone);
+ }
+ mc.UpdateBody(list);
}
void FillInDefaultLoopDecreases(LoopStmt loopStmt, Expression guard, List<Expression> theDecreases, ICallable enclosingMethod) {
@@ -5346,35 +6598,24 @@ namespace Microsoft.Dafny
loopStmt.InferredDecreases = true;
}
if (loopStmt.InferredDecreases) {
- string s = "decreases ";
- if (theDecreases.Count != 0) {
- string sep = "";
- foreach (var d in theDecreases) {
- s += sep + Printer.ExprToString(d);
- sep = ", ";
- }
- }
- s += ";"; // always terminate with a semi-colon, even in the case of an empty decreases clause
- ReportAdditionalInformation(loopStmt.Tok, s, loopStmt.Tok.val.Length);
+ string s = "decreases " + Util.Comma(", ", theDecreases, Printer.ExprToString);
+ reporter.Info(MessageSource.Resolver, loopStmt.Tok, s);
}
}
- private void ResolveConcreteUpdateStmt(ConcreteUpdateStatement s, bool specContextOnly, ICodeContext codeContext) {
+ private void ResolveConcreteUpdateStmt(ConcreteUpdateStatement s, ICodeContext codeContext) {
Contract.Requires(codeContext != null);
// First, resolve all LHS's and expression-looking RHS's.
- int errorCountBeforeCheckingLhs = ErrorCount;
+ int errorCountBeforeCheckingLhs = reporter.Count(ErrorLevel.Error);
var update = s as UpdateStmt;
var lhsNameSet = new HashSet<string>(); // used to check for duplicate identifiers on the left (full duplication checking for references and the like is done during verification)
foreach (var lhs in s.Lhss) {
- var ec = ErrorCount;
- ResolveExpression(lhs, new ResolveOpts(codeContext, true, specContextOnly));
- if (ec == ErrorCount) {
- if (update == null && specContextOnly && !AssignStmt.LhsIsToGhost(lhs) && !codeContext.IsGhost) {
- Error(lhs, "cannot assign to non-ghost variable in a ghost context");
- }
+ var ec = reporter.Count(ErrorLevel.Error);
+ ResolveExpression(lhs, new ResolveOpts(codeContext, true));
+ if (ec == reporter.Count(ErrorLevel.Error)) {
if (lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne) {
- Error(lhs, "cannot assign to a range of array elements (try the 'forall' statement)");
+ reporter.Error(MessageSource.Resolver, lhs, "cannot assign to a range of array elements (try the 'forall' statement)");
}
}
}
@@ -5382,18 +6623,18 @@ namespace Microsoft.Dafny
// Resolve RHSs
if (update == null) {
var suchThat = (AssignSuchThatStmt)s; // this is the other possible subclass
- ResolveAssignSuchThatStmt(suchThat, specContextOnly, codeContext);
+ ResolveAssignSuchThatStmt(suchThat, codeContext);
} else {
- ResolveUpdateStmt(update, specContextOnly, codeContext, errorCountBeforeCheckingLhs);
+ ResolveUpdateStmt(update, codeContext, errorCountBeforeCheckingLhs);
}
- ResolveAttributes(s.Attributes, new ResolveOpts(codeContext, true, true));
+ ResolveAttributes(s.Attributes, new ResolveOpts(codeContext, true));
}
/// <summary>
/// Resolve the RHSs and entire UpdateStmt (LHSs should already have been checked by the caller).
/// errorCountBeforeCheckingLhs is passed in so that this method can determined if any resolution errors were found during
/// LHS or RHS checking, because only if no errors were found is update.ResolvedStmt changed.
/// </summary>
- private void ResolveUpdateStmt(UpdateStmt update, bool specContextOnly, ICodeContext codeContext, int errorCountBeforeCheckingLhs) {
+ private void ResolveUpdateStmt(UpdateStmt update, ICodeContext codeContext, int errorCountBeforeCheckingLhs) {
Contract.Requires(update != null);
Contract.Requires(codeContext != null);
IToken firstEffectfulRhs = null;
@@ -5403,7 +6644,7 @@ namespace Microsoft.Dafny
bool isEffectful;
if (rhs is TypeRhs) {
var tr = (TypeRhs)rhs;
- ResolveTypeRhs(tr, update, specContextOnly, codeContext);
+ ResolveTypeRhs(tr, update, codeContext);
isEffectful = tr.InitCall != null;
} else if (rhs is HavocRhs) {
isEffectful = false;
@@ -5411,17 +6652,11 @@ namespace Microsoft.Dafny
var er = (ExprRhs)rhs;
if (er.Expr is ApplySuffix) {
var a = (ApplySuffix)er.Expr;
- // Note, in the following line, the dontCareAboutCompilation could be more precise. It could be computed as in the else
- // branch if the ApplySuffix is really just the RHS of an assignment. However, if "update" is really a call statement,
- // then we should not let the LHS influence the call to ResolveApplySuffix. Unfortunately, we don't know which case
- // we're in until ResolveApplySuffix has returned (where a non-null cRhs indicates that "update" is a call statement).
- // So, we'll be conservative and will simply pass in specContextOnly here.
- var cRhs = ResolveApplySuffix(a, new ResolveOpts(codeContext, true, specContextOnly/*see note on previous line*/), true);
+ var cRhs = ResolveApplySuffix(a, new ResolveOpts(codeContext, true), true);
isEffectful = cRhs != null;
methodCallInfo = methodCallInfo ?? cRhs;
} else {
- var dontCareAboutCompilation = specContextOnly || (j < update.Lhss.Count && AssignStmt.LhsIsToGhost(update.Lhss[j]));
- ResolveExpression(er.Expr, new ResolveOpts(codeContext, true, dontCareAboutCompilation));
+ ResolveExpression(er.Expr, new ResolveOpts(codeContext, true));
isEffectful = false;
}
}
@@ -5435,10 +6670,10 @@ namespace Microsoft.Dafny
if (firstEffectfulRhs == null) {
if (update.Lhss.Count == 0) {
Contract.Assert(update.Rhss.Count == 1); // guaranteed by the parser
- Error(update, "expected method call, found expression");
+ reporter.Error(MessageSource.Resolver, update, "expected method call, found expression");
} else if (update.Lhss.Count != update.Rhss.Count) {
- Error(update, "the number of left-hand sides ({0}) and right-hand sides ({1}) must match for a multi-assignment", update.Lhss.Count, update.Rhss.Count);
- } else if (ErrorCount == errorCountBeforeCheckingLhs) {
+ reporter.Error(MessageSource.Resolver, update, "the number of left-hand sides ({0}) and right-hand sides ({1}) must match for a multi-assignment", update.Lhss.Count, update.Rhss.Count);
+ } else if (reporter.Count(ErrorLevel.Error) == errorCountBeforeCheckingLhs) {
// add the statements here in a sequence, but don't use that sequence later for translation (instead, should translate properly as multi-assignment)
for (int i = 0; i < update.Lhss.Count; i++) {
var a = new AssignStmt(update.Tok, update.EndTok, update.Lhss[i].Resolved, update.Rhss[i]);
@@ -5448,19 +6683,19 @@ namespace Microsoft.Dafny
} else if (update.CanMutateKnownState) {
if (1 < update.Rhss.Count) {
- Error(firstEffectfulRhs, "cannot have effectful parameter in multi-return statement.");
+ reporter.Error(MessageSource.Resolver, firstEffectfulRhs, "cannot have effectful parameter in multi-return statement.");
} else { // it might be ok, if it is a TypeRhs
Contract.Assert(update.Rhss.Count == 1);
if (methodCallInfo != null) {
- Error(methodCallInfo.Tok, "cannot have method call in return statement.");
+ reporter.Error(MessageSource.Resolver, methodCallInfo.Tok, "cannot have method call in return statement.");
} else {
// we have a TypeRhs
Contract.Assert(update.Rhss[0] is TypeRhs);
var tr = (TypeRhs)update.Rhss[0];
Contract.Assert(tr.InitCall != null); // there were effects, so this must have been a call.
if (tr.CanAffectPreviouslyKnownExpressions) {
- Error(tr.Tok, "can only have initialization methods which modify at most 'this'.");
- } else if (ErrorCount == errorCountBeforeCheckingLhs) {
+ reporter.Error(MessageSource.Resolver, tr.Tok, "can only have initialization methods which modify at most 'this'.");
+ } else if (reporter.Count(ErrorLevel.Error) == errorCountBeforeCheckingLhs) {
var a = new AssignStmt(update.Tok, update.EndTok, update.Lhss[0].Resolved, tr);
update.ResolvedStatements.Add(a);
}
@@ -5470,17 +6705,17 @@ namespace Microsoft.Dafny
} else {
// if there was an effectful RHS, that must be the only RHS
if (update.Rhss.Count != 1) {
- Error(firstEffectfulRhs, "an update statement is allowed an effectful RHS only if there is just one RHS");
+ reporter.Error(MessageSource.Resolver, firstEffectfulRhs, "an update statement is allowed an effectful RHS only if there is just one RHS");
} else if (methodCallInfo == null) {
// must be a single TypeRhs
if (update.Lhss.Count != 1) {
Contract.Assert(2 <= update.Lhss.Count); // the parser allows 0 Lhss only if the whole statement looks like an expression (not a TypeRhs)
- Error(update.Lhss[1].tok, "the number of left-hand sides ({0}) and right-hand sides ({1}) must match for a multi-assignment", update.Lhss.Count, update.Rhss.Count);
- } else if (ErrorCount == errorCountBeforeCheckingLhs) {
+ reporter.Error(MessageSource.Resolver, update.Lhss[1].tok, "the number of left-hand sides ({0}) and right-hand sides ({1}) must match for a multi-assignment", update.Lhss.Count, update.Rhss.Count);
+ } else if (reporter.Count(ErrorLevel.Error) == errorCountBeforeCheckingLhs) {
var a = new AssignStmt(update.Tok, update.EndTok, update.Lhss[0].Resolved, update.Rhss[0]);
update.ResolvedStatements.Add(a);
}
- } else if (ErrorCount == errorCountBeforeCheckingLhs) {
+ } else if (reporter.Count(ErrorLevel.Error) == errorCountBeforeCheckingLhs) {
// a call statement
var resolvedLhss = new List<Expression>();
foreach (var ll in update.Lhss) {
@@ -5492,12 +6727,11 @@ namespace Microsoft.Dafny
}
foreach (var a in update.ResolvedStatements) {
- ResolveStatement(a, specContextOnly, codeContext);
+ ResolveStatement(a, codeContext);
}
- update.IsGhost = update.ResolvedStatements.TrueForAll(ss => ss.IsGhost);
}
- private void ResolveAssignSuchThatStmt(AssignSuchThatStmt s, bool specContextOnly, ICodeContext codeContext) {
+ private void ResolveAssignSuchThatStmt(AssignSuchThatStmt s, ICodeContext codeContext) {
Contract.Requires(s != null);
Contract.Requires(codeContext != null);
@@ -5507,80 +6741,57 @@ namespace Microsoft.Dafny
foreach (var lhs in s.Lhss) {
var ide = lhs.Resolved as IdentifierExpr;
if (ide == null) {
- Error(lhs, "the assign-such-that statement currently only supports local-variable LHSs");
+ reporter.Error(MessageSource.Resolver, lhs, "the assign-such-that statement currently only supports local-variable LHSs");
} else {
varLhss.Add(ide.Var);
}
}
}
- s.IsGhost = s.Lhss.TrueForAll(AssignStmt.LhsIsToGhost);
- var ec = ErrorCount;
- ResolveExpression(s.Expr, new ResolveOpts(codeContext, true, specContextOnly));
- if (ec == ErrorCount && !s.IsGhost && s.AssumeToken == null && !specContextOnly) {
- CheckIsNonGhost(s.Expr);
-
- var missingBounds = new List<IVariable>();
- CheckTypeInference(s.Expr); // we need to resolve operators before the call to DiscoverBoundsAux
- var allBounds = DiscoverBoundsAux(s.Tok, varLhss, s.Expr, true, true, true, missingBounds);
- if (missingBounds.Count != 0) {
- s.MissingBounds = missingBounds; // so that an error message can be produced during compilation
- } else {
- Contract.Assert(allBounds != null);
- s.Bounds = new List<ComprehensionExpr.BoundedPool>();
- foreach (var pair in allBounds) {
- Contract.Assert(1 <= pair.Item2.Count);
- // TODO: The following could be improved by picking the bound that is most likely to give rise to an efficient compiled program
- s.Bounds.Add(pair.Item2[0]);
- }
- }
- }
+ var ec = reporter.Count(ErrorLevel.Error);
+ ResolveExpression(s.Expr, new ResolveOpts(codeContext, true));
+ ConstrainTypes(s.Expr.Type, Type.Bool, s.Expr, "type of RHS of assign-such-that statement must be boolean (got {0})", s.Expr.Type);
}
- bool ResolveAlternatives(List<GuardedAlternative> alternatives, bool specContextOnly, AlternativeLoopStmt loopToCatchBreaks, ICodeContext codeContext) {
+ void ResolveAlternatives(List<GuardedAlternative> alternatives, AlternativeLoopStmt loopToCatchBreaks, ICodeContext codeContext) {
Contract.Requires(alternatives != null);
Contract.Requires(codeContext != null);
- bool isGhost = specContextOnly;
- // first, resolve the guards, which tells us whether or not the entire statement is a ghost statement
+ // first, resolve the guards
foreach (var alternative in alternatives) {
- int prevErrorCount = ErrorCount;
- ResolveExpression(alternative.Guard, new ResolveOpts(codeContext, true, specContextOnly));
+ int prevErrorCount = reporter.Count(ErrorLevel.Error);
+ ResolveExpression(alternative.Guard, new ResolveOpts(codeContext, true));
Contract.Assert(alternative.Guard.Type != null); // follows from postcondition of ResolveExpression
- bool successfullyResolved = ErrorCount == prevErrorCount;
- if (!UnifyTypes(alternative.Guard.Type, Type.Bool)) {
- Error(alternative.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, alternative.Guard.Type);
- }
- if (!specContextOnly && successfullyResolved) {
- isGhost = isGhost || UsesSpecFeatures(alternative.Guard);
- }
+ bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount;
+ ConstrainTypes(alternative.Guard.Type, Type.Bool, alternative.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, alternative.Guard.Type);
}
if (loopToCatchBreaks != null) {
loopStack.Add(loopToCatchBreaks); // push
- if (loopToCatchBreaks.Labels == null) { // otherwise, "loopToCatchBreak" is already in "inSpecOnlyContext" map
- inSpecOnlyContext.Add(loopToCatchBreaks, specContextOnly);
- }
}
foreach (var alternative in alternatives) {
scope.PushMarker();
+ if (alternative.IsExistentialGuard) {
+ var exists = (ExistsExpr)alternative.Guard;
+ foreach (var v in exists.BoundVars) {
+ ScopePushAndReport(scope, v, "bound-variable");
+ }
+ }
foreach (Statement ss in alternative.Body) {
- ResolveStatement(ss, isGhost, codeContext);
+ ResolveStatement(ss, codeContext);
}
scope.PopMarker();
}
if (loopToCatchBreaks != null) {
loopStack.RemoveAt(loopStack.Count - 1); // pop
}
-
- return isGhost;
}
/// <summary>
/// Resolves the given call statement.
/// Assumes all LHSs have already been resolved (and checked for mutability).
/// </summary>
- void ResolveCallStmt(CallStmt s, bool specContextOnly, ICodeContext codeContext, Type receiverType) {
+ void ResolveCallStmt(CallStmt s, ICodeContext codeContext, Type receiverType) {
Contract.Requires(s != null);
Contract.Requires(codeContext != null);
bool isInitCall = receiverType != null;
@@ -5588,11 +6799,7 @@ namespace Microsoft.Dafny
var callee = s.Method;
Contract.Assert(callee != null); // follows from the invariant of CallStmt
if (!isInitCall && callee is Constructor) {
- Error(s, "a constructor is allowed to be called only when an object is being allocated");
- }
- s.IsGhost = callee.IsGhost;
- if (specContextOnly && !callee.IsGhost) {
- Error(s, "only ghost methods can be called from this context");
+ reporter.Error(MessageSource.Resolver, s, "a constructor is allowed to be called only when an object is being allocated");
}
// resolve left-hand sides
@@ -5600,79 +6807,46 @@ namespace Microsoft.Dafny
Contract.Assume(lhs.Type != null); // a sanity check that LHSs have already been resolved
}
// resolve arguments
- if (!s.IsGhost && s.Receiver.WasResolved()) {
- CheckIsNonGhost(s.Receiver);
- }
int j = 0;
foreach (Expression e in s.Args) {
- bool allowGhost = s.IsGhost || callee.Ins.Count <= j || callee.Ins[j].IsGhost;
- var ec = ErrorCount;
- ResolveExpression(e, new ResolveOpts(codeContext, true, allowGhost));
- if (ec == ErrorCount && !allowGhost) {
- CheckIsNonGhost(e);
- }
+ ResolveExpression(e, new ResolveOpts(codeContext, true));
j++;
}
if (callee.Ins.Count != s.Args.Count) {
- Error(s, "wrong number of method arguments (got {0}, expected {1})", s.Args.Count, callee.Ins.Count);
+ reporter.Error(MessageSource.Resolver, s, "wrong number of method arguments (got {0}, expected {1})", s.Args.Count, callee.Ins.Count);
} else if (callee.Outs.Count != s.Lhs.Count) {
if (isInitCall) {
- Error(s, "a method called as an initialization method must not have any result arguments");
+ reporter.Error(MessageSource.Resolver, s, "a method called as an initialization method must not have any result arguments");
} else {
- Error(s, "wrong number of method result arguments (got {0}, expected {1})", s.Lhs.Count, callee.Outs.Count);
+ reporter.Error(MessageSource.Resolver, s, "wrong number of method result arguments (got {0}, expected {1})", s.Lhs.Count, callee.Outs.Count);
}
} else {
if (isInitCall) {
if (callee.IsStatic) {
- Error(s.Tok, "a method called as an initialization method must not be 'static'");
+ reporter.Error(MessageSource.Resolver, s.Tok, "a method called as an initialization method must not be 'static'");
}
} else if (!callee.IsStatic) {
if (!scope.AllowInstance && s.Receiver is ThisExpr) {
// The call really needs an instance, but that instance is given as 'this', which is not
// available in this context. For more details, see comment in the resolution of a
// FunctionCallExpr.
- Error(s.Receiver, "'this' is not allowed in a 'static' context");
+ reporter.Error(MessageSource.Resolver, s.Receiver, "'this' is not allowed in a 'static' context");
} else if (s.Receiver is StaticReceiverExpr) {
- Error(s.Receiver, "call to instance method requires an instance");
+ reporter.Error(MessageSource.Resolver, s.Receiver, "call to instance method requires an instance");
}
}
// type check the arguments
for (int i = 0; i < callee.Ins.Count; i++) {
Type st = SubstType(callee.Ins[i].Type, s.MethodSelect.TypeArgumentSubstitutions());
- if (!UnifyTypes(cce.NonNull(s.Args[i].Type), st)) {
- Error(s, "incorrect type of method in-parameter {0} (expected {1}, got {2})", i, st, s.Args[i].Type);
- }
+ ConstrainTypes(s.Args[i].Type, st, s, "incorrect type of method in-parameter {0} (expected {1}, got {2})", i, st, s.Args[i].Type);
}
for (int i = 0; i < callee.Outs.Count; i++) {
Type st = SubstType(callee.Outs[i].Type, s.MethodSelect.TypeArgumentSubstitutions());
var lhs = s.Lhs[i];
- if (!UnifyTypes(cce.NonNull(lhs.Type), st)) {
- Error(s, "incorrect type of method out-parameter {0} (expected {1}, got {2})", i, st, lhs.Type);
+ if (!ConstrainTypes(lhs.Type, st, s, "incorrect type of method out-parameter {0} (expected {1}, got {2})", i, st, lhs.Type)) {
} else {
var resolvedLhs = lhs.Resolved;
- if (!specContextOnly && (s.IsGhost || callee.Outs[i].IsGhost)) {
- // LHS must denote a ghost
- if (resolvedLhs is IdentifierExpr) {
- var ll = (IdentifierExpr)resolvedLhs;
- if (!ll.Var.IsGhost) {
- if (ll is AutoGhostIdentifierExpr && ll.Var is LocalVariable) {
- // the variable was actually declared in this statement, so auto-declare it as ghost
- ((LocalVariable)ll.Var).MakeGhost();
- } else {
- Error(s, "actual out-parameter {0} is required to be a ghost variable", i);
- }
- }
- } else if (resolvedLhs is MemberSelectExpr) {
- var ll = (MemberSelectExpr)resolvedLhs;
- if (!ll.Member.IsGhost) {
- Error(s, "actual out-parameter {0} is required to be a ghost field", i);
- }
- } else {
- // this is an array update, and arrays are always non-ghost
- Error(s, "actual out-parameter {0} is required to be a ghost variable", i);
- }
- }
// LHS must denote a mutable field.
CheckIsLvalue(resolvedLhs, codeContext);
}
@@ -5697,7 +6871,7 @@ namespace Microsoft.Dafny
}
}
if (Contract.Exists(callee.Decreases.Expressions, e => e is WildcardExpr) && !codeContext.AllowsNontermination) {
- Error(s.Tok, "a call to a possibly non-terminating method is allowed only if the calling method is also declared (with 'decreases *') to be possibly non-terminating");
+ reporter.Error(MessageSource.Resolver, s.Tok, "a call to a possibly non-terminating method is allowed only if the calling method is also declared (with 'decreases *') to be possibly non-terminating");
}
}
@@ -5712,30 +6886,28 @@ namespace Microsoft.Dafny
if (lhs is IdentifierExpr) {
var ll = (IdentifierExpr)lhs;
if (!ll.Var.IsMutable) {
- Error(lhs, "LHS of assignment must denote a mutable variable");
+ reporter.Error(MessageSource.Resolver, lhs, "LHS of assignment must denote a mutable variable");
}
} else if (lhs is MemberSelectExpr) {
var ll = (MemberSelectExpr)lhs;
var field = ll.Member as Field;
if (field == null || !field.IsUserMutable) {
- Error(lhs, "LHS of assignment must denote a mutable field");
+ reporter.Error(MessageSource.Resolver, lhs, "LHS of assignment must denote a mutable field");
}
} else if (lhs is SeqSelectExpr) {
var ll = (SeqSelectExpr)lhs;
- if (!UnifyTypes(ll.Seq.Type, ResolvedArrayType(ll.Seq.tok, 1, new InferredTypeProxy(), codeContext))) {
- Error(ll.Seq, "LHS of array assignment must denote an array element (found {0})", ll.Seq.Type);
- }
+ ConstrainTypes(ll.Seq.Type, ResolvedArrayType(ll.Seq.tok, 1, new InferredTypeProxy(), codeContext), ll.Seq, "LHS of array assignment must denote an array element (found {0})", ll.Seq.Type);
if (!ll.SelectOne) {
- Error(ll.Seq, "cannot assign to a range of array elements (try the 'forall' statement)");
+ reporter.Error(MessageSource.Resolver, ll.Seq, "cannot assign to a range of array elements (try the 'forall' statement)");
}
} else if (lhs is MultiSelectExpr) {
// nothing to check; this can only denote an array element
} else {
- Error(lhs, "LHS of assignment must denote a mutable variable or field");
+ reporter.Error(MessageSource.Resolver, lhs, "LHS of assignment must denote a mutable variable or field");
}
}
- void ResolveBlockStatement(BlockStmt blockStmt, bool specContextOnly, ICodeContext codeContext) {
+ void ResolveBlockStatement(BlockStmt blockStmt, ICodeContext codeContext) {
Contract.Requires(blockStmt != null);
Contract.Requires(codeContext != null);
@@ -5747,18 +6919,15 @@ namespace Microsoft.Dafny
Contract.Assert(lnode.Name != null); // LabelNode's with .Label==null are added only during resolution of the break statements with 'stmt' as their target, which hasn't happened yet
var prev = labeledStatements.Find(lnode.Name);
if (prev == ss) {
- Error(lnode.Tok, "duplicate label");
+ reporter.Error(MessageSource.Resolver, lnode.Tok, "duplicate label");
} else if (prev != null) {
- Error(lnode.Tok, "label shadows an enclosing label");
+ reporter.Error(MessageSource.Resolver, lnode.Tok, "label shadows an enclosing label");
} else {
- bool b = labeledStatements.Push(lnode.Name, ss);
- Contract.Assert(b); // since we just checked for duplicates, we expect the Push to succeed
- if (l == ss.Labels) { // add it only once
- inSpecOnlyContext.Add(ss, specContextOnly);
- }
+ var r = labeledStatements.Push(lnode.Name, ss);
+ Contract.Assert(r == Scope<Statement>.PushResult.Success); // since we just checked for duplicates, we expect the Push to succeed
}
}
- ResolveStatement(ss, specContextOnly, codeContext);
+ ResolveStatement(ss, codeContext);
labeledStatements.PopMarker();
}
}
@@ -5771,13 +6940,13 @@ namespace Microsoft.Dafny
if (stmt is PredicateStmt) {
// cool
} else if (stmt is PrintStmt) {
- Error(stmt, "print statement is not allowed inside a forall statement");
+ reporter.Error(MessageSource.Resolver, stmt, "print statement is not allowed inside a forall statement");
} else if (stmt is BreakStmt) {
// this case is checked already in the first pass through the forall-statement body, by doing so from an empty set of labeled statements and resetting the loop-stack
} else if (stmt is ReturnStmt) {
- Error(stmt, "return statement is not allowed inside a forall statement");
+ reporter.Error(MessageSource.Resolver, stmt, "return statement is not allowed inside a forall statement");
} else if (stmt is YieldStmt) {
- Error(stmt, "yield statement is not allowed inside a forall statement");
+ reporter.Error(MessageSource.Resolver, stmt, "yield statement is not allowed inside a forall statement");
} else if (stmt is AssignSuchThatStmt) {
var s = (AssignSuchThatStmt)stmt;
foreach (var lhs in s.Lhss) {
@@ -5793,15 +6962,17 @@ namespace Microsoft.Dafny
if (s.Update != null) {
CheckForallStatementBodyRestrictions(s.Update, kind);
}
+ } else if (stmt is LetStmt) {
+ // Are we fine?
} else if (stmt is AssignStmt) {
var s = (AssignStmt)stmt;
CheckForallStatementBodyLhs(s.Tok, s.Lhs.Resolved, kind);
var rhs = s.Rhs; // ExprRhs and HavocRhs are fine, but TypeRhs is not
if (rhs is TypeRhs) {
if (kind == ForallStmt.ParBodyKind.Assign) {
- Error(rhs.Tok, "new allocation not supported in forall statements");
+ reporter.Error(MessageSource.Resolver, rhs.Tok, "new allocation not supported in forall statements");
} else {
- Error(rhs.Tok, "new allocation not allowed in ghost context");
+ reporter.Error(MessageSource.Resolver, rhs.Tok, "new allocation not allowed in ghost context");
}
}
} else if (stmt is CallStmt) {
@@ -5810,14 +6981,14 @@ namespace Microsoft.Dafny
var idExpr = lhs as IdentifierExpr;
if (idExpr != null) {
if (scope.ContainsDecl(idExpr.Var)) {
- Error(stmt, "body of forall statement is attempting to update a variable declared outside the forall statement");
+ reporter.Error(MessageSource.Resolver, stmt, "body of forall statement is attempting to update a variable declared outside the forall statement");
}
} else {
- Error(stmt, "the body of the enclosing forall statement is not allowed to update heap locations");
+ reporter.Error(MessageSource.Resolver, stmt, "the body of the enclosing forall statement is not allowed to update heap locations");
}
}
if (s.Method.Mod.Expressions.Count != 0) {
- Error(stmt, "the body of the enclosing forall statement is not allowed to update heap locations, so any call must be to a method with an empty modifies clause");
+ reporter.Error(MessageSource.Resolver, stmt, "the body of the enclosing forall statement is not allowed to update heap locations, so any call must be to a method with an empty modifies clause");
}
if (!s.Method.IsGhost) {
// The reason for this restriction is that the compiler is going to omit the forall statement altogether--it has
@@ -5825,7 +6996,7 @@ namespace Microsoft.Dafny
// a method that prints something, all calls to non-ghost methods are disallowed. (Note, if this restriction
// is somehow lifted in the future, then it is still necessary to enforce s.Method.Mod.Expressions.Count != 0 for
// calls to non-ghost methods.)
- Error(s, "the body of the enclosing forall statement is not allowed to call non-ghost methods");
+ reporter.Error(MessageSource.Resolver, s, "the body of the enclosing forall statement is not allowed to call non-ghost methods");
}
} else if (stmt is BlockStmt) {
@@ -5869,7 +7040,7 @@ namespace Microsoft.Dafny
var s = (ForallStmt)stmt;
switch (s.Kind) {
case ForallStmt.ParBodyKind.Assign:
- Error(stmt, "a forall statement with heap updates is not allowed inside the body of another forall statement");
+ reporter.Error(MessageSource.Resolver, stmt, "a forall statement with heap updates is not allowed inside the body of another forall statement");
break;
case ForallStmt.ParBodyKind.Call:
case ForallStmt.ParBodyKind.Proof:
@@ -5900,10 +7071,10 @@ namespace Microsoft.Dafny
var idExpr = lhs as IdentifierExpr;
if (idExpr != null) {
if (scope.ContainsDecl(idExpr.Var)) {
- Error(tok, "body of forall statement is attempting to update a variable declared outside the forall statement");
+ reporter.Error(MessageSource.Resolver, tok, "body of forall statement is attempting to update a variable declared outside the forall statement");
}
} else if (kind != ForallStmt.ParBodyKind.Assign) {
- Error(tok, "the body of the enclosing forall statement is not allowed to update heap locations");
+ reporter.Error(MessageSource.Resolver, tok, "the body of the enclosing forall statement is not allowed to update heap locations");
}
}
@@ -5911,8 +7082,9 @@ namespace Microsoft.Dafny
/// Check that a statment is a valid hint for a calculation.
/// ToDo: generalize the part for compound statements to take a delegate?
/// </summary>
- public void CheckHintRestrictions(Statement stmt) {
+ public void CheckHintRestrictions(Statement stmt, ISet<LocalVariable> localsAllowedInUpdates) {
Contract.Requires(stmt != null);
+ Contract.Requires(localsAllowedInUpdates != null);
if (stmt is PredicateStmt) {
// cool
} else if (stmt is PrintStmt) {
@@ -5920,69 +7092,71 @@ namespace Microsoft.Dafny
} else if (stmt is BreakStmt) {
// already checked while resolving hints
} else if (stmt is ReturnStmt) {
- Error(stmt, "return statement is not allowed inside a hint");
+ reporter.Error(MessageSource.Resolver, stmt, "return statement is not allowed inside a hint");
} else if (stmt is YieldStmt) {
- Error(stmt, "yield statement is not allowed inside a hint");
+ reporter.Error(MessageSource.Resolver, stmt, "yield statement is not allowed inside a hint");
} else if (stmt is AssignSuchThatStmt) {
var s = (AssignSuchThatStmt)stmt;
foreach (var lhs in s.Lhss) {
- CheckHintLhs(s.Tok, lhs.Resolved);
+ CheckHintLhs(s.Tok, lhs.Resolved, localsAllowedInUpdates);
}
} else if (stmt is AssignStmt) {
var s = (AssignStmt)stmt;
- CheckHintLhs(s.Tok, s.Lhs.Resolved);
+ CheckHintLhs(s.Tok, s.Lhs.Resolved, localsAllowedInUpdates);
} else if (stmt is CallStmt) {
var s = (CallStmt)stmt;
if (s.Method.Mod.Expressions.Count != 0) {
- Error(stmt, "calls to methods with side-effects are not allowed inside a hint");
+ reporter.Error(MessageSource.Resolver, stmt, "calls to methods with side-effects are not allowed inside a hint");
}
} else if (stmt is UpdateStmt) {
var s = (UpdateStmt)stmt;
foreach (var ss in s.ResolvedStatements) {
- CheckHintRestrictions(ss);
+ CheckHintRestrictions(ss, localsAllowedInUpdates);
}
} else if (stmt is VarDeclStmt) {
var s = (VarDeclStmt)stmt;
+ s.Locals.Iter(local => localsAllowedInUpdates.Add(local));
if (s.Update != null) {
- CheckHintRestrictions(s.Update);
+ CheckHintRestrictions(s.Update, localsAllowedInUpdates);
}
+ } else if (stmt is LetStmt) {
+ // Are we fine?
} else if (stmt is BlockStmt) {
var s = (BlockStmt)stmt;
- scope.PushMarker();
+ var newScopeForLocals = new HashSet<LocalVariable>(localsAllowedInUpdates);
foreach (var ss in s.Body) {
- CheckHintRestrictions(ss);
+ CheckHintRestrictions(ss, newScopeForLocals);
}
- scope.PopMarker();
} else if (stmt is IfStmt) {
var s = (IfStmt)stmt;
- CheckHintRestrictions(s.Thn);
+ CheckHintRestrictions(s.Thn, localsAllowedInUpdates);
if (s.Els != null) {
- CheckHintRestrictions(s.Els);
+ CheckHintRestrictions(s.Els, localsAllowedInUpdates);
}
} else if (stmt is AlternativeStmt) {
var s = (AlternativeStmt)stmt;
foreach (var alt in s.Alternatives) {
foreach (var ss in alt.Body) {
- CheckHintRestrictions(ss);
+ CheckHintRestrictions(ss, localsAllowedInUpdates);
}
}
} else if (stmt is WhileStmt) {
var s = (WhileStmt)stmt;
if (s.Mod.Expressions != null && s.Mod.Expressions.Count != 0) {
- Error(s.Mod.Expressions[0].tok, "a while statement used inside a hint is not allowed to have a modifies clause");
+ reporter.Error(MessageSource.Resolver, s.Mod.Expressions[0].tok, "a while statement used inside a hint is not allowed to have a modifies clause");
}
if (s.Body != null) {
- CheckHintRestrictions(s.Body);
+ CheckHintRestrictions(s.Body, localsAllowedInUpdates);
}
} else if (stmt is AlternativeLoopStmt) {
var s = (AlternativeLoopStmt)stmt;
foreach (var alt in s.Alternatives) {
foreach (var ss in alt.Body) {
- CheckHintRestrictions(ss);
+ CheckHintRestrictions(ss, localsAllowedInUpdates);
}
}
@@ -5990,7 +7164,7 @@ namespace Microsoft.Dafny
var s = (ForallStmt)stmt;
switch (s.Kind) {
case ForallStmt.ParBodyKind.Assign:
- Error(stmt, "a forall statement with heap updates is not allowed inside a hint");
+ reporter.Error(MessageSource.Resolver, stmt, "a forall statement with heap updates is not allowed inside a hint");
break;
case ForallStmt.ParBodyKind.Call:
case ForallStmt.ParBodyKind.Proof:
@@ -6004,14 +7178,14 @@ namespace Microsoft.Dafny
} else if (stmt is CalcStmt) {
var s = (CalcStmt)stmt;
foreach (var h in s.Hints) {
- CheckHintRestrictions(h);
+ CheckHintRestrictions(h, new HashSet<LocalVariable>());
}
} else if (stmt is MatchStmt) {
var s = (MatchStmt)stmt;
foreach (var kase in s.Cases) {
foreach (var ss in kase.Body) {
- CheckHintRestrictions(ss);
+ CheckHintRestrictions(ss, localsAllowedInUpdates);
}
}
@@ -6020,25 +7194,24 @@ namespace Microsoft.Dafny
}
}
- void CheckHintLhs(IToken tok, Expression lhs) {
+ void CheckHintLhs(IToken tok, Expression lhs, ISet<LocalVariable> localsAllowedInUpdates) {
+ Contract.Requires(tok != null);
+ Contract.Requires(lhs != null);
+ Contract.Requires(localsAllowedInUpdates != null);
var idExpr = lhs as IdentifierExpr;
if (idExpr == null) {
- Error(tok, "a hint is not allowed to update heap locations");
- } else if (scope.ContainsDecl(idExpr.Var)) {
- Error(tok, "a hint is not allowed to update a variable declared outside the hint");
+ reporter.Error(MessageSource.Resolver, tok, "a hint is not allowed to update heap locations");
+ } else if (!localsAllowedInUpdates.Contains(idExpr.Var)) {
+ reporter.Error(MessageSource.Resolver, tok, "a hint is not allowed to update a variable declared outside the hint");
}
}
- Type ResolveTypeRhs(TypeRhs rr, Statement stmt, bool specContextOnly, ICodeContext codeContext) {
+ Type ResolveTypeRhs(TypeRhs rr, Statement stmt, ICodeContext codeContext) {
Contract.Requires(rr != null);
Contract.Requires(stmt != null);
Contract.Requires(codeContext != null);
Contract.Ensures(Contract.Result<Type>() != null);
- // "new" is not allowed in ghost contexts
- if (specContextOnly) {
- Error(rr.Tok, "'new' is not allowed in ghost contexts");
- }
if (rr.Type == null) {
if (rr.ArrayDimensions != null) {
// ---------- new T[EE]
@@ -6048,9 +7221,7 @@ namespace Microsoft.Dafny
foreach (Expression dim in rr.ArrayDimensions) {
Contract.Assert(dim != null);
ResolveExpression(dim, new ResolveOpts(codeContext, true));
- if (!UnifyTypes(dim.Type, new OperationTypeProxy(true, false, false, false, false))) {
- Error(stmt, "new must use an integer-based expression for the array size (got {0} for index {1})", dim.Type, i);
- }
+ ConstrainTypes(dim.Type, new OperationTypeProxy(true, false, false, false, false, false), stmt, "new must use an integer-based expression for the array size (got {0} for index {1})", dim.Type, i);
i++;
}
rr.Type = ResolvedArrayType(stmt.Tok, rr.ArrayDimensions.Count, rr.EType, codeContext);
@@ -6059,7 +7230,7 @@ namespace Microsoft.Dafny
if (rr.Arguments == null) {
ResolveType(stmt.Tok, rr.EType, codeContext, ResolveTypeOptionEnum.InferTypeProxies, null);
if (!rr.EType.IsRefType) {
- Error(stmt, "new can be applied only to reference types (got {0})", rr.EType);
+ reporter.Error(MessageSource.Resolver, stmt, "new can be applied only to reference types (got {0})", rr.EType);
}
} else {
string initCallName = null;
@@ -6082,28 +7253,28 @@ namespace Microsoft.Dafny
initCallTok = rr.Tok;
}
if (!rr.EType.IsRefType) {
- Error(stmt, "new can be applied only to reference types (got {0})", rr.EType);
+ reporter.Error(MessageSource.Resolver, stmt, "new can be applied only to reference types (got {0})", rr.EType);
} else {
// ---------- new C.Init(EE)
Contract.Assert(initCallName != null);
- var prevErrorCount = ErrorCount;
+ var prevErrorCount = reporter.Count(ErrorLevel.Error);
// We want to create a MemberSelectExpr for the initializing method. To do that, we create a throw-away receiver of the appropriate
// type, create an dot-suffix expression around this receiver, and then resolve it in the usual way for dot-suffix expressions.
var lhs = new ImplicitThisExpr(initCallTok) { Type = rr.EType };
var callLhs = new ExprDotName(initCallTok, lhs, initCallName, ret == null ? null : ret.LastComponent.OptTypeArguments);
- ResolveDotSuffix(callLhs, true, rr.Arguments, new ResolveOpts(codeContext, true, specContextOnly), true);
- if (prevErrorCount == ErrorCount) {
+ ResolveDotSuffix(callLhs, true, rr.Arguments, new ResolveOpts(codeContext, true), true);
+ if (prevErrorCount == reporter.Count(ErrorLevel.Error)) {
Contract.Assert(callLhs.ResolvedExpression is MemberSelectExpr); // since ResolveApplySuffix succeeded and call.Lhs denotes an expression (not a module or a type)
var methodSel = (MemberSelectExpr)callLhs.ResolvedExpression;
if (methodSel.Member is Method) {
rr.InitCall = new CallStmt(initCallTok, stmt.EndTok, new List<Expression>(), methodSel, rr.Arguments);
- ResolveCallStmt(rr.InitCall, specContextOnly, codeContext, rr.EType);
+ ResolveCallStmt(rr.InitCall, codeContext, rr.EType);
if (rr.InitCall.Method is Constructor) {
callsConstructor = true;
}
} else {
- Error(initCallTok, "object initialization must denote an initializing method or constructor ({0})", initCallName);
+ reporter.Error(MessageSource.Resolver, initCallTok, "object initialization must denote an initializing method or constructor ({0})", initCallName);
}
}
}
@@ -6113,10 +7284,10 @@ namespace Microsoft.Dafny
if (udt != null) {
var cl = (ClassDecl)udt.ResolvedClass; // cast is guaranteed by the call to rr.EType.IsRefType above, together with the "rr.EType is UserDefinedType" test
if (cl is TraitDecl) {
- Error(stmt, "new cannot be applied to a trait");
+ reporter.Error(MessageSource.Resolver, stmt, "new cannot be applied to a trait");
}
if (!callsConstructor && cl.HasConstructor) {
- Error(stmt, "when allocating an object of type '{0}', one of its constructor methods must be called", cl.Name);
+ reporter.Error(MessageSource.Resolver, stmt, "when allocating an object of type '{0}', one of its constructor methods must be called", cl.Name);
}
}
}
@@ -6126,12 +7297,14 @@ namespace Microsoft.Dafny
return rr.Type;
}
- MemberDecl ResolveMember(IToken tok, Type receiverType, string memberName, out NonProxyType nptype) {
+ MemberDecl ResolveMember(IToken tok, Type receiverType, string memberName, out NonProxyType nptype, bool classMembersOnly = false) {
Contract.Requires(tok != null);
Contract.Requires(receiverType != null);
Contract.Requires(memberName != null);
Contract.Ensures(Contract.Result<MemberDecl>() == null || Contract.ValueAtReturn(out nptype) != null);
+ PartiallySolveTypeConstraints(); // so that we can try to pick up the type of the receiver
+
nptype = null; // prepare for the worst
receiverType = receiverType.NormalizeExpand();
var opProxy = receiverType as OperationTypeProxy;
@@ -6145,7 +7318,7 @@ namespace Microsoft.Dafny
}
}
if (receiverType is TypeProxy) {
- Error(tok, "type of the receiver is not fully determined at this program point", receiverType);
+ reporter.Error(MessageSource.Resolver, tok, "type of the receiver is not fully determined at this program point", receiverType);
return null;
}
Contract.Assert(receiverType is NonProxyType); // there are only two kinds of types: proxies and non-proxies
@@ -6158,20 +7331,20 @@ namespace Microsoft.Dafny
if (!classMembers[cd].TryGetValue(memberName, out member)) {
var kind = cd is IteratorDecl ? "iterator" : "class";
if (memberName == "_ctor") {
- Error(tok, "{0} {1} does not have an anonymous constructor", kind, ctype.Name);
+ reporter.Error(MessageSource.Resolver, tok, "{0} {1} does not have an anonymous constructor", kind, ctype.Name);
} else {
// search the static members of the enclosing module or its imports
- if (moduleInfo.StaticMembers.TryGetValue(memberName, out member)) {
+ if (!classMembersOnly && moduleInfo.StaticMembers.TryGetValue(memberName, out member)) {
Contract.Assert(member.IsStatic); // moduleInfo.StaticMembers is supposed to contain only static members of the module's implicit class _default
if (member is AmbiguousMemberDecl) {
var ambiguousMember = (AmbiguousMemberDecl)member;
- Error(tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", memberName, ambiguousMember.ModuleNames());
+ reporter.Error(MessageSource.Resolver, tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", memberName, ambiguousMember.ModuleNames());
} else {
nptype = GetReceiverType(tok, member);
return member;
}
} else {
- Error(tok, "member {0} does not exist in {2} {1}", memberName, ctype.Name, kind);
+ reporter.Error(MessageSource.Resolver, tok, "member {0} does not exist in {2} {1}", memberName, ctype.Name, kind);
}
}
return null;
@@ -6185,7 +7358,7 @@ namespace Microsoft.Dafny
if (dtd != null) {
MemberDecl member;
if (!datatypeMembers[dtd].TryGetValue(memberName, out member)) {
- Error(tok, "member {0} does not exist in datatype {1}", memberName, dtd.Name);
+ reporter.Error(MessageSource.Resolver, tok, "member {0} does not exist in datatype {1}", memberName, dtd.Name);
return null;
} else {
nptype = (UserDefinedType)receiverType;
@@ -6211,7 +7384,7 @@ namespace Microsoft.Dafny
}
}
- Error(tok, "type {0} does not have a member {1}", receiverType, memberName);
+ reporter.Error(MessageSource.Resolver, tok, "type {0} does not have a member {1}", receiverType, memberName);
return null;
}
@@ -6265,7 +7438,8 @@ namespace Microsoft.Dafny
if (arg == t.Arg) {
return type;
} else if (type is SetType) {
- return new SetType(arg);
+ var st = (SetType)type;
+ return new SetType(st.Finite, arg);
} else if (type is MultiSetType) {
return new MultiSetType(arg);
} else if (type is SeqType) {
@@ -6371,24 +7545,10 @@ namespace Microsoft.Dafny
{
public readonly ICodeContext codeContext;
public readonly bool twoState;
- public readonly bool DontCareAboutCompilation;
public ResolveOpts(ICodeContext codeContext, bool twoState) {
Contract.Requires(codeContext != null);
this.codeContext = codeContext;
this.twoState = twoState;
- DontCareAboutCompilation = codeContext.IsGhost;
- }
- public ResolveOpts(ICodeContext codeContext, bool twoState, bool dontCareAboutCompilation) {
- Contract.Requires(codeContext != null);
- this.codeContext = codeContext;
- this.twoState = twoState;
- this.DontCareAboutCompilation = dontCareAboutCompilation;
- }
- public ResolveOpts(ResolveOpts r, bool dontCareAboutCompilation) {
- Contract.Requires(r != null);
- this.codeContext = r.codeContext;
- this.twoState = r.twoState;
- this.DontCareAboutCompilation = dontCareAboutCompilation;
}
}
@@ -6434,10 +7594,10 @@ namespace Microsoft.Dafny
} else if (expr is NegationExpression) {
var e = (NegationExpression)expr;
- var errorCount = ErrorCount;
+ var errorCount = reporter.Count(ErrorLevel.Error);
ResolveExpression(e.E, opts);
e.Type = e.E.Type;
- if (errorCount != ErrorCount) {
+ if (errorCount != reporter.Count(ErrorLevel.Error)) {
// there were errors resolving the operand; take the quick way out and
// just let the (already erronous) subexpression be what the negation expression
// will resolve to
@@ -6471,9 +7631,9 @@ namespace Microsoft.Dafny
if (e.Value == null) {
e.Type = new ObjectTypeProxy();
} else if (e.Value is BigInteger) {
- e.Type = new OperationTypeProxy(true, false, false, false, false);
+ e.Type = new OperationTypeProxy(true, false, false, false, false, false);
} else if (e.Value is Basetypes.BigDec) {
- e.Type = new OperationTypeProxy(false, true, false, false, false);
+ e.Type = new OperationTypeProxy(false, true, false, false, false, false);
} else if (e.Value is bool) {
e.Type = Type.Bool;
} else if (e is CharLiteralExpr) {
@@ -6487,7 +7647,7 @@ namespace Microsoft.Dafny
}
} else if (expr is ThisExpr) {
if (!scope.AllowInstance) {
- Error(expr, "'this' is not allowed in a 'static' context");
+ reporter.Error(MessageSource.Resolver, expr, "'this' is not allowed in a 'static' context");
}
if (currentClass != null) {
expr.Type = GetThisType(expr.tok, currentClass); // do this regardless of scope.AllowInstance, for better error reporting
@@ -6499,19 +7659,19 @@ namespace Microsoft.Dafny
if (e.Var != null) {
expr.Type = e.Var.Type;
} else {
- Error(expr, "Identifier does not denote a local variable, parameter, or bound variable: {0}", e.Name);
+ reporter.Error(MessageSource.Resolver, expr, "Identifier does not denote a local variable, parameter, or bound variable: {0}", e.Name);
}
} else if (expr is DatatypeValue) {
DatatypeValue dtv = (DatatypeValue)expr;
TopLevelDecl d;
if (!moduleInfo.TopLevels.TryGetValue(dtv.DatatypeName, out d)) {
- Error(expr.tok, "Undeclared datatype: {0}", dtv.DatatypeName);
+ reporter.Error(MessageSource.Resolver, expr.tok, "Undeclared datatype: {0}", dtv.DatatypeName);
} else if (d is AmbiguousTopLevelDecl) {
var ad = (AmbiguousTopLevelDecl)d;
- Error(expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", dtv.DatatypeName, ad.ModuleNames());
+ reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", dtv.DatatypeName, ad.ModuleNames());
} else if (!(d is DatatypeDecl)) {
- Error(expr.tok, "Expected datatype: {0}", dtv.DatatypeName);
+ reporter.Error(MessageSource.Resolver, expr.tok, "Expected datatype: {0}", dtv.DatatypeName);
} else {
ResolveDatatypeValue(opts, dtv, (DatatypeDecl)d);
}
@@ -6522,12 +7682,11 @@ namespace Microsoft.Dafny
foreach (Expression ee in e.Elements) {
ResolveExpression(ee, opts);
Contract.Assert(ee.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(elementType, ee.Type)) {
- Error(ee, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", ee.Type, elementType);
- }
+ ConstrainTypes(elementType, ee.Type, ee, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", ee.Type, elementType);
}
if (expr is SetDisplayExpr) {
- expr.Type = new SetType(elementType);
+ var se = (SetDisplayExpr)expr;
+ expr.Type = new SetType(se.Finite, elementType);
} else if (expr is MultiSetDisplayExpr) {
expr.Type = new MultiSetType(elementType);
} else {
@@ -6540,32 +7699,28 @@ namespace Microsoft.Dafny
foreach (ExpressionPair p in e.Elements) {
ResolveExpression(p.A, opts);
Contract.Assert(p.A.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(domainType, p.A.Type)) {
- Error(p.A, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", p.A.Type, domainType);
- }
+ ConstrainTypes(domainType, p.A.Type, p.A, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", p.A.Type, domainType);
ResolveExpression(p.B, opts);
Contract.Assert(p.B.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(rangeType, p.B.Type)) {
- Error(p.B, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", p.B.Type, rangeType);
- }
+ ConstrainTypes(rangeType, p.B.Type, p.B, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", p.B.Type, rangeType);
}
expr.Type = new MapType(e.Finite, domainType, rangeType);
} else if (expr is NameSegment) {
var e = (NameSegment)expr;
ResolveNameSegment(e, true, null, opts, false);
if (e.Type is Resolver_IdentifierExpr.ResolverType_Module) {
- Error(e.tok, "name of module ({0}) is used as a variable", e.Name);
+ reporter.Error(MessageSource.Resolver, e.tok, "name of module ({0}) is used as a variable", e.Name);
} else if (e.Type is Resolver_IdentifierExpr.ResolverType_Type) {
- Error(e.tok, "name of type ({0}) is used as a variable", e.Name);
+ reporter.Error(MessageSource.Resolver, e.tok, "name of type ({0}) is used as a variable", e.Name);
}
} else if (expr is ExprDotName) {
var e = (ExprDotName)expr;
ResolveDotSuffix(e, true, null, opts, false);
if (e.Type is Resolver_IdentifierExpr.ResolverType_Module) {
- Error(e.tok, "name of module ({0}) is used as a variable", e.SuffixName);
+ reporter.Error(MessageSource.Resolver, e.tok, "name of module ({0}) is used as a variable", e.SuffixName);
} else if (e.Type is Resolver_IdentifierExpr.ResolverType_Type) {
- Error(e.tok, "name of type ({0}) is used as a variable", e.SuffixName);
+ reporter.Error(MessageSource.Resolver, e.tok, "name of type ({0}) is used as a variable", e.SuffixName);
}
} else if (expr is ApplySuffix) {
@@ -6605,7 +7760,7 @@ namespace Microsoft.Dafny
var field = (Field)member;
e.Member = field;
if (e.Obj is StaticReceiverExpr) {
- Error(expr, "a field must be selected via an object, not just a class name");
+ reporter.Error(MessageSource.Resolver, expr, "a field must be selected via an object, not just a class name");
}
var ctype = nptype as UserDefinedType;
if (ctype == null) {
@@ -6617,7 +7772,7 @@ namespace Microsoft.Dafny
e.Type = SubstType(field.Type, subst);
}
} else {
- Error(expr, "member {0} in type {1} does not refer to a field or a function", e.MemberName, nptype);
+ reporter.Error(MessageSource.Resolver, expr, "member {0} in type {1} does not refer to a field or a function", e.MemberName, nptype);
}
} else if (expr is SeqSelectExpr) {
@@ -6630,17 +7785,13 @@ namespace Microsoft.Dafny
ResolveExpression(e.Array, opts);
Contract.Assert(e.Array.Type != null); // follows from postcondition of ResolveExpression
Type elementType = new InferredTypeProxy();
- if (!UnifyTypes(e.Array.Type, ResolvedArrayType(e.Array.tok, e.Indices.Count, elementType, opts.codeContext))) {
- Error(e.Array, "array selection requires an array{0} (got {1})", e.Indices.Count, e.Array.Type);
- }
+ ConstrainTypes(e.Array.Type, ResolvedArrayType(e.Array.tok, e.Indices.Count, elementType, opts.codeContext), e.Array, "array selection requires an array{0} (got {1})", e.Indices.Count, e.Array.Type);
int i = 0;
foreach (Expression idx in e.Indices) {
Contract.Assert(idx != null);
ResolveExpression(idx, opts);
Contract.Assert(idx.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(idx.Type, new OperationTypeProxy(true, false, false, false, false))) {
- Error(idx, "array selection requires integer-based numeric indices (got {0} for index {1})", idx.Type, i);
- }
+ ConstrainTypes(idx.Type, new OperationTypeProxy(true, false, false, false, false, false), idx, "array selection requires integer-based numeric indices (got {0} for index {1})", idx.Type, i);
i++;
}
e.Type = elementType;
@@ -6655,131 +7806,71 @@ namespace Microsoft.Dafny
if (UnifyTypes(e.Seq.Type, new SeqType(elementType))) {
ResolveExpression(e.Index, opts);
Contract.Assert(e.Index.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.Index.Type, Type.Int)) {
- Error(e.Index, "sequence update requires integer index (got {0})", e.Index.Type);
- }
+ ConstrainTypes(e.Index.Type, Type.Int, e.Index, "sequence update requires integer index (got {0})", e.Index.Type);
ResolveExpression(e.Value, opts);
Contract.Assert(e.Value.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.Value.Type, elementType)) {
- Error(e.Value, "sequence update requires the value to have the element type of the sequence (got {0})", e.Value.Type);
- }
+ ConstrainTypes(e.Value.Type, elementType, e.Value, "sequence update requires the value to have the element type of the sequence (got {0})", e.Value.Type);
expr.Type = e.Seq.Type;
} else if (UnifyTypes(e.Seq.Type, new MapType(true, domainType, rangeType))) {
ResolveExpression(e.Index, opts);
- if (!UnifyTypes(e.Index.Type, domainType)) {
- Error(e.Index, "map update requires domain element to be of type {0} (got {1})", domainType, e.Index.Type);
- }
+ ConstrainTypes(e.Index.Type, domainType, e.Index, "map update requires domain element to be of type {0} (got {1})", domainType, e.Index.Type);
ResolveExpression(e.Value, opts);
- if (!UnifyTypes(e.Value.Type, rangeType)) {
- Error(e.Value, "map update requires the value to have the range type {0} (got {1})", rangeType, e.Value.Type);
- }
+ ConstrainTypes(e.Value.Type, rangeType, e.Value, "map update requires the value to have the range type {0} (got {1})", rangeType, e.Value.Type);
expr.Type = e.Seq.Type;
} else if (UnifyTypes(e.Seq.Type, new MapType(false, domainType, rangeType))) {
ResolveExpression(e.Index, opts);
- if (!UnifyTypes(e.Index.Type, domainType)) {
- Error(e.Index, "imap update requires domain element to be of type {0} (got {1})", domainType, e.Index.Type);
- }
+ ConstrainTypes(e.Index.Type, domainType, e.Index, "imap update requires domain element to be of type {0} (got {1})", domainType, e.Index.Type);
ResolveExpression(e.Value, opts);
- if (!UnifyTypes(e.Value.Type, rangeType)) {
- Error(e.Value, "imap update requires the value to have the range type {0} (got {1})", rangeType, e.Value.Type);
- }
+ ConstrainTypes(e.Value.Type, rangeType, e.Value, "imap update requires the value to have the range type {0} (got {1})", rangeType, e.Value.Type);
expr.Type = e.Seq.Type;
} else if (UnifyTypes(e.Seq.Type, new MultiSetType(elementType))) {
ResolveExpression(e.Index, opts);
- if (!UnifyTypes(e.Index.Type, elementType)) {
- Error(e.Index, "multiset update requires domain element to be of type {0} (got {1})", elementType, e.Index.Type);
- }
+ ConstrainTypes(e.Index.Type, elementType, e.Index, "multiset update requires domain element to be of type {0} (got {1})", elementType, e.Index.Type);
ResolveExpression(e.Value, opts);
- if (!UnifyTypes(e.Value.Type, new OperationTypeProxy(true, false, false, false, false))) {
- Error(e.Value, "multiset update requires integer-based numeric value (got {0})", e.Value.Type);
- }
+ ConstrainTypes(e.Value.Type, new OperationTypeProxy(true, false, false, false, false, false), e.Value, "multiset update requires integer-based numeric value (got {0})", e.Value.Type);
expr.Type = e.Seq.Type;
} else if (e.Seq.Type.IsDatatype) {
- var dt = e.Seq.Type.AsDatatype;
- DatatypeCtor ctor = null;
-
- // Let en = e and e(n-1) = en.Seq
- // We're currently looking at e = e(n-1)[en.Index := en.Value]
- // Let e(n-2) = e(n-1).Seq, e(n-3) = e(n-2).Seq, etc.
- // Let's extract e = e0[e1.Index := e1.Value]...[en.Index := en.Value]
- var IndexToValue = new Dictionary<string,Tuple<DatatypeDestructor,Expression>>();
+ // combine a nest of datatype updates into one single DatatypeUpdate expression
+ var memberUpdates = new List<Tuple<IToken, string, Expression>>();
Expression eIter = e;
while (eIter is SeqUpdateExpr) {
var ei = (SeqUpdateExpr)eIter;
-
- if (!(ei.Index is NameSegment|| (ei.Index is LiteralExpr && ((LiteralExpr)ei.Index).Value is BigInteger))) {
- Error(expr, "datatype updates must be to datatype destructors");
+ if (ei.Index is NameSegment) {
+ var seg = (NameSegment)ei.Index;
+ memberUpdates.Add(new Tuple<IToken,string,Expression>(ei.Index.tok, seg.Name, ei.Value));
+ } else if (ei.Index is LiteralExpr && ((LiteralExpr)ei.Index).Value is BigInteger) {
+ var lit = (LiteralExpr)ei.Index;
+ var nm = lit.tok.val; // note, take the string of digits, not the parsed integer
+ memberUpdates.Add(new Tuple<IToken,string,Expression>(ei.Index.tok, nm, ei.Value));
} else {
- string destructor_str = null;
-
- if (ei.Index is NameSegment) {
- var seg = (NameSegment)ei.Index;
- destructor_str = seg.Name;
- } else {
- Contract.Assert(ei.Index is LiteralExpr && ((LiteralExpr)ei.Index).Value is BigInteger);
- destructor_str = ((LiteralExpr)ei.Index).tok.val; // note, take the string of digits, not the parsed integer
- }
-
- Contract.Assert(destructor_str != null);
- if (IndexToValue.ContainsKey(destructor_str)) {
- // Don't bother trying to optimize this case; we'd have to drop one of the updates,
- // which makes resolving the dropped update an annoyance.
- Warning(ei.tok, "update to {0} overwritten by another update to {1}", destructor_str, destructor_str);
- break;
- }
- MemberDecl member;
- if (!datatypeMembers[dt].TryGetValue(destructor_str, out member)) {
- Error(expr, "member {0} does not exist in datatype {1}", destructor_str, dt.Name);
- } else {
- DatatypeDestructor destructor = (DatatypeDestructor)member;
- if (ctor != null && ctor != destructor.EnclosingCtor) {
- // Don't bother optimizing case where destructors come from different constructors
- break;
- }
- IndexToValue.Add(destructor_str, Tuple.Create(destructor, ei.Value));
- ctor = destructor.EnclosingCtor;
- }
+ reporter.Error(MessageSource.Resolver, expr, "datatype updates must be to datatype destructors");
}
eIter = ei.Seq;
}
- var e0 = eIter;
-
- // Rewrite an update of the form "dt[dtor := E]" to be "let d' := dt in dtCtr(E, d'.dtor2, d'.dtor3,...)"
- // Wrapping it in a let expr avoids exponential growth in the size of the expression
- // More generally, rewrite "E0[dtor1 := E1][dtor2 := E2]...[dtorn := En]" to
- // "let d' := E0 in dtCtr(...mixtures of Ek and d'.dtorj...)"
-
- // Create a unique name for d', the variable we introduce in the let expression
- string tmpName = FreshTempVarName("dt_update_tmp#", opts.codeContext);
- IdentifierExpr tmpVarIdExpr = new IdentifierExpr(e0.tok, tmpName);
- BoundVar tmpVarBv = new BoundVar(e0.tok, tmpName, e0.Type);
-
- // Build the arguments to the datatype constructor, using the updated value in the appropriate slot
- List<Expression> ctor_args = new List<Expression>();
- foreach (Formal d in ctor.Formals) {
- Expression v = null;
- foreach (var dvPair in IndexToValue.Values) {
- var destructor = dvPair.Item1;
- if (d == destructor.CorrespondingFormal) {
- Contract.Assert(v == null);
- v = dvPair.Item2;
- }
- }
- ctor_args.Add(v ?? new ExprDotName(expr.tok, tmpVarIdExpr, d.Name, null));
+ var let = ResolveDatatypeUpdate(expr.tok, eIter, e.Seq.Type.AsDatatype, memberUpdates, true, opts);
+ if (let != null) {
+ reporter.Warning(MessageSource.Resolver, expr.tok, "datatype update syntax D[f := E] is deprecated; the new syntax is D.(f := E)");
+ e.ResolvedUpdateExpr = let;
+ expr.Type = e.Seq.Type;
}
- DatatypeValue ctor_call = new DatatypeValue(expr.tok, ctor.EnclosingDatatype.Name, ctor.Name, ctor_args);
-
- CasePattern tmpVarPat = new CasePattern(e0.tok, tmpVarBv);
- LetExpr let = new LetExpr(e0.tok, new List<CasePattern>() { tmpVarPat }, new List<Expression>() { e0 }, ctor_call, true);
-
- ResolveExpression(let, opts);
- e.ResolvedUpdateExpr = let;
+ } else {
+ reporter.Error(MessageSource.Resolver, expr, "update requires a sequence, map, multiset, or datatype (got {0})", e.Seq.Type);
+ }
- expr.Type = e0.Type;
+ } else if (expr is DatatypeUpdateExpr) {
+ var e = (DatatypeUpdateExpr)expr;
+ ResolveExpression(e.Root, opts);
+ var ty = e.Root.Type;
+ if (!ty.IsDatatype) {
+ reporter.Error(MessageSource.Resolver, expr, "datatype update expression requires a root expression of a datatype (got {0})", ty);
} else {
- Error(expr, "update requires a sequence, map, or datatype (got {0})", e.Seq.Type);
+ var let = ResolveDatatypeUpdate(expr.tok, e.Root, ty.AsDatatype, e.Updates, false, opts);
+ if (let != null) {
+ e.ResolvedExpression = let;
+ expr.Type = ty;
+ }
}
} else if (expr is FunctionCallExpr) {
@@ -6794,14 +7885,12 @@ namespace Microsoft.Dafny
}
var fnType = e.Function.Type.AsArrowType;
if (fnType == null) {
- Error(e.tok, "non-function expression (of type {0}) is called with parameters", e.Function.Type);
+ reporter.Error(MessageSource.Resolver, e.tok, "non-function expression (of type {0}) is called with parameters", e.Function.Type);
} else if (fnType.Arity != e.Args.Count) {
- Error(e.tok, "wrong number of arguments to function application (function type '{0}' expects {1}, got {2})", fnType, fnType.Arity, e.Args.Count);
+ reporter.Error(MessageSource.Resolver, e.tok, "wrong number of arguments to function application (function type '{0}' expects {1}, got {2})", fnType, fnType.Arity, e.Args.Count);
} else {
for (var i = 0; i < fnType.Arity; i++) {
- if (!UnifyTypes(fnType.Args[i], e.Args[i].Type)) {
- Error(e.Args[i].tok, "type mismatch for argument {0} (function expects {1}, got {2})", i, fnType.Args[i], e.Args[i].Type);
- }
+ ConstrainTypes(fnType.Args[i], e.Args[i].Type, e.Args[i].tok, "type mismatch for argument {0} (function expects {1}, got {2})", i, fnType.Args[i], e.Args[i].Type);
}
}
expr.Type = fnType == null ? new InferredTypeProxy() : fnType.Result;
@@ -6809,7 +7898,7 @@ namespace Microsoft.Dafny
} else if (expr is OldExpr) {
OldExpr e = (OldExpr)expr;
if (!opts.twoState) {
- Error(expr, "old expressions are not allowed in this context");
+ reporter.Error(MessageSource.Resolver, expr, "old expressions are not allowed in this context");
}
ResolveExpression(e.E, opts);
expr.Type = e.E.Type;
@@ -6817,8 +7906,8 @@ namespace Microsoft.Dafny
} else if (expr is MultiSetFormingExpr) {
MultiSetFormingExpr e = (MultiSetFormingExpr)expr;
ResolveExpression(e.E, opts);
- if (!UnifyTypes(e.E.Type, new SetType(new InferredTypeProxy())) && !UnifyTypes(e.E.Type, new SeqType(new InferredTypeProxy()))) {
- Error(e.tok, "can only form a multiset from a seq or set.");
+ if (!UnifyTypes(e.E.Type, new SetType(true, new InferredTypeProxy())) && !UnifyTypes(e.E.Type, new SeqType(new InferredTypeProxy()))) {
+ reporter.Error(MessageSource.Resolver, e.tok, "can only form a multiset from a seq or set.");
}
expr.Type = new MultiSetType(e.E.Type.AsCollectionType.Arg);
@@ -6828,20 +7917,16 @@ namespace Microsoft.Dafny
Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression
switch (e.Op) {
case UnaryOpExpr.Opcode.Not:
- if (!UnifyTypes(e.E.Type, Type.Bool)) {
- Error(expr, "logical negation expects a boolean argument (instead got {0})", e.E.Type);
- }
+ ConstrainTypes(e.E.Type, Type.Bool, expr, "logical negation expects a boolean argument (instead got {0})", e.E.Type);
expr.Type = Type.Bool;
break;
case UnaryOpExpr.Opcode.Cardinality:
- if (!UnifyTypes(e.E.Type, new CollectionTypeProxy(new InferredTypeProxy(), false))) {
- Error(expr, "size operator expects a collection argument (instead got {0})", e.E.Type);
- }
+ ConstrainTypes(e.E.Type, new CollectionTypeProxy(new InferredTypeProxy(), false, false), expr, "size operator expects a collection argument (instead got {0})", e.E.Type);
expr.Type = Type.Int;
break;
case UnaryOpExpr.Opcode.Fresh:
if (!opts.twoState) {
- Error(expr, "fresh expressions are not allowed in this context");
+ reporter.Error(MessageSource.Resolver, expr, "fresh expressions are not allowed in this context");
}
// the type of e.E must be either an object or a collection of objects
Type t = e.E.Type.NormalizeExpand();
@@ -6856,7 +7941,7 @@ namespace Microsoft.Dafny
} else if (t.IsDatatype) {
// fine, treat this as the datatype itself.
} else {
- Error(expr, "the argument of a fresh expression must denote an object or a collection of objects (instead got {0})", e.E.Type);
+ reporter.Error(MessageSource.Resolver, expr, "the argument of a fresh expression must denote an object or a collection of objects (instead got {0})", e.E.Type);
}
expr.Type = Type.Bool;
break;
@@ -6869,15 +7954,11 @@ namespace Microsoft.Dafny
ResolveType(e.tok, e.ToType, opts.codeContext, new ResolveTypeOption(ResolveTypeOptionEnum.DontInfer), null);
ResolveExpression(e.E, opts);
if (e.ToType.IsNumericBased(Type.NumericPersuation.Int)) {
- if (!UnifyTypes(e.E.Type, new OperationTypeProxy(true, true, false, false, false))) {
- Error(expr, "type conversion to an int-based type is allowed only from numeric types (got {0})", e.E.Type);
- }
+ ConstrainTypes(e.E.Type, new OperationTypeProxy(true, true, false, false, false, false), expr, "type conversion to an int-based type is allowed only from numeric types (got {0})", e.E.Type);
} else if (e.ToType.IsNumericBased(Type.NumericPersuation.Real)) {
- if (!UnifyTypes(e.E.Type, new OperationTypeProxy(true, true, false, false, false))) {
- Error(expr, "type conversion to a real-based type is allowed only from numeric types (got {0})", e.E.Type);
- }
+ ConstrainTypes(e.E.Type, new OperationTypeProxy(true, true, false, false, false, false), expr, "type conversion to a real-based type is allowed only from numeric types (got {0})", e.E.Type);
} else {
- Error(expr, "type conversions are not supported to this type (got {0})", e.ToType);
+ reporter.Error(MessageSource.Resolver, expr, "type conversions are not supported to this type (got {0})", e.ToType);
}
e.Type = e.ToType;
@@ -6893,12 +7974,8 @@ namespace Microsoft.Dafny
case BinaryExpr.Opcode.Exp:
case BinaryExpr.Opcode.And:
case BinaryExpr.Opcode.Or:
- if (!UnifyTypes(e.E0.Type, Type.Bool)) {
- Error(expr, "first argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
- }
- if (!UnifyTypes(e.E1.Type, Type.Bool)) {
- Error(expr, "second argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type);
- }
+ ConstrainTypes(e.E0.Type, Type.Bool, expr, "first argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
+ ConstrainTypes(e.E1.Type, Type.Bool, expr, "second argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type);
expr.Type = Type.Bool;
break;
@@ -6918,20 +7995,18 @@ namespace Microsoft.Dafny
// unification will succeed.
} else {
// The types are not comparable and do not unify. It's time for an error message.
- Error(expr, "arguments must have the same type (got {0} and {1})", e.E0.Type, e.E1.Type);
+ reporter.Error(MessageSource.Resolver, expr, "arguments must have the same type (got {0} and {1})", e.E0.Type, e.E1.Type);
}
expr.Type = Type.Bool;
break;
case BinaryExpr.Opcode.Disjoint:
// TODO: the error messages are backwards from what (ideally) they should be. this is necessary because UnifyTypes can't backtrack.
- if (!UnifyTypes(e.E0.Type, e.E1.Type)) {
- Error(expr, "arguments must have the same type (got {0} and {1})", e.E0.Type, e.E1.Type);
- }
- if (!UnifyTypes(e.E0.Type, new SetType(new InferredTypeProxy())) &&
+ ConstrainTypes(e.E0.Type, e.E1.Type, expr, "arguments must have the same type (got {0} and {1})", e.E0.Type, e.E1.Type);
+ if (!UnifyTypes(e.E0.Type, new SetType(true, new InferredTypeProxy())) &&
!UnifyTypes(e.E0.Type, new MultiSetType(new InferredTypeProxy())) &&
!UnifyTypes(e.E0.Type, new MapType(true, new InferredTypeProxy(), new InferredTypeProxy()))) {
- Error(expr, "arguments must be of a [multi]set or map type (got {0})", e.E0.Type);
+ reporter.Error(MessageSource.Resolver, expr, "arguments must be of a [multi]set or map type (got {0})", e.E0.Type);
}
expr.Type = Type.Bool;
break;
@@ -6940,34 +8015,25 @@ namespace Microsoft.Dafny
case BinaryExpr.Opcode.Le:
case BinaryExpr.Opcode.Add: {
if (e.Op == BinaryExpr.Opcode.Lt && (e.E0.Type.NormalizeExpand().IsIndDatatype || e.E0.Type.IsTypeParameter)) {
- if (UnifyTypes(e.E1.Type, new DatatypeProxy(false, false))) {
+ if (ConstrainTypes(e.E1.Type, new DatatypeProxy(false, false), expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type)) {
e.ResolvedOp = BinaryExpr.ResolvedOpcode.RankLt;
- } else {
- Error(expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type);
}
expr.Type = Type.Bool;
} else if (e.Op == BinaryExpr.Opcode.Lt && e.E1.Type.NormalizeExpand().IsIndDatatype) {
- if (UnifyTypes(e.E0.Type, new DatatypeProxy(false, true))) {
+ if (ConstrainTypes(e.E0.Type, new DatatypeProxy(false, true), expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E0.Type)) {
e.ResolvedOp = BinaryExpr.ResolvedOpcode.RankLt;
- } else {
- Error(expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E0.Type);
}
expr.Type = Type.Bool;
} else {
- bool err = false;
bool isComparison = e.Op != BinaryExpr.Opcode.Add;
- if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true, true, isComparison, true, true))) {
- Error(expr, "arguments to {0} must be of a numeric type{2} or a collection type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type,
- isComparison ? ", char," : "");
- err = true;
- }
- if (!UnifyTypes(e.E1.Type, e.E0.Type)) {
- Error(expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type);
- err = true;
- }
+ var good0 = ConstrainTypes(e.E0.Type, new OperationTypeProxy(true, true, isComparison, true, true, true),
+ expr, "arguments to {0} must be of a numeric type{2} or a collection type (instead got {1})",
+ BinaryExpr.OpcodeString(e.Op), e.E0.Type, isComparison ? ", char," : "");
+ var good1 = ConstrainTypes(e.E1.Type, e.E0.Type,
+ expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type);
if (isComparison) {
expr.Type = Type.Bool;
- } else if (!err) {
+ } else if (good0 && good1) {
expr.Type = e.E0.Type;
}
}
@@ -6979,34 +8045,24 @@ namespace Microsoft.Dafny
case BinaryExpr.Opcode.Gt:
case BinaryExpr.Opcode.Ge: {
if (e.Op == BinaryExpr.Opcode.Gt && e.E0.Type.NormalizeExpand().IsIndDatatype) {
- if (UnifyTypes(e.E1.Type, new DatatypeProxy(false, true))) {
+ if (ConstrainTypes(e.E1.Type, new DatatypeProxy(false, true), expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type)) {
e.ResolvedOp = BinaryExpr.ResolvedOpcode.RankGt;
- } else {
- Error(expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type);
}
expr.Type = Type.Bool;
} else if (e.Op == BinaryExpr.Opcode.Gt && (e.E1.Type.NormalizeExpand().IsIndDatatype || e.E1.Type.IsTypeParameter)) {
- if (UnifyTypes(e.E0.Type, new DatatypeProxy(false, false))) {
+ if (ConstrainTypes(e.E0.Type, new DatatypeProxy(false, false), expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E0.Type)) {
e.ResolvedOp = BinaryExpr.ResolvedOpcode.RankGt;
- } else {
- Error(expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E0.Type);
}
expr.Type = Type.Bool;
} else {
- bool err = false;
bool isComparison = e.Op == BinaryExpr.Opcode.Gt || e.Op == BinaryExpr.Opcode.Ge;
- if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true, true, isComparison, false, true))) {
- Error(expr, "arguments to {0} must be of a numeric type{2} or a set type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type,
- isComparison ? ", char, " : "");
- err = true;
- }
- if (!UnifyTypes(e.E1.Type, e.E0.Type)) {
- Error(expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type);
- err = true;
- }
+ var good0 = ConstrainTypes(e.E0.Type, new OperationTypeProxy(true, true, isComparison, false, true, true),
+ expr, "arguments to {0} must be of a numeric type{2} or a set type (instead got {1})",
+ BinaryExpr.OpcodeString(e.Op), e.E0.Type, isComparison ? ", char, " : "");
+ var good1 = ConstrainTypes(e.E1.Type, e.E0.Type, expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type);
if (isComparison) {
expr.Type = Type.Bool;
- } else if (!err) {
+ } else if (good0 && good1) {
expr.Type = e.E0.Type;
}
}
@@ -7015,29 +8071,23 @@ namespace Microsoft.Dafny
case BinaryExpr.Opcode.In:
case BinaryExpr.Opcode.NotIn:
- if (!UnifyTypes(e.E1.Type, new CollectionTypeProxy(e.E0.Type, true))) {
- Error(expr, "second argument to \"{0}\" must be a set, multiset, or sequence with elements of type {1}, or a map with domain {1} (instead got {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type);
- }
+ ConstrainTypes(e.E1.Type, new CollectionTypeProxy(e.E0.Type, true, true), expr, "second argument to \"{0}\" must be a set, multiset, or sequence with elements of type {1}, or a map with domain {1} (instead got {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type);
expr.Type = Type.Bool;
break;
case BinaryExpr.Opcode.Div:
- if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true, true, false, false, false))) {
- Error(expr, "first argument to {0} must be of numeric type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
- }
- if (!UnifyTypes(e.E1.Type, e.E0.Type)) {
- Error(expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type);
- }
+ ConstrainTypes(e.E0.Type, new OperationTypeProxy(true, true, false, false, false, false),
+ expr, "first argument to {0} must be of numeric type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
+ ConstrainTypes(e.E1.Type, e.E0.Type,
+ expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type);
expr.Type = e.E0.Type;
break;
case BinaryExpr.Opcode.Mod:
- if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true, false, false, false, false))) {
- Error(expr, "first argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
- }
- if (!UnifyTypes(e.E1.Type, e.E0.Type)) {
- Error(expr, "second argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type);
- }
+ ConstrainTypes(e.E0.Type, new OperationTypeProxy(true, false, false, false, false, false),
+ expr, "first argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
+ ConstrainTypes(e.E1.Type, e.E0.Type,
+ expr, "second argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type);
expr.Type = e.E0.Type;
break;
@@ -7055,15 +8105,12 @@ namespace Microsoft.Dafny
switch (e.Op) {
case TernaryExpr.Opcode.PrefixEqOp:
case TernaryExpr.Opcode.PrefixNeqOp:
- if (!UnifyTypes(e.E0.Type, Type.Int)) {
- Error(e.E0, "prefix-equality limit argument must be an integer expression (got {0})", e.E0.Type);
- }
- if (!UnifyTypes(e.E1.Type, new DatatypeProxy(true))) {
- Error(expr, "arguments to prefix equality must be codatatypes (instead of {0})", e.E1.Type);
- }
- if (!UnifyTypes(e.E1.Type, e.E2.Type)) {
- Error(expr, "arguments must have the same type (got {0} and {1})", e.E1.Type, e.E2.Type);
- }
+ ConstrainTypes(e.E0.Type, Type.Int,
+ e.E0, "prefix-equality limit argument must be an integer expression (got {0})", e.E0.Type);
+ ConstrainTypes(e.E1.Type, new DatatypeProxy(true),
+ expr, "arguments to prefix equality must be codatatypes (instead of {0})", e.E1.Type);
+ ConstrainTypes(e.E1.Type, e.E2.Type,
+ expr, "arguments must have the same type (got {0} and {1})", e.E1.Type, e.E2.Type);
expr.Type = Type.Bool;
break;
default:
@@ -7079,7 +8126,7 @@ namespace Microsoft.Dafny
}
scope.PushMarker();
if (e.LHSs.Count != e.RHSs.Count) {
- Error(expr, "let expression must have same number of LHSs (found {0}) as RHSs (found {1})", e.LHSs.Count, e.RHSs.Count);
+ reporter.Error(MessageSource.Resolver, expr, "let expression must have same number of LHSs (found {0}) as RHSs (found {1})", e.LHSs.Count, e.RHSs.Count);
}
var i = 0;
foreach (var lhs in e.LHSs) {
@@ -7088,199 +8135,131 @@ namespace Microsoft.Dafny
// Check for duplicate names now, because not until after resolving the case pattern do we know if identifiers inside it refer to bound variables or nullary constructors
var c = 0;
foreach (var v in lhs.Vars) {
- if (!scope.Push(v.Name, v)) {
- Error(v, "Duplicate let-variable name: {0}", v.Name);
- }
+ ScopePushAndReport(scope, v, "let-variable");
c++;
}
if (c == 0) {
// Every identifier-looking thing in the pattern resolved to a constructor; that is, this LHS is a constant literal
- Error(lhs.tok, "LHS is a constant literal; to be legal, it must introduce at least one bound variable");
+ reporter.Error(MessageSource.Resolver, lhs.tok, "LHS is a constant literal; to be legal, it must introduce at least one bound variable");
}
i++;
}
} else {
// let-such-that expression
if (e.RHSs.Count != 1) {
- Error(expr, "let-such-that expression must have just one RHS (found {0})", e.RHSs.Count);
+ reporter.Error(MessageSource.Resolver, expr, "let-such-that expression must have just one RHS (found {0})", e.RHSs.Count);
}
// the bound variables are in scope in the RHS of a let-such-that expression
scope.PushMarker();
foreach (var lhs in e.LHSs) {
Contract.Assert(lhs.Var != null); // the parser already checked that every LHS is a BoundVar, not a general pattern
var v = lhs.Var;
- if (!scope.Push(v.Name, v)) {
- Error(v, "Duplicate let-variable name: {0}", v.Name);
- }
+ ScopePushAndReport(scope, v, "let-variable");
ResolveType(v.tok, v.Type, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null);
}
foreach (var rhs in e.RHSs) {
ResolveExpression(rhs, opts);
- if (!UnifyTypes(rhs.Type, Type.Bool)) {
- Error(rhs.tok, "type of RHS of let-such-that expression must be boolean (got {0})", rhs.Type);
- }
- }
- if (!opts.DontCareAboutCompilation && !e.BoundVars.All(bv => bv.IsGhost)) {
- needFiniteBoundsChecks_LetSuchThatExpr.Add(e);
+ ConstrainTypes(rhs.Type, Type.Bool, rhs.tok, "type of RHS of let-such-that expression must be boolean (got {0})", rhs.Type);
}
}
ResolveExpression(e.Body, opts);
- ResolveAttributes(e.Attributes, new ResolveOpts(opts, true));
+ ResolveAttributes(e.Attributes, opts);
scope.PopMarker();
expr.Type = e.Body.Type;
-
} else if (expr is NamedExpr) {
var e = (NamedExpr)expr;
ResolveExpression(e.Body, opts);
if (e.Contract != null) ResolveExpression(e.Contract, opts);
e.Type = e.Body.Type;
} else if (expr is QuantifierExpr) {
- QuantifierExpr e = (QuantifierExpr)expr;
- int prevErrorCount = ErrorCount;
+ var e = (QuantifierExpr)expr;
+ opts.codeContext.ContainsQuantifier = true;
+ Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution
+ int prevErrorCount = reporter.Count(ErrorLevel.Error);
bool _val = true;
bool typeQuantifier = Attributes.ContainsBool(e.Attributes, "typeQuantifier", ref _val) && _val;
allTypeParameters.PushMarker();
ResolveTypeParameters(e.TypeArgs, true, e);
scope.PushMarker();
foreach (BoundVar v in e.BoundVars) {
- if (!scope.Push(v.Name, v)) {
- Error(v, "Duplicate bound-variable name: {0}", v.Name);
- }
+ ScopePushAndReport(scope, v, "bound-variable");
var option = typeQuantifier ? new ResolveTypeOption(e) : new ResolveTypeOption(ResolveTypeOptionEnum.InferTypeProxies);
ResolveType(v.tok, v.Type, opts.codeContext, option, typeQuantifier ? e.TypeArgs : null);
}
if (e.TypeArgs.Count > 0 && !typeQuantifier) {
- Error(expr, "a quantifier cannot quantify over types. Possible fix: use the experimental attribute :typeQuantifier");
+ reporter.Error(MessageSource.Resolver, expr, "a quantifier cannot quantify over types. Possible fix: use the experimental attribute :typeQuantifier");
}
if (e.Range != null) {
- ResolveExpression(e.Range, new ResolveOpts(opts, true));
+ ResolveExpression(e.Range, opts);
Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.Range.Type, Type.Bool)) {
- Error(expr, "range of quantifier must be of type bool (instead got {0})", e.Range.Type);
- }
+ ConstrainTypes(e.Range.Type, Type.Bool, expr, "range of quantifier must be of type bool (instead got {0})", e.Range.Type);
}
- ResolveExpression(e.Term, new ResolveOpts(opts, true));
+ ResolveExpression(e.Term, opts);
Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.Term.Type, Type.Bool)) {
- Error(expr, "body of quantifier must be of type bool (instead got {0})", e.Term.Type);
- }
+ ConstrainTypes(e.Term.Type, Type.Bool, expr, "body of quantifier must be of type bool (instead got {0})", e.Term.Type);
// Since the body is more likely to infer the types of the bound variables, resolve it
// first (above) and only then resolve the attributes (below).
- ResolveAttributes(e.Attributes, new ResolveOpts(opts, true));
+ ResolveAttributes(e.Attributes, opts);
scope.PopMarker();
allTypeParameters.PopMarker();
expr.Type = Type.Bool;
- if (prevErrorCount == ErrorCount) {
- var missingBounds = new List<BoundVar>();
- CheckTypeInference(e.LogicalBody()); // we need to resolve operators before the call to DiscoverBounds
- e.Bounds = DiscoverBounds(e.tok, e.BoundVars, e.LogicalBody(), e is ExistsExpr, false, missingBounds);
- if (missingBounds.Count != 0) {
- // Report errors here about quantifications that depend on the allocation state.
- var mb = missingBounds;
- if (opts.codeContext is Function) {
- mb = new List<BoundVar>(); // (who cares if we allocate another array; this happens only in the case of a resolution error anyhow)
- foreach (var bv in missingBounds) {
- if (bv.Type.IsRefType) {
- Error(expr, "a quantifier involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of '{0}'", bv.Name);
- } else {
- mb.Add(bv);
- }
- }
- }
- if (mb.Count != 0) {
- e.MissingBounds = mb;
- }
- }
- }
-
} else if (expr is SetComprehension) {
var e = (SetComprehension)expr;
- int prevErrorCount = ErrorCount;
+ int prevErrorCount = reporter.Count(ErrorLevel.Error);
scope.PushMarker();
foreach (BoundVar v in e.BoundVars) {
- if (!scope.Push(v.Name, v)) {
- Error(v, "Duplicate bound-variable name: {0}", v.Name);
- }
+ ScopePushAndReport(scope, v, "bound-variable");
ResolveType(v.tok, v.Type, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null);
}
ResolveExpression(e.Range, opts);
Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.Range.Type, Type.Bool)) {
- Error(expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type);
- }
+ ConstrainTypes(e.Range.Type, Type.Bool, expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type);
ResolveExpression(e.Term, opts);
Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression
- ResolveAttributes(e.Attributes, new ResolveOpts(opts, true));
+ ResolveAttributes(e.Attributes, opts);
scope.PopMarker();
- expr.Type = new SetType(e.Term.Type);
-
- if (opts.DontCareAboutCompilation && (e.Term.Type.IsRefType || e.Term.Type.IsBoolType) || e.Term.Type.IsCharType) {
- // ok, term type is finite and we're in a ghost context
- } else {
- needFiniteBoundsChecks_SetComprehension.Add(e);
- }
+ expr.Type = new SetType(e.Finite, e.Term.Type);
} else if (expr is MapComprehension) {
var e = (MapComprehension)expr;
- int prevErrorCount = ErrorCount;
+ int prevErrorCount = reporter.Count(ErrorLevel.Error);
scope.PushMarker();
if (e.BoundVars.Count != 1) {
- Error(e.tok, "a map comprehension must have exactly one bound variable.");
+ reporter.Error(MessageSource.Resolver, e.tok, "a map comprehension must have exactly one bound variable.");
}
foreach (BoundVar v in e.BoundVars) {
- if (!scope.Push(v.Name, v)) {
- Error(v, "Duplicate bound-variable name: {0}", v.Name);
- }
+ ScopePushAndReport(scope, v, "bound-variable");
ResolveType(v.tok, v.Type, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null);
}
ResolveExpression(e.Range, opts);
Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.Range.Type, Type.Bool)) {
- Error(expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type);
- }
+ ConstrainTypes(e.Range.Type, Type.Bool, expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type);
ResolveExpression(e.Term, opts);
Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression
- ResolveAttributes(e.Attributes, new ResolveOpts(opts, true));
+ ResolveAttributes(e.Attributes, opts);
scope.PopMarker();
expr.Type = new MapType(e.Finite, e.BoundVars[0].Type, e.Term.Type);
- if (prevErrorCount == ErrorCount) {
- var missingBounds = new List<BoundVar>();
- CheckTypeInference(e.Range); // we need to resolve operators before the call to DiscoverBounds
- e.Bounds = DiscoverBounds(e.tok, e.BoundVars, e.Range, true, false, missingBounds);
- if (missingBounds.Count != 0) {
- e.MissingBounds = missingBounds;
- if (e.Finite) {
- foreach (var bv in e.MissingBounds) {
- Error(expr, "a map comprehension must produce a finite domain, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name);
- }
- }
- }
- }
} else if (expr is LambdaExpr) {
var e = (LambdaExpr)expr;
- int prevErrorCount = ErrorCount;
+ int prevErrorCount = reporter.Count(ErrorLevel.Error);
scope.PushMarker();
foreach (BoundVar v in e.BoundVars) {
- if (!scope.Push(v.Name, v)) {
- Error(v, "Duplicate bound-variable name: {0}", v.Name);
- }
+ ScopePushAndReport(scope, v, "bound-variable");
ResolveType(v.tok, v.Type, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null);
}
if (e.Range != null) {
ResolveExpression(e.Range, opts);
Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.Range.Type, Type.Bool)) {
- Error(expr, "Precondition must be boolean (got {0})", e.Range.Type);
- }
+ ConstrainTypes(e.Range.Type, Type.Bool, expr, "Precondition must be boolean (got {0})", e.Range.Type);
}
foreach (var read in e.Reads) {
- ResolveFrameExpression(read, true, false, opts.codeContext);
+ ResolveFrameExpression(read, true, opts.codeContext);
}
ResolveExpression(e.Term, opts);
@@ -7288,17 +8267,17 @@ namespace Microsoft.Dafny
scope.PopMarker();
expr.Type = new ArrowType(e.tok, Util.Map(e.BoundVars, v => v.Type), e.Body.Type, builtIns.SystemModule);
} else if (expr is WildcardExpr) {
- expr.Type = new SetType(new ObjectType());
+ expr.Type = new SetType(true, new ObjectType());
} else if (expr is StmtExpr) {
var e = (StmtExpr)expr;
- int prevErrorCount = ErrorCount;
- ResolveStatement(e.S, true, opts.codeContext);
- if (ErrorCount == prevErrorCount) {
+ int prevErrorCount = reporter.Count(ErrorLevel.Error);
+ ResolveStatement(e.S, opts.codeContext);
+ if (reporter.Count(ErrorLevel.Error) == prevErrorCount) {
var r = e.S as UpdateStmt;
if (r != null && r.ResolvedStatements.Count == 1) {
var call = r.ResolvedStatements[0] as CallStmt;
if (call.Method.Mod.Expressions.Count != 0) {
- Error(call, "calls to methods with side-effects are not allowed inside a statement expression");
+ reporter.Error(MessageSource.Resolver, call, "calls to methods with side-effects are not allowed inside a statement expression");
}
}
}
@@ -7314,107 +8293,502 @@ namespace Microsoft.Dafny
Contract.Assert(e.Thn.Type != null); // follows from postcondition of ResolveExpression
ResolveExpression(e.Els, opts);
Contract.Assert(e.Els.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.Test.Type, Type.Bool)) {
- Error(expr, "guard condition in if-then-else expression must be a boolean (instead got {0})", e.Test.Type);
- }
- if (UnifyTypes(e.Thn.Type, e.Els.Type)) {
+ ConstrainTypes(e.Test.Type, Type.Bool, expr, "guard condition in if-then-else expression must be a boolean (instead got {0})", e.Test.Type);
+ if (ConstrainTypes(e.Thn.Type, e.Els.Type, expr, "the two branches of an if-then-else expression must have the same type (got {0} and {1})", e.Thn.Type, e.Els.Type)) {
expr.Type = e.Thn.Type;
- } else {
- Error(expr, "the two branches of an if-then-else expression must have the same type (got {0} and {1})", e.Thn.Type, e.Els.Type);
}
} else if (expr is MatchExpr) {
- var me = (MatchExpr)expr;
- ResolveExpression(me.Source, opts);
- Contract.Assert(me.Source.Type != null); // follows from postcondition of ResolveExpression
- UserDefinedType sourceType = null;
- DatatypeDecl dtd = null;
- if (me.Source.Type.IsDatatype) {
- sourceType = (UserDefinedType)me.Source.Type.NormalizeExpand();
- dtd = cce.NonNull((DatatypeDecl)sourceType.ResolvedClass);
- }
- var subst = new Dictionary<TypeParameter, Type>();
- Dictionary<string, DatatypeCtor> ctors;
- if (dtd == null) {
- Error(me.Source, "the type of the match source expression must be a datatype (instead found {0})", me.Source.Type);
- ctors = null;
+ ResolveMatchExpr(expr, opts);
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
+ }
+
+ if (expr.Type == null) {
+ // some resolution error occurred
+ expr.Type = new InferredTypeProxy();
+ }
+ }
+
+ /// <summary>
+ /// Attempts to produce a let expression from the given datatype updates. Returns that let expression upon success, and null otherwise.
+ /// </summary>
+ Expression ResolveDatatypeUpdate(IToken tok, Expression root, DatatypeDecl dt, List<Tuple<IToken, string, Expression>> memberUpdates, bool sequentialUpdates, ResolveOpts opts) {
+ Contract.Requires(tok != null);
+ Contract.Requires(root != null);
+ Contract.Requires(dt != null);
+ Contract.Requires(memberUpdates != null);
+ Contract.Requires(opts != null);
+
+ // Let en = e and e(n-1) = en.Seq
+ // We're currently looking at e = e(n-1)[en.Index := en.Value]
+ // Let e(n-2) = e(n-1).Seq, e(n-3) = e(n-2).Seq, etc.
+ // Let's extract e = e0[e1.Index := e1.Value]...[en.Index := en.Value]
+ DatatypeCtor ctor = null;
+ Tuple<IToken, string, Expression> ctorSource = default(Tuple<IToken, string, Expression>); // relevant only if "ctor" is non-null
+ var memberNames = new HashSet<string>();
+ var updates = new Dictionary<Formal, Expression>();
+ foreach (var entry in memberUpdates) {
+ var destructor_str = entry.Item2;
+ if (memberNames.Contains(destructor_str)) {
+ if (sequentialUpdates) {
+ reporter.Warning(MessageSource.Resolver, entry.Item1, "update to '{0}' overwritten by another update to '{0}'", destructor_str);
+ } else {
+ reporter.Error(MessageSource.Resolver, entry.Item1, "duplicate update member '{0}'", destructor_str);
+ }
} else {
- Contract.Assert(sourceType != null); // dtd and sourceType are set together above
- ctors = datatypeCtors[dtd];
- Contract.Assert(ctors != null); // dtd should have been inserted into datatypeCtors during a previous resolution stage
+ memberNames.Add(destructor_str);
+ MemberDecl member;
+ if (!datatypeMembers[dt].TryGetValue(destructor_str, out member)) {
+ reporter.Error(MessageSource.Resolver, entry.Item1, "member '{0}' does not exist in datatype '{1}'", destructor_str, dt.Name);
+ } else {
+ var destructor = (DatatypeDestructor)member;
+ if (ctor != null && ctor != destructor.EnclosingCtor) {
+ if (sequentialUpdates) {
+ // This would eventually give rise to a verification error. However, since the 'sequentialUpdates' case is being depricated,
+ // we don't mind giving resolution error about this now.
+ }
+ reporter.Error(MessageSource.Resolver, entry.Item1, "updated datatype members must belong to the same constructor " +
+ "('{0}' belongs to '{1}' and '{2}' belongs to '{3}'", entry.Item2, destructor.EnclosingCtor.Name, ctorSource.Item2, ctor.Name);
+ } else {
+ updates.Add(destructor.CorrespondingFormal, entry.Item3);
+ ctor = destructor.EnclosingCtor;
+ ctorSource = entry;
+ }
+ }
+ }
+ }
+
+ if (ctor != null) {
+ // Rewrite an update of the form "dt[dtor := E]" to be "let d' := dt in dtCtr(E, d'.dtor2, d'.dtor3,...)"
+ // Wrapping it in a let expr avoids exponential growth in the size of the expression
+ // More generally, rewrite "E0[dtor1 := E1][dtor2 := E2]...[dtorn := En]" where "E0" is "root" to
+ // "let d' := E0 in dtCtr(...mixtures of Ek and d'.dtorj...)"
- // build the type-parameter substitution map for this use of the datatype
- for (int i = 0; i < dtd.TypeArgs.Count; i++) {
- subst.Add(dtd.TypeArgs[i], sourceType.TypeArgs[i]);
+ // Create a unique name for d', the variable we introduce in the let expression
+ var tmpName = FreshTempVarName("dt_update_tmp#", opts.codeContext);
+ var tmpVarIdExpr = new IdentifierExpr(new AutoGeneratedToken(tok), tmpName);
+ var tmpVarBv = new BoundVar(new AutoGeneratedToken(tok), tmpName, root.Type);
+
+ // Build the arguments to the datatype constructor, using the updated value in the appropriate slot
+ var ctor_args = new List<Expression>();
+ foreach (Formal d in ctor.Formals) {
+ Expression v;
+ if (updates.TryGetValue(d, out v)) {
+ ctor_args.Add(v);
+ } else {
+ ctor_args.Add(new ExprDotName(tok, tmpVarIdExpr, d.Name, null));
}
}
- ISet<string> memberNamesUsed = new HashSet<string>();
- expr.Type = new InferredTypeProxy();
- foreach (MatchCaseExpr mc in me.Cases) {
- DatatypeCtor ctor = null;
- if (ctors != null) {
- Contract.Assert(dtd != null);
- if (!ctors.TryGetValue(mc.Id, out ctor)) {
- Error(mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name);
+ DatatypeValue ctor_call = new DatatypeValue(tok, ctor.EnclosingDatatype.Name, ctor.Name, ctor_args);
+
+ CasePattern tmpVarPat = new CasePattern(tok, tmpVarBv);
+ LetExpr let = new LetExpr(tok, new List<CasePattern>() { tmpVarPat }, new List<Expression>() { root }, ctor_call, true);
+
+ ResolveExpression(let, opts);
+ return let;
+ }
+ return null;
+ }
+
+ void ResolveMatchExpr(Expression expr, ResolveOpts opts) {
+ var me = (MatchExpr)expr;
+ DesugarMatchExprWithTupleExpression(me);
+
+ ResolveExpression(me.Source, opts);
+ Contract.Assert(me.Source.Type != null); // follows from postcondition of ResolveExpression
+ UserDefinedType sourceType = null;
+ DatatypeDecl dtd = null;
+ if (me.Source.Type.IsDatatype) {
+ sourceType = (UserDefinedType)me.Source.Type.NormalizeExpand();
+ dtd = cce.NonNull((DatatypeDecl)sourceType.ResolvedClass);
+ }
+ var subst = new Dictionary<TypeParameter, Type>();
+ Dictionary<string, DatatypeCtor> ctors;
+ if (dtd == null) {
+ reporter.Error(MessageSource.Resolver, me.Source, "the type of the match source expression must be a datatype (instead found {0})", me.Source.Type);
+ ctors = null;
+ } else {
+ Contract.Assert(sourceType != null); // dtd and sourceType are set together above
+ ctors = datatypeCtors[dtd];
+ Contract.Assert(ctors != null); // dtd should have been inserted into datatypeCtors during a previous resolution stage
+
+ // build the type-parameter substitution map for this use of the datatype
+ for (int i = 0; i < dtd.TypeArgs.Count; i++) {
+ subst.Add(dtd.TypeArgs[i], sourceType.TypeArgs[i]);
+ }
+ }
+
+ // convert CasePattern in MatchCaseExpr to BoundVar and flatten the MatchCaseExpr.
+ List<Tuple<CasePattern, BoundVar>> patternSubst = new List<Tuple<CasePattern, BoundVar>>();
+ if (dtd != null) {
+ DesugarMatchCaseExpr(me, dtd, patternSubst, opts.codeContext);
+ }
+
+ ISet<string> memberNamesUsed = new HashSet<string>();
+ expr.Type = new InferredTypeProxy();
+ foreach (MatchCaseExpr mc in me.Cases) {
+ DatatypeCtor ctor = null;
+ if (ctors != null) {
+ Contract.Assert(dtd != null);
+ if (!ctors.TryGetValue(mc.Id, out ctor)) {
+ reporter.Error(MessageSource.Resolver, mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name);
+ } else {
+ Contract.Assert(ctor != null); // follows from postcondition of TryGetValue
+ mc.Ctor = ctor;
+ if (ctor.Formals.Count != mc.Arguments.Count) {
+ reporter.Error(MessageSource.Resolver, mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count);
+ }
+ if (memberNamesUsed.Contains(mc.Id)) {
+ reporter.Error(MessageSource.Resolver, mc.tok, "member {0} appears in more than one case", mc.Id);
} else {
- Contract.Assert(ctor != null); // follows from postcondition of TryGetValue
- mc.Ctor = ctor;
- if (ctor.Formals.Count != mc.Arguments.Count) {
- Error(mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count);
- }
- if (memberNamesUsed.Contains(mc.Id)) {
- Error(mc.tok, "member {0} appears in more than one case", mc.Id);
- } else {
- memberNamesUsed.Add(mc.Id); // add mc.Id to the set of names used
- }
+ memberNamesUsed.Add(mc.Id); // add mc.Id to the set of names used
}
}
- scope.PushMarker();
- int i = 0;
+ }
+ scope.PushMarker();
+ int i = 0;
+ if (mc.Arguments != null) {
foreach (BoundVar v in mc.Arguments) {
- if (!scope.Push(v.Name, v)) {
- Error(v, "Duplicate parameter name: {0}", v.Name);
- }
+ scope.Push(v.Name, v);
ResolveType(v.tok, v.Type, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null);
if (ctor != null && i < ctor.Formals.Count) {
Formal formal = ctor.Formals[i];
Type st = SubstType(formal.Type, subst);
- if (!UnifyTypes(v.Type, st)) {
- Error(expr, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st);
- }
+ ConstrainTypes(v.Type, st,
+ expr, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st);
v.IsGhost = formal.IsGhost;
+
+ // update the type of the boundvars in the MatchCaseToken
+ if (v.tok is MatchCaseToken) {
+ MatchCaseToken mt = (MatchCaseToken)v.tok;
+ foreach (Tuple<IToken, BoundVar, bool> entry in mt.varList) {
+ UnifyTypes(entry.Item2.Type, v.Type); // TODO: What to do if this unification fails? Or can it? --KRML
+ }
+ }
}
i++;
}
+ }
+ ResolveExpression(mc.Body, opts);
+ // substitute body to replace the case pat with v. This needs to happen
+ // after the body is resolved so we can scope the bv correctly.
+ if (patternSubst.Count > 0) {
+ MatchCaseExprSubstituteCloner cloner = new MatchCaseExprSubstituteCloner(patternSubst);
+ mc.UpdateBody(cloner.CloneExpr(mc.Body));
+ // resolve it again since we just cloned it.
ResolveExpression(mc.Body, opts);
- Contract.Assert(mc.Body.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(expr.Type, mc.Body.Type)) {
- Error(mc.Body.tok, "type of case bodies do not agree (found {0}, previous types {1})", mc.Body.Type, expr.Type);
+ }
+
+ Contract.Assert(mc.Body.Type != null); // follows from postcondition of ResolveExpression
+ ConstrainTypes(expr.Type, mc.Body.Type, mc.Body.tok, "type of case bodies do not agree (found {0}, previous types {1})", mc.Body.Type, expr.Type);
+ scope.PopMarker();
+ }
+ if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) {
+ // We could complain about the syntactic omission of constructors:
+ // reporter.Error(MessageSource.Resolver, expr, "match expression does not cover all constructors");
+ // but instead we let the verifier do a semantic check.
+ // So, for now, record the missing constructors:
+ foreach (var ctr in dtd.Ctors) {
+ if (!memberNamesUsed.Contains(ctr.Name)) {
+ me.MissingCases.Add(ctr);
+ }
+ }
+ Contract.Assert(memberNamesUsed.Count + me.MissingCases.Count == dtd.Ctors.Count);
+ }
+ }
+
+ /*
+ * Convert
+ * match (x, y)
+ * case (Zero, _) => Zero
+ * case (Suc(_), Zero) => x
+ * case (Suc(a), Suc(b)) => minus(a, b)
+ * To:
+ * match x
+ * case Zero => match y
+ * case _ => zero
+ * case Suc(_) => match y
+ * case Zero => x
+ * case Suc(a) => match y
+ * case (b) => minus(a,b)
+ */
+ private void DesugarMatchExprWithTupleExpression(MatchExpr me) {
+ // (x, y) is treated as a 2-tuple constructor
+ if (me.Source is DatatypeValue) {
+ var e = (DatatypeValue)me.Source;
+ if (e.Arguments.Count < 1) {
+ reporter.Error(MessageSource.Resolver, me.tok, "match source tuple needs at least 1 argument");
+ } else {
+ Expression source = e.Arguments[0];
+ List<MatchCaseExpr> cases = new List<MatchCaseExpr>();
+ foreach (MatchCaseExpr mc in me.Cases) {
+ if (mc.CasePatterns == null || mc.CasePatterns.Count != e.Arguments.Count) {
+ reporter.Error(MessageSource.Resolver, mc.tok, "case arguments count does not match source arguments count");
+ } else {
+ CasePattern cp = mc.CasePatterns[0];
+ List<CasePattern> patterns;
+ if (cp.Arguments != null) {
+ patterns = cp.Arguments;
+ } else {
+ patterns = new List<CasePattern>();
+ }
+
+ Expression body = mc.Body;
+ for (int i = e.Arguments.Count; 1 <= --i; ) {
+ // others go into the body
+ body = CreateMatchCaseExprBody(me.tok, e.Arguments[i], mc.CasePatterns[i], body);
+ }
+ cases.Add(new MatchCaseExpr(cp.tok, cp.Id, patterns, body));
+ }
}
- scope.PopMarker();
+ me.UpdateSource(source);
+ me.UpdateCases(cases);
}
- if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) {
- // We could complain about the syntactic omission of constructors:
- // Error(expr, "match expression does not cover all constructors");
- // but instead we let the verifier do a semantic check.
- // So, for now, record the missing constructors:
- foreach (var ctr in dtd.Ctors) {
- if (!memberNamesUsed.Contains(ctr.Name)) {
- me.MissingCases.Add(ctr);
+ }
+ }
+
+ Expression CreateMatchCaseExprBody(Boogie.IToken tok, Expression source, CasePattern cp, Expression body) {
+ List<MatchCaseExpr> cases = new List<MatchCaseExpr>();
+ List<CasePattern> patterns;
+ if (cp.Var != null) {
+ var bv = cp.Var;
+ if (LocalVariable.HasWildcardName(bv)) {
+ return body;
+ } else {
+ patterns = new List<CasePattern>();
+ }
+ } else {
+ patterns = cp.Arguments;
+ }
+ cases.Add(new MatchCaseExpr(cp.tok, cp.Id, patterns, body));
+ return new MatchExpr(tok, source, cases, false);
+ }
+
+ /*
+ * Convert
+ * match xs
+ * case Cons(y, Cons(z, zs)) => last(Cons(z, zs))
+ * case Cons(y, Nil) => y
+ * To
+ * match xs
+ * case Cons(y, ys) => match ys
+ * case Nil => y
+ * case Cons(z, zs) => last(ys)
+ * */
+ void DesugarMatchCaseExpr(MatchExpr me, DatatypeDecl dtd, List<Tuple<CasePattern, BoundVar>> patterns, ICodeContext codeContext) {
+ Contract.Assert(dtd != null);
+ Dictionary<string, DatatypeCtor> ctors = datatypeCtors[dtd];
+ if (ctors == null) {
+ // no constructors, there is no need to desugar
+ return;
+ }
+
+ Type type = new InferredTypeProxy();
+ string name = FreshTempVarName("_mc#", codeContext);
+ foreach (MatchCaseExpr mc in me.Cases) {
+ if (mc.Arguments != null) {
+ // already desugared. This happens during the second pass resolver after cloning.
+ Contract.Assert(mc.CasePatterns == null);
+ return;
+ }
+
+ BoundVar sourceVar = new BoundVar(new MatchCaseToken(me.tok), name, type);
+ Contract.Assert(mc.Arguments == null);
+ Contract.Assert(mc.CasePatterns != null);
+ Contract.Assert(ctors != null);
+ DatatypeCtor ctor = null;
+ if (ctors.TryGetValue(mc.Id, out ctor)) {
+ scope.PushMarker();
+ foreach (CasePattern pat in mc.CasePatterns) {
+ FindDuplicateIdentifier(pat, ctors, true);
+ }
+ List<BoundVar> arguments = new List<BoundVar>();
+ foreach (CasePattern pat in mc.CasePatterns) {
+ if (pat.Var != null) {
+ BoundVar v = pat.Var;
+ arguments.Add(v);
+ } else {
+ DesugarMatchCasePattern(mc, pat, sourceVar);
+ patterns.Add(new Tuple<CasePattern, BoundVar>(pat, sourceVar));
+ arguments.Add(sourceVar);
}
}
- Contract.Assert(memberNamesUsed.Count + me.MissingCases.Count == dtd.Ctors.Count);
+ mc.Arguments = arguments;
+ mc.CasePatterns = null;
+ scope.PopMarker();
+ }
+ }
+
+
+ List<MatchCaseExpr> newCases = new List<MatchCaseExpr>();
+
+ // need to consolidate the cases.
+ // Convert
+ // match xs
+ // case Cons(y, #mc#0) => match #mc#0
+ // case Cons((z, zs) => body
+ // case Cons(y, #mc#0) => match #mc#0
+ // case Nil => y
+ // into
+ // match xs
+ // case Cons(y, #mc#0) => match #mc#0
+ // case Cons((z, zs) => body
+ // case Nil => y
+ bool thingsChanged = false;
+ Dictionary<string, MatchCaseExpr> caseMap = new Dictionary<string, MatchCaseExpr>();
+ List<MatchCaseExpr> mcWithWildCard = new List<MatchCaseExpr>();
+ foreach (MatchCaseExpr mc in me.Cases) {
+ // check each CasePattern to see if it has wildcard.
+ if (CaseExprHasWildCard(mc)) {
+ mcWithWildCard.Add(mc);
+ } else {
+ thingsChanged |= CombineMatchCaseExpr(mc, newCases, caseMap, codeContext);
+ }
+ }
+
+ foreach (MatchCaseExpr mc in mcWithWildCard) {
+ // now process with cases with wildcard
+ thingsChanged |= CombineMatchCaseExpr(mc, newCases, caseMap, codeContext);
+ }
+
+ if (thingsChanged) {
+ me.UpdateCases(newCases);
+ }
+ }
+
+ void DesugarMatchCasePattern(MatchCaseExpr mc, CasePattern pat, BoundVar v) {
+ // convert
+ // case Cons(y, Cons(z, zs)) => body
+ // to
+ // case Cons(y, #mc#) => match #mc#
+ // case Cons(z, zs) => body
+
+ Expression source = new NameSegment(new AutoGeneratedToken(pat.tok), v.Name, null);
+ List<MatchCaseExpr> cases = new List<MatchCaseExpr>();
+ cases.Add(new MatchCaseExpr(pat.tok, pat.Id, pat.Arguments == null ? new List<CasePattern>() : pat.Arguments, mc.Body));
+ MatchExpr e = new MatchExpr(pat.tok, source, cases, false);
+ mc.UpdateBody(e);
+ }
+
+
+ bool CaseExprHasWildCard(MatchCase mc) {
+ if (mc.Arguments != null) {
+ foreach (BoundVar bv in mc.Arguments) {
+ if (LocalVariable.HasWildcardName(bv)) {
+ return true;
+ }
}
+ }
+ return false;
+ }
+ bool CombineMatchCaseExpr(MatchCaseExpr mc, List<MatchCaseExpr> newCases, Dictionary<string, MatchCaseExpr> caseMap, ICodeContext codeContext) {
+ bool thingsChanged = false;
+ MatchCaseExpr old_mc;
+ if (caseMap.TryGetValue(mc.Id, out old_mc)) {
+ // already has a case with the same ctor, try to consolidate the body.
+ if (SameMatchCaseExpr(old_mc, mc, codeContext)) {
+ MatchExpr old = (MatchExpr)old_mc.Body;
+ MatchExpr current = (MatchExpr)mc.Body;
+ foreach (MatchCaseExpr c in current.Cases) {
+ old.Cases.Add(c);
+ }
+ // add the token from mc to old_mc so the identifiers will show correctly in the IDE
+ List<BoundVar> arguments = new List<BoundVar>();
+ Contract.Assert(old_mc.Arguments.Count == mc.Arguments.Count);
+ for (int i = 0; i < old_mc.Arguments.Count; i++) {
+ var bv = old_mc.Arguments[i];
+ MatchCaseToken mcToken;
+ if (!(bv.tok is MatchCaseToken)) {
+ // create a MatchCaseToken
+ mcToken = new MatchCaseToken(bv.tok);
+ // clone the bv but with the MatchCaseToken
+ var bvNew = new BoundVar(mcToken, bv.Name, bv.Type);
+ bvNew.IsGhost = bv.IsGhost;
+ arguments.Add(bvNew);
+ } else {
+ mcToken = (MatchCaseToken)bv.tok;
+ arguments.Add(bv);
+ }
+ mcToken.AddVar(bv.tok, bv, true);
+ mcToken.AddVar(mc.Arguments[i].tok, mc.Arguments[i], true);
+ }
+ old_mc.Arguments = arguments;
+ thingsChanged = true;
+ } else {
+ // duplicate cases, do nothing for now. The error will be reported during resolving
+ }
} else {
- Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
+ // it is a new case.
+ newCases.Add(mc);
+ caseMap.Add(mc.Id, mc);
}
+ return thingsChanged;
+ }
- if (expr.Type == null) {
- // some resolution error occurred
- expr.Type = new InferredTypeProxy();
+
+ bool SameMatchCaseExpr(MatchCaseExpr one, MatchCaseExpr other, ICodeContext codeContext) {
+ // this method is called after all the CasePattern in the match cases are converted
+ // into BoundVars.
+ Contract.Assert(one.CasePatterns == null && one.Arguments != null);
+ Contract.Assert(other.CasePatterns == null && other.Arguments != null);
+ // In order to combine the two match cases, the bodies need to be a MatchExpr and
+ // the arguments and the source of the body are the same.
+ // We do string equals since they should be in the same scope.
+ if (one.Arguments.Count != other.Arguments.Count) {
+ return false;
+ }
+ if (!(one.Body is MatchExpr) || !(other.Body is MatchExpr)) {
+ return false;
+ }
+ var source1 = ((MatchExpr)one.Body).Source;
+ var source2 = ((MatchExpr)other.Body).Source;
+ if (!(source1 is NameSegment) || !(source2 is NameSegment)) {
+ return false;
+ }
+ if (!((NameSegment)source1).Name.Equals(((NameSegment)source2).Name)) {
+ return false;
+ }
+ for (int i = 0; i < one.Arguments.Count; i++) {
+ BoundVar bv1 = one.Arguments[i];
+ BoundVar bv2 = other.Arguments[i];
+ if (!LocalVariable.HasWildcardName(bv1) && !LocalVariable.HasWildcardName(bv2)) {
+ if (!bv1.Name.Equals(bv2.Name)) {
+ // need to substitute bv2 with bv1 in the matchstmt body
+ // what if match body already has the bv?? need to make a new bv
+ Type type = new InferredTypeProxy();
+ string name = FreshTempVarName("_mc#", codeContext);
+ MatchCaseToken mcToken = new MatchCaseToken(one.tok);
+ BoundVar bv = new BoundVar(mcToken, name, type);
+ mcToken.AddVar(bv1.tok, bv1, true);
+ mcToken.AddVar(bv2.tok, bv2, true);
+ // substitute the appeareance of old bv with the new bv in the match case
+ SubstituteMatchCaseBoundVar(one, bv1, bv);
+ SubstituteMatchCaseBoundVar(other, bv2, bv);
+ }
+ }
+ }
+ return true;
+ }
+
+ void SubstituteMatchCaseBoundVar(MatchCaseExpr mc, BoundVar oldBv, BoundVar newBv) {
+ List<BoundVar> arguments = new List<BoundVar>();
+ for (int i = 0; i < mc.Arguments.Count; i++) {
+ BoundVar bv = mc.Arguments[i];
+ if (bv == oldBv) {
+ arguments.Add(newBv);
+ } else {
+ arguments.Add(bv);
+ }
}
+ mc.Arguments = arguments;
+
+ // substitue the oldBv with newBv in the body
+ MatchCaseExprSubstituteCloner cloner = new MatchCaseExprSubstituteCloner(oldBv, newBv);
+ Expression clone = cloner.CloneExpr(mc.Body);
+ mc.UpdateBody(clone);
}
void ResolveCasePattern(CasePattern pat, Type sourceType, ICodeContext context) {
@@ -7431,29 +8805,30 @@ namespace Microsoft.Dafny
// Find the constructor in the given datatype
// If what was parsed was just an identifier, we will interpret it as a datatype constructor, if possible
DatatypeCtor ctor = null;
- if (pat.Var == null || (pat.Var != null && pat.Var.Type is TypeProxy && dtd != null)) {
- if (datatypeCtors[dtd].TryGetValue(pat.Id, out ctor)) {
- pat.Ctor = ctor;
- pat.Var = null;
+ if (dtd != null) {
+ if (pat.Var == null || (pat.Var != null && pat.Var.Type is TypeProxy)) {
+ if (datatypeCtors[dtd].TryGetValue(pat.Id, out ctor)) {
+ pat.Ctor = ctor;
+ pat.Var = null;
+ }
}
}
+
if (pat.Var != null) {
// this is a simple resolution
var v = pat.Var;
ResolveType(v.tok, v.Type, context, ResolveTypeOptionEnum.InferTypeProxies, null);
- if (!UnifyTypes(v.Type, sourceType)) {
- Error(v.tok, "type of corresponding source/RHS ({0}) does not match type of bound variable ({1})", sourceType, v.Type);
- }
+ ConstrainTypes(v.Type, sourceType, v.tok, "type of corresponding source/RHS ({0}) does not match type of bound variable ({1})", sourceType, v.Type);
pat.AssembleExpr(null);
} else if (dtd == null) {
- Error(pat.tok, "to use a pattern, the type of the source/RHS expression must be a datatype (instead found {0})", sourceType);
+ reporter.Error(MessageSource.Resolver, pat.tok, "to use a pattern, the type of the source/RHS expression must be a datatype (instead found {0})", sourceType);
} else if (ctor == null) {
- Error(pat.tok, "constructor {0} does not exist in datatype {1}", pat.Id, dtd.Name);
+ reporter.Error(MessageSource.Resolver, pat.tok, "constructor {0} does not exist in datatype {1}", pat.Id, dtd.Name);
} else {
var argCount = pat.Arguments == null ? 0 : pat.Arguments.Count;
if (ctor.Formals.Count != argCount) {
- Error(pat.tok, "pattern for constructor {0} has wrong number of formals (found {1}, expected {2})", pat.Id, argCount, ctor.Formals.Count);
+ reporter.Error(MessageSource.Resolver, pat.tok, "pattern for constructor {0} has wrong number of formals (found {1}, expected {2})", pat.Id, argCount, ctor.Formals.Count);
}
// build the type-parameter substitution map for this use of the datatype
Contract.Assert(dtd.TypeArgs.Count == udt.TypeArgs.Count); // follows from the type previously having been successfully resolved
@@ -7463,13 +8838,15 @@ namespace Microsoft.Dafny
}
// recursively call ResolveCasePattern on each of the arguments
var j = 0;
- foreach (var arg in pat.Arguments) {
- if (j < ctor.Formals.Count) {
- var formal = ctor.Formals[j];
- Type st = SubstType(formal.Type, subst);
- ResolveCasePattern(arg, st, context);
+ if (pat.Arguments != null) {
+ foreach (var arg in pat.Arguments) {
+ if (j < ctor.Formals.Count) {
+ var formal = ctor.Formals[j];
+ Type st = SubstType(formal.Type, subst);
+ ResolveCasePattern(arg, st, context);
+ }
+ j++;
}
- j++;
}
if (j == ctor.Formals.Count) {
pat.AssembleExpr(udt.TypeArgs);
@@ -7502,12 +8879,12 @@ namespace Microsoft.Dafny
Contract.Requires(!expr.WasResolved());
Contract.Requires(opts != null);
Contract.Ensures(Contract.Result<Expression>() == null || args != null);
-
+
if (expr.OptTypeArguments != null) {
foreach (var ty in expr.OptTypeArguments) {
ResolveType(expr.tok, ty, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null);
if (ty.IsSubrangeType) {
- Error(expr.tok, "sorry, cannot instantiate type parameter with a subrange type");
+ reporter.Error(MessageSource.Resolver, expr.tok, "sorry, cannot instantiate type parameter with a subrange type");
}
}
}
@@ -7530,7 +8907,7 @@ namespace Microsoft.Dafny
if (v != null) {
// ----- 0. local variable, parameter, or bound variable
if (expr.OptTypeArguments != null) {
- Error(expr.tok, "variable '{0}' does not take any type parameters", expr.Name);
+ reporter.Error(MessageSource.Resolver, expr.tok, "variable '{0}' does not take any type parameters", expr.Name);
}
var rr = new IdentifierExpr(expr.tok, expr.Name);
rr.Var = v; rr.Type = v.Type;
@@ -7539,10 +8916,10 @@ namespace Microsoft.Dafny
// ----- 1. member of the enclosing class
Expression receiver;
if (member.IsStatic) {
- receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass);
+ receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass, true);
} else {
if (!scope.AllowInstance) {
- Error(expr.tok, "'this' is not allowed in a 'static' context");
+ reporter.Error(MessageSource.Resolver, expr.tok, "'this' is not allowed in a 'static' context"); //TODO: Rephrase this
// nevertheless, set "receiver" to a value so we can continue resolution
}
receiver = new ImplicitThisExpr(expr.tok);
@@ -7553,10 +8930,10 @@ namespace Microsoft.Dafny
// ----- 2. datatype constructor
if (pair.Item2) {
// there is more than one constructor with this name
- Error(expr.tok, "the name '{0}' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, '{1}.{0}')", expr.Name, pair.Item1.EnclosingDatatype.Name);
+ reporter.Error(MessageSource.Resolver, expr.tok, "the name '{0}' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, '{1}.{0}')", expr.Name, pair.Item1.EnclosingDatatype.Name);
} else {
if (expr.OptTypeArguments != null) {
- Error(expr.tok, "datatype constructor does not take any type parameters ('{0}')", expr.Name);
+ reporter.Error(MessageSource.Resolver, expr.tok, "datatype constructor does not take any type parameters ('{0}')", expr.Name);
}
var rr = new DatatypeValue(expr.tok, pair.Item1.EnclosingDatatype.Name, expr.Name, args ?? new List<Expression>());
ResolveDatatypeValue(opts, rr, pair.Item1.EnclosingDatatype);
@@ -7571,7 +8948,7 @@ namespace Microsoft.Dafny
// ----- 3. Member of the enclosing module
if (decl is AmbiguousTopLevelDecl) {
var ad = (AmbiguousTopLevelDecl)decl;
- Error(expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.Name, ad.ModuleNames());
+ reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.Name, ad.ModuleNames());
} else {
// We have found a module name or a type name, neither of which is an expression. However, the NameSegment we're
// looking at may be followed by a further suffix that makes this into an expresion. We postpone the rest of the
@@ -7585,15 +8962,15 @@ namespace Microsoft.Dafny
Contract.Assert(member.IsStatic); // moduleInfo.StaticMembers is supposed to contain only static members of the module's implicit class _default
if (member is AmbiguousMemberDecl) {
var ambiguousMember = (AmbiguousMemberDecl)member;
- Error(expr.tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", expr.Name, ambiguousMember.ModuleNames());
+ reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", expr.Name, ambiguousMember.ModuleNames());
} else {
- var receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass);
+ var receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass, true);
r = ResolveExprDotCall(expr.tok, receiver, member, expr.OptTypeArguments, opts.codeContext, allowMethodCall);
}
} else {
// ----- None of the above
- Error(expr.tok, "unresolved identifier: {0}", expr.Name);
+ reporter.Error(MessageSource.Resolver, expr.tok, "unresolved identifier: {0}", expr.Name);
}
if (r == null) {
@@ -7625,7 +9002,7 @@ namespace Microsoft.Dafny
foreach (var ty in expr.OptTypeArguments) {
ResolveType(expr.tok, ty, opts.codeContext, option, defaultTypeArguments);
if (ty.IsSubrangeType) {
- Error(expr.tok, "sorry, cannot instantiate type parameter with a subrange type");
+ reporter.Error(MessageSource.Resolver, expr.tok, "sorry, cannot instantiate type parameter with a subrange type");
}
}
}
@@ -7649,7 +9026,7 @@ namespace Microsoft.Dafny
if (expr.OptTypeArguments == null) {
r = new Resolver_IdentifierExpr(expr.tok, tp);
} else {
- Error(expr.tok, "Type parameter expects no type arguments: {0}", expr.Name);
+ reporter.Error(MessageSource.Resolver, expr.tok, "Type parameter expects no type arguments: {0}", expr.Name);
}
#if ASYNC_TASK_TYPES // At the moment, there is no way for a class member to part of a type name, but this changes with async task types
} else if (currentClass != null && classMembers.TryGetValue(currentClass, out members) && members.TryGetValue(expr.Name, out member)) {
@@ -7659,7 +9036,7 @@ namespace Microsoft.Dafny
receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass);
} else {
if (!scope.AllowInstance) {
- Error(expr.tok, "'this' is not allowed in a 'static' context");
+ reporter.Error(MessageSource.Resolver, expr.tok, "'this' is not allowed in a 'static' context");
// nevertheless, set "receiver" to a value so we can continue resolution
}
receiver = new ImplicitThisExpr(expr.tok);
@@ -7671,7 +9048,7 @@ namespace Microsoft.Dafny
// ----- 2. Member of the enclosing module
if (decl is AmbiguousTopLevelDecl) {
var ad = (AmbiguousTopLevelDecl)decl;
- Error(expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.Name, ad.ModuleNames());
+ reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.Name, ad.ModuleNames());
} else {
// We have found a module name or a type name, neither of which is a type expression. However, the NameSegment we're
// looking at may be followed by a further suffix that makes this into a type expresion. We postpone the rest of the
@@ -7685,21 +9062,15 @@ namespace Microsoft.Dafny
// ----- 3. static member of the enclosing module
Contract.Assert(member.IsStatic); // moduleInfo.StaticMembers is supposed to contain only static members of the module's implicit class _default
if (ReallyAmbiguousThing(ref member)) {
- Error(expr.tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", expr.Name, ((AmbiguousMemberDecl)member).ModuleNames());
+ reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", expr.Name, ((AmbiguousMemberDecl)member).ModuleNames());
} else {
var receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass);
r = ResolveExprDotCall(expr.tok, receiver, member, expr.OptTypeArguments, opts.codeContext, allowMethodCall);
}
#endif
- } else if (option.Opt == ResolveTypeOptionEnum.AllowPrefixExtend && expr.OptTypeArguments == null) {
- // it woulc plausibly be a type parameter, but isn't; we will declare it automatically
- tp = new TypeParameter(expr.tok, expr.Name, defaultTypeArguments.Count, option.Parent);
- defaultTypeArguments.Add(tp);
- r = new Resolver_IdentifierExpr(expr.tok, tp);
- allTypeParameters.Push(expr.Name, tp);
} else {
// ----- None of the above
- Error(expr.tok, "Undeclared top-level type or type parameter: {0} (did you forget to qualify a name?)", expr.Name);
+ reporter.Error(MessageSource.Resolver, expr.tok, "Undeclared top-level type or type parameter: {0} (did you forget to qualify a name or declare a module import 'opened?')", expr.Name);
}
if (r == null) {
@@ -7720,7 +9091,7 @@ namespace Microsoft.Dafny
if (optTypeArguments != null) {
// type arguments were supplied; they must be equal in number to those expected
if (n != decl.TypeArgs.Count) {
- Error(tok, "Wrong number of type arguments ({0} instead of {1}) passed to {2}: {3}", n, decl.TypeArgs.Count, decl.WhatKind, name);
+ reporter.Error(MessageSource.Resolver, tok, "Wrong number of type arguments ({0} instead of {1}) passed to {2}: {3}", n, decl.TypeArgs.Count, decl.WhatKind, name);
}
}
List<Type> tpArgs = new List<Type>();
@@ -7772,7 +9143,7 @@ namespace Microsoft.Dafny
foreach (var ty in expr.OptTypeArguments) {
ResolveType(expr.tok, ty, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null);
if (ty.IsSubrangeType) {
- Error(expr.tok, "sorry, cannot instantiate type parameter with a subrange type");
+ reporter.Error(MessageSource.Resolver, expr.tok, "sorry, cannot instantiate type parameter with a subrange type");
}
}
}
@@ -7795,10 +9166,10 @@ namespace Microsoft.Dafny
// ----- 0. datatype constructor
if (pair.Item2) {
// there is more than one constructor with this name
- Error(expr.tok, "the name '{0}' denotes a datatype constructor in module {2}, but does not do so uniquely; add an explicit qualification (for example, '{1}.{0}')", expr.SuffixName, pair.Item1.EnclosingDatatype.Name, ((ModuleDecl)ri.Decl).Name);
+ reporter.Error(MessageSource.Resolver, expr.tok, "the name '{0}' denotes a datatype constructor in module {2}, but does not do so uniquely; add an explicit qualification (for example, '{1}.{0}')", expr.SuffixName, pair.Item1.EnclosingDatatype.Name, ((ModuleDecl)ri.Decl).Name);
} else {
if (expr.OptTypeArguments != null) {
- Error(expr.tok, "datatype constructor does not take any type parameters ('{0}')", expr.SuffixName);
+ reporter.Error(MessageSource.Resolver, expr.tok, "datatype constructor does not take any type parameters ('{0}')", expr.SuffixName);
}
var rr = new DatatypeValue(expr.tok, pair.Item1.EnclosingDatatype.Name, expr.SuffixName, args ?? new List<Expression>());
ResolveExpression(rr, opts);
@@ -7813,7 +9184,7 @@ namespace Microsoft.Dafny
// ----- 1. Member of the specified module
if (decl is AmbiguousTopLevelDecl) {
var ad = (AmbiguousTopLevelDecl)decl;
- Error(expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.SuffixName, ad.ModuleNames());
+ reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.SuffixName, ad.ModuleNames());
} else {
// We have found a module name or a type name, neither of which is an expression. However, the ExprDotName we're
// looking at may be followed by a further suffix that makes this into an expresion. We postpone the rest of the
@@ -7826,13 +9197,13 @@ namespace Microsoft.Dafny
Contract.Assert(member.IsStatic); // moduleInfo.StaticMembers is supposed to contain only static members of the module's implicit class _default
if (member is AmbiguousMemberDecl) {
var ambiguousMember = (AmbiguousMemberDecl)member;
- Error(expr.tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", expr.SuffixName, ambiguousMember.ModuleNames());
+ reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", expr.SuffixName, ambiguousMember.ModuleNames());
} else {
- var receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass);
+ var receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass, true);
r = ResolveExprDotCall(expr.tok, receiver, member, expr.OptTypeArguments, opts.codeContext, allowMethodCall);
}
} else {
- Error(expr.tok, "unresolved identifier: {0}", expr.SuffixName);
+ reporter.Error(MessageSource.Resolver, expr.tok, "unresolved identifier: {0}", expr.SuffixName);
}
} else if (lhs != null && lhs.Type is Resolver_IdentifierExpr.ResolverType_Type) {
@@ -7851,10 +9222,10 @@ namespace Microsoft.Dafny
Dictionary<string, MemberDecl> members;
if (classMembers.TryGetValue(cd, out members) && members.TryGetValue(expr.SuffixName, out member)) {
if (!member.IsStatic) {
- Error(expr.tok, "accessing member '{0}' requires an instance expression", expr.SuffixName);
+ reporter.Error(MessageSource.Resolver, expr.tok, "accessing member '{0}' requires an instance expression", expr.SuffixName); //TODO Unify with similar error messages
// nevertheless, continue creating an expression that approximates a correct one
}
- var receiver = new StaticReceiverExpr(expr.tok, (UserDefinedType)ty.NormalizeExpand(), (ClassDecl)member.EnclosingClass);
+ var receiver = new StaticReceiverExpr(expr.tok, (UserDefinedType)ty.NormalizeExpand(), (ClassDecl)member.EnclosingClass, false);
r = ResolveExprDotCall(expr.tok, receiver, member, expr.OptTypeArguments, opts.codeContext, allowMethodCall);
}
} else if (ty.IsDatatype) {
@@ -7864,7 +9235,7 @@ namespace Microsoft.Dafny
DatatypeCtor ctor;
if (datatypeCtors.TryGetValue(dt, out members) && members.TryGetValue(expr.SuffixName, out ctor)) {
if (expr.OptTypeArguments != null) {
- Error(expr.tok, "datatype constructor does not take any type parameters ('{0}')", expr.SuffixName);
+ reporter.Error(MessageSource.Resolver, expr.tok, "datatype constructor does not take any type parameters ('{0}')", expr.SuffixName);
}
var rr = new DatatypeValue(expr.tok, ctor.EnclosingDatatype.Name, expr.SuffixName, args ?? new List<Expression>());
ResolveDatatypeValue(opts, rr, ctor.EnclosingDatatype);
@@ -7877,19 +9248,20 @@ namespace Microsoft.Dafny
}
}
if (r == null) {
- Error(expr.tok, "member '{0}' does not exist in type '{1}'", expr.SuffixName, ri.TypeParamDecl != null ? ri.TypeParamDecl.Name : ri.Decl.Name);
+ reporter.Error(MessageSource.Resolver, expr.tok, "member '{0}' does not exist in type '{1}'", expr.SuffixName, ri.TypeParamDecl != null ? ri.TypeParamDecl.Name : ri.Decl.Name);
}
} else if (lhs != null) {
// ----- 4. Look up name in the type of the Lhs
+ bool classMemberOnly = UserDefinedType.DenotesClass(expr.Lhs.Type) == null ? false : true;
NonProxyType nptype;
- member = ResolveMember(expr.tok, expr.Lhs.Type, expr.SuffixName, out nptype);
+ member = ResolveMember(expr.tok, expr.Lhs.Type, expr.SuffixName, out nptype, classMemberOnly);
if (member != null) {
Expression receiver;
if (!member.IsStatic) {
receiver = expr.Lhs;
} else {
Contract.Assert(expr.Lhs.Type.IsRefType); // only reference types have static methods
- receiver = new StaticReceiverExpr(expr.tok, (UserDefinedType)expr.Lhs.Type.NormalizeExpand(), (ClassDecl)member.EnclosingClass);
+ receiver = new StaticReceiverExpr(expr.tok, (UserDefinedType)expr.Lhs.Type.NormalizeExpand(), (ClassDecl)member.EnclosingClass, false);
}
r = ResolveExprDotCall(expr.tok, receiver, member, expr.OptTypeArguments, opts.codeContext, allowMethodCall);
}
@@ -7937,7 +9309,7 @@ namespace Microsoft.Dafny
foreach (var ty in expr.OptTypeArguments) {
ResolveType(expr.tok, ty, opts.codeContext, option, defaultTypeArguments);
if (ty.IsSubrangeType) {
- Error(expr.tok, "sorry, cannot instantiate type parameter with a subrange type");
+ reporter.Error(MessageSource.Resolver, expr.tok, "sorry, cannot instantiate type parameter with a subrange type");
}
}
}
@@ -7956,7 +9328,7 @@ namespace Microsoft.Dafny
// ----- 0. Member of the specified module
if (decl is AmbiguousTopLevelDecl) {
var ad = (AmbiguousTopLevelDecl)decl;
- Error(expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.SuffixName, ad.ModuleNames());
+ reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.SuffixName, ad.ModuleNames());
} else {
// We have found a module name or a type name. We create a temporary expression that will never be seen by the compiler
// or verifier, just to have a placeholder where we can recorded what we have found.
@@ -7967,14 +9339,14 @@ namespace Microsoft.Dafny
// ----- 1. static member of the specified module
Contract.Assert(member.IsStatic); // moduleInfo.StaticMembers is supposed to contain only static members of the module's implicit class _default
if (ReallyAmbiguousThing(ref member)) {
- Error(expr.tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", expr.SuffixName, ((AmbiguousMemberDecl)member).ModuleNames());
+ reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", expr.SuffixName, ((AmbiguousMemberDecl)member).ModuleNames());
} else {
var receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass);
r = ResolveExprDotCall(expr.tok, receiver, member, expr.OptTypeArguments, opts.codeContext, allowMethodCall);
}
#endif
} else {
- Error(expr.tok, "module '{0}' does not declare a type '{1}'", ri.Decl.Name, expr.SuffixName);
+ reporter.Error(MessageSource.Resolver, expr.tok, "module '{0}' does not declare a type '{1}'", ri.Decl.Name, expr.SuffixName);
}
} else if (lhs != null && lhs.Type is Resolver_IdentifierExpr.ResolverType_Type) {
@@ -7990,7 +9362,7 @@ namespace Microsoft.Dafny
return new ResolveTypeReturn(ty, expr);
}
if (r == null) {
- Error(expr.tok, "member '{0}' does not exist in type '{1}' or cannot be part of type name", expr.SuffixName, ri.TypeParamDecl != null ? ri.TypeParamDecl.Name : ri.Decl.Name);
+ reporter.Error(MessageSource.Resolver, expr.tok, "member '{0}' does not exist in type '{1}' or cannot be part of type name", expr.SuffixName, ri.TypeParamDecl != null ? ri.TypeParamDecl.Name : ri.Decl.Name);
}
}
@@ -8027,14 +9399,14 @@ namespace Microsoft.Dafny
if (member is Field) {
if (optTypeArguments != null) {
- Error(tok, "a field ({0}) does not take any type arguments (got {1})", member.Name, optTypeArguments.Count);
+ reporter.Error(MessageSource.Resolver, tok, "a field ({0}) does not take any type arguments (got {1})", member.Name, optTypeArguments.Count);
}
rr.Type = SubstType(((Field)member).Type, subst);
} else if (member is Function) {
var fn = (Function)member;
int suppliedTypeArguments = optTypeArguments == null ? 0 : optTypeArguments.Count;
if (optTypeArguments != null && suppliedTypeArguments != fn.TypeArgs.Count) {
- Error(tok, "function '{0}' expects {1} type arguments (got {2})", member.Name, fn.TypeArgs.Count, suppliedTypeArguments);
+ reporter.Error(MessageSource.Resolver, tok, "function '{0}' expects {1} type arguments (got {2})", member.Name, fn.TypeArgs.Count, suppliedTypeArguments);
}
rr.TypeApplication = new List<Type>();
if (udt != null && udt.ResolvedClass != null) {
@@ -8052,11 +9424,11 @@ namespace Microsoft.Dafny
var m = (Method)member;
if (!allowMethodCall) {
// it's a method and method calls are not allowed in the given context
- Error(tok, "expression is not allowed to invoke a method ({0})", member.Name);
+ reporter.Error(MessageSource.Resolver, tok, "expression is not allowed to invoke a method ({0})", member.Name);
}
int suppliedTypeArguments = optTypeArguments == null ? 0 : optTypeArguments.Count;
if (optTypeArguments != null && suppliedTypeArguments != m.TypeArgs.Count) {
- Error(tok, "method '{0}' expects {1} type arguments (got {2})", member.Name, m.TypeArgs.Count, suppliedTypeArguments);
+ reporter.Error(MessageSource.Resolver, tok, "method '{0}' expects {1} type arguments (got {2})", member.Name, m.TypeArgs.Count, suppliedTypeArguments);
}
rr.TypeApplication = new List<Type>();
if (udt != null && udt.ResolvedClass != null) {
@@ -8101,7 +9473,7 @@ namespace Microsoft.Dafny
Contract.Requires(opts != null);
Contract.Ensures(Contract.Result<MethodCallInformation>() == null || allowMethodCall);
Expression r = null; // upon success, the expression to which the ApplySuffix resolves
- var errorCount = ErrorCount;
+ var errorCount = reporter.Count(ErrorLevel.Error);
if (e.Lhs is NameSegment) {
r = ResolveNameSegment((NameSegment)e.Lhs, true, e.Args, opts, allowMethodCall);
// note, if r is non-null, then e.Args have been resolved and r is a resolved expression that incorporates e.Args
@@ -8119,18 +9491,18 @@ namespace Microsoft.Dafny
if (fnType == null) {
var lhs = e.Lhs.Resolved;
if (lhs != null && lhs.Type is Resolver_IdentifierExpr.ResolverType_Module) {
- Error(e.tok, "name of module ({0}) is used as a function", ((Resolver_IdentifierExpr)lhs).Decl.Name);
+ reporter.Error(MessageSource.Resolver, e.tok, "name of module ({0}) is used as a function", ((Resolver_IdentifierExpr)lhs).Decl.Name);
} else if (lhs != null && lhs.Type is Resolver_IdentifierExpr.ResolverType_Type) {
// It may be a conversion expression
var ri = (Resolver_IdentifierExpr)lhs;
if (ri.TypeParamDecl != null) {
- Error(e.tok, "name of type parameter ({0}) is used as a function", ri.TypeParamDecl.Name);
+ reporter.Error(MessageSource.Resolver, e.tok, "name of type parameter ({0}) is used as a function", ri.TypeParamDecl.Name);
} else {
var decl = ri.Decl;
var ty = new UserDefinedType(e.tok, decl.Name, decl, ri.TypeArgs);
if (ty.AsNewtype != null) {
if (e.Args.Count != 1) {
- Error(e.tok, "conversion operation to {0} got wrong number of arguments (expected 1, got {1})", decl.Name, e.Args.Count);
+ reporter.Error(MessageSource.Resolver, e.tok, "conversion operation to {0} got wrong number of arguments (expected 1, got {1})", decl.Name, e.Args.Count);
}
var conversionArg = 1 <= e.Args.Count ? e.Args[0] :
ty.IsNumericBased(Type.NumericPersuation.Int) ? LiteralExpr.CreateIntLiteral(e.tok, 0) :
@@ -8142,7 +9514,7 @@ namespace Microsoft.Dafny
ResolveExpression(e.Args[i], opts);
}
} else {
- Error(e.tok, "name of type ({0}) is used as a function", decl.Name);
+ reporter.Error(MessageSource.Resolver, e.tok, "name of type ({0}) is used as a function", decl.Name);
}
}
} else {
@@ -8152,10 +9524,10 @@ namespace Microsoft.Dafny
var cRhs = new MethodCallInformation(e.tok, mse, e.Args);
return cRhs;
} else {
- Error(e.tok, "method call is not allowed to be used in an expression context ({0})", mse.Member.Name);
+ reporter.Error(MessageSource.Resolver, e.tok, "method call is not allowed to be used in an expression context ({0})", mse.Member.Name);
}
} else if (lhs != null) { // if e.Lhs.Resolved is null, then e.Lhs was not successfully resolved and an error has already been reported
- Error(e.tok, "non-function expression (of type {0}) is called with parameters", e.Lhs.Type);
+ reporter.Error(MessageSource.Resolver, e.tok, "non-function expression (of type {0}) is called with parameters", e.Lhs.Type);
}
}
} else {
@@ -8163,14 +9535,13 @@ namespace Microsoft.Dafny
var callee = mse == null ? null : mse.Member as Function;
if (fnType.Arity != e.Args.Count) {
var what = callee != null ? string.Format("function '{0}'", callee.Name) : string.Format("function type '{0}'", fnType);
- Error(e.tok, "wrong number of arguments to function application ({0} expects {1}, got {2})", what, fnType.Arity, e.Args.Count);
+ reporter.Error(MessageSource.Resolver, e.tok, "wrong number of arguments to function application ({0} expects {1}, got {2})", what, fnType.Arity, e.Args.Count);
} else {
for (var i = 0; i < fnType.Arity; i++) {
- if (!UnifyTypes(fnType.Args[i], e.Args[i].Type)) {
- Error(e.Args[i].tok, "type mismatch for argument {0} (function expects {1}, got {2})", i, fnType.Args[i], e.Args[i].Type);
- }
+ ConstrainTypes(fnType.Args[i], e.Args[i].Type,
+ e.Args[i].tok, "type mismatch for argument {0} (function expects {1}, got {2})", i, fnType.Args[i], e.Args[i].Type);
}
- if (errorCount != ErrorCount) {
+ if (errorCount != reporter.Count(ErrorLevel.Error)) {
// do nothing else; error has been reported
} else if (callee != null) {
// produce a FunctionCallExpr instead of an ApplyExpr(MemberSelectExpr)
@@ -8194,9 +9565,7 @@ namespace Microsoft.Dafny
ResolveExpression(farg, opts);
Contract.Assert(farg.Type != null); // follows from postcondition of ResolveExpression
Type s = SubstType(callee.Formals[i].Type, rr.TypeArgumentSubstitutions);
- if (!UnifyTypes(farg.Type, s)) {
- Error(rr, "incorrect type of function argument {0} (expected {1}, got {2})", i, s, farg.Type);
- }
+ ConstrainTypes(farg.Type, s, rr, "incorrect type of function argument {0} (expected {1}, got {2})", i, s, farg.Type);
}
rr.Type = SubstType(callee.ResultType, rr.TypeArgumentSubstitutions);
// further bookkeeping
@@ -8237,12 +9606,12 @@ namespace Microsoft.Dafny
DatatypeCtor ctor;
if (!datatypeCtors[dt].TryGetValue(dtv.MemberName, out ctor)) {
- Error(dtv.tok, "undeclared constructor {0} in datatype {1}", dtv.MemberName, dtv.DatatypeName);
+ reporter.Error(MessageSource.Resolver, dtv.tok, "undeclared constructor {0} in datatype {1}", dtv.MemberName, dtv.DatatypeName);
} else {
Contract.Assert(ctor != null); // follows from postcondition of TryGetValue
dtv.Ctor = ctor;
if (ctor.Formals.Count != dtv.Arguments.Count) {
- Error(dtv.tok, "wrong number of arguments to datatype constructor {0} (found {1}, expected {2})", ctor.Name, dtv.Arguments.Count, ctor.Formals.Count);
+ reporter.Error(MessageSource.Resolver, dtv.tok, "wrong number of arguments to datatype constructor {0} (found {1}, expected {2})", ctor.Name, dtv.Arguments.Count, ctor.Formals.Count);
}
}
int j = 0;
@@ -8252,9 +9621,7 @@ namespace Microsoft.Dafny
Contract.Assert(arg.Type != null); // follows from postcondition of ResolveExpression
if (formal != null) {
Type st = SubstType(formal.Type, subst);
- if (!UnifyTypes(arg.Type, st)) {
- Error(arg.tok, "incorrect type of datatype constructor argument (found {0}, expected {1})", arg.Type, st);
- }
+ ConstrainTypes(arg.Type, st, arg.tok, "incorrect type of datatype constructor argument (found {0}, expected {1})", arg.Type, st);
}
j++;
}
@@ -8290,21 +9657,21 @@ namespace Microsoft.Dafny
/// Generate an error for every non-ghost feature used in "expr".
/// Requires "expr" to have been successfully resolved.
/// </summary>
- void CheckIsNonGhost(Expression expr) {
+ void CheckIsCompilable(Expression expr) {
Contract.Requires(expr != null);
Contract.Requires(expr.WasResolved()); // this check approximates the requirement that "expr" be resolved
if (expr is IdentifierExpr) {
var e = (IdentifierExpr)expr;
if (e.Var != null && e.Var.IsGhost) {
- Error(expr, "ghost variables are allowed only in specification contexts");
+ reporter.Error(MessageSource.Resolver, expr, "ghost variables are allowed only in specification contexts");
return;
}
} else if (expr is MemberSelectExpr) {
var e = (MemberSelectExpr)expr;
if (e.Member != null && e.Member.IsGhost) {
- Error(expr, "ghost fields are allowed only in specification contexts");
+ reporter.Error(MessageSource.Resolver, expr, "ghost fields are allowed only in specification contexts");
return;
}
@@ -8312,14 +9679,14 @@ namespace Microsoft.Dafny
var e = (FunctionCallExpr)expr;
if (e.Function != null) {
if (e.Function.IsGhost) {
- Error(expr, "function calls are allowed only in specification contexts (consider declaring the function a 'function method')");
+ reporter.Error(MessageSource.Resolver, expr, "function calls are allowed only in specification contexts (consider declaring the function a 'function method')");
return;
}
// function is okay, so check all NON-ghost arguments
- CheckIsNonGhost(e.Receiver);
+ CheckIsCompilable(e.Receiver);
for (int i = 0; i < e.Function.Formals.Count; i++) {
if (!e.Function.Formals[i].IsGhost) {
- CheckIsNonGhost(e.Args[i]);
+ CheckIsCompilable(e.Args[i]);
}
}
}
@@ -8331,26 +9698,26 @@ namespace Microsoft.Dafny
// note that if resolution is successful, then |e.Arguments| == |e.Ctor.Formals|
for (int i = 0; i < e.Arguments.Count; i++) {
if (!e.Ctor.Formals[i].IsGhost) {
- CheckIsNonGhost(e.Arguments[i]);
+ CheckIsCompilable(e.Arguments[i]);
}
}
return;
} else if (expr is OldExpr) {
- Error(expr, "old expressions are allowed only in specification and ghost contexts");
+ reporter.Error(MessageSource.Resolver, expr, "old expressions are allowed only in specification and ghost contexts");
return;
} else if (expr is UnaryOpExpr) {
var e = (UnaryOpExpr)expr;
if (e.Op == UnaryOpExpr.Opcode.Fresh) {
- Error(expr, "fresh expressions are allowed only in specification and ghost contexts");
+ reporter.Error(MessageSource.Resolver, expr, "fresh expressions are allowed only in specification and ghost contexts");
return;
}
} else if (expr is StmtExpr) {
var e = (StmtExpr)expr;
// ignore the statement
- CheckIsNonGhost(e.E);
+ CheckIsCompilable(e.E);
return;
} else if (expr is BinaryExpr) {
@@ -8358,7 +9725,7 @@ namespace Microsoft.Dafny
switch (e.ResolvedOp_PossiblyStillUndetermined) {
case BinaryExpr.ResolvedOpcode.RankGt:
case BinaryExpr.ResolvedOpcode.RankLt:
- Error(expr, "rank comparisons are allowed only in specification and ghost contexts");
+ reporter.Error(MessageSource.Resolver, expr, "rank comparisons are allowed only in specification and ghost contexts");
return;
default:
break;
@@ -8369,7 +9736,7 @@ namespace Microsoft.Dafny
switch (e.Op) {
case TernaryExpr.Opcode.PrefixEqOp:
case TernaryExpr.Opcode.PrefixNeqOp:
- Error(expr, "prefix equalities are allowed only in specification and ghost contexts");
+ reporter.Error(MessageSource.Resolver, expr, "prefix equalities are allowed only in specification and ghost contexts");
return;
default:
break;
@@ -8381,54 +9748,77 @@ namespace Microsoft.Dafny
var i = 0;
foreach (var ee in e.RHSs) {
if (!e.LHSs[i].Vars.All(bv => bv.IsGhost)) {
- CheckIsNonGhost(ee);
+ CheckIsCompilable(ee);
}
i++;
}
- CheckIsNonGhost(e.Body);
+ CheckIsCompilable(e.Body);
} else {
Contract.Assert(e.RHSs.Count == 1);
var lhsVarsAreAllGhost = e.BoundVars.All(bv => bv.IsGhost);
if (!lhsVarsAreAllGhost) {
- CheckIsNonGhost(e.RHSs[0]);
+ CheckIsCompilable(e.RHSs[0]);
+ }
+ CheckIsCompilable(e.Body);
+
+ // fill in bounds for this to-be-compiled let-such-that expression
+ Contract.Assert(e.RHSs.Count == 1); // if we got this far, the resolver will have checked this condition successfully
+ var constraint = e.RHSs[0];
+ List<IVariable> missingBounds;
+ var vars = new List<IVariable>(e.BoundVars);
+ var bestBounds = DiscoverBestBounds_MultipleVars(vars, constraint, true, false, out missingBounds);
+ if (missingBounds.Count != 0) {
+ e.Constraint_MissingBounds = missingBounds;
+ foreach (var bv in e.Constraint_MissingBounds) {
+ reporter.Error(MessageSource.Resolver, e, "a non-ghost let-such-that constraint must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name);
+ }
+ } else {
+ e.Constraint_Bounds = bestBounds;
}
- CheckIsNonGhost(e.Body);
}
return;
- } else if (expr is QuantifierExpr) {
- var e = (QuantifierExpr)expr;
- if (e.MissingBounds != null) {
- foreach (var bv in e.MissingBounds) {
- Error(expr, "quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name);
+ } else if (expr is LambdaExpr) {
+ var e = expr as LambdaExpr;
+ CheckIsCompilable(e.Body);
+ return;
+ } else if (expr is ComprehensionExpr) {
+ var e = (ComprehensionExpr)expr;
+ var uncompilableBoundVars = e.UncompilableBoundVars();
+ if (uncompilableBoundVars.Count != 0) {
+ string what;
+ if (e is SetComprehension) {
+ what = ((SetComprehension)e).Finite ? "set comprehensions" : "iset comprehensions";
+ } else if (e is MapComprehension) {
+ what = ((MapComprehension)e).Finite ? "map comprehensions" : "imap comprehensions";
+ } else {
+ Contract.Assume(e is QuantifierExpr); // otherwise, unexpected ComprehensionExpr (since LambdaExpr is handled separately above)
+ Contract.Assert(((QuantifierExpr)e).SplitQuantifier == null); // No split quantifiers during resolution
+ what = "quantifiers";
}
- return;
- }
- } else if (expr is MapComprehension) {
- var e = (MapComprehension)expr;
- if (e.MissingBounds != null && !e.Finite) {
- foreach (var bv in e.MissingBounds) {
- Error(expr, "imaps in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name);
+ foreach (var bv in uncompilableBoundVars) {
+ reporter.Error(MessageSource.Resolver, expr, "{0} in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for '{1}'", what, bv.Name);
}
return;
}
+ // don't recurse down any attributes
+ if (e.Range != null) { CheckIsCompilable(e.Range); }
+ CheckIsCompilable(e.Term);
+ return;
+
} else if (expr is NamedExpr) {
- if (!moduleInfo.IsGhost)
- CheckIsNonGhost(((NamedExpr)expr).Body);
+ if (!moduleInfo.IsAbstract)
+ CheckIsCompilable(((NamedExpr)expr).Body);
return;
} else if (expr is ChainingExpression) {
// We don't care about the different operators; we only want the operands, so let's get them directly from
// the chaining expression
var e = (ChainingExpression)expr;
- e.Operands.ForEach(CheckIsNonGhost);
- return;
- } else if (expr is LambdaExpr) {
- var e = expr as LambdaExpr;
- CheckIsNonGhost(e.Body);
+ e.Operands.ForEach(CheckIsCompilable);
return;
}
foreach (var ee in expr.SubExpressions) {
- CheckIsNonGhost(ee);
+ CheckIsCompilable(ee);
}
}
@@ -8444,9 +9834,9 @@ namespace Microsoft.Dafny
if (member == null) {
// error has already been reported by ResolveMember
} else if (member is Method) {
- Error(e, "member {0} in type {1} refers to a method, but only functions can be used in this context", e.Name, cce.NonNull(ctype).Name);
+ reporter.Error(MessageSource.Resolver, e, "member {0} in type {1} refers to a method, but only functions can be used in this context", e.Name, cce.NonNull(ctype).Name);
} else if (!(member is Function)) {
- Error(e, "member {0} in type {1} does not refer to a function", e.Name, cce.NonNull(ctype).Name);
+ reporter.Error(MessageSource.Resolver, e, "member {0} in type {1} does not refer to a function", e.Name, cce.NonNull(ctype).Name);
} else {
Function function = (Function)member;
e.Function = function;
@@ -8454,10 +9844,10 @@ namespace Microsoft.Dafny
((FixpointPredicate)function).Uses.Add(e);
}
if (e.Receiver is StaticReceiverExpr && !function.IsStatic) {
- Error(e, "an instance function must be selected via an object, not just a class name");
+ reporter.Error(MessageSource.Resolver, e, "an instance function must be selected via an object, not just a class name");
}
if (function.Formals.Count != e.Args.Count) {
- Error(e, "wrong number of function arguments (got {0}, expected {1})", e.Args.Count, function.Formals.Count);
+ reporter.Error(MessageSource.Resolver, e, "wrong number of function arguments (got {0}, expected {1})", e.Args.Count, function.Formals.Count);
} else {
Contract.Assert(ctype != null); // follows from postcondition of ResolveMember
if (!function.IsStatic) {
@@ -8469,9 +9859,9 @@ namespace Microsoft.Dafny
// in the event that a static function calls another static function (and note that we need the
// type of the receiver in order to find the method, so we could not have made this check
// earlier).
- Error(e.Receiver, "'this' is not allowed in a 'static' context");
+ reporter.Error(MessageSource.Resolver, e.Receiver, "'this' is not allowed in a 'static' context");
} else if (e.Receiver is StaticReceiverExpr) {
- Error(e.Receiver, "call to instance function requires an instance");
+ reporter.Error(MessageSource.Resolver, e.Receiver, "call to instance function requires an instance");
}
}
// build the type substitution map
@@ -8488,9 +9878,7 @@ namespace Microsoft.Dafny
ResolveExpression(farg, opts);
Contract.Assert(farg.Type != null); // follows from postcondition of ResolveExpression
Type s = SubstType(function.Formals[i].Type, e.TypeArgumentSubstitutions);
- if (!UnifyTypes(farg.Type, s)) {
- Error(e, "incorrect type of function argument {0} (expected {1}, got {2})", i, s, farg.Type);
- }
+ ConstrainTypes(farg.Type, s, e, "incorrect type of function argument {0} (expected {1}, got {2})", i, s, farg.Type);
}
e.Type = SubstType(function.ResultType, e.TypeArgumentSubstitutions);
}
@@ -8538,201 +9926,175 @@ namespace Microsoft.Dafny
}
/// <summary>
- /// For a description, see DiscoverBoundsAux.
+ /// For a list of variables "bvars", returns a list of best bounds for each respective variable. If no bound is found for a variable "v", then the bound for
+ /// "v" in the returned list is set to "null" and "v" is added to "missingBounds".
/// </summary>
- public static List<ComprehensionExpr.BoundedPool> DiscoverBounds<VT>(IToken tok, List<VT> bvars, Expression expr, bool polarity, bool returnAllBounds, List<VT> missingBounds) where VT : IVariable {
- var pairs = DiscoverBoundsAux(tok, bvars, expr, polarity, returnAllBounds, false, missingBounds);
- if (pairs == null) {
- return null;
+ public static List<ComprehensionExpr.BoundedPool> DiscoverBestBounds_MultipleVars<VT>(List<VT> bvars, Expression expr, bool polarity, bool onlyFiniteBounds, out List<VT> missingBounds) where VT : IVariable {
+ foreach (var bv in bvars) {
+ var c = GetImpliedTypeConstraint(bv, bv.Type, null);
+ expr = polarity ? Expression.CreateAnd(c, expr) : Expression.CreateImplies(c, expr);
}
- var bounds = new List<ComprehensionExpr.BoundedPool>();
- foreach (var pr in pairs) {
- Contract.Assert(1 <= pr.Item2.Count);
- bounds.AddRange(pr.Item2);
+ List<ComprehensionExpr.BoundedPool> knownBounds = null;
+ var orgCount = bvars.Count;
+ var bests = new List<ComprehensionExpr.BoundedPool>();
+ bool changed = true;
+ // compute the bounds of the BV until no new information is obtained.
+ while (changed) {
+ changed = false;
+ var all = DiscoverAllBounds_Aux_MultipleVars(bvars, expr, polarity, knownBounds);
+ bests = all.ConvertAll(tup => ComprehensionExpr.BoundedPool.GetBest(tup.Item2, onlyFiniteBounds));
+ // check to see if we found new bounds in this iteration
+ int count = 0;
+ // figure out how many bounds are not determined yet.
+ for (int i = 0; i < bests.Count; i++) {
+ if (bests[i] == null || (bests[i] is ComprehensionExpr.RefBoundedPool)) {
+ count++;
+ }
+ }
+ // if there are bounds that are not determined yet and the number of undetermined bounds
+ // changed, we will need to do another iteration.
+ if (count >0 && count != orgCount) {
+ changed = true;
+ knownBounds = bests;
+ orgCount = count;
+ }
}
- return bounds;
+ missingBounds = new List<VT>();
+ for (var i = 0; i < bvars.Count; i++) {
+ if (bests[i] == null) {
+ missingBounds.Add(bvars[i]);
+ }
+ }
+ return bests;
+ }
+
+ public static List<ComprehensionExpr.BoundedPool> DiscoverAllBounds_SingleVar<VT>(VT v, Expression expr) where VT : IVariable {
+ expr = Expression.CreateAnd(GetImpliedTypeConstraint(v, v.Type, null), expr);
+ return DiscoverAllBounds_Aux_SingleVar(new List<VT> { v }, 0, expr, true, null);
+ }
+
+ private static List<Tuple<VT, List<ComprehensionExpr.BoundedPool>>> DiscoverAllBounds_Aux_MultipleVars<VT>(List<VT> bvars, Expression expr, bool polarity, List<ComprehensionExpr.BoundedPool> knownBounds) where VT : IVariable {
+ Contract.Requires(bvars != null);
+ Contract.Requires(expr != null);
+ var bb = new List<Tuple<VT, List<ComprehensionExpr.BoundedPool>>>();
+ for (var j = 0; j < bvars.Count; j++) {
+ var bounds = DiscoverAllBounds_Aux_SingleVar(bvars, j, expr, polarity, knownBounds);
+ bb.Add(new Tuple<VT, List<ComprehensionExpr.BoundedPool>>(bvars[j], bounds));
+ }
+ return bb;
}
/// <summary>
- /// Tries to find a bounded pool for each of the bound variables "bvars" of "expr". If this process
- /// fails, then "null" is returned and the bound variables for which the process fails are added to "missingBounds".
- /// If "returnAllBounds" is false, then:
- /// -- at most one BoundedPool per variable is returned
- /// -- every IntBoundedPool returned has both a lower and an upper bound
- /// -- no SubSetBoundedPool or SuperSetBoundedPool is returned
- /// If "returnAllBounds" is true, then:
- /// -- a variable may give rise to several BoundedPool's
- /// -- IntBoundedPool bounds may have just one component
- /// -- a non-null return value means that some bound were found for each variable (but, for example, perhaps one
- /// variable only has lower bounds, no upper bounds)
- /// Requires "expr" to be successfully resolved.
- /// If "allowAnyIntegers", then integer variables will always be given a bound, but this bound may be WiggleWaggle if
- /// there is no better bound.
+ /// Returns a list of (possibly partial) bounds for "bvars[j]", each of which can be written without mentioning any variable in "bvars[j..]" that is not bounded.
/// </summary>
- public static List<Tuple<VT, List<ComprehensionExpr.BoundedPool>>> DiscoverBoundsAux<VT>(IToken tok, List<VT> bvars, Expression expr, bool polarity, bool returnAllBounds, bool allowAnyIntegers, List<VT> missingBounds) where VT : IVariable {
- Contract.Requires(tok != null);
+ private static List<ComprehensionExpr.BoundedPool> DiscoverAllBounds_Aux_SingleVar<VT>(List<VT> bvars, int j, Expression expr, bool polarity, List<ComprehensionExpr.BoundedPool> knownBounds) where VT : IVariable {
Contract.Requires(bvars != null);
- Contract.Requires(missingBounds != null);
+ Contract.Requires(0 <= j && j < bvars.Count);
Contract.Requires(expr != null);
- Contract.Requires(expr.Type != null); // a sanity check (but not a complete proof) that "expr" has been resolved
- Contract.Ensures(
- (returnAllBounds && Contract.OldValue(missingBounds.Count) <= missingBounds.Count) ||
- (!returnAllBounds &&
- Contract.Result<List<Tuple<VT, List<ComprehensionExpr.BoundedPool>>>>() != null &&
- Contract.Result<List<Tuple<VT, List<ComprehensionExpr.BoundedPool>>>>().Count == bvars.Count &&
- Contract.OldValue(missingBounds.Count) == missingBounds.Count) ||
- (!returnAllBounds &&
- Contract.Result<List<Tuple<VT, List<ComprehensionExpr.BoundedPool>>>>() == null &&
- Contract.OldValue(missingBounds.Count) < missingBounds.Count));
-
- var allBounds = new List<Tuple<VT, List<ComprehensionExpr.BoundedPool>>>();
- bool foundError = false;
- foreach (var bv in bvars) {
- var c = TypeConstraint(bv, bv.Type);
- expr = polarity ? Expression.CreateAnd(c, expr) : Expression.CreateImplies(c, expr);
- }
- for (int j = 0; j < bvars.Count; j++) {
- var bv = bvars[j];
- var bounds = new List<ComprehensionExpr.BoundedPool>();
- if (bv.Type.IsBoolType) {
- // easy
- bounds.Add(new ComprehensionExpr.BoolBoundedPool());
- } else {
- bool foundBoundsForBv = false;
- if (bv.Type.IsIndDatatype && bv.Type.AsIndDatatype.HasFinitePossibleValues) {
- bounds.Add(new ComprehensionExpr.DatatypeBoundedPool(bv.Type.AsIndDatatype));
- foundBoundsForBv = true;
- }
- // Go through the conjuncts of the range expression to look for bounds.
- Expression lowerBound = null;
- Expression upperBound = null;
- if (returnAllBounds && lowerBound != null) {
- bounds.Add(new ComprehensionExpr.IntBoundedPool(lowerBound, upperBound));
- lowerBound = null;
- foundBoundsForBv = true;
- }
- foreach (var conjunct in NormalizedConjuncts(expr, polarity)) {
- var c = conjunct as BinaryExpr;
- if (c == null) {
- goto CHECK_NEXT_CONJUNCT;
- }
- var e0 = c.E0;
- var e1 = c.E1;
- int whereIsBv = SanitizeForBoundDiscovery(bvars, j, c.ResolvedOp, ref e0, ref e1);
- if (whereIsBv < 0) {
- goto CHECK_NEXT_CONJUNCT;
- }
- switch (c.ResolvedOp) {
- case BinaryExpr.ResolvedOpcode.InSet:
- if (whereIsBv == 0) {
- bounds.Add(new ComprehensionExpr.SetBoundedPool(e1));
- foundBoundsForBv = true;
- if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE;
- }
- break;
- case BinaryExpr.ResolvedOpcode.Subset:
- if (returnAllBounds) {
- if (whereIsBv == 0) {
- bounds.Add(new ComprehensionExpr.SubSetBoundedPool(e1));
- } else {
- bounds.Add(new ComprehensionExpr.SuperSetBoundedPool(e0));
- }
- foundBoundsForBv = true;
- }
- break;
- case BinaryExpr.ResolvedOpcode.InMultiSet:
- if (whereIsBv == 0) {
- bounds.Add(new ComprehensionExpr.SetBoundedPool(e1));
- foundBoundsForBv = true;
- if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE;
- }
- break;
- case BinaryExpr.ResolvedOpcode.InSeq:
- if (whereIsBv == 0) {
- bounds.Add(new ComprehensionExpr.SeqBoundedPool(e1));
- foundBoundsForBv = true;
- if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE;
- }
- break;
- case BinaryExpr.ResolvedOpcode.InMap:
- if (whereIsBv == 0 && e1.Type.AsMapType.Finite) {
- bounds.Add(new ComprehensionExpr.MapBoundedPool(e1));
- foundBoundsForBv = true;
- if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE;
- }
- break;
- case BinaryExpr.ResolvedOpcode.EqCommon:
- if (bv.Type is IntType) {
- var otherOperand = whereIsBv == 0 ? e1 : e0;
- bounds.Add(new ComprehensionExpr.IntBoundedPool(otherOperand, Expression.CreateIncrement(otherOperand, 1)));
- foundBoundsForBv = true;
- if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE;
- } else if (returnAllBounds && bv.Type is SetType) {
- var otherOperand = whereIsBv == 0 ? e1 : e0;
- bounds.Add(new ComprehensionExpr.SubSetBoundedPool(otherOperand));
- foundBoundsForBv = true;
- }
- break;
- case BinaryExpr.ResolvedOpcode.Gt:
- case BinaryExpr.ResolvedOpcode.Ge:
- Contract.Assert(false); throw new cce.UnreachableException(); // promised by postconditions of NormalizedConjunct
- case BinaryExpr.ResolvedOpcode.Lt:
- if (e0.Type.IsNumericBased(Type.NumericPersuation.Int)) {
- if (whereIsBv == 0 && upperBound == null) {
- upperBound = e1; // bv < E
- } else if (whereIsBv == 1 && lowerBound == null) {
- lowerBound = Expression.CreateIncrement(e0, 1); // E < bv
- }
- }
- break;
- case BinaryExpr.ResolvedOpcode.Le:
- if (e0.Type.IsNumericBased(Type.NumericPersuation.Int)) {
- if (whereIsBv == 0 && upperBound == null) {
- upperBound = Expression.CreateIncrement(e1, 1); // bv <= E
- } else if (whereIsBv == 1 && lowerBound == null) {
- lowerBound = e0; // E <= bv
- }
- }
- break;
- default:
- break;
+ var bv = bvars[j];
+ var bounds = new List<ComprehensionExpr.BoundedPool>();
+
+ // Maybe the type itself gives a bound
+ if (bv.Type.IsBoolType) {
+ bounds.Add(new ComprehensionExpr.BoolBoundedPool());
+ } else if (bv.Type.IsCharType) {
+ bounds.Add(new ComprehensionExpr.CharBoundedPool());
+ } else if (bv.Type.IsRefType) {
+ bounds.Add(new ComprehensionExpr.RefBoundedPool(bv.Type));
+ } else if (bv.Type.IsIndDatatype && bv.Type.AsIndDatatype.HasFinitePossibleValues) {
+ bounds.Add(new ComprehensionExpr.DatatypeBoundedPool(bv.Type.AsIndDatatype));
+ } else if (bv.Type.IsNumericBased(Type.NumericPersuation.Int)) {
+ bounds.Add(new AssignSuchThatStmt.WiggleWaggleBound());
+ }
+
+ // Go through the conjuncts of the range expression to look for bounds.
+ foreach (var conjunct in NormalizedConjuncts(expr, polarity)) {
+ var c = conjunct as BinaryExpr;
+ if (c == null) {
+ // We only know what to do with binary expressions
+ continue;
+ }
+ var e0 = c.E0;
+ var e1 = c.E1;
+ int whereIsBv = SanitizeForBoundDiscovery(bvars, j, c.ResolvedOp, knownBounds, ref e0, ref e1);
+ if (whereIsBv < 0) {
+ continue;
+ }
+ switch (c.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.InSet:
+ if (whereIsBv == 0 && e1.Type.AsSetType.Finite) {
+ bounds.Add(new ComprehensionExpr.SetBoundedPool(e1));
}
- if ((lowerBound != null && upperBound != null) ||
- (returnAllBounds && (lowerBound != null || upperBound != null))) {
- // we have found two halves (or, in the case of returnAllBounds, we have found some bound)
- bounds.Add(new ComprehensionExpr.IntBoundedPool(lowerBound, upperBound));
- lowerBound = null;
- upperBound = null;
- foundBoundsForBv = true;
- if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE;
- }
- CHECK_NEXT_CONJUNCT: ;
- }
- if (!foundBoundsForBv) {
- // we have checked every conjunct in the range expression and still have not discovered good bounds
- if (allowAnyIntegers && bv.Type is IntType) {
- bounds.Add(new AssignSuchThatStmt.WiggleWaggleBound());
+ break;
+ case BinaryExpr.ResolvedOpcode.Subset:
+ if (whereIsBv == 0) {
+ bounds.Add(new ComprehensionExpr.SubSetBoundedPool(e1));
} else {
- missingBounds.Add(bv); // record failing bound variable
- foundError = true;
+ bounds.Add(new ComprehensionExpr.SuperSetBoundedPool(e0));
}
- }
+ break;
+ case BinaryExpr.ResolvedOpcode.InMultiSet:
+ if (whereIsBv == 0) {
+ bounds.Add(new ComprehensionExpr.SetBoundedPool(e1));
+ }
+ break;
+ case BinaryExpr.ResolvedOpcode.InSeq:
+ if (whereIsBv == 0) {
+ bounds.Add(new ComprehensionExpr.SeqBoundedPool(e1));
+ }
+ break;
+ case BinaryExpr.ResolvedOpcode.InMap:
+ if (whereIsBv == 0 && e1.Type.AsMapType.Finite) {
+ bounds.Add(new ComprehensionExpr.MapBoundedPool(e1));
+ }
+ break;
+ case BinaryExpr.ResolvedOpcode.EqCommon:
+ // TODO: Use the new ComprehensionExpr.ExactBoundedPool
+ if (bv.Type.IsNumericBased(Type.NumericPersuation.Int)) {
+ var otherOperand = whereIsBv == 0 ? e1 : e0;
+ bounds.Add(new ComprehensionExpr.IntBoundedPool(otherOperand, Expression.CreateIncrement(otherOperand, 1)));
+ } else if (bv.Type is SetType) {
+ var otherOperand = whereIsBv == 0 ? e1 : e0;
+ bounds.Add(new ComprehensionExpr.SubSetBoundedPool(otherOperand));
+ }
+ break;
+ case BinaryExpr.ResolvedOpcode.Gt:
+ case BinaryExpr.ResolvedOpcode.Ge:
+ Contract.Assert(false); throw new cce.UnreachableException(); // promised by postconditions of NormalizedConjunct
+ case BinaryExpr.ResolvedOpcode.Lt:
+ if (e0.Type.IsNumericBased(Type.NumericPersuation.Int)) {
+ if (whereIsBv == 0) { // bv < E
+ bounds.Add(new ComprehensionExpr.IntBoundedPool(null, e1));
+ } else { // E < bv
+ bounds.Add(new ComprehensionExpr.IntBoundedPool(Expression.CreateIncrement(e0, 1), null));
+ }
+ }
+ break;
+ case BinaryExpr.ResolvedOpcode.Le:
+ if (e0.Type.IsNumericBased(Type.NumericPersuation.Int)) {
+ if (whereIsBv == 0) { // bv <= E
+ bounds.Add(new ComprehensionExpr.IntBoundedPool(null, Expression.CreateIncrement(e1, 1)));
+ } else { // E <= bv
+ bounds.Add(new ComprehensionExpr.IntBoundedPool(e0, null));
+ }
+ }
+ break;
+ default:
+ break;
}
- CHECK_NEXT_BOUND_VARIABLE: ; // should goto here only if the bound for the current variable has been discovered (otherwise, return with null from this method)
- allBounds.Add(new Tuple<VT, List<ComprehensionExpr.BoundedPool>>(bv, bounds));
}
- return foundError ? null : allBounds;
+ return bounds;
}
- static Expression TypeConstraint(IVariable bv, Type ty) {
+ static Expression GetImpliedTypeConstraint(IVariable bv, Type ty, ErrorReporter reporter) {
Contract.Requires(bv != null);
Contract.Requires(ty != null);
ty = ty.NormalizeExpand();
var dd = ty.AsNewtype;
if (dd != null) {
- var c = TypeConstraint(bv, dd.BaseType);
+ var c = GetImpliedTypeConstraint(bv, dd.BaseType, reporter);
if (dd.Var != null) {
- c = Expression.CreateAnd(c, new Translator().Substitute(dd.Constraint, dd.Var, Expression.CreateIdentExpr(bv)));
+ c = Expression.CreateAnd(c, new Translator(reporter).Substitute(dd.Constraint, dd.Var, Expression.CreateIdentExpr(bv)));
}
return c;
}
@@ -8750,7 +10112,7 @@ namespace Microsoft.Dafny
/// The other of "e0" and "e1" is an expression whose free variables are not among "boundVars[bvi..]".
/// Ensures that the resulting "e0" and "e1" are not ConcreteSyntaxExpression's.
/// </summary>
- static int SanitizeForBoundDiscovery<VT>(List<VT> boundVars, int bvi, BinaryExpr.ResolvedOpcode op, ref Expression e0, ref Expression e1) where VT : IVariable {
+ static int SanitizeForBoundDiscovery<VT>(List<VT> boundVars, int bvi, BinaryExpr.ResolvedOpcode op, List<ComprehensionExpr.BoundedPool> knownBounds, ref Expression e0, ref Expression e1) where VT : IVariable {
Contract.Requires(e0 != null);
Contract.Requires(e1 != null);
Contract.Requires(boundVars != null);
@@ -8845,10 +10207,10 @@ namespace Microsoft.Dafny
}
// Finally, check that the other side does not contain "bv" or any of the bound variables
- // listed after "bv" in the quantifier.
+ // listed after "bv" in the quantifier that is not bounded.
var fv = FreeVariables(thatSide);
for (int i = bvi; i < boundVars.Count; i++) {
- if (fv.Contains(boundVars[i])) {
+ if (fv.Contains(boundVars[i]) && (knownBounds == null || knownBounds[i] == null)) {
return -1;
}
}
@@ -8998,6 +10360,8 @@ namespace Microsoft.Dafny
} else if (expr is QuantifierExpr) {
var e = (QuantifierExpr)expr;
+ Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution
+
var s = FreeVariables(e.LogicalBody());
foreach (var bv in e.BoundVars) {
s.Remove(bv);
@@ -9009,8 +10373,10 @@ namespace Microsoft.Dafny
var s = FreeVariables(e.Source);
foreach (MatchCaseExpr mc in e.Cases) {
var t = FreeVariables(mc.Body);
- foreach (var bv in mc.Arguments) {
- t.Remove(bv);
+ foreach (var cp in mc.CasePatterns) {
+ foreach (var bv in cp.Vars) {
+ t.Remove(bv);
+ }
}
s.UnionWith(t);
}
@@ -9073,39 +10439,32 @@ namespace Microsoft.Dafny
Type argType = new InferredTypeProxy();
IndexableTypeProxy expectedType = new IndexableTypeProxy(domainType, elementType, argType, true, true, true);
- if (!UnifyTypes(e.Seq.Type, expectedType)) {
- Error(e, "sequence/array/multiset/map selection requires a sequence, array, multiset, or map (got {0})", e.Seq.Type);
+ if (!ConstrainTypes(e.Seq.Type, expectedType, e, "sequence/array/multiset/map selection requires a sequence, array, multiset, or map (got {0})", e.Seq.Type)) {
seqErr = true;
}
if (!e.SelectOne) // require sequence or array
{
if (!allowNonUnitArraySelection) {
// require seq
- if (!UnifyTypes(expectedType, new SeqType(new InferredTypeProxy()))) {
- Error(e, "selection requires a sequence (got {0})", e.Seq.Type);
- }
+ ConstrainTypes(expectedType, new SeqType(new InferredTypeProxy()), e, "selection requires a sequence (got {0})", e.Seq.Type);
} else {
if (UnifyTypes(expectedType, new MapType(true, new InferredTypeProxy(), new InferredTypeProxy()))) {
- Error(e, "cannot multiselect a map (got {0} as map type)", e.Seq.Type);
+ reporter.Error(MessageSource.Resolver, e, "cannot multiselect a map (got {0} as map type)", e.Seq.Type);
} else if (UnifyTypes(expectedType, new MapType(false, new InferredTypeProxy(), new InferredTypeProxy()))) {
- Error(e, "cannot multiselect an imap (got {0} as imap type)", e.Seq.Type);
+ reporter.Error(MessageSource.Resolver, e, "cannot multiselect an imap (got {0} as imap type)", e.Seq.Type);
}
}
}
if (e.E0 != null) {
ResolveExpression(e.E0, opts);
Contract.Assert(e.E0.Type != null); // follows from postcondition of ResolveExpression
- if (!UnifyTypes(e.E0.Type, domainType)) {
- Error(e.E0, "sequence/array/multiset/map selection requires {1} indices (got {0})", e.E0.Type, domainType);
- }
+ ConstrainTypes(e.E0.Type, domainType, e.E0, "sequence/array/multiset/map selection requires {1} indices (got {0})", e.E0.Type, domainType);
}
if (e.E1 != null) {
ResolveExpression(e.E1, opts);
Contract.Assert(e.E1.Type != null); // follows from postcondition of ResolveExpression
- var domType = e.E0 == null ? domainType : new OperationTypeProxy(true, false, false, false, false); // reuse 'domainType' if .E0 did not use it; otherwise, create a new proxy to allow .E1 to be any integer-based numeric type, independent of the integer-based numeric type used by .E0
- if (!UnifyTypes(e.E1.Type, domType)) {
- Error(e.E1, "sequence/array/multiset/map selection requires {1} indices (got {0})", e.E1.Type, domType);
- }
+ var domType = e.E0 == null ? domainType : new OperationTypeProxy(true, false, false, false, false, false); // reuse 'domainType' if .E0 did not use it; otherwise, create a new proxy to allow .E1 to be any integer-based numeric type, independent of the integer-based numeric type used by .E0
+ ConstrainTypes(e.E1.Type, domType, e.E1, "sequence/array/multiset/map selection requires {1} indices (got {0})", e.E1.Type, domType);
}
if (!seqErr) {
if (e.SelectOne) {
@@ -9281,8 +10640,15 @@ namespace Microsoft.Dafny
IdentifierExpr e = (IdentifierExpr)expr;
return cce.NonNull(e.Var).IsGhost;
} else if (expr is DatatypeValue) {
- DatatypeValue dtv = (DatatypeValue)expr;
- return dtv.Arguments.Exists(arg => UsesSpecFeatures(arg));
+ var e = (DatatypeValue)expr;
+ // check all NON-ghost arguments
+ // note that if resolution is successful, then |e.Arguments| == |e.Ctor.Formals|
+ for (int i = 0; i < e.Arguments.Count; i++) {
+ if (!e.Ctor.Formals[i].IsGhost && UsesSpecFeatures(e.Arguments[i])) {
+ return true;
+ }
+ }
+ return false;
} else if (expr is DisplayExpression) {
DisplayExpression e = (DisplayExpression)expr;
return e.Elements.Exists(ee => UsesSpecFeatures(ee));
@@ -9296,7 +10662,7 @@ namespace Microsoft.Dafny
} else {
return false;
}
- } else if (expr is SeqSelectExpr) {
+ } else if (expr is SeqSelectExpr) {
SeqSelectExpr e = (SeqSelectExpr)expr;
return UsesSpecFeatures(e.Seq) ||
(e.E0 != null && UsesSpecFeatures(e.E0)) ||
@@ -9310,11 +10676,20 @@ namespace Microsoft.Dafny
UsesSpecFeatures(e.Index) ||
UsesSpecFeatures(e.Value);
} else if (expr is FunctionCallExpr) {
- FunctionCallExpr e = (FunctionCallExpr)expr;
- if (cce.NonNull(e.Function).IsGhost) {
+ var e = (FunctionCallExpr)expr;
+ if (e.Function.IsGhost) {
return true;
}
- return e.Args.Exists(arg => UsesSpecFeatures(arg));
+ // check all NON-ghost arguments
+ if (UsesSpecFeatures(e.Receiver)) {
+ return true;
+ }
+ for (int i = 0; i < e.Function.Formals.Count; i++) {
+ if (!e.Function.Formals[i].IsGhost && UsesSpecFeatures(e.Args[i])) {
+ return true;
+ }
+ }
+ return false;
} else if (expr is ApplyExpr) {
ApplyExpr e = (ApplyExpr)expr;
return UsesSpecFeatures(e.Function) || e.Args.Exists(UsesSpecFeatures);
@@ -9355,20 +10730,20 @@ namespace Microsoft.Dafny
return true; // let-such-that is always ghost
}
} else if (expr is NamedExpr) {
- return moduleInfo.IsGhost ? false : UsesSpecFeatures(((NamedExpr)expr).Body);
- } else if (expr is ComprehensionExpr) {
- if (expr is QuantifierExpr && ((QuantifierExpr)expr).Bounds == null) {
- return true; // the quantifier cannot be compiled if the resolver found no bounds
- }
- return Contract.Exists(expr.SubExpressions, se => UsesSpecFeatures(se));
+ return moduleInfo.IsAbstract ? false : UsesSpecFeatures(((NamedExpr)expr).Body);
+ } else if (expr is QuantifierExpr) {
+ var e = (QuantifierExpr)expr;
+ Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution
+ return e.UncompilableBoundVars().Count != 0 || UsesSpecFeatures(e.LogicalBody());
} else if (expr is SetComprehension) {
var e = (SetComprehension)expr;
- return (e.Range != null && UsesSpecFeatures(e.Range)) || (e.Term != null && UsesSpecFeatures(e.Term));
+ return !e.Finite || e.UncompilableBoundVars().Count != 0 || (e.Range != null && UsesSpecFeatures(e.Range)) || (e.Term != null && UsesSpecFeatures(e.Term));
} else if (expr is MapComprehension) {
var e = (MapComprehension)expr;
- return (UsesSpecFeatures(e.Range)) || (UsesSpecFeatures(e.Term));
+ return !e.Finite || e.UncompilableBoundVars().Count != 0 || UsesSpecFeatures(e.Range) || UsesSpecFeatures(e.Term);
} else if (expr is LambdaExpr) {
- return Contract.Exists(expr.SubExpressions, UsesSpecFeatures);
+ var e = (LambdaExpr)expr;
+ return UsesSpecFeatures(e.Term);
} else if (expr is WildcardExpr) {
return false;
} else if (expr is StmtExpr) {
@@ -9588,7 +10963,7 @@ namespace Microsoft.Dafny
// this call is disqualified from being a co-call, because it has a postcondition
// (a postcondition could be allowed, as long as it does not get to be used with
// co-recursive calls, because that could be unsound; for example, consider
- // "ensures false;")
+ // "ensures false")
if (!dealsWithCodatatypes) {
e.CoCall = FunctionCallExpr.CoCallResolution.No;
} else {
@@ -9665,17 +11040,27 @@ namespace Microsoft.Dafny
}
}
- // Pushes name-->thing association and returns "true", if name has not already been pushed since the last marker.
- // If name already has been pushed since the last marker, does nothing and returns "false".
- public bool Push(string name, Thing thing) {
+ public enum PushResult { Duplicate, Shadow, Success }
+
+ /// <summary>
+ /// Pushes name-->thing association and returns "Success", if name has not already been pushed since the last marker.
+ /// If name already has been pushed since the last marker, does nothing and returns "Duplicate".
+ /// If the appropriate command-line option is supplied, then this method will also check if "name" shadows a previous
+ /// name; if it does, then it will return "Shadow" instead of "Success".
+ /// </summary>
+ public PushResult Push(string name, Thing thing) {
Contract.Requires(name != null);
Contract.Requires(thing != null);
if (Find(name, true) != null) {
- return false;
+ return PushResult.Duplicate;
} else {
+ var r = PushResult.Success;
+ if (DafnyOptions.O.WarnShadowing && Find(name, false) != null) {
+ r = PushResult.Shadow;
+ }
names.Add(name);
things.Add(thing);
- return true;
+ return r;
}
}
@@ -9700,6 +11085,11 @@ namespace Microsoft.Dafny
return Find(name, false);
}
+ public Thing FindInCurrentScope(string name) {
+ Contract.Requires(name != null);
+ return Find(name, true);
+ }
+
public bool ContainsDecl(Thing t) {
return things.Exists(thing => thing == t);
}
diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs
index 72649b5f..2e18c4e6 100644
--- a/Source/Dafny/Rewriter.cs
+++ b/Source/Dafny/Rewriter.cs
@@ -2,27 +2,29 @@
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using Bpl = Microsoft.Boogie;
+using IToken = Microsoft.Boogie.IToken;
namespace Microsoft.Dafny
{
- [ContractClass(typeof(IRewriterContracts))]
- public interface IRewriter
+ public abstract class IRewriter
{
- void PreResolve(ModuleDefinition m);
- void PostResolve(ModuleDefinition m);
- // After SCC/Cyclicity/Recursivity analysis:
- void PostCyclicityResolve(ModuleDefinition m);
- }
- [ContractClassFor(typeof(IRewriter))]
- abstract class IRewriterContracts : IRewriter
- {
- public void PreResolve(ModuleDefinition m) {
+ protected readonly ErrorReporter reporter;
+
+ public IRewriter(ErrorReporter reporter) {
+ Contract.Requires(reporter != null);
+ this.reporter = reporter;
+ }
+
+ internal virtual void PreResolve(ModuleDefinition m) {
Contract.Requires(m != null);
}
- public void PostResolve(ModuleDefinition m) {
+
+ internal virtual void PostResolve(ModuleDefinition m) {
Contract.Requires(m != null);
}
- public void PostCyclicityResolve(ModuleDefinition m) {
+
+ // After SCC/Cyclicity/Recursivity analysis:
+ internal virtual void PostCyclicityResolve(ModuleDefinition m) {
Contract.Requires(m != null);
}
}
@@ -36,6 +38,312 @@ namespace Microsoft.Dafny
}
}
+ public class TriggerGeneratingRewriter : IRewriter {
+ internal TriggerGeneratingRewriter(ErrorReporter reporter) : base(reporter) {
+ Contract.Requires(reporter != null);
+ }
+
+ internal override void PostCyclicityResolve(ModuleDefinition m) {
+ var finder = new Triggers.QuantifierCollector(reporter);
+
+ foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) {
+ finder.Visit(decl, false);
+ }
+
+ var triggersCollector = new Triggers.TriggersCollector(finder.exprsInOldContext);
+ foreach (var quantifierCollection in finder.quantifierCollections) {
+ quantifierCollection.ComputeTriggers(triggersCollector);
+ quantifierCollection.CommitTriggers();
+ }
+ }
+ }
+
+ internal class QuantifierSplittingRewriter : IRewriter {
+ internal QuantifierSplittingRewriter(ErrorReporter reporter) : base(reporter) {
+ Contract.Requires(reporter != null);
+ }
+
+ internal override void PostResolve(ModuleDefinition m) {
+ var splitter = new Triggers.QuantifierSplitter();
+ foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) {
+ splitter.Visit(decl);
+ }
+ splitter.Commit();
+ }
+ }
+
+ // write out the quantifier for ForallStmt
+ public class ForallStmtRewriter : IRewriter
+ {
+ public ForallStmtRewriter(ErrorReporter reporter) : base(reporter) {
+ Contract.Requires(reporter != null);
+ }
+
+ internal override void PostResolve(ModuleDefinition m) {
+ var forallvisiter = new ForAllStmtVisitor();
+ foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) {
+ forallvisiter.Visit(decl, true);
+ }
+ }
+
+ internal class ForAllStmtVisitor : TopDownVisitor<bool>
+ {
+ protected override bool VisitOneStmt(Statement stmt, ref bool st) {
+ if (stmt is ForallStmt) {
+ ForallStmt s = (ForallStmt)stmt;
+ if (s.Kind == ForallStmt.ParBodyKind.Proof) {
+ Expression term = s.Ens.Count != 0 ? s.Ens[0].E : new LiteralExpr(s.Tok, true);
+ for (int i = 1; i < s.Ens.Count; i++) {
+ term = new BinaryExpr(s.Tok, BinaryExpr.ResolvedOpcode.And, term, s.Ens[i].E);
+ }
+ List<Expression> exprList = new List<Expression>();
+ ForallExpr expr = new ForallExpr(s.Tok, s.BoundVars, s.Range, term, s.Attributes);
+ expr.Type = Type.Bool; // resolve here
+ exprList.Add(expr);
+ s.ForallExpressions = exprList;
+ } else if (s.Kind == ForallStmt.ParBodyKind.Assign) {
+ if (s.BoundVars.Count != 0) {
+ var s0 = (AssignStmt)s.S0;
+ if (s0.Rhs is ExprRhs) {
+ List<Expression> exprList = new List<Expression>();
+ Expression Fi = null;
+ Func<Expression, Expression> lhsBuilder = null;
+ var lhs = s0.Lhs.Resolved;
+ var i = s.BoundVars[0];
+ if (s.BoundVars.Count == 1) {
+ //var lhsContext = null;
+ // Detect the following cases:
+ // 0: forall i | R(i) { F(i).f := E(i); }
+ // 1: forall i | R(i) { A[F(i)] := E(i); }
+ // 2: forall i | R(i) { F(i)[N] := E(i); }
+ if (lhs is MemberSelectExpr) {
+ var ll = (MemberSelectExpr)lhs;
+ Fi = ll.Obj;
+ lhsBuilder = e => { var l = new MemberSelectExpr(ll.tok, e, ll.MemberName); l.Member = ll.Member; l.Type = ll.Type; return l; };
+ } else if (lhs is SeqSelectExpr) {
+ var ll = (SeqSelectExpr)lhs;
+ Contract.Assert(ll.SelectOne);
+ if (!Translator.ContainsFreeVariable(ll.Seq, false, i)) {
+ Fi = ll.E0;
+ lhsBuilder = e => { var l = new SeqSelectExpr(ll.tok, true, ll.Seq, e, null); l.Type = ll.Type; return l; };
+ } else if (!Translator.ContainsFreeVariable(ll.E0, false, i)) {
+ Fi = ll.Seq;
+ lhsBuilder = e => { var l = new SeqSelectExpr(ll.tok, true, e, ll.E0, null); l.Type = ll.Type; return l; };
+ }
+ }
+ }
+ var rhs = ((ExprRhs)s0.Rhs).Expr;
+ bool usedInversion = false;
+ if (Fi != null) {
+ var j = new BoundVar(i.tok, i.Name + "#inv", Fi.Type);
+ var jj = Expression.CreateIdentExpr(j);
+ var jList = new List<BoundVar>() { j };
+ var vals = InvertExpression(i, j, s.Range, Fi);
+#if DEBUG_PRINT
+ Console.WriteLine("DEBUG: Trying to invert:");
+ Console.WriteLine("DEBUG: " + Printer.ExprToString(s.Range) + " && " + j.Name + " == " + Printer.ExprToString(Fi));
+ if (vals == null) {
+ Console.WriteLine("DEBUG: Can't");
+ } else {
+ Console.WriteLine("DEBUG: The inverse is the disjunction of the following:");
+ foreach (var val in vals) {
+ Console.WriteLine("DEBUG: " + Printer.ExprToString(val.Range) + " && " + Printer.ExprToString(val.FInverse) + " == " + i.Name);
+ }
+ }
+#endif
+ if (vals != null) {
+ foreach (var val in vals) {
+ lhs = lhsBuilder(jj);
+ Attributes attributes = new Attributes("trigger", new List<Expression>() { lhs }, s.Attributes);
+ var expr = new ForallExpr(s.Tok, jList, val.Range, new BinaryExpr(s.Tok, BinaryExpr.ResolvedOpcode.EqCommon, lhs, Substitute(rhs, i, val.FInverse)), attributes);
+ expr.Type = Type.Bool; //resolve here
+ exprList.Add(expr);
+ }
+ usedInversion = true;
+ }
+ }
+ if (!usedInversion) {
+ var expr = new ForallExpr(s.Tok, s.BoundVars, s.Range, new BinaryExpr(s.Tok, BinaryExpr.ResolvedOpcode.EqCommon, lhs, rhs), s.Attributes);
+ expr.Type = Type.Bool; // resolve here
+ exprList.Add(expr);
+ }
+ s.ForallExpressions = exprList;
+ }
+ }
+ } else if (s.Kind == ForallStmt.ParBodyKind.Call) {
+ var s0 = (CallStmt)s.S0;
+ Expression term = s0.Method.Ens.Count != 0 ? s0.Method.Ens[0].E : new LiteralExpr(s.Tok, true);
+ for (int i = 1; i < s0.Method.Ens.Count; i++) {
+ term = new BinaryExpr(s.Tok, BinaryExpr.ResolvedOpcode.And, term, s0.Method.Ens[i].E);
+ }
+ List<Expression> exprList = new List<Expression>();
+ ForallExpr expr = new ForallExpr(s.Tok, s.BoundVars, s.Range, term, s.Attributes);
+ expr.Type = Type.Bool; // resolve here
+ exprList.Add(expr);
+ } else {
+ Contract.Assert(false); // unexpected kind
+ }
+ }
+ return true; //visit the sub-parts with the same "st"
+ }
+
+ internal class ForallStmtTranslationValues
+ {
+ public readonly Expression Range;
+ public readonly Expression FInverse;
+ public ForallStmtTranslationValues(Expression range, Expression fInverse) {
+ Contract.Requires(range != null);
+ Contract.Requires(fInverse != null);
+ Range = range;
+ FInverse = fInverse;
+ }
+ public ForallStmtTranslationValues Subst(IVariable j, Expression e) {
+ Contract.Requires(j != null);
+ Contract.Requires(e != null);
+ Dictionary<TypeParameter, Type> typeMap = new Dictionary<TypeParameter, Type>();
+ var substMap = new Dictionary<IVariable, Expression>();
+ substMap.Add(j, e);
+ Translator.Substituter sub = new Translator.Substituter(null, substMap, typeMap, null);
+ var v = new ForallStmtTranslationValues(sub.Substitute(Range), sub.Substitute(FInverse));
+ return v;
+ }
+ }
+
+ /// <summary>
+ /// Find piecewise inverse of F under R. More precisely, find lists of expressions P and F-1
+ /// such that
+ /// R(i) && j == F(i)
+ /// holds iff the disjunction of the following predicates holds:
+ /// P_0(j) && F-1_0(j) == i
+ /// ...
+ /// P_{n-1}(j) && F-1_{n-1}(j) == i
+ /// If no such disjunction is found, return null.
+ /// If such a disjunction is found, return for each disjunct:
+ /// * The predicate P_k(j), which is an expression that may have free occurrences of j (but no free occurrences of i)
+ /// * The expression F-1_k(j), which also may have free occurrences of j but not of i
+ /// </summary>
+ private List<ForallStmtTranslationValues> InvertExpression(BoundVar i, BoundVar j, Expression R, Expression F) {
+ Contract.Requires(i != null);
+ Contract.Requires(j != null);
+ Contract.Requires(R != null);
+ Contract.Requires(F != null);
+ var vals = new List<ForallStmtTranslationValues>(InvertExpressionIter(i, j, R, F));
+ if (vals.Count == 0) {
+ return null;
+ } else {
+ return vals;
+ }
+ }
+ /// <summary>
+ /// See InvertExpression.
+ /// </summary>
+ private IEnumerable<ForallStmtTranslationValues> InvertExpressionIter(BoundVar i, BoundVar j, Expression R, Expression F) {
+ Contract.Requires(i != null);
+ Contract.Requires(j != null);
+ Contract.Requires(R != null);
+ Contract.Requires(F != null);
+ F = F.Resolved;
+ if (!Translator.ContainsFreeVariable(F, false, i)) {
+ // We're looking at R(i) && j == K.
+ // We cannot invert j == K, but if we're lucky, R(i) contains a conjunct i==G.
+ Expression r = Expression.CreateBoolLiteral(R.tok, true);
+ Expression G = null;
+ foreach (var c in Expression.Conjuncts(R)) {
+ if (G == null && c is BinaryExpr) {
+ var bin = (BinaryExpr)c;
+ if (BinaryExpr.IsEqualityOp(bin.ResolvedOp)) {
+ var id = bin.E0.Resolved as IdentifierExpr;
+ if (id != null && id.Var == i) {
+ G = bin.E1;
+ continue;
+ }
+ id = bin.E1.Resolved as IdentifierExpr;
+ if (id != null && id.Var == i) {
+ G = bin.E0;
+ continue;
+ }
+ }
+ }
+ r = Expression.CreateAnd(r, c);
+ }
+ if (G != null) {
+ var jIsK = Expression.CreateEq(Expression.CreateIdentExpr(j), F, j.Type);
+ var rr = Substitute(r, i, G);
+ yield return new ForallStmtTranslationValues(Expression.CreateAnd(rr, jIsK), G);
+ }
+ } else if (F is IdentifierExpr) {
+ var e = (IdentifierExpr)F;
+ if (e.Var == i) {
+ // We're looking at R(i) && j == i, which is particularly easy to invert: R(j) && j == i
+ var jj = Expression.CreateIdentExpr(j);
+ yield return new ForallStmtTranslationValues(Substitute(R, i, jj), jj);
+ }
+ } else if (F is BinaryExpr) {
+ var bin = (BinaryExpr)F;
+ if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Add && (bin.E0.Type.IsIntegerType || bin.E0.Type.IsRealType)) {
+ if (!Translator.ContainsFreeVariable(bin.E1, false, i)) {
+ // We're looking at: R(i) && j == f(i) + K.
+ // By a recursive call, we'll ask to invert: R(i) && j' == f(i).
+ // For each P_0(j') && f-1_0(j') == i we get back, we yield:
+ // P_0(j - K) && f-1_0(j - K) == i
+ var jMinusK = Expression.CreateSubtract(Expression.CreateIdentExpr(j), bin.E1);
+ foreach (var val in InvertExpression(i, j, R, bin.E0)) {
+ yield return val.Subst(j, jMinusK);
+ }
+ } else if (!Translator.ContainsFreeVariable(bin.E0, false, i)) {
+ // We're looking at: R(i) && j == K + f(i)
+ // Do as in previous case, but with operands reversed.
+ var jMinusK = Expression.CreateSubtract(Expression.CreateIdentExpr(j), bin.E0);
+ foreach (var val in InvertExpression(i, j, R, bin.E1)) {
+ yield return val.Subst(j, jMinusK);
+ }
+ }
+ } else if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Sub && (bin.E0.Type.IsIntegerType || bin.E0.Type.IsRealType)) {
+ if (!Translator.ContainsFreeVariable(bin.E1, false, i)) {
+ // We're looking at: R(i) && j == f(i) - K
+ // Recurse on f(i) and then replace j := j + K
+ var jPlusK = Expression.CreateAdd(Expression.CreateIdentExpr(j), bin.E1);
+ foreach (var val in InvertExpression(i, j, R, bin.E0)) {
+ yield return val.Subst(j, jPlusK);
+ }
+ } else if (!Translator.ContainsFreeVariable(bin.E0, false, i)) {
+ // We're looking at: R(i) && j == K - f(i)
+ // Recurse on f(i) and then replace j := K - j
+ var kMinusJ = Expression.CreateAdd(Expression.CreateIdentExpr(j), bin.E0);
+ foreach (var val in InvertExpression(i, j, R, bin.E1)) {
+ yield return val.Subst(j, kMinusJ);
+ }
+ }
+ }
+ } else if (F is ITEExpr) {
+ var ife = (ITEExpr)F;
+ // We're looking at R(i) && j == if A(i) then B(i) else C(i), which is equivalent to the disjunction of:
+ // R(i) && A(i) && j == B(i)
+ // R(i) && !A(i) && j == C(i)
+ // We recurse on each one, yielding the results
+ var r = Expression.CreateAnd(R, ife.Test);
+ var valsThen = InvertExpression(i, j, r, ife.Thn);
+ if (valsThen != null) {
+ r = Expression.CreateAnd(R, Expression.CreateNot(ife.tok, ife.Test));
+ var valsElse = InvertExpression(i, j, r, ife.Els);
+ if (valsElse != null) {
+ foreach (var val in valsThen) { yield return val; }
+ foreach (var val in valsElse) { yield return val; }
+ }
+ }
+ }
+ }
+
+ Expression Substitute(Expression expr, IVariable v, Expression e) {
+ Dictionary<IVariable, Expression/*!*/> substMap = new Dictionary<IVariable, Expression>();
+ Dictionary<TypeParameter, Type> typeMap = new Dictionary<TypeParameter, Type>();
+ substMap.Add(v, e);
+ Translator.Substituter sub = new Translator.Substituter(null, substMap, typeMap, null);
+ return sub.Substitute(expr);
+ }
+ }
+ }
+
/// <summary>
/// AutoContracts is an experimental feature that will fill much of the dynamic-frames boilerplate
/// into a class. From the user's perspective, what needs to be done is simply:
@@ -45,10 +353,10 @@ namespace Microsoft.Dafny
/// AutoContracts will then:
///
/// Declare:
- /// ghost var Repr: set(object);
+ /// ghost var Repr: set(object)
///
/// For function/predicate Valid(), insert:
- /// reads this, Repr;
+ /// reads this, Repr
/// Into body of Valid(), insert (at the beginning of the body):
/// this in Repr && null !in Repr
/// and also insert, for every array-valued field A declared in the class:
@@ -60,23 +368,32 @@ namespace Microsoft.Dafny
///
/// For every constructor, add:
/// modifies this;
- /// ensures Valid() && fresh(Repr - {this});
+ /// ensures Valid() && fresh(Repr - {this})
/// At the end of the body of the constructor, add:
/// Repr := {this};
/// if (A != null) { Repr := Repr + {A}; }
/// if (F != null) { Repr := Repr + {F} + F.Repr; }
///
- /// For every method, add:
- /// requires Valid();
- /// modifies Repr;
- /// ensures Valid() && fresh(Repr - old(Repr));
+ /// For every non-static non-ghost method that is not a "simple query method",
+ /// add:
+ /// requires Valid()
+ /// modifies Repr
+ /// ensures Valid() && fresh(Repr - old(Repr))
/// At the end of the body of the method, add:
/// if (A != null) { Repr := Repr + {A}; }
/// if (F != null) { Repr := Repr + {F} + F.Repr; }
+ /// For every non-static method that is either ghost or is a "simple query method",
+ /// add:
+ /// requires Valid()
/// </summary>
public class AutoContractsRewriter : IRewriter
{
- public void PreResolve(ModuleDefinition m) {
+ public AutoContractsRewriter(ErrorReporter reporter)
+ : base(reporter) {
+ Contract.Requires(reporter != null);
+ }
+
+ internal override void PreResolve(ModuleDefinition m) {
foreach (var d in m.TopLevelDecls) {
bool sayYes = true;
if (d is ClassDecl && Attributes.ContainsBool(d.Attributes, "autocontracts", ref sayYes) && sayYes) {
@@ -89,7 +406,7 @@ namespace Microsoft.Dafny
// Add: ghost var Repr: set<object>;
// ...unless a field with that name is already present
if (!cl.Members.Exists(member => member is Field && member.Name == "Repr")) {
- Type ty = new SetType(new ObjectType());
+ Type ty = new SetType(true, new ObjectType());
cl.Members.Add(new Field(new AutoGeneratedToken(cl.tok), "Repr", true, ty, null));
}
@@ -119,7 +436,7 @@ namespace Microsoft.Dafny
// ensures fresh(Repr - {this});
var freshness = new UnaryOpExpr(tok, UnaryOpExpr.Opcode.Fresh, new BinaryExpr(tok, BinaryExpr.Opcode.Sub,
new MemberSelectExpr(tok, new ImplicitThisExpr(tok), "Repr"),
- new SetDisplayExpr(tok, new List<Expression>() { new ThisExpr(tok) })));
+ new SetDisplayExpr(tok, true, new List<Expression>() { new ThisExpr(tok) })));
ctor.Ens.Insert(1, new MaybeFreeExpression(freshness));
} else if (member is Method && !member.IsStatic) {
var m = (Method)member;
@@ -132,7 +449,7 @@ namespace Microsoft.Dafny
}
}
- public void PostResolve(ModuleDefinition m) {
+ internal override void PostResolve(ModuleDefinition m) {
foreach (var d in m.TopLevelDecls) {
bool sayYes = true;
if (d is ClassDecl && Attributes.ContainsBool(d.Attributes, "autocontracts", ref sayYes) && sayYes) {
@@ -141,9 +458,6 @@ namespace Microsoft.Dafny
}
}
- public void PostCyclicityResolve(ModuleDefinition m) {
- }
-
void ProcessClassPostResolve(ClassDecl cl) {
// Find all fields of a reference type, and make a note of whether or not the reference type has a Repr field.
// Also, find the Repr field and the function Valid in class "cl"
@@ -246,8 +560,8 @@ namespace Microsoft.Dafny
if (ctor.Body != null) {
var bodyStatements = ((BlockStmt)ctor.Body).Body;
// Repr := {this};
- var e = new SetDisplayExpr(tok, new List<Expression>() { self });
- e.Type = new SetType(new ObjectType());
+ var e = new SetDisplayExpr(tok, true, new List<Expression>() { self });
+ e.Type = new SetType(true, new ObjectType());
Statement s = new AssignStmt(tok, tok, Repr, new ExprRhs(e));
s.IsGhost = true;
bodyStatements.Add(s);
@@ -255,7 +569,7 @@ namespace Microsoft.Dafny
AddSubobjectReprs(tok, subobjects, bodyStatements, self, implicitSelf, cNull, Repr);
}
- } else if (member is Method && !member.IsStatic) {
+ } else if (member is Method && !member.IsStatic && !member.IsGhost) {
var m = (Method)member;
if (Valid != null && !IsSimpleQueryMethod(m)) {
if (member.RefinementBase == null) {
@@ -297,8 +611,8 @@ namespace Microsoft.Dafny
foreach (var ff in subobjects) {
var F = Resolver.NewMemberSelectExpr(tok, implicitSelf, ff.Item1, null); // create a resolved MemberSelectExpr
- Expression e = new SetDisplayExpr(tok, new List<Expression>() { F });
- e.Type = new SetType(new ObjectType()); // resolve here
+ Expression e = new SetDisplayExpr(tok, true, new List<Expression>() { F });
+ e.Type = new SetType(true, new ObjectType()); // resolve here
var rhs = new BinaryExpr(tok, BinaryExpr.Opcode.Add, Repr, e);
rhs.ResolvedOp = BinaryExpr.ResolvedOpcode.Union; // resolve here
rhs.Type = Repr.Type; // resolve here
@@ -318,7 +632,7 @@ namespace Microsoft.Dafny
e = BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.NeqCommon, F, cNull);
var thn = new BlockStmt(tok, tok, new List<Statement>() { s });
thn.IsGhost = true;
- s = new IfStmt(tok, tok, e, thn, null);
+ s = new IfStmt(tok, tok, false, e, thn, null);
s.IsGhost = true;
// finally, add s to the body
bodyStatements.Add(s);
@@ -386,20 +700,20 @@ namespace Microsoft.Dafny
/// specifically asks to see it via the reveal_foo() lemma
/// </summary>
public class OpaqueFunctionRewriter : IRewriter {
- readonly ResolutionErrorReporter reporter;
protected Dictionary<Function, Function> fullVersion; // Given an opaque function, retrieve the full
protected Dictionary<Function, Function> original; // Given a full version of an opaque function, find the original opaque version
protected Dictionary<Lemma, Function> revealOriginal; // Map reveal_* lemmas back to their original functions
- public OpaqueFunctionRewriter(ResolutionErrorReporter reporter)
- : base() {
- this.reporter = reporter;
+ public OpaqueFunctionRewriter(ErrorReporter reporter)
+ : base(reporter) {
+ Contract.Requires(reporter != null);
+
fullVersion = new Dictionary<Function, Function>();
original = new Dictionary<Function, Function>();
revealOriginal = new Dictionary<Lemma, Function>();
}
- public void PreResolve(ModuleDefinition m) {
+ internal override void PreResolve(ModuleDefinition m) {
foreach (var d in m.TopLevelDecls) {
if (d is ClassDecl) {
DuplicateOpaqueClassFunctions((ClassDecl)d);
@@ -407,7 +721,7 @@ namespace Microsoft.Dafny
}
}
- public void PostResolve(ModuleDefinition m) {
+ internal override void PostResolve(ModuleDefinition m) {
// Fix up the ensures clause of the full version of the function,
// since it may refer to the original opaque function
foreach (var fn in ModuleDefinition.AllFunctions(m.TopLevelDecls)) {
@@ -432,7 +746,7 @@ namespace Microsoft.Dafny
}
}
- public void PostCyclicityResolve(ModuleDefinition m) {
+ internal override void PostCyclicityResolve(ModuleDefinition m) {
// Add layer quantifier if the function is recursive
foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) {
if (decl is Lemma) {
@@ -477,7 +791,7 @@ namespace Microsoft.Dafny
if (!Attributes.Contains(f.Attributes, "opaque")) {
// Nothing to do
} else if (f.IsProtected) {
- reporter.Error(f.tok, ":opaque is not allowed to be applied to protected functions (this will be allowed when the language introduces 'opaque'/'reveal' as keywords)");
+ reporter.Error(MessageSource.Rewriter, f.tok, ":opaque is not allowed to be applied to protected functions (this will be allowed when the language introduces 'opaque'/'reveal' as keywords)");
} else if (!RefinementToken.IsInherited(f.tok, c.Module)) {
// Create a copy, which will be the internal version with a full body
// which will allow us to verify that the ensures are true
@@ -496,6 +810,7 @@ namespace Microsoft.Dafny
// Annotate the new function so we remember that we introduced it
fWithBody.Attributes = new Attributes("opaque_full", new List<Expression>(), fWithBody.Attributes);
+ fWithBody.Attributes = new Attributes("auto_generated", new List<Expression>(), fWithBody.Attributes);
// Create a lemma to allow the user to selectively reveal the function's body
// That is, given:
@@ -505,7 +820,7 @@ namespace Microsoft.Dafny
// ensures foo(x, y) < 10;
// { x + y }
// We produce:
- // lemma {:axiom} reveal_foo()
+ // lemma {:axiom} {:auto_generated} reveal_foo()
// ensures forall x:int, y:int {:trigger foo(x,y)} :: 0 <= x < 5 && 0 <= y < 5 ==> foo(x,y) == foo_FULL(x,y);
Expression reqExpr = new LiteralExpr(f.tok, true);
foreach (Expression req in f.Req) {
@@ -518,10 +833,7 @@ namespace Microsoft.Dafny
typeVars.Add(cloner.CloneTypeParam(tp));
}
- List<BoundVar> boundVars = new List<BoundVar>();
- foreach (Formal formal in f.Formals) {
- boundVars.Add(new BoundVar(f.tok, formal.Name, cloner.CloneType(formal.Type)));
- }
+ var boundVars = f.Formals.ConvertAll(formal => new BoundVar(formal.tok, formal.Name, cloner.CloneType(formal.Type)));
// Build the implication connecting the function's requires to the connection with the revealed-body version
Func<Function, Expression> func_builder = func =>
@@ -552,6 +864,7 @@ namespace Microsoft.Dafny
// Add an axiom attribute so that the compiler won't complain about the lemma's lack of a body
Attributes lemma_attrs = new Attributes("axiom", new List<Expression>(), null);
+ lemma_attrs = new Attributes("auto_generated", new List<Expression>(), lemma_attrs);
var reveal = new Lemma(f.tok, "reveal_" + f.Name, f.HasStaticKeyword, new List<TypeParameter>(), new List<Formal>(), new List<Formal>(), new List<MaybeFreeExpression>(),
new Specification<FrameExpression>(new List<FrameExpression>(), null), newEnsuresList,
@@ -692,19 +1005,16 @@ namespace Microsoft.Dafny
/// </summary>
public class AutoReqFunctionRewriter : IRewriter {
Function parentFunction;
- Resolver resolver;
OpaqueFunctionRewriter opaqueInfo;
bool containsMatch; // TODO: Track this per-requirement, rather than per-function
- public AutoReqFunctionRewriter(Resolver r, OpaqueFunctionRewriter o) {
- this.resolver = r;
+ public AutoReqFunctionRewriter(ErrorReporter reporter, OpaqueFunctionRewriter o)
+ : base(reporter) {
+ Contract.Requires(reporter != null);
this.opaqueInfo = o;
}
- public void PreResolve(ModuleDefinition m) {
- }
-
- public void PostResolve(ModuleDefinition m) {
+ internal override void PostResolve(ModuleDefinition m) {
var components = m.CallGraph.TopologicallySortedComponents();
foreach (var scComponent in components) { // Visit the call graph bottom up, so anything we call already has its prequisites calculated
@@ -777,9 +1087,6 @@ namespace Microsoft.Dafny
}
}
- public void PostCyclicityResolve(ModuleDefinition m) {
- }
-
Expression subVars(List<Formal> formals, List<Expression> values, Expression e, Expression f_this) {
Contract.Assert(formals != null);
Contract.Assert(values != null);
@@ -810,7 +1117,7 @@ namespace Microsoft.Dafny
}
if (!tip.Equals("")) {
- resolver.ReportAdditionalInformation(f.tok, tip, f.tok.val.Length);
+ reporter.Info(MessageSource.Rewriter, f.tok, tip);
if (DafnyOptions.O.AutoReqPrintFile != null) {
using (System.IO.TextWriter writer = new System.IO.StreamWriter(DafnyOptions.O.AutoReqPrintFile, true)) {
writer.WriteLine(f.Name);
@@ -837,7 +1144,7 @@ namespace Microsoft.Dafny
}
if (!tip.Equals("")) {
- resolver.ReportAdditionalInformation(method.tok, tip, method.tok.val.Length);
+ reporter.Info(MessageSource.Rewriter, method.tok, tip);
if (DafnyOptions.O.AutoReqPrintFile != null) {
using (System.IO.TextWriter writer = new System.IO.StreamWriter(DafnyOptions.O.AutoReqPrintFile, true)) {
writer.WriteLine(method.Name);
@@ -928,6 +1235,10 @@ namespace Microsoft.Dafny
reqs.AddRange(generateAutoReqs(e.Seq));
reqs.AddRange(generateAutoReqs(e.Index));
reqs.AddRange(generateAutoReqs(e.Value));
+ } else if (expr is DatatypeUpdateExpr) {
+ foreach (var ee in expr.SubExpressions) {
+ reqs.AddRange(generateAutoReqs(ee));
+ }
} else if (expr is FunctionCallExpr) {
FunctionCallExpr e = (FunctionCallExpr)expr;
@@ -1032,7 +1343,7 @@ namespace Microsoft.Dafny
Expression allReqsSatisfied = andify(e.Term.tok, auto_reqs);
Expression allReqsSatisfiedAndTerm = Expression.CreateAnd(allReqsSatisfied, e.Term);
e.UpdateTerm(allReqsSatisfiedAndTerm);
- resolver.ReportAdditionalInformation(e.tok, "autoreq added (" + Printer.ExtendedExprToString(allReqsSatisfied) + ") &&", e.tok.val.Length);
+ reporter.Info(MessageSource.Rewriter, e.tok, "autoreq added (" + Printer.ExtendedExprToString(allReqsSatisfied) + ") &&");
}
} else if (expr is SetComprehension) {
var e = (SetComprehension)expr;
@@ -1079,7 +1390,12 @@ namespace Microsoft.Dafny
/// </summary>
public class TimeLimitRewriter : IRewriter
{
- public void PreResolve(ModuleDefinition m) {
+ public TimeLimitRewriter(ErrorReporter reporter)
+ : base(reporter) {
+ Contract.Requires(reporter != null);
+ }
+
+ internal override void PreResolve(ModuleDefinition m) {
foreach (var d in m.TopLevelDecls) {
if (d is ClassDecl) {
var c = (ClassDecl)d;
@@ -1088,15 +1404,15 @@ namespace Microsoft.Dafny
// Check for the timeLimitMultiplier attribute
if (Attributes.Contains(member.Attributes, "timeLimitMultiplier")) {
Attributes attrs = member.Attributes;
- for (; attrs != null; attrs = attrs.Prev) {
- if (attrs.Name == "timeLimitMultiplier") {
- if (attrs.Args.Count == 1 && attrs.Args[0] is LiteralExpr) {
- var arg = attrs.Args[0] as LiteralExpr;
+ foreach (var attr in attrs.AsEnumerable()) {
+ if (attr.Name == "timeLimitMultiplier") {
+ if (attr.Args.Count == 1 && attr.Args[0] is LiteralExpr) {
+ var arg = attr.Args[0] as LiteralExpr;
System.Numerics.BigInteger value = (System.Numerics.BigInteger)arg.Value;
if (value.Sign > 0) {
int current_limit = DafnyOptions.O.ProverKillTime > 0 ? DafnyOptions.O.ProverKillTime : 10; // Default to 10 seconds
- attrs.Args[0] = new LiteralExpr(attrs.Args[0].tok, value * current_limit);
- attrs.Name = "timeLimit";
+ attr.Args[0] = new LiteralExpr(attr.Args[0].tok, value * current_limit);
+ attr.Name = "timeLimit";
}
}
}
@@ -1107,16 +1423,470 @@ namespace Microsoft.Dafny
}
}
}
+ }
+
+
+ class MatchCaseExprSubstituteCloner : Cloner
+ {
+ private List<Tuple<CasePattern, BoundVar>> patternSubst;
+ private BoundVar oldvar;
+ private BoundVar var;
+
+ // the cloner is called after resolving the body of matchexpr, trying
+ // to replace casepattern in the body that has been replaced by bv
+ public MatchCaseExprSubstituteCloner(List<Tuple<CasePattern, BoundVar>> subst) {
+ this.patternSubst = subst;
+ this.oldvar = null;
+ this.var = null;
+ }
+
+ public MatchCaseExprSubstituteCloner(BoundVar oldvar, BoundVar var) {
+ this.patternSubst = null;
+ this.oldvar = oldvar;
+ this.var = var;
+ }
+
+ public override BoundVar CloneBoundVar(BoundVar bv) {
+ // replace bv with this.var if bv == oldvar
+ BoundVar bvNew;
+ if (oldvar != null && bv.Name.Equals(oldvar.Name)) {
+ bvNew = new BoundVar(new AutoGeneratedToken(bv.tok), var.Name, CloneType(bv.Type));
+ } else {
+ bvNew = new BoundVar(Tok(bv.tok), bv.Name, CloneType(bv.Type));
+ }
+ bvNew.IsGhost = bv.IsGhost;
+ return bvNew;
+ }
+
+ public override NameSegment CloneNameSegment(Expression expr) {
+ var e = (NameSegment)expr;
+ if (oldvar != null && e.Name.Equals(oldvar.Name)) {
+ return new NameSegment(new AutoGeneratedToken(e.tok), var.Name, e.OptTypeArguments == null ? null : e.OptTypeArguments.ConvertAll(CloneType));
+ } else {
+ return new NameSegment(Tok(e.tok), e.Name, e.OptTypeArguments == null ? null : e.OptTypeArguments.ConvertAll(CloneType));
+ }
+ }
+
+ public override Expression CloneApplySuffix(ApplySuffix e) {
+ // if the ApplySuffix matches the CasePattern, then replace it with the BoundVar.
+ CasePattern cp = null;
+ BoundVar bv = null;
+ if (FindMatchingPattern(e, out cp, out bv)) {
+ if (bv.tok is MatchCaseToken) {
+ Contract.Assert(e.Args.Count == cp.Arguments.Count);
+ for (int i = 0; i < e.Args.Count; i++) {
+ ((MatchCaseToken)bv.tok).AddVar(e.Args[i].tok, cp.Arguments[i].Var, false);
+ }
+ }
+ return new NameSegment(new AutoGeneratedToken(e.tok), bv.Name, null);
+ } else {
+ return new ApplySuffix(Tok(e.tok), CloneExpr(e.Lhs), e.Args.ConvertAll(CloneExpr));
+ }
+ }
- public void PostResolve(ModuleDefinition m)
+ private bool FindMatchingPattern(ApplySuffix e, out CasePattern pattern, out BoundVar bv) {
+ pattern = null;
+ bv = null;
+ if (patternSubst == null) {
+ return false;
+ }
+ Expression lhs = e.Lhs;
+ if (!(lhs is NameSegment)) {
+ return false;
+ }
+ string applyName = ((NameSegment)lhs).Name;
+ foreach (Tuple<CasePattern, BoundVar> pair in patternSubst) {
+ CasePattern cp = pair.Item1;
+ string ctorName = cp.Id;
+ if (!(applyName.Equals(ctorName)) || (e.Args.Count != cp.Arguments.Count)) {
+ continue;
+ }
+ bool found = true;
+ for (int i = 0; i < e.Args.Count; i++) {
+ var arg1 = e.Args[i];
+ var arg2 = cp.Arguments[i];
+ if (arg1.Resolved is IdentifierExpr) {
+ var bv1 = ((IdentifierExpr)arg1.Resolved).Var;
+ if (bv1 != arg2.Var) {
+ found = false;
+ }
+ } else {
+ found = false;
+ }
+ }
+ if (found) {
+ pattern = cp;
+ bv = pair.Item2;
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ // MatchCaseToken is used to record BoundVars that are consolidated due to rewrite of
+ // nested match patterns. We want to record the original BoundVars that are consolidated
+ // so that they will show up in the IDE correctly.
+ public class MatchCaseToken : AutoGeneratedToken
+ {
+ public readonly List<Tuple<IToken, BoundVar, bool>> varList;
+ public MatchCaseToken(IToken tok)
+ : base(tok) {
+ varList = new List<Tuple<IToken, BoundVar, bool>>();
+ }
+
+ public void AddVar(IToken tok, BoundVar var, bool isDefinition) {
+ varList.Add(new Tuple<IToken, BoundVar, bool>(tok, var, isDefinition));
+ }
+ }
+
+ // A cloner that replace the original token with AutoGeneratedToken.
+ class AutoGeneratedTokenCloner : Cloner
+ {
+ public override IToken Tok(IToken tok) {
+ return new AutoGeneratedToken(tok);
+ }
+ }
+
+ // ===========================================================================================
+
+ public class InductionRewriter : IRewriter {
+ internal InductionRewriter(ErrorReporter reporter) : base(reporter) {
+ Contract.Requires(reporter != null);
+ }
+
+ internal override void PostCyclicityResolve(ModuleDefinition m) {
+ if (DafnyOptions.O.Induction == 0) {
+ // Don't bother inferring :induction attributes. This will also have the effect of not warning about malformed :induction attributes
+ } else {
+ foreach (var decl in m.TopLevelDecls) {
+ if (decl is ClassDecl) {
+ var cl = (ClassDecl)decl;
+ foreach (var member in cl.Members) {
+ if (member is FixpointLemma) {
+ var method = (FixpointLemma)member;
+ ProcessMethodExpressions(method);
+ ComputeLemmaInduction(method.PrefixLemma);
+ ProcessMethodExpressions(method.PrefixLemma);
+ } else if (member is Method) {
+ var method = (Method)member;
+ ComputeLemmaInduction(method);
+ ProcessMethodExpressions(method);
+ } else if (member is FixpointPredicate) {
+ var function = (FixpointPredicate)member;
+ ProcessFunctionExpressions(function);
+ ProcessFunctionExpressions(function.PrefixPredicate);
+ } else if (member is Function) {
+ var function = (Function)member;
+ ProcessFunctionExpressions(function);
+ }
+ }
+ } else if (decl is NewtypeDecl) {
+ var nt = (NewtypeDecl)decl;
+ if (nt.Constraint != null) {
+ var visitor = new Induction_Visitor(this);
+ visitor.Visit(nt.Constraint);
+ }
+ }
+ }
+ }
+ }
+
+ void ProcessMethodExpressions(Method method) {
+ Contract.Requires(method != null);
+ var visitor = new Induction_Visitor(this);
+ method.Req.ForEach(mfe => visitor.Visit(mfe.E));
+ method.Ens.ForEach(mfe => visitor.Visit(mfe.E));
+ if (method.Body != null) {
+ visitor.Visit(method.Body);
+ }
+ }
+
+ void ProcessFunctionExpressions(Function function) {
+ Contract.Requires(function != null);
+ var visitor = new Induction_Visitor(this);
+ function.Req.ForEach(visitor.Visit);
+ function.Ens.ForEach(visitor.Visit);
+ if (function.Body != null) {
+ visitor.Visit(function.Body);
+ }
+ }
+
+ void ComputeLemmaInduction(Method method) {
+ Contract.Requires(method != null);
+ if (method.Body != null && method.IsGhost && method.Mod.Expressions.Count == 0 && method.Outs.Count == 0 && !(method is FixpointLemma)) {
+ var specs = new List<Expression>();
+ method.Req.ForEach(mfe => specs.Add(mfe.E));
+ method.Ens.ForEach(mfe => specs.Add(mfe.E));
+ ComputeInductionVariables(method.tok, method.Ins, specs, method, ref method.Attributes);
+ }
+ }
+
+ void ComputeInductionVariables<VarType>(IToken tok, List<VarType> boundVars, List<Expression> searchExprs, Method lemma, ref Attributes attributes) where VarType : class, IVariable {
+ Contract.Requires(tok != null);
+ Contract.Requires(boundVars != null);
+ Contract.Requires(searchExprs != null);
+ Contract.Requires(DafnyOptions.O.Induction != 0);
+
+ var args = Attributes.FindExpressions(attributes, "induction"); // we only look at the first one we find, since it overrides any other ones
+ if (args == null) {
+ if (DafnyOptions.O.Induction < 2) {
+ // No explicit induction variables and we're asked not to infer anything, so we're done
+ return;
+ } else if (DafnyOptions.O.Induction == 2 && lemma != null) {
+ // We're asked to infer induction variables only for quantifiers, not for lemmas
+ return;
+ }
+ // GO INFER below (only select boundVars)
+ } else if (args.Count == 0) {
+ // {:induction} is treated the same as {:induction true}, which says to automatically infer induction variables
+ // GO INFER below (all boundVars)
+ } else if (args.Count == 1 && args[0] is LiteralExpr && ((LiteralExpr)args[0]).Value is bool) {
+ // {:induction false} or {:induction true}
+ if (!(bool)((LiteralExpr)args[0]).Value) {
+ // we're told not to infer anything
+ return;
+ }
+ // GO INFER below (all boundVars)
+ } else {
+ // Here, we're expecting the arguments to {:induction args} to be a sublist of "boundVars".
+ var goodArguments = new List<Expression>();
+ var i = 0;
+ foreach (var arg in args) {
+ var ie = arg.Resolved as IdentifierExpr;
+ if (ie != null) {
+ var j = boundVars.FindIndex(i, v => v == ie.Var);
+ if (i <= j) {
+ goodArguments.Add(ie);
+ i = j;
+ continue;
+ }
+ if (0 <= boundVars.FindIndex(v => v == ie.Var)) {
+ reporter.Warning(MessageSource.Rewriter, arg.tok, "{0}s given as :induction arguments must be given in the same order as in the {1}; ignoring attribute",
+ lemma != null ? "lemma parameter" : "bound variable", lemma != null ? "lemma" : "quantifier");
+ return;
+ }
+ }
+ reporter.Warning(MessageSource.Rewriter, arg.tok, "invalid :induction attribute argument; expected {0}{1}; ignoring attribute",
+ i == 0 ? "'false' or 'true' or " : "",
+ lemma != null ? "lemma parameter" : "bound variable");
+ return;
+ }
+ // The argument list was legal, so let's use it for the _induction attribute
+ attributes = new Attributes("_induction", goodArguments, attributes);
+ return;
+ }
+
+ // Okay, here we go, coming up with good induction setting for the given situation
+ var inductionVariables = new List<Expression>();
+ foreach (IVariable n in boundVars) {
+ if (!n.Type.IsTypeParameter && (args != null || searchExprs.Exists(expr => VarOccursInArgumentToRecursiveFunction(expr, n)))) {
+ inductionVariables.Add(new IdentifierExpr(n));
+ }
+ }
+ if (inductionVariables.Count != 0) {
+ // We found something usable, so let's record that in an attribute
+ attributes = new Attributes("_induction", inductionVariables, attributes);
+ // And since we're inferring something, let's also report that in a hover text.
+ var s = Printer.OneAttributeToString(attributes, "induction");
+ if (lemma is PrefixLemma) {
+ s = lemma.Name + " " + s;
+ }
+ reporter.Info(MessageSource.Rewriter, tok, s);
+ }
+ }
+ class Induction_Visitor : BottomUpVisitor
{
- // Nothing to do here
+ readonly InductionRewriter IndRewriter;
+ public Induction_Visitor(InductionRewriter inductionRewriter) {
+ Contract.Requires(inductionRewriter != null);
+ IndRewriter = inductionRewriter;
+ }
+ protected override void VisitOneExpr(Expression expr) {
+ var q = expr as QuantifierExpr;
+ if (q != null && q.SplitQuantifier == null) {
+ IndRewriter.ComputeInductionVariables(q.tok, q.BoundVars, new List<Expression>() { q.LogicalBody() }, null, ref q.Attributes);
+ }
+ }
+ void VisitInductionStmt(Statement stmt) {
+ Contract.Requires(stmt != null);
+ // visit a selection of subexpressions
+ if (stmt is AssertStmt) {
+ var s = (AssertStmt)stmt;
+ Visit(s.Expr);
+ }
+ // recursively visit all substatements
+ foreach (var s in stmt.SubStatements) {
+ VisitInductionStmt(s);
+ }
+ }
}
- public void PostCyclicityResolve(ModuleDefinition m) {
- // Nothing to do here
+ /// <summary>
+ /// Returns 'true' iff by looking at 'expr' the Induction Heuristic determines that induction should be applied to 'n'.
+ /// More precisely:
+ /// DafnyInductionHeuristic Return 'true'
+ /// ----------------------- -------------
+ /// 0 always
+ /// 1 if 'n' occurs as any subexpression (of 'expr')
+ /// 2 if 'n' occurs as any subexpression of any index argument of an array/sequence select expression or any argument to a recursive function
+ /// 3 if 'n' occurs as a prominent subexpression of any index argument of an array/sequence select expression or any argument to a recursive function
+ /// 4 if 'n' occurs as any subexpression of any argument to a recursive function
+ /// 5 if 'n' occurs as a prominent subexpression of any argument to a recursive function
+ /// 6 if 'n' occurs as a prominent subexpression of any decreases-influencing argument to a recursive function
+ /// Parameter 'n' is allowed to be a ThisSurrogate.
+ /// </summary>
+ public static bool VarOccursInArgumentToRecursiveFunction(Expression expr, IVariable n) {
+ switch (DafnyOptions.O.InductionHeuristic) {
+ case 0: return true;
+ case 1: return Translator.ContainsFreeVariable(expr, false, n);
+ default: return VarOccursInArgumentToRecursiveFunction(expr, n, false);
+ }
}
+ /// <summary>
+ /// Worker routine for VarOccursInArgumentToRecursiveFunction(expr,n), where the additional parameter 'exprIsProminent' says whether or
+ /// not 'expr' has prominent status in its context.
+ /// DafnyInductionHeuristic cases 0 and 1 are assumed to be handled elsewhere (i.e., a precondition of this method is DafnyInductionHeuristic is at least 2).
+ /// Parameter 'n' is allowed to be a ThisSurrogate.
+ /// </summary>
+ static bool VarOccursInArgumentToRecursiveFunction(Expression expr, IVariable n, bool exprIsProminent) {
+ Contract.Requires(expr != null);
+ Contract.Requires(n != null);
+
+ // The following variable is what gets passed down to recursive calls if the subexpression does not itself acquire prominent status.
+ var subExprIsProminent = DafnyOptions.O.InductionHeuristic == 2 || DafnyOptions.O.InductionHeuristic == 4 ? /*once prominent, always prominent*/exprIsProminent : /*reset the prominent status*/false;
+
+ if (expr is IdentifierExpr) {
+ var e = (IdentifierExpr)expr;
+ return exprIsProminent && e.Var == n;
+ } else if (expr is SeqSelectExpr) {
+ var e = (SeqSelectExpr)expr;
+ var q = DafnyOptions.O.InductionHeuristic < 4 || subExprIsProminent;
+ return VarOccursInArgumentToRecursiveFunction(e.Seq, n, subExprIsProminent) || // this subexpression does not acquire "prominent" status
+ (e.E0 != null && VarOccursInArgumentToRecursiveFunction(e.E0, n, q)) || // this one does (unless arrays/sequences are excluded)
+ (e.E1 != null && VarOccursInArgumentToRecursiveFunction(e.E1, n, q)); // ditto
+ } else if (expr is MultiSelectExpr) {
+ var e = (MultiSelectExpr)expr;
+ var q = DafnyOptions.O.InductionHeuristic < 4 || subExprIsProminent;
+ return VarOccursInArgumentToRecursiveFunction(e.Array, n, subExprIsProminent) ||
+ e.Indices.Exists(exp => VarOccursInArgumentToRecursiveFunction(exp, n, q));
+ } else if (expr is FunctionCallExpr) {
+ var e = (FunctionCallExpr)expr;
+ // For recursive functions: arguments are "prominent"
+ // For non-recursive function: arguments are "prominent" if the call is
+ var rec = e.Function.IsRecursive && e.CoCall != FunctionCallExpr.CoCallResolution.Yes;
+ var decr = e.Function.Decreases.Expressions;
+ bool variantArgument;
+ if (DafnyOptions.O.InductionHeuristic < 6) {
+ variantArgument = rec;
+ } else {
+ // The receiver is considered to be "variant" if the function is recursive and the receiver participates
+ // in the effective decreases clause of the function. The receiver participates if it's a free variable
+ // of a term in the explicit decreases clause.
+ variantArgument = rec && decr.Exists(ee => Translator.ContainsFreeVariable(ee, true, null));
+ }
+ if (VarOccursInArgumentToRecursiveFunction(e.Receiver, n, variantArgument || subExprIsProminent)) {
+ return true;
+ }
+ Contract.Assert(e.Function.Formals.Count == e.Args.Count);
+ for (int i = 0; i < e.Function.Formals.Count; i++) {
+ var f = e.Function.Formals[i];
+ var exp = e.Args[i];
+ if (DafnyOptions.O.InductionHeuristic < 6) {
+ variantArgument = rec;
+ } else if (rec) {
+ // The argument position is considered to be "variant" if the function is recursive and...
+ // ... it has something to do with why the callee is well-founded, which happens when...
+ if (f is ImplicitFormal) {
+ // ... it is the argument is the implicit _k parameter, which is always first in the effective decreases clause of a prefix lemma, or
+ variantArgument = true;
+ } else if (decr.Exists(ee => Translator.ContainsFreeVariable(ee, false, f))) {
+ // ... it participates in the effective decreases clause of the function, which happens when it is
+ // a free variable of a term in the explicit decreases clause, or
+ variantArgument = true;
+ } else {
+ // ... the callee is a prefix predicate.
+ variantArgument = true;
+ }
+ }
+ if (VarOccursInArgumentToRecursiveFunction(exp, n, variantArgument || subExprIsProminent)) {
+ return true;
+ }
+ }
+ return false;
+ } else if (expr is TernaryExpr) {
+ var e = (TernaryExpr)expr;
+ switch (e.Op) {
+ case TernaryExpr.Opcode.PrefixEqOp:
+ case TernaryExpr.Opcode.PrefixNeqOp:
+ return VarOccursInArgumentToRecursiveFunction(e.E0, n, true) ||
+ VarOccursInArgumentToRecursiveFunction(e.E1, n, subExprIsProminent) ||
+ VarOccursInArgumentToRecursiveFunction(e.E2, n, subExprIsProminent);
+ default:
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected ternary expression
+ }
+ } else if (expr is DatatypeValue) {
+ var e = (DatatypeValue)expr;
+ var q = n.Type.IsDatatype ? exprIsProminent : subExprIsProminent; // prominent status continues, if we're looking for a variable whose type is a datatype
+ return e.Arguments.Exists(exp => VarOccursInArgumentToRecursiveFunction(exp, n, q));
+ } else if (expr is UnaryExpr) {
+ var e = (UnaryExpr)expr;
+ // both Not and SeqLength preserve prominence
+ return VarOccursInArgumentToRecursiveFunction(e.E, n, exprIsProminent);
+ } else if (expr is BinaryExpr) {
+ var e = (BinaryExpr)expr;
+ bool q;
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.Add:
+ case BinaryExpr.ResolvedOpcode.Sub:
+ case BinaryExpr.ResolvedOpcode.Mul:
+ case BinaryExpr.ResolvedOpcode.Div:
+ case BinaryExpr.ResolvedOpcode.Mod:
+ case BinaryExpr.ResolvedOpcode.Union:
+ case BinaryExpr.ResolvedOpcode.Intersection:
+ case BinaryExpr.ResolvedOpcode.SetDifference:
+ case BinaryExpr.ResolvedOpcode.Concat:
+ // these operators preserve prominence
+ q = exprIsProminent;
+ break;
+ default:
+ // whereas all other binary operators do not
+ q = subExprIsProminent;
+ break;
+ }
+ return VarOccursInArgumentToRecursiveFunction(e.E0, n, q) ||
+ VarOccursInArgumentToRecursiveFunction(e.E1, n, q);
+ } else if (expr is StmtExpr) {
+ var e = (StmtExpr)expr;
+ // ignore the statement
+ return VarOccursInArgumentToRecursiveFunction(e.E, n);
+
+ } else if (expr is ITEExpr) {
+ var e = (ITEExpr)expr;
+ return VarOccursInArgumentToRecursiveFunction(e.Test, n, subExprIsProminent) || // test is not "prominent"
+ VarOccursInArgumentToRecursiveFunction(e.Thn, n, exprIsProminent) || // but the two branches are
+ VarOccursInArgumentToRecursiveFunction(e.Els, n, exprIsProminent);
+ } else if (expr is OldExpr ||
+ expr is ConcreteSyntaxExpression ||
+ expr is BoxingCastExpr ||
+ expr is UnboxingCastExpr) {
+ foreach (var exp in expr.SubExpressions) {
+ if (VarOccursInArgumentToRecursiveFunction(exp, n, exprIsProminent)) { // maintain prominence
+ return true;
+ }
+ }
+ return false;
+ } else {
+ // in all other cases, reset the prominence status and recurse on the subexpressions
+ foreach (var exp in expr.SubExpressions) {
+ if (VarOccursInArgumentToRecursiveFunction(exp, n, subExprIsProminent)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
}
}
diff --git a/Source/Dafny/Scanner.cs b/Source/Dafny/Scanner.cs
index 3427477b..cb3dbe7e 100644
--- a/Source/Dafny/Scanner.cs
+++ b/Source/Dafny/Scanner.cs
@@ -211,23 +211,35 @@ public class UTF8Buffer: Buffer {
public class Scanner {
const char EOL = '\n';
const int eofSym = 0; /* pdt */
- const int maxT = 136;
- const int noSym = 136;
+ const int maxT = 140;
+ const int noSym = 140;
[ContractInvariantMethod]
void objectInvariant(){
- Contract.Invariant(buffer!=null);
+ Contract.Invariant(this._buffer != null);
Contract.Invariant(t != null);
Contract.Invariant(start != null);
Contract.Invariant(tokens != null);
Contract.Invariant(pt != null);
Contract.Invariant(tval != null);
Contract.Invariant(Filename != null);
+ Contract.Invariant(FullFilename != null);
Contract.Invariant(errorHandler != null);
}
- public Buffer/*!*/ buffer; // scanner buffer
+ private Buffer/*!*/ _buffer; // scanner buffer
+
+ public Buffer/*!*/ buffer {
+ get {
+ Contract.Ensures(Contract.Result<Buffer>() != null);
+ return this._buffer;
+ }
+ set {
+ Contract.Requires(value != null);
+ this._buffer = value;
+ }
+ }
Token/*!*/ t; // current token
int ch; // current input character
@@ -247,51 +259,53 @@ public class Scanner {
private string/*!*/ Filename;
private Errors/*!*/ errorHandler;
+ internal string/*!*/ FullFilename { get; private set; }
+
static Scanner() {
start = new Hashtable(128);
for (int i = 63; i <= 63; ++i) start[i] = 1;
for (int i = 65; i <= 90; ++i) start[i] = 1;
for (int i = 95; i <= 95; ++i) start[i] = 1;
for (int i = 98; i <= 122; ++i) start[i] = 1;
- for (int i = 49; i <= 57; ++i) start[i] = 48;
- start[97] = 49;
- start[39] = 50;
- start[48] = 51;
+ for (int i = 49; i <= 57; ++i) start[i] = 49;
+ start[97] = 50;
+ start[39] = 51;
+ start[48] = 52;
start[34] = 21;
start[64] = 26;
start[58] = 89;
start[44] = 29;
start[124] = 90;
- start[8226] = 31;
+ start[8226] = 32;
start[46] = 91;
- start[59] = 32;
+ start[59] = 33;
start[61] = 92;
start[45] = 93;
- start[123] = 35;
- start[125] = 36;
- start[91] = 37;
- start[93] = 38;
- start[40] = 39;
- start[41] = 40;
+ start[123] = 36;
+ start[125] = 37;
+ start[91] = 38;
+ start[93] = 39;
+ start[40] = 40;
+ start[41] = 41;
start[60] = 94;
start[62] = 95;
start[33] = 96;
- start[8800] = 42;
- start[42] = 43;
- start[96] = 66;
- start[35] = 69;
- start[8804] = 71;
- start[8805] = 72;
- start[8660] = 74;
- start[8658] = 76;
- start[8656] = 77;
- start[38] = 78;
- start[8743] = 80;
- start[8744] = 82;
- start[172] = 83;
- start[8704] = 84;
- start[8707] = 85;
- start[43] = 86;
+ start[8800] = 43;
+ start[42] = 44;
+ start[43] = 67;
+ start[96] = 68;
+ start[35] = 70;
+ start[8804] = 72;
+ start[8805] = 73;
+ start[8660] = 75;
+ start[8658] = 77;
+ start[8656] = 78;
+ start[38] = 79;
+ start[8743] = 81;
+ start[8744] = 83;
+ start[172] = 84;
+ start[8704] = 85;
+ start[8707] = 86;
start[47] = 87;
start[37] = 88;
start[Buffer.EOF] = -1;
@@ -299,15 +313,16 @@ public class Scanner {
}
// [NotDelayed]
- public Scanner (string/*!*/ fileName, Errors/*!*/ errorHandler, bool useBaseName = false) : base() {
- Contract.Requires(fileName != null);
- Contract.Requires(errorHandler != null);
+ public Scanner (string/*!*/ fullFilename, string/*!*/ fileName, Errors/*!*/ errorHandler, bool useBaseName = false) : base() {
+ Contract.Requires(fileName != null);
+ Contract.Requires(errorHandler != null);
this.errorHandler = errorHandler;
pt = tokens = new Token(); // first token is a dummy
t = new Token(); // dummy because t is a non-null field
try {
Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
- buffer = new Buffer(stream, false);
+ this._buffer = new Buffer(stream, false);
+ this.FullFilename = fullFilename;
Filename = useBaseName? GetBaseName(fileName): fileName;
Init();
} catch (IOException) {
@@ -316,21 +331,22 @@ public class Scanner {
}
// [NotDelayed]
- public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fileName, bool useBaseName = false) : base() {
- Contract.Requires(s != null);
- Contract.Requires(errorHandler != null);
- Contract.Requires(fileName != null);
+ public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fullFilename, string/*!*/ fileName, bool useBaseName = false) : base() {
+ Contract.Requires(s != null);
+ Contract.Requires(errorHandler != null);
+ Contract.Requires(fileName != null);
pt = tokens = new Token(); // first token is a dummy
t = new Token(); // dummy because t is a non-null field
- buffer = new Buffer(s, true);
+ this._buffer = new Buffer(s, true);
this.errorHandler = errorHandler;
+ this.FullFilename = fullFilename;
this.Filename = useBaseName? GetBaseName(fileName) : fileName;
Init();
}
- string GetBaseName(string fileName) {
- return System.IO.Path.GetFileName(fileName); // Return basename
- }
+ string GetBaseName(string fileName) {
+ return System.IO.Path.GetFileName(fileName); // Return basename
+ }
void Init() {
pos = -1; line = 1; col = 0;
@@ -503,75 +519,79 @@ public class Scanner {
case "object": t.kind = 11; break;
case "string": t.kind = 12; break;
case "set": t.kind = 13; break;
- case "multiset": t.kind = 14; break;
- case "seq": t.kind = 15; break;
- case "map": t.kind = 16; break;
- case "imap": t.kind = 17; break;
- case "assume": t.kind = 29; break;
- case "calc": t.kind = 30; break;
- case "case": t.kind = 31; break;
- case "then": t.kind = 32; break;
- case "else": t.kind = 33; break;
- case "decreases": t.kind = 34; break;
- case "invariant": t.kind = 35; break;
- case "function": t.kind = 36; break;
- case "predicate": t.kind = 37; break;
- case "inductive": t.kind = 38; break;
- case "lemma": t.kind = 39; break;
- case "copredicate": t.kind = 40; break;
- case "modifies": t.kind = 41; break;
- case "reads": t.kind = 42; break;
- case "requires": t.kind = 43; break;
- case "include": t.kind = 58; break;
- case "abstract": t.kind = 59; break;
- case "module": t.kind = 60; break;
- case "refines": t.kind = 61; break;
- case "import": t.kind = 62; break;
- case "opened": t.kind = 63; break;
- case "as": t.kind = 65; break;
- case "default": t.kind = 66; break;
- case "class": t.kind = 67; break;
- case "extends": t.kind = 68; break;
- case "trait": t.kind = 69; break;
- case "ghost": t.kind = 70; break;
- case "static": t.kind = 71; break;
- case "protected": t.kind = 72; break;
- case "datatype": t.kind = 73; break;
- case "codatatype": t.kind = 74; break;
- case "var": t.kind = 75; break;
- case "newtype": t.kind = 76; break;
- case "type": t.kind = 77; break;
- case "iterator": t.kind = 78; break;
- case "yields": t.kind = 79; break;
- case "returns": t.kind = 80; break;
- case "method": t.kind = 81; break;
- case "colemma": t.kind = 82; break;
- case "comethod": t.kind = 83; break;
- case "constructor": t.kind = 84; break;
- case "free": t.kind = 85; break;
- case "ensures": t.kind = 86; break;
- case "yield": t.kind = 87; break;
- case "label": t.kind = 89; break;
- case "break": t.kind = 90; break;
- case "where": t.kind = 91; break;
- case "return": t.kind = 93; break;
- case "new": t.kind = 95; break;
- case "if": t.kind = 96; break;
- case "while": t.kind = 97; break;
- case "match": t.kind = 98; break;
- case "assert": t.kind = 99; break;
- case "print": t.kind = 100; break;
- case "forall": t.kind = 101; break;
- case "parallel": t.kind = 102; break;
- case "modify": t.kind = 103; break;
- case "exists": t.kind = 122; break;
- case "in": t.kind = 124; break;
- case "false": t.kind = 129; break;
- case "true": t.kind = 130; break;
- case "null": t.kind = 131; break;
- case "this": t.kind = 132; break;
- case "fresh": t.kind = 133; break;
- case "old": t.kind = 134; break;
+ case "iset": t.kind = 14; break;
+ case "multiset": t.kind = 15; break;
+ case "seq": t.kind = 16; break;
+ case "map": t.kind = 17; break;
+ case "imap": t.kind = 18; break;
+ case "assume": t.kind = 31; break;
+ case "calc": t.kind = 32; break;
+ case "case": t.kind = 33; break;
+ case "then": t.kind = 34; break;
+ case "else": t.kind = 35; break;
+ case "decreases": t.kind = 36; break;
+ case "invariant": t.kind = 37; break;
+ case "function": t.kind = 38; break;
+ case "predicate": t.kind = 39; break;
+ case "inductive": t.kind = 40; break;
+ case "lemma": t.kind = 41; break;
+ case "copredicate": t.kind = 42; break;
+ case "modifies": t.kind = 43; break;
+ case "reads": t.kind = 44; break;
+ case "requires": t.kind = 45; break;
+ case "include": t.kind = 60; break;
+ case "abstract": t.kind = 61; break;
+ case "ghost": t.kind = 62; break;
+ case "static": t.kind = 63; break;
+ case "protected": t.kind = 64; break;
+ case "extern": t.kind = 65; break;
+ case "module": t.kind = 66; break;
+ case "exclusively": t.kind = 67; break;
+ case "refines": t.kind = 68; break;
+ case "import": t.kind = 69; break;
+ case "opened": t.kind = 70; break;
+ case "as": t.kind = 72; break;
+ case "default": t.kind = 73; break;
+ case "export": t.kind = 74; break;
+ case "extends": t.kind = 75; break;
+ case "class": t.kind = 77; break;
+ case "trait": t.kind = 78; break;
+ case "datatype": t.kind = 79; break;
+ case "codatatype": t.kind = 80; break;
+ case "var": t.kind = 81; break;
+ case "newtype": t.kind = 82; break;
+ case "type": t.kind = 83; break;
+ case "iterator": t.kind = 84; break;
+ case "yields": t.kind = 85; break;
+ case "returns": t.kind = 86; break;
+ case "method": t.kind = 87; break;
+ case "colemma": t.kind = 88; break;
+ case "comethod": t.kind = 89; break;
+ case "constructor": t.kind = 90; break;
+ case "free": t.kind = 91; break;
+ case "ensures": t.kind = 92; break;
+ case "yield": t.kind = 93; break;
+ case "label": t.kind = 95; break;
+ case "break": t.kind = 96; break;
+ case "where": t.kind = 97; break;
+ case "return": t.kind = 99; break;
+ case "new": t.kind = 100; break;
+ case "if": t.kind = 101; break;
+ case "while": t.kind = 102; break;
+ case "match": t.kind = 103; break;
+ case "assert": t.kind = 104; break;
+ case "print": t.kind = 105; break;
+ case "forall": t.kind = 106; break;
+ case "parallel": t.kind = 107; break;
+ case "modify": t.kind = 108; break;
+ case "exists": t.kind = 127; break;
+ case "in": t.kind = 129; break;
+ case "false": t.kind = 133; break;
+ case "true": t.kind = 134; break;
+ case "null": t.kind = 135; break;
+ case "this": t.kind = 136; break;
+ case "fresh": t.kind = 137; break;
+ case "old": t.kind = 138; break;
default: break;
}
}
@@ -674,11 +694,11 @@ public class Scanner {
if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f') {AddCh(); goto case 15;}
else {goto case 0;}
case 20:
- {t.kind = 18; break;}
+ {t.kind = 19; break;}
case 21:
if (ch <= 9 || ch >= 11 && ch <= 12 || ch >= 14 && ch <= '!' || ch >= '#' && ch <= '[' || ch >= ']' && ch <= 65535) {AddCh(); goto case 21;}
else if (ch == '"') {AddCh(); goto case 28;}
- else if (ch == 92) {AddCh(); goto case 54;}
+ else if (ch == 92) {AddCh(); goto case 55;}
else {goto case 0;}
case 22:
if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f') {AddCh(); goto case 23;}
@@ -697,239 +717,239 @@ public class Scanner {
else {goto case 0;}
case 27:
if (ch <= '!' || ch >= '#' && ch <= 65535) {AddCh(); goto case 27;}
- else if (ch == '"') {AddCh(); goto case 55;}
+ else if (ch == '"') {AddCh(); goto case 56;}
else {goto case 0;}
case 28:
- {t.kind = 19; break;}
+ {t.kind = 20; break;}
case 29:
- {t.kind = 21; break;}
+ {t.kind = 22; break;}
case 30:
- {t.kind = 23; break;}
- case 31:
{t.kind = 24; break;}
+ case 31:
+ {t.kind = 25; break;}
case 32:
{t.kind = 26; break;}
case 33:
- {t.kind = 27; break;}
- case 34:
{t.kind = 28; break;}
+ case 34:
+ {t.kind = 29; break;}
case 35:
- {t.kind = 44; break;}
+ {t.kind = 30; break;}
case 36:
- {t.kind = 45; break;}
- case 37:
{t.kind = 46; break;}
- case 38:
+ case 37:
{t.kind = 47; break;}
- case 39:
+ case 38:
{t.kind = 48; break;}
- case 40:
+ case 39:
{t.kind = 49; break;}
+ case 40:
+ {t.kind = 50; break;}
case 41:
- {t.kind = 53; break;}
+ {t.kind = 51; break;}
case 42:
- {t.kind = 54; break;}
- case 43:
{t.kind = 55; break;}
+ case 43:
+ {t.kind = 56; break;}
case 44:
- if (ch == 'n') {AddCh(); goto case 45;}
- else {goto case 0;}
+ {t.kind = 57; break;}
case 45:
- if (ch <= '&' || ch >= '(' && ch <= '/' || ch >= ':' && ch <= '>' || ch == '@' || ch >= '[' && ch <= '^' || ch == '`' || ch >= '{' && ch <= 65535) {apx++; AddCh(); goto case 46;}
+ if (ch == 'n') {AddCh(); goto case 46;}
else {goto case 0;}
case 46:
+ if (ch <= '&' || ch >= '(' && ch <= '/' || ch >= ':' && ch <= '>' || ch == '@' || ch >= '[' && ch <= '^' || ch == '`' || ch >= '{' && ch <= 65535) {apx++; AddCh(); goto case 47;}
+ else {goto case 0;}
+ case 47:
{
tlen -= apx;
SetScannerBehindT();
- t.kind = 56; break;}
- case 47:
- {t.kind = 57; break;}
+ t.kind = 58; break;}
case 48:
+ {t.kind = 59; break;}
+ case 49:
recEnd = pos; recKind = 2;
- if (ch >= '0' && ch <= '9') {AddCh(); goto case 48;}
- else if (ch == '_') {AddCh(); goto case 56;}
+ if (ch >= '0' && ch <= '9') {AddCh(); goto case 49;}
+ else if (ch == '_') {AddCh(); goto case 57;}
else if (ch == '.') {AddCh(); goto case 12;}
else {t.kind = 2; break;}
- case 49:
+ case 50:
recEnd = pos; recKind = 1;
if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'q' || ch >= 's' && ch <= 'z') {AddCh(); goto case 2;}
- else if (ch == 'r') {AddCh(); goto case 57;}
+ else if (ch == 'r') {AddCh(); goto case 58;}
else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
- case 50:
+ case 51:
recEnd = pos; recKind = 1;
- if (ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 58;}
- else if (ch == 39) {AddCh(); goto case 59;}
+ if (ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 59;}
+ else if (ch == 39) {AddCh(); goto case 60;}
else if (ch <= 9 || ch >= 11 && ch <= 12 || ch >= 14 && ch <= '&' || ch >= '(' && ch <= '/' || ch >= ':' && ch <= '>' || ch == '@' || ch == '[' || ch >= ']' && ch <= '^' || ch == '`' || ch >= '{' && ch <= 65535) {AddCh(); goto case 15;}
- else if (ch == 92) {AddCh(); goto case 53;}
+ else if (ch == 92) {AddCh(); goto case 54;}
else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
- case 51:
+ case 52:
recEnd = pos; recKind = 2;
- if (ch >= '0' && ch <= '9') {AddCh(); goto case 48;}
- else if (ch == '_') {AddCh(); goto case 56;}
+ if (ch >= '0' && ch <= '9') {AddCh(); goto case 49;}
+ else if (ch == '_') {AddCh(); goto case 57;}
else if (ch == 'x') {AddCh(); goto case 9;}
else if (ch == '.') {AddCh(); goto case 12;}
else {t.kind = 2; break;}
- case 52:
+ case 53:
recEnd = pos; recKind = 1;
- if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 52;}
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 53;}
else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
- case 53:
+ case 54:
if (ch == '"' || ch == 39 || ch == '0' || ch == 92 || ch == 'n' || ch == 'r' || ch == 't') {AddCh(); goto case 15;}
else if (ch == 'u') {AddCh(); goto case 16;}
else {goto case 0;}
- case 54:
+ case 55:
if (ch == '"' || ch == 39 || ch == '0' || ch == 92 || ch == 'n' || ch == 'r' || ch == 't') {AddCh(); goto case 21;}
else if (ch == 'u') {AddCh(); goto case 22;}
else {goto case 0;}
- case 55:
- recEnd = pos; recKind = 19;
- if (ch == '"') {AddCh(); goto case 27;}
- else {t.kind = 19; break;}
case 56:
- if (ch >= '0' && ch <= '9') {AddCh(); goto case 48;}
- else {goto case 0;}
+ recEnd = pos; recKind = 20;
+ if (ch == '"') {AddCh(); goto case 27;}
+ else {t.kind = 20; break;}
case 57:
- recEnd = pos; recKind = 1;
- if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'q' || ch >= 's' && ch <= 'z') {AddCh(); goto case 3;}
- else if (ch == 'r') {AddCh(); goto case 60;}
- else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
+ if (ch >= '0' && ch <= '9') {AddCh(); goto case 49;}
+ else {goto case 0;}
case 58:
recEnd = pos; recKind = 1;
- if (ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 61;}
- else if (ch == 39) {AddCh(); goto case 62;}
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'q' || ch >= 's' && ch <= 'z') {AddCh(); goto case 3;}
+ else if (ch == 'r') {AddCh(); goto case 61;}
else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
case 59:
recEnd = pos; recKind = 1;
- if (ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 61;}
- else if (ch == 39) {AddCh(); goto case 7;}
+ if (ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 62;}
+ else if (ch == 39) {AddCh(); goto case 63;}
else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
case 60:
recEnd = pos; recKind = 1;
- if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'b' && ch <= 'z') {AddCh(); goto case 4;}
- else if (ch == 'a') {AddCh(); goto case 63;}
+ if (ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 62;}
+ else if (ch == 39) {AddCh(); goto case 7;}
else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
case 61:
recEnd = pos; recKind = 1;
- if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 8;}
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'b' && ch <= 'z') {AddCh(); goto case 4;}
+ else if (ch == 'a') {AddCh(); goto case 64;}
else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
case 62:
- recEnd = pos; recKind = 18;
+ recEnd = pos; recKind = 1;
if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 8;}
- else {t.kind = 18; break;}
+ else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
case 63:
+ recEnd = pos; recKind = 19;
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 8;}
+ else {t.kind = 19; break;}
+ case 64:
recEnd = pos; recKind = 1;
if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'x' || ch == 'z') {AddCh(); goto case 5;}
- else if (ch == 'y') {AddCh(); goto case 64;}
+ else if (ch == 'y') {AddCh(); goto case 65;}
else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
- case 64:
+ case 65:
recEnd = pos; recKind = 5;
if (ch == 39 || ch == '0' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 6;}
- else if (ch >= '1' && ch <= '9') {AddCh(); goto case 65;}
+ else if (ch >= '1' && ch <= '9') {AddCh(); goto case 66;}
else {t.kind = 5; break;}
- case 65:
+ case 66:
recEnd = pos; recKind = 5;
- if (ch == 39 || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 52;}
- else if (ch >= '0' && ch <= '9') {AddCh(); goto case 65;}
+ if (ch == 39 || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 53;}
+ else if (ch >= '0' && ch <= '9') {AddCh(); goto case 66;}
else {t.kind = 5; break;}
- case 66:
- {t.kind = 88; break;}
case 67:
- {t.kind = 92; break;}
+ {t.kind = 76; break;}
case 68:
{t.kind = 94; break;}
case 69:
- {t.kind = 104; break;}
+ {t.kind = 98; break;}
case 70:
- {t.kind = 106; break;}
+ {t.kind = 109; break;}
case 71:
- {t.kind = 107; break;}
+ {t.kind = 111; break;}
case 72:
- {t.kind = 108; break;}
+ {t.kind = 112; break;}
case 73:
- {t.kind = 109; break;}
+ {t.kind = 113; break;}
case 74:
- {t.kind = 110; break;}
+ {t.kind = 114; break;}
case 75:
- {t.kind = 111; break;}
+ {t.kind = 115; break;}
case 76:
- {t.kind = 112; break;}
+ {t.kind = 116; break;}
case 77:
- {t.kind = 114; break;}
+ {t.kind = 117; break;}
case 78:
- if (ch == '&') {AddCh(); goto case 79;}
- else {goto case 0;}
+ {t.kind = 119; break;}
case 79:
- {t.kind = 115; break;}
+ if (ch == '&') {AddCh(); goto case 80;}
+ else {goto case 0;}
case 80:
- {t.kind = 116; break;}
+ {t.kind = 120; break;}
case 81:
- {t.kind = 117; break;}
+ {t.kind = 121; break;}
case 82:
- {t.kind = 118; break;}
+ {t.kind = 122; break;}
case 83:
- {t.kind = 120; break;}
+ {t.kind = 123; break;}
case 84:
- {t.kind = 121; break;}
+ {t.kind = 125; break;}
case 85:
- {t.kind = 123; break;}
+ {t.kind = 126; break;}
case 86:
- {t.kind = 125; break;}
+ {t.kind = 128; break;}
case 87:
- {t.kind = 127; break;}
+ {t.kind = 131; break;}
case 88:
- {t.kind = 128; break;}
+ {t.kind = 132; break;}
case 89:
- recEnd = pos; recKind = 20;
+ recEnd = pos; recKind = 21;
if (ch == ':') {AddCh(); goto case 30;}
- else if (ch == '=') {AddCh(); goto case 67;}
- else if (ch == '|') {AddCh(); goto case 68;}
- else {t.kind = 20; break;}
+ else if (ch == '|') {AddCh(); goto case 31;}
+ else if (ch == '=') {AddCh(); goto case 69;}
+ else {t.kind = 21; break;}
case 90:
- recEnd = pos; recKind = 22;
- if (ch == '|') {AddCh(); goto case 81;}
- else {t.kind = 22; break;}
+ recEnd = pos; recKind = 23;
+ if (ch == '|') {AddCh(); goto case 82;}
+ else {t.kind = 23; break;}
case 91:
- recEnd = pos; recKind = 25;
+ recEnd = pos; recKind = 27;
if (ch == '.') {AddCh(); goto case 97;}
- else {t.kind = 25; break;}
+ else {t.kind = 27; break;}
case 92:
- recEnd = pos; recKind = 64;
- if (ch == '>') {AddCh(); goto case 33;}
+ recEnd = pos; recKind = 71;
+ if (ch == '>') {AddCh(); goto case 34;}
else if (ch == '=') {AddCh(); goto case 98;}
- else {t.kind = 64; break;}
+ else {t.kind = 71; break;}
case 93:
- recEnd = pos; recKind = 126;
- if (ch == '>') {AddCh(); goto case 34;}
- else {t.kind = 126; break;}
+ recEnd = pos; recKind = 130;
+ if (ch == '>') {AddCh(); goto case 35;}
+ else {t.kind = 130; break;}
case 94:
- recEnd = pos; recKind = 50;
+ recEnd = pos; recKind = 52;
if (ch == '=') {AddCh(); goto case 99;}
- else {t.kind = 50; break;}
+ else {t.kind = 52; break;}
case 95:
- recEnd = pos; recKind = 51;
- if (ch == '=') {AddCh(); goto case 70;}
- else {t.kind = 51; break;}
+ recEnd = pos; recKind = 53;
+ if (ch == '=') {AddCh(); goto case 71;}
+ else {t.kind = 53; break;}
case 96:
- recEnd = pos; recKind = 119;
- if (ch == '=') {AddCh(); goto case 41;}
- else if (ch == 'i') {AddCh(); goto case 44;}
- else {t.kind = 119; break;}
+ recEnd = pos; recKind = 124;
+ if (ch == '=') {AddCh(); goto case 42;}
+ else if (ch == 'i') {AddCh(); goto case 45;}
+ else {t.kind = 124; break;}
case 97:
- recEnd = pos; recKind = 135;
- if (ch == '.') {AddCh(); goto case 47;}
- else {t.kind = 135; break;}
+ recEnd = pos; recKind = 139;
+ if (ch == '.') {AddCh(); goto case 48;}
+ else {t.kind = 139; break;}
case 98:
- recEnd = pos; recKind = 52;
- if (ch == '>') {AddCh(); goto case 75;}
- else {t.kind = 52; break;}
+ recEnd = pos; recKind = 54;
+ if (ch == '>') {AddCh(); goto case 76;}
+ else {t.kind = 54; break;}
case 99:
- recEnd = pos; recKind = 105;
+ recEnd = pos; recKind = 110;
if (ch == '=') {AddCh(); goto case 100;}
- else {t.kind = 105; break;}
+ else {t.kind = 110; break;}
case 100:
- recEnd = pos; recKind = 113;
- if (ch == '>') {AddCh(); goto case 73;}
- else {t.kind = 113; break;}
+ recEnd = pos; recKind = 118;
+ if (ch == '>') {AddCh(); goto case 74;}
+ else {t.kind = 118; break;}
}
t.val = new String(tval, 0, tlen);
@@ -973,6 +993,4 @@ public class Scanner {
} // end Scanner
public delegate void ErrorProc(int n, string filename, int line, int col);
-
-
} \ No newline at end of file
diff --git a/Source/Dafny/SccGraph.cs b/Source/Dafny/SccGraph.cs
index 01a72fc5..20b4f65e 100644
--- a/Source/Dafny/SccGraph.cs
+++ b/Source/Dafny/SccGraph.cs
@@ -6,8 +6,8 @@ namespace Microsoft.Dafny {
public class Graph<Node> where Node : class
{
- enum VisitedStatus { Unvisited, OnStack, Visited }
- class Vertex {
+ public enum VisitedStatus { Unvisited, OnStack, Visited }
+ public class Vertex {
public readonly Node N;
public readonly List<Vertex/*!*/>/*!*/ Successors = new List<Vertex/*!*/>();
public List<Vertex/*!*/> SccMembers; // non-null only for the representative of the SCC
@@ -65,6 +65,10 @@ namespace Microsoft.Dafny {
{
}
+ public IEnumerable<Vertex> GetVertices() {
+ return vertices.Values;
+ }
+
/// <summary>
/// Idempotently adds a vertex 'n' to the graph.
/// </summary>
@@ -97,7 +101,7 @@ namespace Microsoft.Dafny {
/// <summary>
/// Returns the vertex for 'n' if 'n' is in the graph. Otherwise, returns null.
/// </summary>
- Vertex FindVertex(Node n) {
+ public Vertex FindVertex(Node n) {
Vertex v;
if (vertices.TryGetValue(n, out v)) {
Contract.Assert(v != null); // follows from postcondition of TryGetValue (since 'vertices' maps to the type Vertex!)
diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs
index 0e83f54a..99571a8c 100644
--- a/Source/Dafny/Translator.cs
+++ b/Source/Dafny/Translator.cs
@@ -93,9 +93,13 @@ namespace Microsoft.Dafny {
}
public class Translator {
+ ErrorReporter reporter;
+ // TODO(wuestholz): Enable this once Dafny's recommended Z3 version includes changeset 0592e765744497a089c42021990740f303901e67.
+ public bool UseOptimizationInZ3 { get; set; }
[NotDelayed]
- public Translator() {
+ public Translator(ErrorReporter reporter) {
+ this.reporter = reporter;
InsertChecksums = 0 < CommandLineOptions.Clo.VerifySnapshots;
Bpl.Program boogieProgram = ReadPrelude();
if (boogieProgram != null) {
@@ -109,11 +113,13 @@ namespace Microsoft.Dafny {
readonly Dictionary<TopLevelDecl, string>/*!*/ classConstants = new Dictionary<TopLevelDecl, string>();
readonly Dictionary<int, string> functionConstants = new Dictionary<int, string>();
readonly Dictionary<Function, string> functionHandles = new Dictionary<Function, string>();
+ readonly List<FuelConstant> functionFuel = new List<FuelConstant>();
readonly Dictionary<Field/*!*/,Bpl.Constant/*!*/>/*!*/ fields = new Dictionary<Field/*!*/,Bpl.Constant/*!*/>();
readonly Dictionary<Field/*!*/, Bpl.Function/*!*/>/*!*/ fieldFunctions = new Dictionary<Field/*!*/, Bpl.Function/*!*/>();
readonly Dictionary<string, Bpl.Constant> fieldConstants = new Dictionary<string,Constant>();
readonly ISet<string> abstractTypes = new HashSet<string>();
readonly ISet<string> opaqueTypes = new HashSet<string>();
+ FuelContext fuelContext = null;
Program program;
[ContractInvariantMethod]
@@ -137,6 +143,7 @@ namespace Microsoft.Dafny {
public readonly Bpl.Type BoxType;
public readonly Bpl.Type TickType;
private readonly Bpl.TypeSynonymDecl setTypeCtor;
+ private readonly Bpl.TypeSynonymDecl isetTypeCtor;
private readonly Bpl.TypeSynonymDecl multiSetTypeCtor;
private readonly Bpl.TypeCtorDecl mapTypeCtor;
private readonly Bpl.TypeCtorDecl imapTypeCtor;
@@ -183,12 +190,12 @@ namespace Microsoft.Dafny {
Contract.Invariant(allocField != null);
}
- public Bpl.Type SetType(IToken tok, Bpl.Type ty) {
+ public Bpl.Type SetType(IToken tok, bool finite, Bpl.Type ty) {
Contract.Requires(tok != null);
Contract.Requires(ty != null);
Contract.Ensures(Contract.Result<Bpl.Type>() != null);
- return new Bpl.TypeSynonymAnnotation(Token.NoToken, setTypeCtor, new List<Bpl.Type> { ty });
+ return new Bpl.TypeSynonymAnnotation(Token.NoToken, finite ? setTypeCtor : isetTypeCtor, new List<Bpl.Type> { ty });
}
public Bpl.Type MultiSetType(IToken tok, Bpl.Type ty) {
@@ -229,7 +236,7 @@ namespace Microsoft.Dafny {
}
public PredefinedDecls(Bpl.TypeCtorDecl charType, Bpl.TypeCtorDecl refType, Bpl.TypeCtorDecl boxType, Bpl.TypeCtorDecl tickType,
- Bpl.TypeSynonymDecl setTypeCtor, Bpl.TypeSynonymDecl multiSetTypeCtor,
+ Bpl.TypeSynonymDecl setTypeCtor, Bpl.TypeSynonymDecl isetTypeCtor, Bpl.TypeSynonymDecl multiSetTypeCtor,
Bpl.TypeCtorDecl mapTypeCtor, Bpl.TypeCtorDecl imapTypeCtor,
Bpl.Function arrayLength, Bpl.Function realTrunc, Bpl.TypeCtorDecl seqTypeCtor, Bpl.TypeCtorDecl fieldNameType,
Bpl.TypeCtorDecl tyType, Bpl.TypeCtorDecl tyTagType,
@@ -242,6 +249,7 @@ namespace Microsoft.Dafny {
Contract.Requires(boxType != null);
Contract.Requires(tickType != null);
Contract.Requires(setTypeCtor != null);
+ Contract.Requires(isetTypeCtor != null);
Contract.Requires(multiSetTypeCtor != null);
Contract.Requires(mapTypeCtor != null);
Contract.Requires(imapTypeCtor != null);
@@ -265,6 +273,7 @@ namespace Microsoft.Dafny {
this.BoxType = new Bpl.CtorType(Token.NoToken, boxType, new List<Bpl.Type>());
this.TickType = new Bpl.CtorType(Token.NoToken, tickType, new List<Bpl.Type>());
this.setTypeCtor = setTypeCtor;
+ this.isetTypeCtor = isetTypeCtor;
this.multiSetTypeCtor = multiSetTypeCtor;
this.mapTypeCtor = mapTypeCtor;
this.imapTypeCtor = imapTypeCtor;
@@ -298,6 +307,7 @@ namespace Microsoft.Dafny {
Bpl.TypeCtorDecl charType = null;
Bpl.TypeCtorDecl refType = null;
Bpl.TypeSynonymDecl setTypeCtor = null;
+ Bpl.TypeSynonymDecl isetTypeCtor = null;
Bpl.TypeSynonymDecl multiSetTypeCtor = null;
Bpl.Function arrayLength = null;
Bpl.Function realTrunc = null;
@@ -361,6 +371,9 @@ namespace Microsoft.Dafny {
if (dt.Name == "MultiSet") {
multiSetTypeCtor = dt;
}
+ if (dt.Name == "ISet") {
+ isetTypeCtor = dt;
+ }
} else if (d is Bpl.Constant) {
Bpl.Constant c = (Bpl.Constant)d;
if (c.Name == "alloc") {
@@ -384,6 +397,8 @@ namespace Microsoft.Dafny {
Console.WriteLine("Error: Dafny prelude is missing declaration of type Seq");
} else if (setTypeCtor == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of type Set");
+ } else if (isetTypeCtor == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type ISet");
} else if (multiSetTypeCtor == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of type MultiSet");
} else if (mapTypeCtor == null) {
@@ -426,7 +441,7 @@ namespace Microsoft.Dafny {
Console.WriteLine("Error: Dafny prelude is missing declaration of constant alloc");
} else {
return new PredefinedDecls(charType, refType, boxType, tickType,
- setTypeCtor, multiSetTypeCtor,
+ setTypeCtor, isetTypeCtor, multiSetTypeCtor,
mapTypeCtor, imapTypeCtor,
arrayLength, realTrunc, seqTypeCtor, fieldNameType,
tyType, tyTagType,
@@ -474,6 +489,9 @@ namespace Microsoft.Dafny {
return new Bpl.Program();
}
+ // compute which function needs fuel constants.
+ ComputeFunctionFuel();
+
foreach (TopLevelDecl d in program.BuiltIns.SystemModule.TopLevelDecls) {
currentDeclaration = d;
if (d is OpaqueTypeDecl) {
@@ -563,6 +581,37 @@ namespace Microsoft.Dafny {
return sink;
}
+ private void ComputeFunctionFuel() {
+ foreach (ModuleDefinition m in program.Modules) {
+ foreach (TopLevelDecl d in m.TopLevelDecls) {
+ if (d is ClassDecl) {
+ ClassDecl c = (ClassDecl)d;
+ foreach (MemberDecl member in c.Members) {
+ if (member is Function) {
+ Function f = (Function)member;
+ // declare the fuel constant
+ if (f.IsFueled) {
+ // const BaseFuel_FunctionA : LayerType
+ Bpl.Constant baseFuel = new Bpl.Constant(f.tok, new Bpl.TypedIdent(f.tok, "BaseFuel_" + f.FullName, predef.LayerType), false);
+ sink.AddTopLevelDeclaration(baseFuel);
+ Bpl.Expr baseFuel_expr = new Bpl.IdentifierExpr(f.tok, baseFuel);
+ // const StartFuel_FunctionA : LayerType
+ Bpl.Constant startFuel = new Bpl.Constant(f.tok, new Bpl.TypedIdent(f.tok, "StartFuel_" + f.FullName, predef.LayerType), false);
+ sink.AddTopLevelDeclaration(startFuel);
+ Bpl.Expr startFuel_expr = new Bpl.IdentifierExpr(f.tok, startFuel);
+ // const StartFuelAssert_FunctionA : LayerType
+ Bpl.Constant startFuelAssert = new Bpl.Constant(f.tok, new Bpl.TypedIdent(f.tok, "StartFuelAssert_" + f.FullName, predef.LayerType), false);
+ sink.AddTopLevelDeclaration(startFuelAssert);
+ Bpl.Expr startFuelAssert_expr = new Bpl.IdentifierExpr(f.tok, startFuelAssert);
+ this.functionFuel.Add(new FuelConstant(f, baseFuel_expr, startFuel_expr, startFuelAssert_expr));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
/// <summary>
/// adding TraitParent axioms
/// if a class A extends trait J and B does not extend anything, then this method adds the followings to the sink:
@@ -616,6 +665,11 @@ namespace Microsoft.Dafny {
}
void AddTypeDecl(NewtypeDecl dd) {
Contract.Requires(dd != null);
+ Contract.Ensures(fuelContext == Contract.OldValue(fuelContext));
+
+ FuelContext oldFuelContext = this.fuelContext;
+ this.fuelContext = FuelSetting.NewFuelContext(dd);
+
AddTypeDecl_Aux(dd.tok, dd.FullName, new List<TypeParameter>());
AddWellformednessCheck(dd);
// Add $Is and $IsAlloc axioms for the newtype
@@ -650,7 +704,8 @@ namespace Microsoft.Dafny {
ie.Var = oVarDafny; ie.Type = ie.Var.Type; // resolve ie here
var constraint = etran.TrExpr(Substitute(dd.Constraint, dd.Var, ie));
var heap = new Bpl.BoundVariable(dd.tok, new Bpl.TypedIdent(dd.tok, predef.HeapVarName, predef.HeapType));
- var ex = new Bpl.ExistsExpr(dd.tok, new List<Variable> { heap }, BplAnd(FunctionCall(dd.tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr), constraint));
+ //TRIG (exists $Heap: Heap :: $IsGoodHeap($Heap) && LitInt(0) <= $o#0 && $o#0 < 100)
+ var ex = new Bpl.ExistsExpr(dd.tok, new List<Variable> { heap }, BplAnd(FunctionCall(dd.tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr), constraint)); // LL_TRIGGER
rhs = BplAnd(rhs, ex);
}
body = BplIff(is_o, rhs);
@@ -658,6 +713,7 @@ namespace Microsoft.Dafny {
sink.AddTopLevelDeclaration(new Bpl.Axiom(dd.tok, BplForall(vars, BplTrigger(is_o), body), name));
});
+ this.fuelContext = oldFuelContext;
}
void AddTypeDecl_Aux(IToken tok, string nm, List<TypeParameter> typeArgs) {
Contract.Requires(tok != null);
@@ -689,8 +745,6 @@ namespace Microsoft.Dafny {
sink.AddTopLevelDeclaration(dt_const);
foreach (DatatypeCtor ctor in dt.Ctors) {
- int i;
-
// Add: function #dt.ctor(tyVars, paramTypes) returns (DatatypeType);
List<Bpl.Variable> argTypes = new List<Bpl.Variable>();
@@ -718,10 +772,11 @@ namespace Microsoft.Dafny {
{
// Add: axiom (forall params :: DatatypeCtorId(#dt.ctor(params)) == ##dt.ctor);
CreateBoundVariables(ctor.Formals, out bvs, out args);
- Bpl.Expr lhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
- lhs = FunctionCall(ctor.tok, BuiltinFunction.DatatypeCtorId, null, lhs);
+ var constructor_call = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
+ var lhs = FunctionCall(ctor.tok, BuiltinFunction.DatatypeCtorId, null, constructor_call);
Bpl.Expr q = Bpl.Expr.Eq(lhs, c);
- sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, BplForall(bvs, q), "Constructor identifier"));
+ var trigger = BplTrigger(constructor_call);
+ sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, BplForall(bvs, trigger, q), "Constructor identifier"));
}
{
@@ -747,14 +802,15 @@ namespace Microsoft.Dafny {
{
// Add: axiom (forall d: DatatypeType :: dt.ctor?(d) ==> (exists params :: d == #dt.ctor(params));
CreateBoundVariables(ctor.Formals, out bvs, out args);
- Bpl.Expr lhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
+ Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
Bpl.Expr dId; var dBv = BplBoundVar("d", predef.DatatypeType, out dId);
- Bpl.Expr q = Bpl.Expr.Eq(dId, lhs);
+ Bpl.Expr q = Bpl.Expr.Eq(dId, rhs);
if (bvs.Count != 0) {
- q = new Bpl.ExistsExpr(ctor.tok, bvs, q);
+ q = new Bpl.ExistsExpr(ctor.tok, bvs, null/*always in a Skolemization context*/, q);
}
Bpl.Expr dtq = FunctionCall(ctor.tok, ctor.QueryField.FullSanitizedName, Bpl.Type.Bool, dId);
- q = BplForall(dBv, null, BplImp(dtq, q));
+ var trigger = BplTrigger(dtq);
+ q = BplForall(dBv, trigger, BplImp(dtq, q));
sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Constructor questionmark has arguments"));
}
@@ -775,14 +831,13 @@ namespace Microsoft.Dafny {
Bpl.Expr h;
var hVar = BplBoundVar("$h", predef.HeapType, out h);
Bpl.Expr conj = Bpl.Expr.True;
- i = 0;
- foreach (Formal arg in ctor.Formals) {
+ for (var i = 0; i < ctor.Formals.Count; i++) {
+ var arg = ctor.Formals[i];
if (is_alloc) {
conj = BplAnd(conj, MkIsAlloc(args[i], arg.Type, h));
} else {
conj = BplAnd(conj, MkIs(args[i], arg.Type));
}
- i++;
}
var c_params = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
var c_ty = ClassTyCon((TopLevelDecl)dt, tyexprs);
@@ -818,8 +873,8 @@ namespace Microsoft.Dafny {
}
// Injectivity axioms for normal arguments
- i = 0;
- foreach (Formal arg in ctor.Formals) {
+ for (int i = 0; i < ctor.Formals.Count; i++) {
+ var arg = ctor.Formals[i];
// function ##dt.ctor#i(DatatypeType) returns (Ti);
var sf = ctor.Destructors[i];
Contract.Assert(sf != null);
@@ -835,75 +890,83 @@ namespace Microsoft.Dafny {
if (dt is IndDatatypeDecl) {
var argType = arg.Type.NormalizeExpand();
if (argType.IsDatatype || argType.IsTypeParameter) {
- // for datatype: axiom (forall params :: DtRank(params_i) < DtRank(#dt.ctor(params)));
- // for type-parameter type: axiom (forall params :: DtRank(Unbox(params_i)) < DtRank(#dt.ctor(params)));
+ // for datatype: axiom (forall params :: {#dt.ctor(params)} DtRank(params_i) < DtRank(#dt.ctor(params)));
+ // for type-parameter type: axiom (forall params :: {#dt.ctor(params)} BoxRank(params_i) < DtRank(#dt.ctor(params)));
CreateBoundVariables(ctor.Formals, out bvs, out args);
Bpl.Expr lhs = FunctionCall(ctor.tok, arg.Type.IsDatatype ? BuiltinFunction.DtRank : BuiltinFunction.BoxRank, null, args[i]);
/* CHECK
Bpl.Expr lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null,
argType.IsDatatype ? args[i] : FunctionCall(ctor.tok, BuiltinFunction.Unbox, predef.DatatypeType, args[i]));
- */
- Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
- rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs);
- q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Lt(lhs, rhs));
+ */
+ Bpl.Expr ct = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
+ var rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ct);
+ var trigger = BplTrigger(ct);
+ q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Lt(lhs, rhs));
sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive rank"));
} else if (argType is SeqType) {
- // axiom (forall params, i: int :: 0 <= i && i < |arg| ==> DtRank(arg[i]) < DtRank(#dt.ctor(params)));
+ // axiom (forall params, i: int {#dt.ctor(params)} :: 0 <= i && i < |arg| ==> DtRank(arg[i]) < DtRank(#dt.ctor(params)));
// that is:
- // axiom (forall params, i: int :: 0 <= i && i < |arg| ==> DtRank(Unbox(Seq#Index(arg,i))) < DtRank(#dt.ctor(params)));
- CreateBoundVariables(ctor.Formals, out bvs, out args);
- Bpl.Variable iVar = new Bpl.BoundVariable(arg.tok, new Bpl.TypedIdent(arg.tok, "i", Bpl.Type.Int));
- bvs.Add(iVar);
- Bpl.IdentifierExpr ie = new Bpl.IdentifierExpr(arg.tok, iVar);
- Bpl.Expr ante = Bpl.Expr.And(
- Bpl.Expr.Le(Bpl.Expr.Literal(0), ie),
- Bpl.Expr.Lt(ie, FunctionCall(arg.tok, BuiltinFunction.SeqLength, null, args[i])));
- Bpl.Expr lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null,
- FunctionCall(arg.tok, BuiltinFunction.Unbox, predef.DatatypeType,
- FunctionCall(arg.tok, BuiltinFunction.SeqIndex, predef.DatatypeType, args[i], ie)));
- Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
- rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs);
- q = new Bpl.ForallExpr(ctor.tok, bvs, new Trigger(lhs.tok, true, new List<Bpl.Expr> { lhs, rhs }), Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs)));
- sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q));
- // axiom (forall params, SeqRank(arg) < DtRank(#dt.ctor(params)));
- CreateBoundVariables(ctor.Formals, out bvs, out args);
- lhs = FunctionCall(ctor.tok, BuiltinFunction.SeqRank, null, args[i]);
- rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
- rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs);
- q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Lt(lhs, rhs));
- sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive seq rank"));
+ // axiom (forall params, i: int {#dt.ctor(params)} :: 0 <= i && i < |arg| ==> DtRank(Unbox(Seq#Index(arg,i))) < DtRank(#dt.ctor(params)));
+ {
+ CreateBoundVariables(ctor.Formals, out bvs, out args);
+ Bpl.Variable iVar = new Bpl.BoundVariable(arg.tok, new Bpl.TypedIdent(arg.tok, "i", Bpl.Type.Int));
+ bvs.Add(iVar);
+ Bpl.IdentifierExpr ie = new Bpl.IdentifierExpr(arg.tok, iVar);
+ Bpl.Expr ante = Bpl.Expr.And(
+ Bpl.Expr.Le(Bpl.Expr.Literal(0), ie),
+ Bpl.Expr.Lt(ie, FunctionCall(arg.tok, BuiltinFunction.SeqLength, null, args[i])));
+ var seqIndex = FunctionCall(arg.tok, BuiltinFunction.SeqIndex, predef.DatatypeType, args[i], ie);
+ Bpl.Expr lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null,
+ FunctionCall(arg.tok, BuiltinFunction.Unbox, predef.DatatypeType, seqIndex));
+ var ct = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
+ var rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ct);
+ q = new Bpl.ForallExpr(ctor.tok, bvs, new Trigger(lhs.tok, true, new List<Bpl.Expr> { seqIndex, ct }), Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs)));
+ sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q));
+ }
+
+ // axiom (forall params {#dt.ctor(params)} :: SeqRank(arg) < DtRank(#dt.ctor(params)));
+ {
+ CreateBoundVariables(ctor.Formals, out bvs, out args);
+ var lhs = FunctionCall(ctor.tok, BuiltinFunction.SeqRank, null, args[i]);
+ var ct = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
+ var rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ct);
+ var trigger = BplTrigger(ct);
+ q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Lt(lhs, rhs));
+ sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive seq rank"));
+ }
} else if (argType is SetType) {
- // axiom (forall params, d: Datatype :: arg[d] ==> DtRank(d) < DtRank(#dt.ctor(params)));
+ // axiom (forall params, d: Datatype {arg[d], #dt.ctor(params)} :: arg[d] ==> DtRank(d) < DtRank(#dt.ctor(params)));
// that is:
- // axiom (forall params, d: Datatype :: arg[Box(d)] ==> DtRank(d) < DtRank(#dt.ctor(params)));
+ // axiom (forall params, d: Datatype {arg[Box(d)], #dt.ctor(params)} :: arg[Box(d)] ==> DtRank(d) < DtRank(#dt.ctor(params)));
CreateBoundVariables(ctor.Formals, out bvs, out args);
Bpl.Variable dVar = new Bpl.BoundVariable(arg.tok, new Bpl.TypedIdent(arg.tok, "d", predef.DatatypeType));
bvs.Add(dVar);
Bpl.IdentifierExpr ie = new Bpl.IdentifierExpr(arg.tok, dVar);
- Bpl.Expr ante = Bpl.Expr.SelectTok(arg.tok, args[i], FunctionCall(arg.tok, BuiltinFunction.Box, null, ie));
+ Bpl.Expr inSet = Bpl.Expr.SelectTok(arg.tok, args[i], FunctionCall(arg.tok, BuiltinFunction.Box, null, ie));
Bpl.Expr lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ie);
- Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
- rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs);
- q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs)));
+ var ct = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
+ var rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ct);
+ var trigger = new Bpl.Trigger(ctor.tok, true, new List<Bpl.Expr> { inSet, ct });
+ q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Imp(inSet, Bpl.Expr.Lt(lhs, rhs)));
sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive set rank"));
} else if (argType is MultiSetType) {
- // axiom (forall params, d: Datatype :: 0 < arg[d] ==> DtRank(d) < DtRank(#dt.ctor(params)));
+ // axiom (forall params, d: Datatype {arg[d], #dt.ctor(params)} :: 0 < arg[d] ==> DtRank(d) < DtRank(#dt.ctor(params)));
// that is:
- // axiom (forall params, d: Datatype :: 0 < arg[Box(d)] ==> DtRank(d) < DtRank(#dt.ctor(params)));
+ // axiom (forall params, d: Datatype {arg[Box(d)], #dt.ctor(params)} :: 0 < arg[Box(d)] ==> DtRank(d) < DtRank(#dt.ctor(params)));
CreateBoundVariables(ctor.Formals, out bvs, out args);
Bpl.Variable dVar = new Bpl.BoundVariable(arg.tok, new Bpl.TypedIdent(arg.tok, "d", predef.DatatypeType));
bvs.Add(dVar);
Bpl.IdentifierExpr ie = new Bpl.IdentifierExpr(arg.tok, dVar);
- Bpl.Expr ante = Bpl.Expr.Gt(Bpl.Expr.SelectTok(arg.tok, args[i], FunctionCall(arg.tok, BuiltinFunction.Box, null, ie)), Bpl.Expr.Literal(0));
+ var inMultiset = Bpl.Expr.SelectTok(arg.tok, args[i], FunctionCall(arg.tok, BuiltinFunction.Box, null, ie));
+ Bpl.Expr ante = Bpl.Expr.Gt(inMultiset, Bpl.Expr.Literal(0));
Bpl.Expr lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ie);
- Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
- rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs);
- q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs)));
+ var ct = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
+ var rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ct);
+ var trigger = new Bpl.Trigger(ctor.tok, true, new List<Bpl.Expr> { inMultiset, ct });
+ q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs)));
sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive multiset rank"));
}
}
-
- i++;
}
}
@@ -1103,9 +1166,9 @@ namespace Microsoft.Dafny {
CoAxHelper(true, (tyargs, vars, lexprs, rexprs, kVar, k, kGtZero, ly, d0, d1) => {
var equal = Bpl.Expr.Eq(d0, d1);
var PEq = CoEqualCall(codecl, lexprs, rexprs, k, LayerSucc(ly), d0, d1);
+ var trigger = BplTrigger(PEq);
sink.AddTopLevelDeclaration(new Axiom(dt.tok,
- BplForall(vars, null, BplImp(BplAnd(equal, kGtZero), PEq)),
- "Prefix equality shortcut"));
+ BplForall(vars, trigger, BplImp(BplAnd(equal, kGtZero), PEq)), "Prefix equality shortcut"));
});
}
}
@@ -1172,7 +1235,7 @@ namespace Microsoft.Dafny {
}
}
- Bpl.Expr LayerSucc(Bpl.Expr e, int amt = 1) {
+ public Bpl.Expr LayerSucc(Bpl.Expr e, int amt = 1) {
if (amt == 0) {
return e;
} else if (amt > 0) {
@@ -1292,6 +1355,7 @@ namespace Microsoft.Dafny {
{
Contract.Requires(sink != null && predef != null);
Contract.Requires(c != null);
+ Contract.Ensures(fuelContext == Contract.OldValue(fuelContext));
sink.AddTopLevelDeclaration(GetClass(c));
if (c is ArrayClassDecl) {
@@ -1378,6 +1442,9 @@ namespace Microsoft.Dafny {
} else if (member is Function) {
var f = (Function)member;
+ FuelContext oldFuelContext = this.fuelContext;
+ this.fuelContext = FuelSetting.NewFuelContext(f);
+
AddClassMember_Function(f);
if (!IsOpaqueFunction(f) && !f.IsBuiltin && !(f.tok is IncludeToken)) { // Opaque function's well-formedness is checked on the full version
AddWellformednessCheck(f);
@@ -1390,9 +1457,11 @@ namespace Microsoft.Dafny {
AddClassMember_Function(cop.PrefixPredicate);
// skip the well-formedness check, because it has already been done for the fixpoint-predicate
}
-
+ this.fuelContext = oldFuelContext;
} else if (member is Method) {
Method m = (Method)member;
+ FuelContext oldFuelContext = this.fuelContext;
+ this.fuelContext = FuelSetting.NewFuelContext(m);
// wellformedness check for method specification
if (m.EnclosingClass is IteratorDecl && m == ((IteratorDecl)m.EnclosingClass).Member_MoveNext) {
@@ -1425,7 +1494,7 @@ namespace Microsoft.Dafny {
sink.AddTopLevelDeclaration(proc);
AddMethodImpl(m, proc, false);
}
-
+ this.fuelContext = oldFuelContext;
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member
}
@@ -1434,13 +1503,15 @@ namespace Microsoft.Dafny {
/// <summary>
/// Returns true if the body of function "f" is available in module "context".
- /// This happens when "f" has a body, "f" is not opaque, and either "f" is declared
- /// in module "context" or "f" is not protected.
+ /// This happens when the following conditions all hold:
+ /// - "f" has a body
+ /// - "f" is not opaque
+ /// - "f" is declared as protected, then "context" is the current module and parameter "revealProtectedBody" is passed in as "true".
/// </summary>
- static bool FunctionBodyIsAvailable(Function f, ModuleDefinition context) {
+ static bool FunctionBodyIsAvailable(Function f, ModuleDefinition context, bool revealProtectedBody) {
Contract.Requires(f != null);
Contract.Requires(context != null);
- return f.Body != null && !IsOpaqueFunction(f) && (f.EnclosingClass.Module == context || !f.IsProtected);
+ return f.Body != null && !IsOpaqueFunction(f) && (!f.IsProtected || (revealProtectedBody && f.EnclosingClass.Module == context));
}
static bool IsOpaqueFunction(Function f) {
Contract.Requires(f != null);
@@ -1458,7 +1529,7 @@ namespace Microsoft.Dafny {
// declare function
AddFunction(f);
// add synonym axiom
- if (f.IsRecursive) {
+ if (f.IsFuelAware()) {
AddSynonymAxiom(f);
}
// add frame axiom
@@ -1471,7 +1542,7 @@ namespace Microsoft.Dafny {
AddFunctionAxiom(f, FunctionAxiomVisibility.ForeignModuleOnly, f.Body.Resolved);
}
// for body-less functions, at least generate its #requires function
- if (f.Body == null) {
+ if (f.Body == null || IsOpaqueFunction(f)) {
var b = FunctionAxiom(f, FunctionAxiomVisibility.ForeignModuleOnly, null, null);
Contract.Assert(b == null);
}
@@ -1485,6 +1556,10 @@ namespace Microsoft.Dafny {
void AddIteratorSpecAndBody(IteratorDecl iter) {
Contract.Requires(iter != null);
+ Contract.Ensures(fuelContext == Contract.OldValue(fuelContext));
+
+ FuelContext oldFuelContext = this.fuelContext;
+ this.fuelContext = FuelSetting.NewFuelContext(iter);
// wellformedness check for method specification
Bpl.Procedure proc = AddIteratorProc(iter, MethodTranslationKind.SpecWellformedness);
@@ -1497,6 +1572,7 @@ namespace Microsoft.Dafny {
// ...and its implementation
AddIteratorImpl(iter, proc);
}
+ this.fuelContext = oldFuelContext;
}
Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) {
@@ -1534,8 +1610,7 @@ namespace Microsoft.Dafny {
req.Add(Requires(p.E.tok, true, etran.TrExpr(p.E), null, comment));
comment = null;
} else {
- bool splitHappened; // we actually don't care
- foreach (var s in TrSplitExpr(p.E, etran, kind == MethodTranslationKind.InterModuleCall ? 0 : int.MaxValue, true, out splitHappened)) {
+ foreach (var s in TrSplitExprForMethodSpec(p.E, etran, kind)) {
if (kind == MethodTranslationKind.IntraModuleCall && RefinementToken.IsInherited(s.E.tok, currentModule)) {
// this precondition was inherited into this module, so just ignore it
} else {
@@ -1552,8 +1627,7 @@ namespace Microsoft.Dafny {
ens.Add(Ensures(p.E.tok, true, etran.TrExpr(p.E), null, comment));
comment = null;
} else {
- bool splitHappened; // we actually don't care
- foreach (var s in TrSplitExpr(p.E, etran, kind == MethodTranslationKind.InterModuleCall ? 0 : int.MaxValue, true, out splitHappened)) {
+ foreach (var s in TrSplitExprForMethodSpec(p.E, etran, kind)) {
if (kind == MethodTranslationKind.Implementation && RefinementToken.IsInherited(s.E.tok, currentModule)) {
// this postcondition was inherited into this module, so just ignore it
} else {
@@ -1599,8 +1673,7 @@ namespace Microsoft.Dafny {
Bpl.StmtList stmts;
// check well-formedness of the preconditions, and then assume each one of them
foreach (var p in iter.Requires) {
- CheckWellformed(p.E, new WFOptions(), localVariables, builder, etran);
- builder.Add(new Bpl.AssumeCmd(p.E.tok, etran.TrExpr(p.E)));
+ CheckWellformedAndAssume(p.E, new WFOptions(), localVariables, builder, etran);
}
// check well-formedness of the modifies and reads clauses
CheckFrameWellFormed(new WFOptions(), iter.Modifies.Expressions, localVariables, builder, etran);
@@ -1612,7 +1685,7 @@ namespace Microsoft.Dafny {
// Next, we assume about this.* whatever we said that the iterator constructor promises
foreach (var p in iter.Member_Init.Ens) {
- builder.Add(new Bpl.AssumeCmd(p.E.tok, etran.TrExpr(p.E)));
+ builder.Add(TrAssumeCmd(p.E.tok, etran.TrExpr(p.E)));
}
// play havoc with the heap, except at the locations prescribed by (this._reads - this._modifies - {this})
@@ -1638,12 +1711,11 @@ namespace Microsoft.Dafny {
validCall.TypeArgumentSubstitutions[p] = new UserDefinedType(p);
} // resolved here.
- builder.Add(new Bpl.AssumeCmd(iter.tok, etran.TrExpr(validCall)));
+ builder.Add(TrAssumeCmd(iter.tok, etran.TrExpr(validCall)));
// check well-formedness of the user-defined part of the yield-requires
foreach (var p in iter.YieldRequires) {
- CheckWellformed(p.E, new WFOptions(), localVariables, builder, etran);
- builder.Add(new Bpl.AssumeCmd(p.E.tok, etran.TrExpr(p.E)));
+ CheckWellformedAndAssume(p.E, new WFOptions(), localVariables, builder, etran);
}
// save the heap (representing the state where yield-requires holds): $_OldIterHeap := Heap;
@@ -1666,13 +1738,13 @@ namespace Microsoft.Dafny {
setDiff.ResolvedOp = BinaryExpr.ResolvedOpcode.SetDifference; setDiff.Type = nw.Type; // resolve here
Expression cond = new UnaryOpExpr(iter.tok, UnaryOpExpr.Opcode.Fresh, setDiff);
cond.Type = Type.Bool; // resolve here
- builder.Add(new Bpl.AssumeCmd(iter.tok, yeEtran.TrExpr(cond)));
+ builder.Add(TrAssumeCmd(iter.tok, yeEtran.TrExpr(cond)));
// check wellformedness of postconditions
var yeBuilder = new Bpl.StmtListBuilder();
var endBuilder = new Bpl.StmtListBuilder();
// In the yield-ensures case: assume this.Valid();
- yeBuilder.Add(new Bpl.AssumeCmd(iter.tok, yeEtran.TrExpr(validCall)));
+ yeBuilder.Add(TrAssumeCmd(iter.tok, yeEtran.TrExpr(validCall)));
Contract.Assert(iter.OutsFields.Count == iter.OutsHistoryFields.Count);
for (int i = 0; i < iter.OutsFields.Count; i++) {
var y = iter.OutsFields[i];
@@ -1689,18 +1761,16 @@ namespace Microsoft.Dafny {
concat.ResolvedOp = BinaryExpr.ResolvedOpcode.Concat; concat.Type = oldThisYs.Type; // resolve here
// In the yield-ensures case: assume this.ys == old(this.ys) + [this.y];
- yeBuilder.Add(new Bpl.AssumeCmd(iter.tok, Bpl.Expr.Eq(yeEtran.TrExpr(thisYs), yeEtran.TrExpr(concat))));
+ yeBuilder.Add(TrAssumeCmd(iter.tok, Bpl.Expr.Eq(yeEtran.TrExpr(thisYs), yeEtran.TrExpr(concat))));
// In the ensures case: assume this.ys == old(this.ys);
- endBuilder.Add(new Bpl.AssumeCmd(iter.tok, Bpl.Expr.Eq(yeEtran.TrExpr(thisYs), yeEtran.TrExpr(oldThisYs))));
+ endBuilder.Add(TrAssumeCmd(iter.tok, Bpl.Expr.Eq(yeEtran.TrExpr(thisYs), yeEtran.TrExpr(oldThisYs))));
}
foreach (var p in iter.YieldEnsures) {
- CheckWellformed(p.E, new WFOptions(), localVariables, yeBuilder, yeEtran);
- yeBuilder.Add(new Bpl.AssumeCmd(p.E.tok, yeEtran.TrExpr(p.E)));
+ CheckWellformedAndAssume(p.E, new WFOptions(), localVariables, yeBuilder, yeEtran);
}
foreach (var p in iter.Ensures) {
- CheckWellformed(p.E, new WFOptions(), localVariables, endBuilder, yeEtran);
- endBuilder.Add(new Bpl.AssumeCmd(p.E.tok, yeEtran.TrExpr(p.E)));
+ CheckWellformedAndAssume(p.E, new WFOptions(), localVariables, endBuilder, yeEtran);
}
builder.Add(new Bpl.IfCmd(iter.tok, null, yeBuilder.Collect(iter.tok), null, endBuilder.Collect(iter.tok)));
@@ -1740,23 +1810,23 @@ namespace Microsoft.Dafny {
// add locals for the yield-history variables and the extra variables
// Assume the precondition and postconditions of the iterator constructor method
foreach (var p in iter.Member_Init.Req) {
- builder.Add(new Bpl.AssumeCmd(p.E.tok, etran.TrExpr(p.E)));
+ builder.Add(TrAssumeCmd(p.E.tok, etran.TrExpr(p.E)));
}
foreach (var p in iter.Member_Init.Ens) {
// these postconditions are two-state predicates, but that's okay, because we haven't changed anything yet
- builder.Add(new Bpl.AssumeCmd(p.E.tok, etran.TrExpr(p.E)));
+ builder.Add(TrAssumeCmd(p.E.tok, etran.TrExpr(p.E)));
}
// add the _yieldCount variable, and assume its initial value to be 0
yieldCountVariable = new Bpl.LocalVariable(iter.tok,
new Bpl.TypedIdent(iter.tok, iter.YieldCountVariable.AssignUniqueName(currentDeclaration.IdGenerator), TrType(iter.YieldCountVariable.Type)));
yieldCountVariable.TypedIdent.WhereExpr = YieldCountAssumption(iter, etran); // by doing this after setting "yieldCountVariable", the variable can be used by YieldCountAssumption
localVariables.Add(yieldCountVariable);
- builder.Add(new Bpl.AssumeCmd(iter.tok, Bpl.Expr.Eq(new Bpl.IdentifierExpr(iter.tok, yieldCountVariable), Bpl.Expr.Literal(0))));
+ builder.Add(TrAssumeCmd(iter.tok, Bpl.Expr.Eq(new Bpl.IdentifierExpr(iter.tok, yieldCountVariable), Bpl.Expr.Literal(0))));
// add a variable $_OldIterHeap
var oih = new Bpl.IdentifierExpr(iter.tok, "$_OldIterHeap", predef.HeapType);
Bpl.Expr wh = BplAnd(
FunctionCall(iter.tok, BuiltinFunction.IsGoodHeap, null, oih),
- FunctionCall(iter.tok, BuiltinFunction.HeapSucc, null, oih, etran.HeapExpr));
+ HeapSucc(oih, etran.HeapExpr));
localVariables.Add(new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, "$_OldIterHeap", predef.HeapType, wh)));
// do an initial YieldHavoc
@@ -1877,10 +1947,8 @@ namespace Microsoft.Dafny {
// danr: Let's create the literal function axioms if there is an arrow type in the signature
if (!(f is FixpointPredicate) && (f.Reads.Count == 0 || f.Formals.Exists(a => a.Type.IsArrowType))) {
var FVs = new HashSet<IVariable>();
- bool usesHeap = false, usesOldHeap = false;
- Type usesThis = null;
foreach (var e in f.Decreases.Expressions) {
- ComputeFreeVariables(e, FVs, ref usesHeap, ref usesOldHeap, ref usesThis, false);
+ ComputeFreeVariables(e, FVs);
}
var decs = new List<Formal>();
foreach (var formal in f.Formals) {
@@ -1947,7 +2015,7 @@ namespace Microsoft.Dafny {
var formals = MkTyParamBinders(GetTypeParams(f), out tyargs);
var args = new List<Bpl.Expr>();
Bpl.BoundVariable layer;
- if (f.IsRecursive) {
+ if (f.IsFuelAware()) {
layer = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$ly", predef.LayerType));
formals.Add(layer);
// Note, "layer" is not added to "args" here; rather, that's done below, as needed
@@ -2014,15 +2082,15 @@ namespace Microsoft.Dafny {
Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new List<Bpl.Expr> { funcAppl });
var typeParams = TrTypeParamDecls(f.TypeArgs);
- Bpl.Expr meat = Bpl.Expr.True;
+ Bpl.Expr post = Bpl.Expr.True;
foreach (Expression p in ens) {
Bpl.Expr q = etran.TrExpr(Substitute(p, null, substMap));
- meat = BplAnd(meat, q);
+ post = BplAnd(post, q);
}
Bpl.Expr whr = GetWhereClause(f.tok, funcAppl, f.ResultType, etran);
- if (whr != null) { meat = Bpl.Expr.And(meat, whr); }
+ if (whr != null) { post = Bpl.Expr.And(post, whr); }
- Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, formals, null, tr, Bpl.Expr.Imp(ante, meat));
+ Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, formals, null, tr, Bpl.Expr.Imp(ante, post));
var activate = AxiomActivation(f, true, true, etran);
string comment = "consequence axiom for " + f.FullSanitizedName;
return new Bpl.Axiom(f.tok, Bpl.Expr.Imp(activate, ax), comment);
@@ -2118,7 +2186,7 @@ namespace Microsoft.Dafny {
var formals = MkTyParamBinders(GetTypeParams(f), out tyargs);
Bpl.BoundVariable layer;
- if (f.IsRecursive) {
+ if (f.IsFuelAware()) {
layer = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$ly", predef.LayerType));
formals.Add(layer);
// Note, "layer" is not added to "args" here; rather, that's done below, as needed
@@ -2173,13 +2241,6 @@ namespace Microsoft.Dafny {
foreach (Expression req in f.Req) {
pre = BplAnd(pre, etran.TrExpr(Substitute(req, null, substMap)));
}
- // useViaContext: (mh != ModuleContextHeight || fh != FunctionContextHeight)
- ModuleDefinition mod = f.EnclosingClass.Module;
- Bpl.Expr useViaContext = visibility == FunctionAxiomVisibility.ForeignModuleOnly ? (Bpl.Expr)Bpl.Expr.True :
- Bpl.Expr.Neq(Bpl.Expr.Literal(mod.CallGraph.GetSCCRepresentativeId(f)), etran.FunctionContextHeight());
-
- // ante := (useViaContext && typeAnte && pre)
- ante = BplAnd(useViaContext, BplAnd(ante, pre));
// Add the precondition function and its axiom (which is equivalent to the ante)
if (body == null || (visibility == FunctionAxiomVisibility.IntraModuleOnly && lits == null)) {
@@ -2192,14 +2253,21 @@ namespace Microsoft.Dafny {
}
var appl = FunctionCall(f.tok, RequiresName(f), Bpl.Type.Bool,
formals.ConvertAll(x => (Bpl.Expr)(new Bpl.IdentifierExpr(f.tok, x))));
- sink.AddTopLevelDeclaration(new Axiom(f.tok, BplForall(formals, BplTrigger(appl), Bpl.Expr.Eq(appl, ante))));
- // you could use it to check that it always works, but it makes VSI-Benchmarks/b3.dfy time out:
- // ante = appl;
+ // axiom (forall params :: { f#requires(params) } ante ==> f#requires(params) == pre);
+ sink.AddTopLevelDeclaration(new Axiom(f.tok, BplForall(formals, BplTrigger(appl),
+ BplImp(ante, Bpl.Expr.Eq(appl, pre)))));
if (body == null) {
return null;
}
}
+ // useViaContext: (mh != ModuleContextHeight || fh != FunctionContextHeight)
+ ModuleDefinition mod = f.EnclosingClass.Module;
+ Bpl.Expr useViaContext = visibility == FunctionAxiomVisibility.ForeignModuleOnly ? (Bpl.Expr)Bpl.Expr.True :
+ Bpl.Expr.Neq(Bpl.Expr.Literal(mod.CallGraph.GetSCCRepresentativeId(f)), etran.FunctionContextHeight());
+ // ante := (useViaContext && typeAnte && pre)
+ ante = BplAnd(useViaContext, BplAnd(ante, pre));
+
// useViaCanCall: f#canCall(args)
Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool);
Bpl.Expr useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs,args));
@@ -2230,9 +2298,9 @@ namespace Microsoft.Dafny {
Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new List<Bpl.Expr> { funcAppl });
var typeParams = TrTypeParamDecls(f.TypeArgs);
- Bpl.Expr meat;
+ Bpl.Expr tastyVegetarianOption;
if (visibility == FunctionAxiomVisibility.ForeignModuleOnly && f.IsProtected) {
- meat = Bpl.Expr.True;
+ tastyVegetarianOption = Bpl.Expr.True;
} else {
var bodyWithSubst = Substitute(body, null, substMap);
if (f is PrefixPredicate) {
@@ -2240,14 +2308,14 @@ namespace Microsoft.Dafny {
bodyWithSubst = PrefixSubstitution(pp, bodyWithSubst);
}
var etranBody = layer == null ? etran : etran.LimitedFunctions(f, new Bpl.IdentifierExpr(f.tok, layer));
- meat = BplAnd(CanCallAssumption(bodyWithSubst, etranBody),
+ tastyVegetarianOption = BplAnd(CanCallAssumption(bodyWithSubst, etranBody),
Bpl.Expr.Eq(funcAppl, etranBody.TrExpr(bodyWithSubst)));
}
QKeyValue kv = null;
if (lits != null) {
kv = new QKeyValue(f.tok, "weight", new List<object>() { Bpl.Expr.Literal(3) }, null);
}
- Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, formals, kv, tr, Bpl.Expr.Imp(ante, meat));
+ Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, formals, kv, tr, Bpl.Expr.Imp(ante, tastyVegetarianOption));
var activate = AxiomActivation(f, visibility == FunctionAxiomVisibility.ForeignModuleOnly, visibility == FunctionAxiomVisibility.IntraModuleOnly, etran);
string comment;
if (overridingClass == null) {
@@ -2314,7 +2382,7 @@ namespace Microsoft.Dafny {
void AddSynonymAxiom(Function f) {
Contract.Requires(f != null);
- Contract.Requires(f.IsRecursive);
+ Contract.Requires(f.IsFuelAware());
Contract.Requires(sink != null && predef != null);
// axiom // layer synonym axiom
// (forall s, $Heap, formals ::
@@ -2397,7 +2465,7 @@ namespace Microsoft.Dafny {
var coArgs = new List<Bpl.Expr>(tyexprs);
var prefixArgs = new List<Bpl.Expr>(tyexprs);
var prefixArgsLimited = new List<Bpl.Expr>(tyexprs);
- if (pp.IsRecursive) {
+ if (pp.IsFuelAware()) {
var sV = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$ly", predef.LayerType));
var s = new Bpl.IdentifierExpr(tok, sV);
var succS = FunctionCall(tok, BuiltinFunction.LayerSucc, null, s);
@@ -2486,11 +2554,11 @@ namespace Microsoft.Dafny {
moreBvs.Add(k);
var z = Bpl.Expr.Eq(kId, Bpl.Expr.Literal(0));
funcID = new Bpl.IdentifierExpr(tok, pp.FullSanitizedName, TrType(pp.ResultType));
- Bpl.Expr prefixLimited = new Bpl.NAryExpr(tok, new Bpl.FunctionCall(funcID), prefixArgsLimited);
- if (pp.FixpointPred is InductivePredicate) {
- prefixLimited = Bpl.Expr.Not(prefixLimited);
- }
- var trueAtZero = new Bpl.ForallExpr(tok, moreBvs, BplImp(BplAnd(ante, z), prefixLimited));
+ Bpl.Expr prefixLimitedBody = new Bpl.NAryExpr(tok, new Bpl.FunctionCall(funcID), prefixArgsLimited);
+ Bpl.Expr prefixLimited = pp.FixpointPred is InductivePredicate ? Bpl.Expr.Not(prefixLimitedBody) : prefixLimitedBody;
+
+ var trigger = BplTrigger(prefixLimitedBody);
+ var trueAtZero = new Bpl.ForallExpr(tok, moreBvs, trigger, BplImp(BplAnd(ante, z), prefixLimited));
sink.AddTopLevelDeclaration(new Bpl.Axiom(tok, Bpl.Expr.Imp(activation, trueAtZero),
"3rd prefix predicate axiom"));
}
@@ -2505,7 +2573,6 @@ namespace Microsoft.Dafny {
/// ($IsAlloc(o, TClassA(G), h) // or h[o, alloc]
/// ==> $IsAlloc(h[o, f], TT(PP), h))
/// && $Is(h[o, f], TT(PP), h);
- /// <summary>
/// This can be optimised later to:
/// axiom (forall o: ref, h: Heap ::
/// { h[o, f] }
@@ -2513,6 +2580,7 @@ namespace Microsoft.Dafny {
/// ==>
/// (h[o, alloc] ==> $IsAlloc(h[o, f], TT(TClassA_Inv_i(dtype(o)),..), h))
/// && $Is(h[o, f], TT(TClassA_Inv_i(dtype(o)),..), h);
+ /// <summary>
void AddAllocationAxiom(Field f, ClassDecl c, bool is_array = false)
{
// IFF you're adding the array axioms, then the field should be null
@@ -2547,12 +2615,7 @@ namespace Microsoft.Dafny {
var tyvars = MkTyParamBinders(GetTypeParams(c), out tyexprs);
Bpl.Expr o_ty = ClassTyCon(c, tyexprs);
-
- // Bpl.Expr is_o = MkIs(o, o_ty); // $Is(o, ..)
- // Changed to use dtype(o) == o_ty instead:
- Bpl.Expr is_o = DType(o, o_ty);
- // Bpl.Expr isalloc_o = MkIsAlloc(o, o_ty, h); // $IsAlloc(o, ..)
- // Changed to use h[o,alloc] instead:
+ Bpl.Expr is_o = c is TraitDecl ? MkIs(o, o_ty) : DType(o, o_ty); // $Is(o, ..) or dtype(o) == o_ty
Bpl.Expr isalloc_o = IsAlloced(c.tok, h, o);
Bpl.Expr is_hf, isalloc_hf;
@@ -2727,131 +2790,118 @@ namespace Microsoft.Dafny {
Bpl.StmtListBuilder builder = new Bpl.StmtListBuilder();
builder.Add(new CommentCmd("AddMethodImpl: " + m + ", " + proc));
ExpressionTranslator etran = new ExpressionTranslator(this, predef, m.tok);
+ InitializeFuelConstant(m.tok, builder, etran);
List<Variable> localVariables = new List<Variable>();
GenerateImplPrelude(m, wellformednessProc, inParams, outParams, builder, localVariables);
+ if (UseOptimizationInZ3 && m.Ins != null)
+ {
+ // We ask Z3 to minimize all parameters of type 'nat'.
+ foreach (var f in m.Ins)
+ {
+ if (f.Type is NatType)
+ {
+ builder.Add(optimizeExpr(true, new IdentifierExpr(f), f.Tok, etran));
+ }
+ }
+ }
+
Bpl.StmtList stmts;
if (!wellformednessProc) {
- if (3 <= DafnyOptions.O.Induction && m.IsGhost && m.Mod.Expressions.Count == 0 && m.Outs.Count == 0 && !(m is FixpointLemma)) {
- var posts = new List<Expression>();
- m.Ens.ForEach(mfe => posts.Add(mfe.E));
- var allIns = new List<Formal>();
- if (!m.IsStatic) {
- allIns.Add(new ThisSurrogate(m.tok, Resolver.GetThisType(m.tok, (ClassDecl)m.EnclosingClass)));
- }
- allIns.AddRange(m.Ins);
- var inductionVars = ApplyInduction(allIns, m.Attributes, posts, delegate(System.IO.TextWriter wr) { wr.Write(m.FullName); });
- if (inductionVars.Count != 0) {
- // Let the parameters be this,x,y of the method M and suppose ApplyInduction returns this,y.
- // Also, let Pre be the precondition and VF be the decreases clause.
- // Then, insert into the method body what amounts to:
- // assume case-analysis-on-parameter[[ y' ]];
- // forall (this', y' | Pre(this', x, y') && VF(this', x, y') << VF(this, x, y)) {
- // this'.M(x, y');
- // }
- // Generate bound variables for the forall statement, and a substitution for the Pre and VF
-
- // assume case-analysis-on-parameter[[ y' ]];
- foreach (var inFormal in m.Ins) {
- var dt = inFormal.Type.AsDatatype;
- if (dt != null) {
- var funcID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(inFormal.tok, "$IsA#" + dt.FullSanitizedName, Bpl.Type.Bool));
- var f = new Bpl.IdentifierExpr(inFormal.tok, inFormal.AssignUniqueName(m.IdGenerator), TrType(inFormal.Type));
- builder.Add(new Bpl.AssumeCmd(inFormal.tok, new Bpl.NAryExpr(inFormal.tok, funcID, new List<Bpl.Expr> { f })));
- }
- }
-
- var parBoundVars = new List<BoundVar>();
- Expression receiverReplacement = null;
- var substMap = new Dictionary<IVariable, Expression>();
- foreach (var iv in inductionVars) {
- BoundVar bv;
- IdentifierExpr ie;
- CloneVariableAsBoundVar(iv.tok, iv, "$ih#" + iv.Name, out bv, out ie);
- parBoundVars.Add(bv);
- if (iv is ThisSurrogate) {
- Contract.Assert(receiverReplacement == null && substMap.Count == 0); // the receiver comes first, if at all
- receiverReplacement = ie;
- } else {
- substMap.Add(iv, ie);
- }
- }
+ var inductionVars = ApplyInduction(m.Ins, m.Attributes);
+ if (inductionVars.Count != 0) {
+ // Let the parameters be this,x,y of the method M and suppose ApplyInduction returns y.
+ // Also, let Pre be the precondition and VF be the decreases clause.
+ // Then, insert into the method body what amounts to:
+ // assume case-analysis-on-parameter[[ y' ]];
+ // forall (y' | Pre(this, x, y') && VF(this, x, y') << VF(this, x, y)) {
+ // this.M(x, y');
+ // }
+ // Generate bound variables for the forall statement, and a substitution for the Pre and VF
+
+ // assume case-analysis-on-parameter[[ y' ]];
+ foreach (var inFormal in m.Ins) {
+ var dt = inFormal.Type.AsDatatype;
+ if (dt != null) {
+ var funcID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(inFormal.tok, "$IsA#" + dt.FullSanitizedName, Bpl.Type.Bool));
+ var f = new Bpl.IdentifierExpr(inFormal.tok, inFormal.AssignUniqueName(m.IdGenerator), TrType(inFormal.Type));
+ builder.Add(TrAssumeCmd(inFormal.tok, new Bpl.NAryExpr(inFormal.tok, funcID, new List<Bpl.Expr> { f })));
+ }
+ }
+
+ var parBoundVars = new List<BoundVar>();
+ var substMap = new Dictionary<IVariable, Expression>();
+ foreach (var iv in inductionVars) {
+ BoundVar bv;
+ IdentifierExpr ie;
+ CloneVariableAsBoundVar(iv.tok, iv, "$ih#" + iv.Name, out bv, out ie);
+ parBoundVars.Add(bv);
+ substMap.Add(iv, ie);
+ }
- // Generate a CallStmt for the recursive call
- Expression recursiveCallReceiver;
- if (receiverReplacement != null) {
- recursiveCallReceiver = receiverReplacement;
- } else if (m.IsStatic) {
- recursiveCallReceiver = new StaticReceiverExpr(m.tok, (ClassDecl)m.EnclosingClass); // this also resolves it
+ // Generate a CallStmt for the recursive call
+ Expression recursiveCallReceiver;
+ if (m.IsStatic) {
+ recursiveCallReceiver = new StaticReceiverExpr(m.tok, (ClassDecl)m.EnclosingClass, true); // this also resolves it
+ } else {
+ recursiveCallReceiver = new ImplicitThisExpr(m.tok);
+ recursiveCallReceiver.Type = Resolver.GetThisType(m.tok, (ClassDecl)m.EnclosingClass); // resolve here
+ }
+ var recursiveCallArgs = new List<Expression>();
+ foreach (var inFormal in m.Ins) {
+ Expression inE;
+ if (substMap.TryGetValue(inFormal, out inE)) {
+ recursiveCallArgs.Add(inE);
} else {
- recursiveCallReceiver = new ImplicitThisExpr(m.tok);
- recursiveCallReceiver.Type = Resolver.GetThisType(m.tok, (ClassDecl)m.EnclosingClass); // resolve here
- }
- var recursiveCallArgs = new List<Expression>();
- foreach (var inFormal in m.Ins) {
- Expression inE;
- if (substMap.TryGetValue(inFormal, out inE)) {
- recursiveCallArgs.Add(inE);
- } else {
- var ie = new IdentifierExpr(inFormal.tok, inFormal.Name);
- ie.Var = inFormal; // resolve here
- ie.Type = inFormal.Type; // resolve here
- recursiveCallArgs.Add(ie);
- }
- }
- var methodSel = new MemberSelectExpr(m.tok, recursiveCallReceiver, m.Name);
- methodSel.Member = m; // resolve here
- methodSel.TypeApplication = new List<Type>();
- methodSel.TypeApplication.AddRange(recursiveCallReceiver.Type.TypeArgs);
- m.TypeArgs.ForEach(tp => methodSel.TypeApplication.Add(new UserDefinedType(tp)));
- methodSel.Type = new InferredTypeProxy(); // this is the last step in resolving 'methodSel'
- var recursiveCall = new CallStmt(m.tok, m.tok, new List<Expression>(), methodSel, recursiveCallArgs);
- recursiveCall.IsGhost = m.IsGhost; // resolve here
-
- Expression parRange = new LiteralExpr(m.tok, true);
- parRange.Type = Type.Bool; // resolve here
- if (receiverReplacement != null) {
- // add "this' != null" to the range
- var nil = new LiteralExpr(receiverReplacement.tok);
- nil.Type = receiverReplacement.Type; // resolve here
- var neqNull = new BinaryExpr(receiverReplacement.tok, BinaryExpr.Opcode.Neq, receiverReplacement, nil);
- neqNull.ResolvedOp = BinaryExpr.ResolvedOpcode.NeqCommon; // resolve here
- neqNull.Type = Type.Bool; // resolve here
- parRange = Expression.CreateAnd(parRange, neqNull);
- }
- foreach (var pre in m.Req) {
- if (!pre.IsFree) {
- parRange = Expression.CreateAnd(parRange, Substitute(pre.E, receiverReplacement, substMap));
- }
- }
- // construct an expression (generator) for: VF' << VF
- ExpressionConverter decrCheck = delegate(Dictionary<IVariable, Expression> decrSubstMap, ExpressionTranslator exprTran) {
- var decrToks = new List<IToken>();
- var decrTypes = new List<Type>();
- var decrCallee = new List<Expr>();
- var decrCaller = new List<Expr>();
- foreach (var ee in m.Decreases.Expressions) {
- decrToks.Add(ee.tok);
- decrTypes.Add(ee.Type.NormalizeExpand());
- decrCaller.Add(exprTran.TrExpr(ee));
- Expression es = Substitute(ee, receiverReplacement, substMap);
- es = Substitute(es, null, decrSubstMap);
- decrCallee.Add(exprTran.TrExpr(es));
- }
- return DecreasesCheck(decrToks, decrTypes, decrTypes, decrCallee, decrCaller, null, null, false, true);
- };
+ var ie = new IdentifierExpr(inFormal.tok, inFormal.Name);
+ ie.Var = inFormal; // resolve here
+ ie.Type = inFormal.Type; // resolve here
+ recursiveCallArgs.Add(ie);
+ }
+ }
+ var methodSel = new MemberSelectExpr(m.tok, recursiveCallReceiver, m.Name);
+ methodSel.Member = m; // resolve here
+ methodSel.TypeApplication = new List<Type>();
+ methodSel.TypeApplication.AddRange(recursiveCallReceiver.Type.TypeArgs);
+ m.TypeArgs.ForEach(tp => methodSel.TypeApplication.Add(new UserDefinedType(tp)));
+ methodSel.Type = new InferredTypeProxy(); // this is the last step in resolving 'methodSel'
+ var recursiveCall = new CallStmt(m.tok, m.tok, new List<Expression>(), methodSel, recursiveCallArgs);
+ recursiveCall.IsGhost = m.IsGhost; // resolve here
+
+ Expression parRange = new LiteralExpr(m.tok, true);
+ parRange.Type = Type.Bool; // resolve here
+ foreach (var pre in m.Req) {
+ if (!pre.IsFree) {
+ parRange = Expression.CreateAnd(parRange, Substitute(pre.E, null, substMap));
+ }
+ }
+ // construct an expression (generator) for: VF' << VF
+ ExpressionConverter decrCheck = delegate(Dictionary<IVariable, Expression> decrSubstMap, ExpressionTranslator exprTran) {
+ var decrToks = new List<IToken>();
+ var decrTypes = new List<Type>();
+ var decrCallee = new List<Expr>();
+ var decrCaller = new List<Expr>();
+ foreach (var ee in m.Decreases.Expressions) {
+ decrToks.Add(ee.tok);
+ decrTypes.Add(ee.Type.NormalizeExpand());
+ decrCaller.Add(exprTran.TrExpr(ee));
+ Expression es = Substitute(ee, null, substMap);
+ es = Substitute(es, null, decrSubstMap);
+ decrCallee.Add(exprTran.TrExpr(es));
+ }
+ return DecreasesCheck(decrToks, decrTypes, decrTypes, decrCallee, decrCaller, null, null, false, true);
+ };
#if VERIFY_CORRECTNESS_OF_TRANSLATION_FORALL_STATEMENT_RANGE
- var definedness = new Bpl.StmtListBuilder();
- var exporter = new Bpl.StmtListBuilder();
- TrForallStmtCall(m.tok, parBoundVars, parRange, decrCheck, recursiveCall, definedness, exporter, localVariables, etran);
- // All done, so put the two pieces together
- builder.Add(new Bpl.IfCmd(m.tok, null, definedness.Collect(m.tok), null, exporter.Collect(m.tok)));
+ var definedness = new Bpl.StmtListBuilder();
+ var exporter = new Bpl.StmtListBuilder();
+ TrForallStmtCall(m.tok, parBoundVars, parRange, decrCheck, null, recursiveCall, definedness, exporter, localVariables, etran);
+ // All done, so put the two pieces together
+ builder.Add(new Bpl.IfCmd(m.tok, null, definedness.Collect(m.tok), null, exporter.Collect(m.tok)));
#else
- TrForallStmtCall(m.tok, parBoundVars, parRange, decrCheck, recursiveCall, null, builder, localVariables, etran);
+ TrForallStmtCall(m.tok, parBoundVars, parRange, decrCheck, null, recursiveCall, null, builder, localVariables, etran);
#endif
- }
}
// translate the body of the method
Contract.Assert(m.Body != null); // follows from method precondition and the if guard
@@ -2861,8 +2911,7 @@ namespace Microsoft.Dafny {
} else {
// check well-formedness of the preconditions, and then assume each one of them
foreach (MaybeFreeExpression p in m.Req) {
- CheckWellformed(p.E, new WFOptions(), localVariables, builder, etran);
- builder.Add(new Bpl.AssumeCmd(p.E.tok, etran.TrExpr(p.E)));
+ CheckWellformedAndAssume(p.E, new WFOptions(), localVariables, builder, etran);
}
// check well-formedness of the modifies clause
CheckFrameWellFormed(new WFOptions(), m.Mod.Expressions, localVariables, builder, etran);
@@ -2878,7 +2927,7 @@ namespace Microsoft.Dafny {
foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(m.tok, m.Mod.Expressions, m.IsGhost, etran.Old, etran, etran.Old))
{
if (tri.IsFree) {
- builder.Add(new Bpl.AssumeCmd(m.tok, tri.Expr));
+ builder.Add(TrAssumeCmd(m.tok, tri.Expr));
}
}
@@ -2900,8 +2949,7 @@ namespace Microsoft.Dafny {
// check wellformedness of postconditions
foreach (MaybeFreeExpression p in m.Ens) {
- CheckWellformed(p.E, new WFOptions(), localVariables, builder, etran);
- builder.Add(new Bpl.AssumeCmd(p.E.tok, etran.TrExpr(p.E)));
+ CheckWellformedAndAssume(p.E, new WFOptions(), localVariables, builder, etran);
}
stmts = builder.Collect(m.tok);
@@ -2922,6 +2970,65 @@ namespace Microsoft.Dafny {
Reset();
}
+ void InitializeFuelConstant(IToken tok, Bpl.StmtListBuilder builder, ExpressionTranslator etran) {
+ if (this.functionFuel.Count > 0) {
+ builder.Add(new CommentCmd("initialize fuel constant"));
+ }
+ FuelContext fuelContext = this.fuelContext;
+ foreach (FuelConstant fuelConstant in this.functionFuel) {
+ Function f = fuelConstant.f;
+ Bpl.Expr baseFuel = fuelConstant.baseFuel;
+ Bpl.Expr startFuel = fuelConstant.startFuel;
+ Bpl.Expr startFuelAssert = fuelConstant.startFuelAssert;
+ // find out what the initial value should be
+ FuelSettingPair settings;
+ var found = fuelContext.TryGetValue(f, out settings);
+ if (!found) {
+ // If the context doesn't define fuel for this function, check for a fuel attribute (which supplies a default value if none is found)
+ settings = FuelSetting.FuelAttrib(f, out found);
+ }
+
+ Bpl.Expr layer = etran.layerInterCluster.LayerN(settings.low, baseFuel);
+ Bpl.Expr layerAssert = etran.layerInterCluster.LayerN(settings.high, baseFuel);
+ builder.Add(TrAssumeCmd(tok, Bpl.Expr.Eq(startFuel, layer)));
+ builder.Add(TrAssumeCmd(tok, Bpl.Expr.Eq(startFuelAssert, layerAssert)));
+ }
+ }
+
+ bool DefineFuelConstant(IToken tok, Attributes attribs, Bpl.StmtListBuilder builder, ExpressionTranslator etran) {
+ bool defineFuel = false;
+ builder.Add(new CommentCmd("Assume Fuel Constant"));
+ FuelContext fuelContext = new FuelContext();
+ FuelSetting.FindFuelAttributes(attribs, fuelContext);
+ foreach (KeyValuePair<Function, FuelSettingPair> fuel in fuelContext) {
+ Function f = fuel.Key;
+ FuelSettingPair settings = fuel.Value;
+ FuelConstant fuelConstant = this.functionFuel.Find(x => x.f == f);
+ if (fuelConstant != null) {
+ Bpl.Expr startFuel = fuelConstant.startFuel;
+ Bpl.Expr startFuelAssert = fuelConstant.startFuelAssert;
+ Bpl.Expr moreFuel_expr = fuelConstant.MoreFuel(sink, predef, f.IdGenerator);
+ Bpl.Expr layer = etran.layerInterCluster.LayerN(settings.low, moreFuel_expr);
+ Bpl.Expr layerAssert = etran.layerInterCluster.LayerN(settings.high, moreFuel_expr);
+ builder.Add(TrAssumeCmd(tok, Bpl.Expr.Eq(startFuel, layer)));
+ builder.Add(TrAssumeCmd(tok, Bpl.Expr.Eq(startFuelAssert, layerAssert)));
+ defineFuel = true;
+ }
+ }
+ return defineFuel;
+ }
+
+ internal static AssumeCmd optimizeExpr(bool minimize, Expression expr, IToken tok, ExpressionTranslator etran)
+ {
+ Contract.Requires(expr != null);
+ Contract.Requires(expr.Type.IsIntegerType || expr.Type.IsRealType);
+ Contract.Requires(tok != null && etran != null);
+
+ var assumeCmd = new AssumeCmd(tok, Expr.True);
+ assumeCmd.Attributes = new QKeyValue(expr.tok, (minimize ? "minimize" : "maximize"), new List<object> { etran.TrExpr(expr) }, null);
+ return assumeCmd;
+ }
+
private void AddFunctionOverrideCheckImpl(Function f)
{
Contract.Requires(f != null);
@@ -2968,7 +3075,7 @@ namespace Microsoft.Dafny {
{
var functionHeight = currentModule.CallGraph.GetSCCRepresentativeId(f);
var splits = new List<SplitExprInfo>();
- bool splitHappened/*we actually don't care*/ = TrSplitExpr(p, splits, true, functionHeight,false, etran);
+ bool splitHappened/*we actually don't care*/ = TrSplitExpr(p, splits, true, functionHeight, true, false, etran);
foreach (var s in splits)
{
if (s.IsChecked && !RefinementToken.IsInherited(s.E.tok, currentModule))
@@ -3004,7 +3111,7 @@ namespace Microsoft.Dafny {
AddFunctionOverrideReqsChk(f, builder, etran, substMap);
//adding assert R <= Rank’;
- AddFunctionOverrideTerminationChk(f, builder, etran, substMap);
+ AddOverrideTerminationChk(f, f.OverriddenFunction, builder, etran, substMap);
//adding assert W <= Frame’
AddFunctionOverrideSubsetChk(f, builder, etran, localVariables, substMap);
@@ -3069,7 +3176,7 @@ namespace Microsoft.Dafny {
//generating class post-conditions
foreach (var en in f.Ens)
{
- builder.Add(new Bpl.AssumeCmd(f.tok, etran.TrExpr(en)));
+ builder.Add(TrAssumeCmd(f.tok, etran.TrExpr(en)));
}
//generating assume J.F(ins) == C.F(ins)
@@ -3077,13 +3184,13 @@ namespace Microsoft.Dafny {
Bpl.FunctionCall funcIdT = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.OverriddenFunction.tok, f.OverriddenFunction.FullSanitizedName, TrType(f.OverriddenFunction.ResultType)));
List<Bpl.Expr> argsC = new List<Bpl.Expr>();
List<Bpl.Expr> argsT = new List<Bpl.Expr>();
- if (f.IsRecursive)
+ if (f.IsFuelAware())
{
- argsC.Add(etran.LayerN(1));
+ argsC.Add(etran.layerInterCluster.GetFunctionFuel(f));
}
- if (f.OverriddenFunction.IsRecursive)
+ if (f.OverriddenFunction.IsFuelAware())
{
- argsT.Add(etran.LayerN(1));
+ argsT.Add(etran.layerInterCluster.GetFunctionFuel(f));
}
argsC.Add(etran.HeapExpr);
argsT.Add(etran.HeapExpr);
@@ -3094,7 +3201,7 @@ namespace Microsoft.Dafny {
}
Bpl.Expr funcExpC = new Bpl.NAryExpr(f.tok, funcIdC, argsC);
Bpl.Expr funcExpT = new Bpl.NAryExpr(f.OverriddenFunction.tok, funcIdT, argsT);
- builder.Add(new Bpl.AssumeCmd(f.tok, Bpl.Expr.Eq(funcExpC, funcExpT)));
+ builder.Add(TrAssumeCmd(f.tok, Bpl.Expr.Eq(funcExpC, funcExpT)));
//generating trait post-conditions with class variables
foreach (var en in f.OverriddenFunction.Ens)
@@ -3104,7 +3211,7 @@ namespace Microsoft.Dafny {
var reqSplitedE = TrSplitExpr(postcond, etran,false, out splitHappened);
foreach (var s in reqSplitedE)
{
- var assert = new Bpl.AssertCmd(f.tok, s.E);
+ var assert = TrAssertCmd(f.tok, s.E);
assert.ErrorData = "Error: the function must provide an equal or more detailed postcondition than in its parent trait";
builder.Add(assert);
}
@@ -3120,7 +3227,7 @@ namespace Microsoft.Dafny {
{
if (tri.IsFree)
{
- builder.Add(new Bpl.AssumeCmd(f.tok, tri.Expr));
+ builder.Add(TrAssumeCmd(f.tok, tri.Expr));
}
}
}
@@ -3167,42 +3274,13 @@ namespace Microsoft.Dafny {
builder.Add(Assert(tok, q, "expression may read an object not in the parent trait context's reads clause", kv));
}
- private void AddFunctionOverrideTerminationChk(Function f, StmtListBuilder builder, ExpressionTranslator etran, Dictionary<IVariable, Expression> substMap)
- {
- var decrToks = new List<IToken>();
- var decrTypes1 = new List<Type>();
- var decrTypes2 = new List<Type>();
- var decrClass = new List<Expr>();
- var decrTrait = new List<Expr>();
- if (f.Decreases != null)
- {
- foreach (var decC in f.Decreases.Expressions)
- {
- decrToks.Add(decC.tok);
- decrTypes1.Add(decC.Type);
- decrClass.Add(etran.TrExpr(decC));
- }
- }
- if (f.OverriddenFunction.Decreases != null)
- {
- foreach (var decT in f.OverriddenFunction.Decreases.Expressions)
- {
- var decCNew = Substitute(decT, null, substMap);
- decrTypes2.Add(decCNew.Type);
- decrTrait.Add(etran.TrExpr(decCNew));
- }
- }
- var decrChk = DecreasesCheck(decrToks, decrTypes1, decrTypes2, decrClass, decrTrait, null, null, true, false);
- builder.Add(new Bpl.AssertCmd(f.tok, decrChk));
- }
-
private void AddFunctionOverrideReqsChk(Function f, StmtListBuilder builder, ExpressionTranslator etran, Dictionary<IVariable, Expression> substMap)
{
//generating trait pre-conditions with class variables
foreach (var req in f.OverriddenFunction.Req)
{
Expression precond = Substitute(req, null, substMap);
- builder.Add(new Bpl.AssumeCmd(f.tok, etran.TrExpr(precond)));
+ builder.Add(TrAssumeCmd(f.tok, etran.TrExpr(precond)));
}
//generating class pre-conditions
foreach (var req in f.Req)
@@ -3211,7 +3289,7 @@ namespace Microsoft.Dafny {
var reqSplitedE = TrSplitExpr(req, etran,false, out splitHappened);
foreach (var s in reqSplitedE)
{
- var assert = new Bpl.AssertCmd(f.tok, s.E);
+ var assert = TrAssertCmd(f.tok, s.E);
assert.ErrorData = "Error: the function must provide an equal or more permissive precondition than in its parent trait";
builder.Add(assert);
}
@@ -3263,7 +3341,7 @@ namespace Microsoft.Dafny {
AddMethodOverrideReqsChk(m, builder, etran, substMap);
//adding assert R <= Rank’;
- AddMethodOverrideTerminationChk(m, builder, etran, substMap);
+ AddOverrideTerminationChk(m, m.OverriddenMethod, builder, etran, substMap);
//adding assert W <= Frame’
AddMethodOverrideSubsetChk(m, builder, etran, localVariables, substMap);
@@ -3301,7 +3379,7 @@ namespace Microsoft.Dafny {
{
if (tri.IsFree)
{
- builder.Add(new Bpl.AssumeCmd(m.tok, tri.Expr));
+ builder.Add(TrAssumeCmd(m.tok, tri.Expr));
}
}
}
@@ -3311,7 +3389,7 @@ namespace Microsoft.Dafny {
//generating class post-conditions
foreach (var en in m.Ens)
{
- builder.Add(new Bpl.AssumeCmd(m.tok, etran.TrExpr(en.E)));
+ builder.Add(TrAssumeCmd(m.tok, etran.TrExpr(en.E)));
}
//generating trait post-conditions with class variables
foreach (var en in m.OverriddenMethod.Ens)
@@ -3321,7 +3399,7 @@ namespace Microsoft.Dafny {
var reqSplitedE = TrSplitExpr(postcond, etran,false, out splitHappened);
foreach (var s in reqSplitedE)
{
- var assert = new Bpl.AssertCmd(m.tok, s.E);
+ var assert = TrAssertCmd(m.tok, s.E);
assert.ErrorData = "Error: the method must provide an equal or more detailed postcondition than in its parent trait";
builder.Add(assert);
}
@@ -3334,7 +3412,7 @@ namespace Microsoft.Dafny {
foreach (var req in m.OverriddenMethod.Req)
{
Expression precond = Substitute(req.E, null, substMap);
- builder.Add(new Bpl.AssumeCmd(m.tok, etran.TrExpr(precond)));
+ builder.Add(TrAssumeCmd(m.tok, etran.TrExpr(precond)));
}
//generating class pre-conditions
foreach (var req in m.Req)
@@ -3343,21 +3421,22 @@ namespace Microsoft.Dafny {
var reqSplitedE = TrSplitExpr(req.E, etran,false, out splitHappened);
foreach (var s in reqSplitedE)
{
- var assert = new Bpl.AssertCmd(m.tok, s.E);
+ var assert = TrAssertCmd(m.tok, s.E);
assert.ErrorData = "Error: the method must provide an equal or more permissive precondition than in its parent trait";
builder.Add(assert);
}
}
}
- private void AddMethodOverrideTerminationChk(Method m, Bpl.StmtListBuilder builder, ExpressionTranslator etran, Dictionary<IVariable, Expression> substMap) {
- Contract.Requires(m != null);
+ private void AddOverrideTerminationChk(ICallable original, ICallable overryd, Bpl.StmtListBuilder builder, ExpressionTranslator etran, Dictionary<IVariable, Expression> substMap) {
+ Contract.Requires(original != null);
+ Contract.Requires(overryd != null);
Contract.Requires(builder != null);
Contract.Requires(etran != null);
Contract.Requires(substMap != null);
// Note, it is as if the trait's method is calling the class's method.
- var contextDecreases = m.OverriddenMethod.Decreases.Expressions;
- var calleeDecreases = m.Decreases.Expressions;
+ var contextDecreases = overryd.Decreases.Expressions;
+ var calleeDecreases = original.Decreases.Expressions;
// We want to check: calleeDecreases <= contextDecreases (note, we can allow equality, since there is a bounded, namely 1, number of dynamic dispatches)
if (Contract.Exists(contextDecreases, e => e is WildcardExpr)) {
// no check needed
@@ -3378,7 +3457,7 @@ namespace Microsoft.Dafny {
N = i;
break;
}
- toks.Add(new NestedToken(m.tok, e1.tok));
+ toks.Add(new NestedToken(original.Tok, e1.tok));
types0.Add(e0.Type.NormalizeExpand());
types1.Add(e1.Type.NormalizeExpand());
callee.Add(etran.TrExpr(e0));
@@ -3411,7 +3490,7 @@ namespace Microsoft.Dafny {
// as "false".
bool allowNoChange = N == decrCountT && decrCountT <= decrCountC;
var decrChk = DecreasesCheck(toks, types0, types1, callee, caller, null, null, allowNoChange, false);
- builder.Add(Assert(m.tok, decrChk, "method's decreases clause must be below or equal to that in the trait"));
+ builder.Add(Assert(original.Tok, decrChk, string.Format("{0}'s decreases clause must be below or equal to that in the trait", original.WhatKind)));
}
private void AddMethodOverrideSubsetChk(Method m, Bpl.StmtListBuilder builder, ExpressionTranslator etran, List<Variable> localVariables, Dictionary<IVariable, Expression> substMap)
@@ -3515,10 +3594,13 @@ namespace Microsoft.Dafny {
private void InsertChecksum(Function f, Bpl.Declaration decl, bool specificationOnly = false)
{
+ Contract.Requires(f != null);
+ Contract.Requires(decl != null);
byte[] data;
using (var writer = new System.IO.StringWriter())
{
var printer = new Printer(writer);
+ writer.Write(f.IsGhost ? "function" : "function method");
printer.PrintAttributes(f.Attributes);
printer.PrintFormals(f.Formals);
writer.Write(": ");
@@ -3539,6 +3621,8 @@ namespace Microsoft.Dafny {
private void InsertChecksum(Bpl.Declaration decl, byte[] data)
{
+ Contract.Requires(decl != null);
+ Contract.Requires(data != null);
var md5 = System.Security.Cryptography.MD5.Create();
var hashedData = md5.ComputeHash(data);
var checksum = BitConverter.ToString(hashedData);
@@ -3615,9 +3699,9 @@ namespace Microsoft.Dafny {
Contract.Requires(tok != null);
Contract.Ensures(Contract.Result<Bpl.Cmd>() != null);
var col = tok.col + (isEndToken ? tok.val.Length : 0);
- string description = string.Format("{0}({1},{2}){3}{4}", tok.filename, tok.line, col, additionalInfo == null ? "" : ": ", additionalInfo ?? "");
+ string description = ErrorReporter.ErrorToString_Internal(additionalInfo == null ? "" : ": ", tok.filename, tok.line, col, additionalInfo ?? "");
QKeyValue kv = new QKeyValue(tok, "captureState", new List<object>() { description }, null);
- return new Bpl.AssumeCmd(tok, Bpl.Expr.True, kv);
+ return TrAssumeCmd(tok, Bpl.Expr.True, kv);
}
Bpl.Cmd CaptureState(Statement stmt) {
Contract.Requires(stmt != null);
@@ -3696,9 +3780,9 @@ namespace Microsoft.Dafny {
/// <summary>
/// Generates:
/// axiom (forall s, h0: HeapType, h1: HeapType, formals... ::
- /// { HeapSucc(h0,h1), F(s,h1,formals) }
+ /// { IsHeapAnchor(h0), HeapSucc(h0,h1), F(s,h1,formals) }
/// heaps are well-formed and formals are allocated AND
- /// HeapSucc(h0,h1)
+ /// IsHeapAnchor(h0) AND HeapSucc(h0,h1)
/// AND
/// (forall(alpha) o: ref, f: Field alpha ::
/// o != null AND h0[o,alloc] AND h1[o,alloc] AND
@@ -3730,7 +3814,7 @@ namespace Microsoft.Dafny {
var typeParams = TrTypeParamDecls(f.TypeArgs);
{
var formals = new List<Variable>();
- if (f.IsRecursive) {
+ if (f.IsFuelAware()) {
formals.Add(new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "$ly", predef.LayerType), true));
}
if (!f.IsStatic) {
@@ -3754,7 +3838,7 @@ namespace Microsoft.Dafny {
List<Bpl.Expr> argsF = new List<Bpl.Expr>();
List<Bpl.Expr> argsFFrame = new List<Bpl.Expr>();
List<Bpl.Expr> argsCanCall = new List<Bpl.Expr>();
- if (f.IsRecursive) {
+ if (f.IsFuelAware()) {
var sV = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$ly", predef.LayerType));
var s = new Bpl.IdentifierExpr(f.tok, sV);
bvars.Add(sV);
@@ -3817,7 +3901,8 @@ namespace Microsoft.Dafny {
Bpl.Expr oNotNullAlloced = Bpl.Expr.And(oNotNull, Bpl.Expr.And(etran0.IsAlloced(f.tok, o), etran1.IsAlloced(f.tok, o)));
Bpl.Expr unchanged = Bpl.Expr.Eq(ReadHeap(f.tok, h0, o, field), ReadHeap(f.tok, h1, o, field));
- Bpl.Expr heapSucc = FunctionCall(f.tok, BuiltinFunction.HeapSucc, null, h0, h1);
+ Bpl.Expr h0IsHeapAnchor = FunctionCall(h0.tok, BuiltinFunction.IsHeapAnchor, null, h0);
+ Bpl.Expr heapSucc = HeapSucc(h0, h1);
Bpl.Expr r0 = InRWClause(f.tok, o, field, f.Reads, etran0, null, null);
Bpl.Expr q0 = new Bpl.ForallExpr(f.tok, new List<TypeVariable> { alpha }, new List<Variable> { oVar, fieldVar },
Bpl.Expr.Imp(Bpl.Expr.And(oNotNullAlloced, r0), unchanged));
@@ -3828,7 +3913,7 @@ namespace Microsoft.Dafny {
var f1args = new List<Bpl.Expr>(tyexprs);
var f0argsCanCall = new List<Bpl.Expr>(tyexprs);
var f1argsCanCall = new List<Bpl.Expr>(tyexprs);
- if (f.IsRecursive) {
+ if (f.IsFuelAware()) {
Bpl.Expr s; var sV = BplBoundVar("$ly", predef.LayerType, out s);
bvars.Add(sV);
f0args.Add(s); f1args.Add(s); // but don't add to f0argsCanCall or f1argsCanCall
@@ -3877,11 +3962,11 @@ namespace Microsoft.Dafny {
var F0 = new Bpl.NAryExpr(f.tok, fn, f0args);
var F1 = new Bpl.NAryExpr(f.tok, fn, f1args);
var eq = Bpl.Expr.Eq(F0, F1);
- var tr = new Bpl.Trigger(f.tok, true, new List<Bpl.Expr> { heapSucc, F1 });
+ var tr = new Bpl.Trigger(f.tok, true, new List<Bpl.Expr> { h0IsHeapAnchor, heapSucc, F1 });
var typeParams = TrTypeParamDecls(f.TypeArgs);
var ax = new Bpl.ForallExpr(f.tok, typeParams, bvars, null, tr,
- Bpl.Expr.Imp(Bpl.Expr.And(wellFormed, heapSucc),
+ Bpl.Expr.Imp(Bpl.Expr.And(wellFormed, Bpl.Expr.And(h0IsHeapAnchor, heapSucc)),
Bpl.Expr.Imp(q0, eq)));
sink.AddTopLevelDeclaration(new Bpl.Axiom(f.tok, ax, comment));
#endif
@@ -3949,7 +4034,8 @@ namespace Microsoft.Dafny {
Bpl.Expr iBounds = InSeqRange(tok, i, etran.TrExpr(e), true, null, false);
Bpl.Expr XsubI = FunctionCall(tok, BuiltinFunction.SeqIndex, predef.BoxType, etran.TrExpr(e), i);
// TODO: the equality in the next line should be changed to one that understands extensionality
- disjunct = new Bpl.ExistsExpr(tok, new List<Variable> { iVar }, Bpl.Expr.And(iBounds, Bpl.Expr.Eq(XsubI, boxO)));
+ //TRIG (exists $i: int :: 0 <= $i && $i < Seq#Length(read($h0, this, _module.DoublyLinkedList.Nodes)) && Seq#Index(read($h0, this, _module.DoublyLinkedList.Nodes), $i) == $Box($o))
+ disjunct = new Bpl.ExistsExpr(tok, new List<Variable> { iVar }, Bpl.Expr.And(iBounds, Bpl.Expr.Eq(XsubI, boxO))); // LL_TRIGGER
} else {
// o == e
disjunct = Bpl.Expr.Eq(o, etran.TrExpr(e));
@@ -4003,7 +4089,7 @@ namespace Microsoft.Dafny {
foreach (Expression p in f.Ens) {
var functionHeight = currentModule.CallGraph.GetSCCRepresentativeId(f);
var splits = new List<SplitExprInfo>();
- bool splitHappened /*we actually don't care*/ = TrSplitExpr(p, splits, true, functionHeight, true, etran);
+ bool splitHappened /*we actually don't care*/ = TrSplitExpr(p, splits, true, functionHeight, true, true, etran);
foreach (var s in splits) {
if (s.IsChecked && !RefinementToken.IsInherited(s.E.tok, currentModule)) {
ens.Add(Ensures(s.E.tok, false, s.E, null, null));
@@ -4025,27 +4111,28 @@ namespace Microsoft.Dafny {
var implInParams = Bpl.Formal.StripWhereClauses(inParams);
var locals = new List<Variable>();
var builder = new Bpl.StmtListBuilder();
+ var builderInitializationArea = new Bpl.StmtListBuilder();
builder.Add(new CommentCmd("AddWellformednessCheck for function " + f));
builder.Add(CaptureState(f.tok, false, "initial state"));
DefineFrame(f.tok, f.Reads, builder, locals, null);
-
- // check well-formedness of the preconditions (including termination, and reads checks), and then
- // assume each one of them
- var wfo = new WFOptions(null, true, true /* do delayed reads checks over requires */);
-
- // check well-formedness of the reads clause
- CheckFrameWellFormed(wfo, f.Reads, locals, builder, etran);
-
- // check the reads of the preconditions now
- foreach (var a in wfo.Asserts) {
- builder.Add(a);
- }
-
+ InitializeFuelConstant(f.tok, builder, etran);
+ // Check well-formedness of the preconditions (including termination), and then
+ // assume each one of them. After all that (in particular, after assuming all
+ // of them), do the postponed reads checks.
+ var wfo = new WFOptions(null, true, true /* do delayed reads checks */);
foreach (Expression p in f.Req) {
- CheckWellformed(p, new WFOptions(null, true /* do reads checks */), locals, builder, etran);
- builder.Add(new Bpl.AssumeCmd(p.tok, etran.TrExpr(p)));
+ CheckWellformedAndAssume(p, wfo, locals, builder, etran);
}
+ wfo.ProcessSavedReadsChecks(locals, builderInitializationArea, builder);
+
+ // Check well-formedness of the reads clause. Note that this is done after assuming
+ // the preconditions. In other words, the well-formedness of the reads clause is
+ // allowed to assume the precondition (yet, the requires clause is checked to
+ // read only those things indicated in the reads clause).
+ wfo = new WFOptions(null, true, true /* do delayed reads checks */);
+ CheckFrameWellFormed(wfo, f.Reads, locals, builder, etran);
+ wfo.ProcessSavedReadsChecks(locals, builderInitializationArea, builder);
// check well-formedness of the decreases clauses (including termination, but no reads checks)
foreach (Expression p in f.Decreases.Expressions)
@@ -4068,8 +4155,8 @@ namespace Microsoft.Dafny {
foreach (var p in GetTypeParams(f)) {
args.Add(trTypeParam(p, null));
}
- if (f.IsRecursive) {
- args.Add(etran.LayerN(1));
+ if (f.IsFuelAware()) {
+ args.Add(etran.layerInterCluster.GetFunctionFuel(f));
}
args.Add(etran.HeapExpr);
if (!f.IsStatic) {
@@ -4083,28 +4170,27 @@ namespace Microsoft.Dafny {
var wh = GetWhereClause(f.tok, funcAppl, f.ResultType, etran);
if (wh != null) {
- postCheckBuilder.Add(new Bpl.AssumeCmd(f.tok, wh));
+ postCheckBuilder.Add(TrAssumeCmd(f.tok, wh));
}
}
// Now for the ensures clauses
foreach (Expression p in f.Ens) {
- CheckWellformed(p, new WFOptions(f, false), locals, postCheckBuilder, etran);
// assume the postcondition for the benefit of checking the remaining postconditions
- postCheckBuilder.Add(new Bpl.AssumeCmd(p.tok, etran.TrExpr(p)));
+ CheckWellformedAndAssume(p, new WFOptions(f, false), locals, postCheckBuilder, etran);
}
// Here goes the body (and include both termination checks and reads checks)
StmtListBuilder bodyCheckBuilder = new StmtListBuilder();
if (f.Body == null) {
// don't fall through to postcondition checks
- bodyCheckBuilder.Add(new Bpl.AssumeCmd(f.tok, Bpl.Expr.False));
+ bodyCheckBuilder.Add(TrAssumeCmd(f.tok, Bpl.Expr.False));
} else {
Bpl.FunctionCall funcID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, TrType(f.ResultType)));
List<Bpl.Expr> args = new List<Bpl.Expr>();
foreach (var p in GetTypeParams(f)) {
args.Add(trTypeParam(p, null));
}
- if (f.IsRecursive) {
- args.Add(etran.LayerN(1));
+ if (f.IsFuelAware()) {
+ args.Add(etran.layerInterCluster.GetFunctionFuel(f));
}
args.Add(etran.HeapExpr);
foreach (Variable p in implInParams) {
@@ -4119,22 +4205,20 @@ namespace Microsoft.Dafny {
* makes reads clauses also guard the requires */
, null);
- CheckWellformedWithResult(f.Body, new WFOptions(null, true), funcAppl, f.ResultType, locals, bodyCheckBuilder, etran);
+ wfo = new WFOptions(null, true, true /* do delayed reads checks */);
+ CheckWellformedWithResult(f.Body, wfo, funcAppl, f.ResultType, locals, bodyCheckBuilder, etran);
+ wfo.ProcessSavedReadsChecks(locals, builderInitializationArea, bodyCheckBuilder);
}
// Combine the two, letting the postcondition be checked on after the "bodyCheckBuilder" branch
- postCheckBuilder.Add(new Bpl.AssumeCmd(f.tok, Bpl.Expr.False));
+ postCheckBuilder.Add(TrAssumeCmd(f.tok, Bpl.Expr.False));
builder.Add(new Bpl.IfCmd(f.tok, null, postCheckBuilder.Collect(f.tok), null, bodyCheckBuilder.Collect(f.tok)));
- // var b$reads_guards_requires#0 : bool
- locals.AddRange(wfo.Locals);
- // This ugly way seems to be the way to add things at the start of a builder:
- StmtList sl = builder.Collect(f.tok);
- // b$reads_guards_requires#0 := true ...
- sl.BigBlocks[0].simpleCmds.InsertRange(0, wfo.AssignLocals);
-
+ var s0 = builderInitializationArea.Collect(f.tok);
+ var s1 = builder.Collect(f.tok);
+ var implBody = new StmtList(new List<BigBlock>(s0.BigBlocks.Concat(s1.BigBlocks)), f.tok);
Bpl.Implementation impl = new Bpl.Implementation(f.tok, proc.Name,
typeParams, Concat(typeInParams, implInParams), new List<Variable>(),
- locals, sl, etran.TrAttributes(f.Attributes, null));
+ locals, implBody, etran.TrAttributes(f.Attributes, null));
sink.AddTopLevelDeclaration(impl);
if (InsertChecksums)
@@ -4241,7 +4325,7 @@ namespace Microsoft.Dafny {
Type t = mc.Ctor.Formals[i].Type;
Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, local), p.Type, etran);
if (wh != null) {
- localTypeAssumptions.Add(new Bpl.AssumeCmd(p.tok, wh));
+ localTypeAssumptions.Add(TrAssumeCmd(p.tok, wh));
}
args.Add(CondApplyBox(mc.tok, new Bpl.IdentifierExpr(p.tok, local), cce.NonNull(p.Type), t));
}
@@ -4293,12 +4377,7 @@ namespace Microsoft.Dafny {
return CanCallAssumption(l, etran);
} else if (expr is MemberSelectExpr) {
MemberSelectExpr e = (MemberSelectExpr)expr;
- Bpl.Expr r;
- if (e.Obj is ThisExpr) {
- r = Bpl.Expr.True;
- } else {
- r = CanCallAssumption(e.Obj, etran);
- }
+ var r = CanCallAssumption(e.Obj, etran);
if (e.Member is DatatypeDestructor) {
var dtor = (DatatypeDestructor)e.Member;
if (dtor.EnclosingCtor.EnclosingDatatype.Ctors.Count == 1) {
@@ -4311,10 +4390,7 @@ namespace Microsoft.Dafny {
} else if (expr is SeqSelectExpr) {
SeqSelectExpr e = (SeqSelectExpr)expr;
Bpl.Expr total = CanCallAssumption(e.Seq, etran);
- Bpl.Expr seq = etran.TrExpr(e.Seq);
- Bpl.Expr e0 = null;
if (e.E0 != null) {
- e0 = etran.TrExpr(e.E0);
total = BplAnd(total, CanCallAssumption(e.E0, etran));
}
if (e.E1 != null) {
@@ -4335,8 +4411,6 @@ namespace Microsoft.Dafny {
return CanCallAssumption(e.ResolvedUpdateExpr, etran);
}
Bpl.Expr total = CanCallAssumption(e.Seq, etran);
- Bpl.Expr seq = etran.TrExpr(e.Seq);
- Bpl.Expr index = etran.TrExpr(e.Index);
total = BplAnd(total, CanCallAssumption(e.Index, etran));
total = BplAnd(total, CanCallAssumption(e.Value, etran));
return total;
@@ -4347,18 +4421,13 @@ namespace Microsoft.Dafny {
e.Args.ConvertAll(ee => CanCallAssumption(ee, etran))));
} else if (expr is FunctionCallExpr) {
FunctionCallExpr e = (FunctionCallExpr)expr;
- // check well-formedness of receiver
Bpl.Expr r = CanCallAssumption(e.Receiver, etran);
- // check well-formedness of the other parameters
r = BplAnd(r, CanCallAssumption(e.Args, etran));
- // if (e.Name != "requires" && e.Name != "reads") {
- Contract.Assert(e.Function != null); // follows from the fact that expr has been successfully resolved
- // get to assume canCall
- Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(expr.tok, e.Function.FullSanitizedName + "#canCall", Bpl.Type.Bool);
- List<Bpl.Expr> args = etran.FunctionInvocationArguments(e, null);
- Bpl.Expr canCallFuncAppl = new Bpl.NAryExpr(expr.tok, new Bpl.FunctionCall(canCallFuncID), args);
- r = BplAnd(r, canCallFuncAppl);
- // }
+ // get to assume canCall
+ Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(expr.tok, e.Function.FullSanitizedName + "#canCall", Bpl.Type.Bool);
+ List<Bpl.Expr> args = etran.FunctionInvocationArguments(e, null);
+ Bpl.Expr canCallFuncAppl = new Bpl.NAryExpr(expr.tok, new Bpl.FunctionCall(canCallFuncID), args);
+ r = BplAnd(r, canCallFuncAppl);
return r;
} else if (expr is DatatypeValue) {
DatatypeValue dtv = (DatatypeValue)expr;
@@ -4379,10 +4448,10 @@ namespace Microsoft.Dafny {
switch (e.ResolvedOp) {
case BinaryExpr.ResolvedOpcode.And:
case BinaryExpr.ResolvedOpcode.Imp:
- t1 = Bpl.Expr.Imp(etran.TrExpr(e.E0), t1);
+ t1 = BplImp(etran.TrExpr(e.E0), t1);
break;
case BinaryExpr.ResolvedOpcode.Or:
- t1 = Bpl.Expr.Imp(Bpl.Expr.Not(etran.TrExpr(e.E0)), t1);
+ t1 = BplImp(Bpl.Expr.Not(etran.TrExpr(e.E0)), t1);
break;
default:
break;
@@ -4413,20 +4482,23 @@ namespace Microsoft.Dafny {
var canCallBody = CanCallAssumption(Substitute(e.Body, null, substMap), etran);
return BplAnd(canCallRHS, canCallBody);
} else {
- // CanCall[[ var b :| RHS(b,g); Body(b,g,h) ]] =
- // (forall b :: typeAntecedent ==>
- // CanCall[[ RHS(b,g) ]] &&
- // (RHS(b,g) ==> CanCall[[ Body(b,g,h) ]]) &&
- // $let$canCall(b,g))
- var bvars = new List<Variable>();
- Bpl.Expr typeAntecedent = etran.TrBoundVariables(e.BoundVars.ToList<BoundVar>(), bvars);
- Contract.Assert(e.RHSs.Count == 1); // this is true of all successfully resolved let-such-that expressions
- var canCallRHS = CanCallAssumption(e.RHSs[0], etran);
- var canCallBody = Bpl.Expr.Imp(etran.TrExpr(e.RHSs[0]), CanCallAssumption(e.Body, etran));
- var d = LetDesugaring(e); // call LetDesugaring to prepare the desugaring and populate letSuchThatExprInfo with something for e
+ // CanCall[[ var b0,b1 :| RHS(b0,b1,g); Body(b0,b1,g,h) ]] =
+ // $let$canCall(g) &&
+ // CanCall[[ Body($let$b0(g), $let$b1(g), h) ]]
+ LetDesugaring(e); // call LetDesugaring to prepare the desugaring and populate letSuchThatExprInfo with something for e
var info = letSuchThatExprInfo[e];
- var canCallFunction = info.CanCallFunctionCall(this, etran);
- var cc = new Bpl.ForallExpr(e.tok, bvars, Bpl.Expr.Imp(typeAntecedent, BplAnd(BplAnd(canCallRHS, canCallBody), canCallFunction)));
+ // $let$canCall(g)
+ var canCall = info.CanCallFunctionCall(this, etran);
+ Dictionary<IVariable, Expression> substMap = new Dictionary<IVariable, Expression>();
+ foreach (var bv in e.BoundVars) {
+ // create a call to $let$x(g)
+ var args = info.SkolemFunctionArgs(bv, this, etran);
+ var call = new BoogieFunctionCall(bv.tok, info.SkolemFunctionName(bv), info.UsesHeap, info.UsesOldHeap, args.Item1, args.Item2);
+ call.Type = bv.Type;
+ substMap.Add(bv, call);
+ }
+ var p = Substitute(e.Body, null, substMap);
+ var cc = BplAnd(canCall, CanCallAssumption(p, etran));
return cc;
}
@@ -4436,45 +4508,64 @@ namespace Microsoft.Dafny {
if (e.Contract != null)
return BplAnd(canCall, CanCallAssumption(e.Contract, etran));
else return canCall;
+
} else if (expr is LambdaExpr) {
var e = (LambdaExpr)expr;
- List<Bpl.Variable> bvars = new List<Bpl.Variable>();
-
+ var bvarsAndAntecedents = new List<Tuple<Bpl.Variable, Bpl.Expr>>();
var varNameGen = CurrentIdGenerator.NestedFreshIdGenerator("$l#");
Bpl.Expr heap; var hVar = BplBoundVar(varNameGen.FreshId("#heap#"), predef.HeapType, out heap);
- bvars.Add(hVar);
+ var et = new ExpressionTranslator(etran, heap);
Dictionary<IVariable, Expression> subst = new Dictionary<IVariable,Expression>();
foreach (var bv in e.BoundVars) {
Bpl.Expr ve; var yVar = BplBoundVar(varNameGen.FreshId(string.Format("#{0}#", bv.Name)), TrType(bv.Type), out ve);
- bvars.Add(yVar);
+ var wh = GetWhereClause(bv.tok, new Bpl.IdentifierExpr(bv.tok, yVar), bv.Type, et);
+ bvarsAndAntecedents.Add(Tuple.Create<Bpl.Variable, Bpl.Expr>(yVar, wh));
subst[bv] = new BoogieWrapper(ve, bv.Type);
}
- ExpressionTranslator et = new ExpressionTranslator(etran, heap);
- var ebody = CanCallAssumption(Substitute(e.Body, null, subst), et);
- return BplForall(bvars, ebody);
+ var canCall = CanCallAssumption(Substitute(e.Body, null, subst), et);
+ if (e.Range != null) {
+ var range = Substitute(e.Range, null, subst);
+ canCall = BplAnd(CanCallAssumption(range, etran), BplImp(etran.TrExpr(range), canCall));
+ }
+
+ // It's important to add the heap last to "bvarsAndAntecedents", because the heap may occur in the antecedents of
+ // the other variables and BplForallTrim processes the given tuples in order.
+ var goodHeap = FunctionCall(e.tok, BuiltinFunction.IsGoodHeap, null, heap);
+ bvarsAndAntecedents.Add(Tuple.Create<Bpl.Variable, Bpl.Expr>(hVar, goodHeap));
+
+ //TRIG (forall $l#0#heap#0: Heap, $l#0#x#0: int :: true)
+ //TRIG (forall $l#0#heap#0: Heap, $l#0#t#0: DatatypeType :: _module.__default.TMap#canCall(_module._default.TMap$A, _module._default.TMap$B, $l#0#heap#0, $l#0#t#0, f#0))
+ //TRIG (forall $l#4#heap#0: Heap, $l#4#x#0: Box :: _0_Monad.__default.Bind#canCall(Monad._default.Associativity$B, Monad._default.Associativity$C, $l#4#heap#0, Apply1(Monad._default.Associativity$A, #$M$B, f#0, $l#4#heap#0, $l#4#x#0), g#0))
+ return BplForallTrim(bvarsAndAntecedents, null, canCall); // L_TRIGGER
+
} else if (expr is ComprehensionExpr) {
var e = (ComprehensionExpr)expr;
- var canCall = CanCallAssumption(e.Term, etran);
var q = e as QuantifierExpr;
- var tyvars = MkTyParamBinders(q != null ? q.TypeArgs : new List<TypeParameter>());
+ if (q != null && q.SplitQuantifier != null) {
+ return CanCallAssumption(q.SplitQuantifierExpression, etran);
+ }
+
+ // Determine the CanCall's for the range and term
+ var canCall = CanCallAssumption(e.Term, etran);
if (e.Range != null) {
canCall = BplAnd(CanCallAssumption(e.Range, etran), BplImp(etran.TrExpr(e.Range), canCall));
}
- if (canCall != Bpl.Expr.True) {
- List<Variable> bvars = new List<Variable>();
- Bpl.Expr typeAntecedent = etran.TrBoundVariables(e.BoundVars, bvars);
- if (Attributes.Contains(e.Attributes, "trigger")) {
- Bpl.Trigger tr = TrTrigger(etran, e.Attributes, expr.tok);
- canCall = new Bpl.ForallExpr(expr.tok, Concat(tyvars, bvars), tr, Bpl.Expr.Imp(typeAntecedent, canCall));
- } else {
- canCall = new Bpl.ForallExpr(expr.tok, Concat(tyvars, bvars), Bpl.Expr.Imp(typeAntecedent, canCall));
+ // Create a list of all possible bound variables
+ var bvarsAndAntecedents = etran.TrBoundVariables_SeparateWhereClauses(e.BoundVars);
+ if (q != null) {
+ var tyvars = MkTyParamBinders(q.TypeArgs);
+ foreach (var tv in tyvars) {
+ bvarsAndAntecedents.Add(Tuple.Create<Bpl.Variable, Bpl.Expr>(tv, null));
}
}
- return canCall;
+ // Produce the quantified CanCall expression, with a suitably reduced set of bound variables
+ var tr = TrTrigger(etran, e.Attributes, expr.tok);
+ return BplForallTrim(bvarsAndAntecedents, tr, canCall);
+
} else if (expr is StmtExpr) {
var e = (StmtExpr)expr;
return CanCallAssumption(e.E, etran);
@@ -4482,8 +4573,8 @@ namespace Microsoft.Dafny {
ITEExpr e = (ITEExpr)expr;
Bpl.Expr total = CanCallAssumption(e.Test, etran);
Bpl.Expr test = etran.TrExpr(e.Test);
- total = BplAnd(total, Bpl.Expr.Imp(test, CanCallAssumption(e.Thn, etran)));
- total = BplAnd(total, Bpl.Expr.Imp(Bpl.Expr.Not(test), CanCallAssumption(e.Els, etran)));
+ total = BplAnd(total, BplImp(test, CanCallAssumption(e.Thn, etran)));
+ total = BplAnd(total, BplImp(Bpl.Expr.Not(test), CanCallAssumption(e.Els, etran)));
return total;
} else if (expr is ConcreteSyntaxExpression) {
var e = (ConcreteSyntaxExpression)expr;
@@ -4541,7 +4632,7 @@ namespace Microsoft.Dafny {
var correctConstructor = FunctionCall(pat.tok, ctor.QueryField.FullSanitizedName, Bpl.Type.Bool, rhs);
if (ctor.EnclosingDatatype.Ctors.Count == 1) {
// There is only one constructor, so the value must have been constructed by it; might as well assume that here.
- builder.Add(new Bpl.AssumeCmd(pat.tok, correctConstructor));
+ builder.Add(TrAssumeCmd(pat.tok, correctConstructor));
} else {
builder.Add(Assert(pat.tok, correctConstructor, string.Format("RHS is not certain to look like the pattern '{0}'", ctor.Name)));
}
@@ -4588,7 +4679,11 @@ namespace Microsoft.Dafny {
/// like it. This is useful in function postconditions, where the result of the function is
/// syntactically given as what looks like a recursive call with the same arguments.
/// "DoReadsChecks" indicates whether or not to perform reads checks. If so, the generated code
- /// will make references to $_Frame.
+ /// will make references to $_Frame. If "saveReadsChecks" is true, then the reads checks will
+ /// be recorded but postponsed. In particular, CheckWellformed will append to .Locals a list of
+ /// fresh local variables and will append to .Assert assertions with appropriate error messages
+ /// that can be used later. As a convenience, the ProcessSavedReadsChecks will make use of .Locals
+ /// and .Asserts (and AssignLocals) and update a given StmtListBuilder.
/// </summary>
private class WFOptions
{
@@ -4602,6 +4697,7 @@ namespace Microsoft.Dafny {
}
public WFOptions(Function selfCallsAllowance, bool doReadsChecks, bool saveReadsChecks = false) {
+ Contract.Requires(!saveReadsChecks || doReadsChecks); // i.e., saveReadsChecks ==> doReadsChecks
SelfCallsAllowance = selfCallsAllowance;
DoReadsChecks = doReadsChecks;
if (saveReadsChecks) {
@@ -4635,6 +4731,24 @@ namespace Microsoft.Dafny {
);
}
}
+
+ public void ProcessSavedReadsChecks(List<Variable> locals, StmtListBuilder builderInitializationArea, StmtListBuilder builder) {
+ Contract.Requires(locals != null);
+ Contract.Requires(builderInitializationArea != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(Locals != null && Asserts != null); // ProcessSavedReadsChecks should be called only if the constructor was called with saveReadsChecks
+
+ // var b$reads_guards#0 : bool ...
+ locals.AddRange(Locals);
+ // b$reads_guards#0 := true ...
+ foreach (var cmd in AssignLocals) {
+ builderInitializationArea.Add(cmd);
+ }
+ // assert b$reads_guards#0; ...
+ foreach (var a in Asserts) {
+ builder.Add(a);
+ }
+ }
}
void TrStmt_CheckWellformed(Expression expr, Bpl.StmtListBuilder builder, List<Variable> locals, ExpressionTranslator etran, bool subsumption) {
@@ -4642,6 +4756,7 @@ namespace Microsoft.Dafny {
Contract.Requires(builder != null);
Contract.Requires(locals != null);
Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
Bpl.QKeyValue kv;
if (subsumption) {
@@ -4653,10 +4768,121 @@ namespace Microsoft.Dafny {
kv = new Bpl.QKeyValue(expr.tok, "subsumption", args, null);
}
CheckWellformed(expr, new WFOptions(kv), locals, builder, etran);
- builder.Add(new Bpl.AssumeCmd(expr.tok, CanCallAssumption(expr, etran)));
+ builder.Add(TrAssumeCmd(expr.tok, CanCallAssumption(expr, etran)));
+ }
+
+ void CheckWellformedAndAssume(Expression expr, WFOptions options, List<Variable> locals, Bpl.StmtListBuilder builder, ExpressionTranslator etran) {
+ Contract.Requires(expr != null);
+ Contract.Requires(expr.Type != null && expr.Type.IsBoolType);
+ Contract.Requires(options != null);
+ Contract.Requires(locals != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
+ if (expr is BinaryExpr) {
+ var e = (BinaryExpr)expr;
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.And:
+ // WF[e0]; assume e0; WF[e1]; assume e1;
+ CheckWellformedAndAssume(e.E0, options, locals, builder, etran);
+ CheckWellformedAndAssume(e.E1, options, locals, builder, etran);
+ return;
+ case BinaryExpr.ResolvedOpcode.Imp: {
+ // if (*) {
+ // WF[e0]; assume e0; WF[e1]; assume e1;
+ // } else {
+ // assume e0 ==> e1;
+ // }
+ var bAnd = new Bpl.StmtListBuilder();
+ CheckWellformedAndAssume(e.E0, options, locals, bAnd, etran);
+ CheckWellformedAndAssume(e.E1, options, locals, bAnd, etran);
+ var bImp = new Bpl.StmtListBuilder();
+ bImp.Add(TrAssumeCmd(expr.tok, etran.TrExpr(expr)));
+ builder.Add(new Bpl.IfCmd(expr.tok, null, bAnd.Collect(expr.tok), null, bImp.Collect(expr.tok)));
+ }
+ return;
+ case BinaryExpr.ResolvedOpcode.Or: {
+ // if (*) {
+ // WF[e0]; assume e0;
+ // } else {
+ // assume !e0;
+ // WF[e1]; assume e1;
+ // }
+ var b0 = new Bpl.StmtListBuilder();
+ CheckWellformedAndAssume(e.E0, options, locals, b0, etran);
+ var b1 = new Bpl.StmtListBuilder();
+ b1.Add(TrAssumeCmd(expr.tok, Bpl.Expr.Not(etran.TrExpr(e.E0))));
+ CheckWellformedAndAssume(e.E1, options, locals, b1, etran);
+ builder.Add(new Bpl.IfCmd(expr.tok, null, b0.Collect(expr.tok), null, b1.Collect(expr.tok)));
+ }
+ return;
+ default:
+ break;
+ }
+ } else if (expr is ITEExpr) {
+ var e = (ITEExpr)expr;
+ // if (*) {
+ // WF[test]; assume test;
+ // WF[thn]; assume thn;
+ // } else {
+ // assume !test;
+ // WF[els]; assume els;
+ // }
+ var bThn = new Bpl.StmtListBuilder();
+ CheckWellformedAndAssume(e.Test, options, locals, bThn, etran);
+ CheckWellformedAndAssume(e.Thn, options, locals, bThn, etran);
+ var bEls = new Bpl.StmtListBuilder();
+ bEls.Add(TrAssumeCmd(expr.tok, Bpl.Expr.Not(etran.TrExpr(e.Test))));
+ CheckWellformedAndAssume(e.Els, options, locals, bEls, etran);
+ builder.Add(new Bpl.IfCmd(expr.tok, null, bThn.Collect(expr.tok), null, bEls.Collect(expr.tok)));
+ return;
+ } else if (expr is QuantifierExpr) {
+ var e = (QuantifierExpr)expr;
+ // For (Q x :: body(x)), introduce fresh local variable x'. Then:
+ // havoc x'
+ // WF[body(x')]; assume body(x');
+ // If the quantifier is universal, then continue as:
+ // assume (\forall x :: body(x));
+ // Create local variables corresponding to the type arguments:
+
+ var typeArgumentCopies = Map(e.TypeArgs, tp => e.Refresh(tp, CurrentIdGenerator));
+ var typeMap = Util.Dict(e.TypeArgs, Map(typeArgumentCopies, tp => (Type)new UserDefinedType(tp)));
+ var newLocals = Map(typeArgumentCopies, tp => new Bpl.LocalVariable(tp.tok, new TypedIdent(tp.tok, nameTypeParam(tp), predef.Ty)));
+ locals.AddRange(newLocals);
+ // Create local variables corresponding to the bound variables:
+ var substMap = SetupBoundVarsAsLocals(e.BoundVars, builder, locals, etran, typeMap);
+ // Get the body of the quantifier and suitably substitute for the type variables and bound variables
+ var body = Substitute(e.LogicalBody(true), null, substMap, typeMap);
+ CheckWellformedAndAssume(body, options, locals, builder, etran);
+
+ if (e is ForallExpr) {
+ // Although we do the WF check on the original quantifier, we assume the split one.
+ // This ensures that cases like forall x :: x != null && f(x.a) do not fail to verify.
+ builder.Add(TrAssumeCmd(expr.tok, etran.TrExpr(e.SplitQuantifierExpression ?? e)));
+ }
+ return;
+ }
+
+ // resort to the behavior of simply checking well-formedness followed by assuming the translated expression
+ CheckWellformed(expr, options, locals, builder, etran);
+
+ // NOTE: If the CheckWellformed call above found a split quantifier, it ignored
+ // the splitting and proceeded to decompose the full quantifier as
+ // normal. This call to TrExpr, on the other hand, will indeed use the
+ // split quantifier.
+ builder.Add(TrAssumeCmd(expr.tok, etran.TrExpr(expr)));
}
+ /// <summary>
+ /// Check the well-formedness of "expr" (but don't leave hanging around any assumptions that affect control flow)
+ /// </summary>
void CheckWellformed(Expression expr, WFOptions options, List<Variable> locals, Bpl.StmtListBuilder builder, ExpressionTranslator etran) {
+ Contract.Requires(expr != null);
+ Contract.Requires(options != null);
+ Contract.Requires(locals != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
CheckWellformedWithResult(expr, options, null, null, locals, builder, etran);
}
@@ -4703,7 +4929,7 @@ namespace Microsoft.Dafny {
var correctConstructor = FunctionCall(e.tok, dtor.EnclosingCtor.QueryField.FullSanitizedName, Bpl.Type.Bool, etran.TrExpr(e.Obj));
if (dtor.EnclosingCtor.EnclosingDatatype.Ctors.Count == 1) {
// There is only one constructor, so the value must be been constructed by it; might as well assume that here.
- builder.Add(new Bpl.AssumeCmd(expr.tok, correctConstructor));
+ builder.Add(TrAssumeCmd(expr.tok, correctConstructor));
} else {
builder.Add(Assert(expr.tok, correctConstructor,
string.Format("destructor '{0}' can only be applied to datatype values constructed by '{1}'", dtor.Name, dtor.EnclosingCtor.Name)));
@@ -4759,7 +4985,8 @@ namespace Microsoft.Dafny {
var range = BplAnd(Bpl.Expr.Le(lowerBound, i), Bpl.Expr.Lt(i, upperBound));
var fieldName = FunctionCall(e.tok, BuiltinFunction.IndexField, null, i);
var allowedToRead = Bpl.Expr.SelectTok(e.tok, etran.TheFrame(e.tok), seq, fieldName);
- var qq = new Bpl.ForallExpr(e.tok, new List<Variable> { iVar }, Bpl.Expr.Imp(range, allowedToRead));
+ var trigger = BplTrigger(allowedToRead); // Note, the assertion we're about to produce only seems useful in the check-only mode (that is, with subsumption 0), but if it were to be assumed, we'll use this entire RHS as the trigger
+ var qq = new Bpl.ForallExpr(e.tok, new List<Variable> { iVar }, trigger, BplImp(range, allowedToRead));
options.AssertSink(this, builder)(expr.tok, qq, "insufficient reads clause to read the indicated range of array elements", options.AssertKv);
}
}
@@ -4767,16 +4994,17 @@ namespace Microsoft.Dafny {
MultiSelectExpr e = (MultiSelectExpr)expr;
CheckWellformed(e.Array, options, locals, builder, etran);
Bpl.Expr array = etran.TrExpr(e.Array);
- int i = 0;
- foreach (Expression idx in e.Indices) {
+ for (int idxId = 0; idxId < e.Indices.Count; idxId++) {
+ var idx = e.Indices[idxId];
CheckWellformed(idx, options, locals, builder, etran);
- Bpl.Expr index = etran.TrExpr(idx);
- Bpl.Expr lower = Bpl.Expr.Le(Bpl.Expr.Literal(0), index);
- Bpl.Expr length = ArrayLength(idx.tok, array, e.Indices.Count, i);
- Bpl.Expr upper = Bpl.Expr.Lt(index, length);
- builder.Add(Assert(idx.tok, Bpl.Expr.And(lower, upper), "index " + i + " out of range", options.AssertKv));
- i++;
+ var index = etran.TrExpr(idx);
+ var lower = Bpl.Expr.Le(Bpl.Expr.Literal(0), index);
+ var length = ArrayLength(idx.tok, array, e.Indices.Count, idxId);
+ var upper = Bpl.Expr.Lt(index, length);
+ var tok = idx is IdentifierExpr ? e.tok : idx.tok; // TODO: Reusing the token of an identifier expression would underline its definition. but this is still not perfect.
+
+ builder.Add(Assert(tok, Bpl.Expr.And(lower, upper), String.Format("index {0} out of range", idxId), options.AssertKv));
}
} else if (expr is SeqUpdateExpr) {
SeqUpdateExpr e = (SeqUpdateExpr)expr;
@@ -4853,7 +5081,7 @@ namespace Microsoft.Dafny {
builder.Add(Assert(expr.tok, precond, "possible violation of function precondition"));
if (options.DoReadsChecks) {
- Type objset = new SetType(new ObjectType());
+ Type objset = new SetType(true, new ObjectType());
Expression wrap = new BoogieWrapper(
FunctionCall(e.tok, Reads(arity), TrType(objset), args),
objset);
@@ -4894,8 +5122,11 @@ namespace Microsoft.Dafny {
CheckSubrange(ee.tok, etran.TrExpr(ee), et, builder);
Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(p.tok, lhs, CondApplyBox(p.tok, etran.TrExpr(ee), cce.NonNull(ee.Type), et));
builder.Add(cmd);
- builder.Add(new Bpl.CommentCmd("assume allocatedness for argument to function"));
- builder.Add(new Bpl.AssumeCmd(e.Args[i].tok, MkIsAlloc(lhs, et, etran.HeapExpr)));
+ if (!etran.UsesOldHeap) {
+ // the argument can't be assumed to be allocated for the old heap
+ builder.Add(new Bpl.CommentCmd("assume allocatedness for argument to function"));
+ builder.Add(TrAssumeCmd(e.Args[i].tok, MkIsAlloc(lhs, et, etran.HeapExpr)));
+ }
}
// Check that every parameter is available in the state in which the function is invoked; this means checking that it has
// the right type and is allocated. These checks usually hold trivially, on account of that the Dafny language only gives
@@ -4933,7 +5164,7 @@ namespace Microsoft.Dafny {
}
if (options.AssertKv == null) {
// assume only if no given assert attribute is given
- builder.Add(new Bpl.AssumeCmd(expr.tok, etran.TrExpr(precond)));
+ builder.Add(TrAssumeCmd(expr.tok, etran.TrExpr(precond)));
}
}
if (options.DoReadsChecks) {
@@ -4985,8 +5216,8 @@ namespace Microsoft.Dafny {
Contract.Assert(false); // unexpected CoCallResolution
goto case FunctionCallExpr.CoCallResolution.No; // please the compiler
}
- CheckCallTermination(expr.tok, contextDecreases, calleeDecreases, allowance, e.Receiver, substMap, etran, etran, builder,
- codeContext.InferredDecreases, hint);
+ CheckCallTermination(expr.tok, contextDecreases, calleeDecreases, allowance, e.Receiver, substMap, e.TypeArgumentSubstitutions,
+ etran, etran, builder, codeContext.InferredDecreases, hint);
}
}
@@ -4994,7 +5225,7 @@ namespace Microsoft.Dafny {
Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(expr.tok, e.Function.FullSanitizedName + "#canCall", Bpl.Type.Bool);
List<Bpl.Expr> args = etran.FunctionInvocationArguments(e, null);
Bpl.Expr canCallFuncAppl = new Bpl.NAryExpr(expr.tok, new Bpl.FunctionCall(canCallFuncID), args);
- builder.Add(new Bpl.AssumeCmd(expr.tok, allowance == null ? canCallFuncAppl : Bpl.Expr.Or(allowance, canCallFuncAppl)));
+ builder.Add(TrAssumeCmd(expr.tok, allowance == null ? canCallFuncAppl : Bpl.Expr.Or(allowance, canCallFuncAppl)));
} else if (expr is DatatypeValue) {
DatatypeValue dtv = (DatatypeValue)expr;
@@ -5091,10 +5322,11 @@ namespace Microsoft.Dafny {
var rIe = new Bpl.IdentifierExpr(pat.tok, r);
CheckWellformedWithResult(e.RHSs[i], options, rIe, pat.Expr.Type, locals, builder, etran);
CheckCasePatternShape(pat, rIe, builder);
- builder.Add(new Bpl.AssumeCmd(pat.tok, Bpl.Expr.Eq(etran.TrExpr(Substitute(pat.Expr, null, substMap)), rIe)));
+ builder.Add(TrAssumeCmd(pat.tok, Bpl.Expr.Eq(etran.TrExpr(Substitute(pat.Expr, null, substMap)), rIe)));
}
CheckWellformedWithResult(Substitute(e.Body, null, substMap), options, result, resultType, locals, builder, etran);
result = null;
+
} else {
// CheckWellformed(var b :| RHS(b); Body(b)) =
// var b where typeAntecedent;
@@ -5121,7 +5353,7 @@ namespace Microsoft.Dafny {
w = BplOr(body, w);
}
builder.Add(Assert(e.tok, w, "cannot establish the existence of LHS values that satisfy the such-that predicate"));
- builder.Add(new Bpl.AssumeCmd(e.tok, etran.TrExpr(rhs)));
+ builder.Add(TrAssumeCmd(e.tok, etran.TrExpr(rhs)));
var letBody = Substitute(e.Body, null, substMap);
CheckWellformed(letBody, options, locals, builder, etran);
if (e.Constraint_Bounds != null) {
@@ -5129,9 +5361,9 @@ namespace Microsoft.Dafny {
var substMap_prime = SetupBoundVarsAsLocals(lhsVars, builder, locals, etran);
var rhs_prime = Substitute(e.RHSs[0], null, substMap_prime);
var letBody_prime = Substitute(e.Body, null, substMap_prime);
- builder.Add(new Bpl.AssumeCmd(e.tok, CanCallAssumption(rhs_prime, etran)));
- builder.Add(new Bpl.AssumeCmd(e.tok, etran.TrExpr(rhs_prime)));
- builder.Add(new Bpl.AssumeCmd(e.tok, CanCallAssumption(letBody_prime, etran)));
+ builder.Add(TrAssumeCmd(e.tok, CanCallAssumption(rhs_prime, etran)));
+ builder.Add(TrAssumeCmd(e.tok, etran.TrExpr(rhs_prime)));
+ builder.Add(TrAssumeCmd(e.tok, CanCallAssumption(letBody_prime, etran)));
var eq = Expression.CreateEq(letBody, letBody_prime, e.Body.Type);
builder.Add(Assert(e.tok, etran.TrExpr(eq), "to be compilable, the value of a let-such-that expression must be uniquely determined"));
}
@@ -5140,11 +5372,11 @@ namespace Microsoft.Dafny {
Contract.Assert(resultType != null);
var bResult = etran.TrExpr(letBody);
CheckSubrange(letBody.tok, bResult, resultType, builder);
- builder.Add(new Bpl.AssumeCmd(letBody.tok, Bpl.Expr.Eq(result, bResult)));
- builder.Add(new Bpl.AssumeCmd(letBody.tok, CanCallAssumption(letBody, etran)));
+ builder.Add(TrAssumeCmd(letBody.tok, Bpl.Expr.Eq(result, bResult)));
+ builder.Add(TrAssumeCmd(letBody.tok, CanCallAssumption(letBody, etran)));
builder.Add(new CommentCmd("CheckWellformedWithResult: Let expression"));
- builder.Add(new Bpl.AssumeCmd(letBody.tok, MkIsAlloc(result, resultType, etran.HeapExpr)));
- builder.Add(new Bpl.AssumeCmd(letBody.tok, MkIs(result, resultType)));
+ builder.Add(TrAssumeCmd(letBody.tok, MkIsAlloc(result, resultType, etran.HeapExpr)));
+ builder.Add(TrAssumeCmd(letBody.tok, MkIs(result, resultType)));
result = null;
}
}
@@ -5162,6 +5394,9 @@ namespace Microsoft.Dafny {
var q = e as QuantifierExpr;
var lam = e as LambdaExpr;
+ // This is a WF check, so we look at the original quantifier, not the split one.
+ // This ensures that cases like forall x :: x != null && f(x.a) do not fail to verify.
+
var typeMap = new Dictionary<TypeParameter, Type>();
var copies = new List<TypeParameter>();
if (q != null) {
@@ -5179,7 +5414,6 @@ namespace Microsoft.Dafny {
var newEtran = etran;
builder.Add(new Bpl.CommentCmd("Begin Comprehension WF check"));
BplIfIf(e.tok, lam != null, null, builder, newBuilder => {
-
if (lam != null) {
// Havoc heap, unless oneShot
if (!lam.OneShot) {
@@ -5200,21 +5434,9 @@ namespace Microsoft.Dafny {
// Check frame WF and that it read covers itself
newOptions = new WFOptions(options.SelfCallsAllowance, true /* check reads clauses */, true /* delay reads checks */);
-
CheckFrameWellFormed(newOptions, reads, locals, newBuilder, newEtran);
-
// new options now contains the delayed reads checks
- locals.AddRange(newOptions.Locals);
- // assign locals to true, but at a scope above
- Contract.Assert(newBuilder != builder);
- foreach (var a in newOptions.AssignLocals) {
- builder.Add(a);
- }
-
- // add asserts to the current builder (right after frame WF)
- foreach (var a in newOptions.Asserts) {
- newBuilder.Add(a);
- }
+ newOptions.ProcessSavedReadsChecks(locals, builder, newBuilder);
// continue doing reads checks, but don't delay them
newOptions = new WFOptions(options.SelfCallsAllowance, true, false);
@@ -5261,7 +5483,7 @@ namespace Microsoft.Dafny {
Bpl.Expr src = etran.TrExpr(me.Source);
Bpl.IfCmd ifCmd = null;
StmtListBuilder elsBldr = new StmtListBuilder();
- elsBldr.Add(new Bpl.AssumeCmd(expr.tok, Bpl.Expr.False));
+ elsBldr.Add(TrAssumeCmd(expr.tok, Bpl.Expr.False));
StmtList els = elsBldr.Collect(expr.tok);
foreach (var missingCtor in me.MissingCases) {
// havoc all bound variables
@@ -5315,11 +5537,11 @@ namespace Microsoft.Dafny {
Contract.Assert(resultType != null);
var bResult = etran.TrExpr(expr);
CheckSubrange(expr.tok, bResult, resultType, builder);
- builder.Add(new Bpl.AssumeCmd(expr.tok, Bpl.Expr.Eq(result, bResult)));
- builder.Add(new Bpl.AssumeCmd(expr.tok, CanCallAssumption(expr, etran)));
+ builder.Add(TrAssumeCmd(expr.tok, Bpl.Expr.Eq(result, bResult)));
+ builder.Add(TrAssumeCmd(expr.tok, CanCallAssumption(expr, etran)));
builder.Add(new CommentCmd("CheckWellformedWithResult: any expression"));
- builder.Add(new Bpl.AssumeCmd(expr.tok, MkIsAlloc(result, resultType, etran.HeapExpr)));
- builder.Add(new Bpl.AssumeCmd(expr.tok, MkIs(result, resultType)));
+ builder.Add(TrAssumeCmd(expr.tok, MkIsAlloc(result, resultType, etran.HeapExpr)));
+ builder.Add(TrAssumeCmd(expr.tok, MkIs(result, resultType)));
}
}
@@ -5464,7 +5686,7 @@ namespace Microsoft.Dafny {
tyargs.Add(TypeToTy(fm.Type));
}
tyargs.Add(TypeToTy(f.ResultType));
- if (f.IsRecursive) {
+ if (f.IsFuelAware()) {
Bpl.Expr ly; vars.Add(BplBoundVar("$ly", predef.LayerType, out ly)); args.Add(ly);
formals.Add(BplFormalVar(null, predef.LayerType, true));
}
@@ -5494,14 +5716,24 @@ namespace Microsoft.Dafny {
var bvars = new List<Bpl.Variable>();
var lhs_args = new List<Bpl.Expr>();
var rhs_args = new List<Bpl.Expr>();
-
+ var func_vars = new List<Bpl.Variable>();
+ var func_args = new List<Bpl.Expr>();
+ var boxed_func_args = new List<Bpl.Expr>();
+
var idGen = f.IdGenerator.NestedFreshIdGenerator("$fh$");
foreach (var fm in f.Formals) {
- var fe = BplBoundVar(idGen.FreshId("x#"), predef.BoxType, bvars);
+ string fm_name = idGen.FreshId("x#");
+ // Box and its [Unbox]args
+ var fe = BplBoundVar(fm_name, predef.BoxType, bvars);
lhs_args.Add(fe);
var be = UnboxIfBoxed(fe, fm.Type);
rhs_args.Add(be);
rhs_dict[fm] = new BoogieWrapper(be, fm.Type);
+ // args and its [Box]args
+ var arg = BplBoundVar(fm_name, TrType(fm.Type), func_vars);
+ func_args.Add(arg);
+ var boxed = BoxIfUnboxed(arg, fm.Type);
+ boxed_func_args.Add(boxed);
}
var h = BplBoundVar("$heap", predef.HeapType, vars);
@@ -5524,7 +5756,6 @@ namespace Microsoft.Dafny {
{
// Requires(Ty.., F#Handle( Ty1, ..., TyN, Layer, self), Heap, arg1, ..., argN)
// = F#Requires(Ty1, .., TyN, Layer, Heap, self, [Unbox] arg1, .., [Unbox] argN)
- // || Scramble(...)
var fhandle = FunctionCall(f.tok, name, predef.HandleType, SnocSelf(args));
var lhs = FunctionCall(f.tok, Requires(arity), Bpl.Type.Bool, Concat(tyargs, Cons(fhandle, Cons(h, lhs_args))));
@@ -5533,9 +5764,7 @@ namespace Microsoft.Dafny {
// In case this is the /requires/ or /reads/ function, then there is no precondition
rhs = Bpl.Expr.True;
} else {
- rhs = BplOr(
- FunctionCall(f.tok, RequiresName(f), Bpl.Type.Bool, Concat(SnocSelf(Snoc(args, h)), rhs_args)),
- MakeScrambler(f.tok, f.FullSanitizedName + "#lessReq", Concat(vars, bvars)));
+ rhs = FunctionCall(f.tok, RequiresName(f), Bpl.Type.Bool, Concat(SnocSelf(Snoc(args, h)), rhs_args));
}
sink.AddTopLevelDeclaration(new Axiom(f.tok,
@@ -5547,7 +5776,7 @@ namespace Microsoft.Dafny {
// = $Frame_F(args...)
var fhandle = FunctionCall(f.tok, name, predef.HandleType, SnocSelf(args));
- Bpl.Expr lhs_inner = FunctionCall(f.tok, Reads(arity), TrType(new SetType(new ObjectType())), Concat(tyargs, Cons(fhandle, Cons(h, lhs_args))));
+ Bpl.Expr lhs_inner = FunctionCall(f.tok, Reads(arity), TrType(new SetType(true, new ObjectType())), Concat(tyargs, Cons(fhandle, Cons(h, lhs_args))));
Bpl.Expr bx; var bxVar = BplBoundVar("$bx", predef.BoxType, out bx);
Bpl.Expr unboxBx = FunctionCall(f.tok, BuiltinFunction.Unbox, predef.RefType, bx);
@@ -5559,17 +5788,21 @@ namespace Microsoft.Dafny {
sink.AddTopLevelDeclaration(new Axiom(f.tok,
BplForall(Cons(bxVar, Concat(vars, bvars)), BplTrigger(lhs), Bpl.Expr.Eq(lhs, rhs))));
}
- }
- return name;
- }
- public Bpl.Expr MakeScrambler(IToken tk, string name, List<Variable> bvars) {
- var f = new Bpl.Function(tk, name,
- bvars.ConvertAll(bv => (Bpl.Variable)BplFormalVar(null, bv.TypedIdent.Type, true)),
- BplFormalVar(null, Bpl.Type.Bool, false));
+ {
+ // F(Ty1, .., TyN, Layer, Heap, self, arg1, .., argN)
+ // = [Unbox]Apply1(Ty.., F#Handle( Ty1, ..., TyN, Layer, self), Heap, [Box]arg1, ..., [Box]argN)
- sink.AddTopLevelDeclaration(f);
- return FunctionCall(tk, name, Bpl.Type.Bool, bvars.ConvertAll(bv => (Bpl.Expr)new Bpl.IdentifierExpr(tk, bv)));
+ var fhandle = FunctionCall(f.tok, name, predef.HandleType, SnocSelf(args));
+ var lhs = FunctionCall(f.tok, f.FullSanitizedName, TrType(f.ResultType), Concat(SnocSelf(Snoc(args, h)), func_args));
+ var rhs = FunctionCall(f.tok, Apply(arity), TrType(f.ResultType), Concat(tyargs, Cons(fhandle, Cons(h, boxed_func_args))));
+ var rhs_unboxed = UnboxIfBoxed(rhs, f.ResultType);
+
+ sink.AddTopLevelDeclaration(new Axiom(f.tok,
+ BplForall(Concat(vars, func_vars), BplTrigger(lhs), Bpl.Expr.Eq(lhs, rhs_unboxed))));
+ }
+ }
+ return name;
}
private void AddArrowTypeAxioms(ArrowTypeDecl ad) {
@@ -5584,7 +5817,7 @@ namespace Microsoft.Dafny {
// [Heap, Box, ..., Box] Bool
var requires_ty = new Bpl.MapType(tok, new List<Bpl.TypeVariable>(), map_args, Bpl.Type.Bool);
// Set Box
- var objset_ty = TrType(new SetType(new ObjectType()));
+ var objset_ty = TrType(new SetType(true, new ObjectType()));
// [Heap, Box, ..., Box] (Set Box)
var reads_ty = new Bpl.MapType(tok, new List<Bpl.TypeVariable>(), map_args, objset_ty);
@@ -5617,11 +5850,9 @@ namespace Microsoft.Dafny {
{
// forall t1, .., tN+1 : Ty, p: [Heap, Box, ..., Box] Box, heap : Heap, b1, ..., bN : Box
- // :: RequriesN(...) ==> ApplyN(t1, .. tN+1, HandleN(h, r, rd), heap, b1, ..., bN) = h[heap, b1, ..., bN]
- //
- // no precondition for these, but:
- // for requires, we add: RequiresN(...) <== r[heap, b1, ..., bN]
- // for reads, we could: ReadsN(...)[bx] ==> rd[heap, b1, ..., bN][bx] , but we don't
+ // :: ApplyN(t1, .. tN+1, HandleN(h, r, rd), heap, b1, ..., bN) == h[heap, b1, ..., bN]
+ // :: RequiresN(t1, .. tN+1, HandleN(h, r, rd), heap, b1, ..., bN) <== r[heap, b1, ..., bN]
+ // :: ReadsN(t1, .. tN+1, HandleN(h, r, rd), heap, b1, ..., bN) == rd[heap, b1, ..., bN]
Action<string, Bpl.Type, string, Bpl.Type, string, Bpl.Type> SelectorSemantics = (selector, selectorTy, selectorVar, selectorVarTy, precond, precondTy) => {
Contract.Assert((precond == null) == (precondTy == null));
var bvars = new List<Bpl.Variable>();
@@ -5722,7 +5953,7 @@ namespace Microsoft.Dafny {
var h0 = BplBoundVar("h0", predef.HeapType, bvars);
var h1 = BplBoundVar("h1", predef.HeapType, bvars);
- var heapSucc = FunctionCall(tok, BuiltinFunction.HeapSucc, null, h0, h1);
+ var heapSucc = HeapSucc(h0, h1);
var goodHeaps = BplAnd(
FunctionCall(tok, BuiltinFunction.IsGoodHeap, null, h0),
FunctionCall(tok, BuiltinFunction.IsGoodHeap, null, h1));
@@ -5746,15 +5977,14 @@ namespace Microsoft.Dafny {
var fld = BplBoundVar("fld", predef.FieldName(tok, a), ivars);
var inner_forall = new Bpl.ForallExpr(tok, Singleton(a), ivars, BplImp(
- BplAnd(new List<Expr> {
+ BplAnd(
Bpl.Expr.Neq(o, predef.Null),
- IsAlloced(tok, h0, o),
- IsAlloced(tok, h1, o),
+ // Note, the MkIsAlloc conjunct of "isness" implies that everything in the reads frame is allocated in "h0", which by HeapSucc(h0,h1) also implies the frame is allocated in "h1"
new Bpl.NAryExpr(tok, new Bpl.MapSelect(tok, 1), new List<Bpl.Expr> {
FunctionCall(tok, Reads(ad.Arity), objset_ty, Concat(types, Cons(f, Cons(hN, boxes)))),
FunctionCall(tok, BuiltinFunction.Box, null, o)
})
- }),
+ ),
Bpl.Expr.Eq(ReadHeap(tok, h0, o, fld), ReadHeap(tok, h1, o, fld))));
Func<Bpl.Expr, Bpl.Expr> fn = h => FunctionCall(tok, fname, Bpl.Type.Bool, Concat(types, Cons(f, Cons<Bpl.Expr>(h, boxes))));
@@ -5775,42 +6005,86 @@ namespace Microsoft.Dafny {
AddFrameForFunction(h1, Apply(ad.Arity));
}
- // consequence axiom
+ // $Is and $IsAlloc axioms
/*
-
- forall t0..tN+1 : Ty, h : Heap, f : Handle, bx1 .. bxN : Box,
- GoodHeap(h)
- && Is&IsAllocBox(bxI, tI, h)
- && Is&IsAlloc(f, Func(t1,..,tN, tN+1), h)
- ==> Is&IsAllocBox(Apply(f,h0,bxs)))
-
- */
+ axiom (forall f: HandleType, t0: Ty, t1: Ty ::
+ { $Is(f, Tclass._System.___hFunc1(t0, t1)) }
+ $Is(f, Tclass._System.___hFunc1(t0, t1))
+ <==> (forall h: Heap, bx0: Box ::
+ { Apply1(t0, t1, f, h, bx0) }
+ $IsGoodHeap(h) && $IsBox(bx0, t0)
+ && precondition of f(bx0) holds in h
+ ==> $IsBox(Apply1(t0, t1, f, h, bx0), t1)));
+ */
{
- var bvars = new List<Bpl.Variable>();
-
- var types = Map(Enumerable.Range(0, arity + 1), i => BplBoundVar("t" + i, predef.Ty, bvars));
-
- var h = BplBoundVar("h", predef.HeapType, bvars);
+ var bvarsOuter = new List<Bpl.Variable>();
+ var f = BplBoundVar("f", predef.HandleType, bvarsOuter);
+ var types = Map(Enumerable.Range(0, arity + 1), i => BplBoundVar("t" + i, predef.Ty, bvarsOuter));
+ var Is = MkIs(f, ClassTyCon(ad, types));
+
+ var bvarsInner = new List<Bpl.Variable>();
+ var h = BplBoundVar("h", predef.HeapType, bvarsInner);
+ var boxes = Map(Enumerable.Range(0, arity), i => BplBoundVar("bx" + i, predef.BoxType, bvarsInner));
var goodHeap = FunctionCall(tok, BuiltinFunction.IsGoodHeap, null, h);
+ var isBoxes = BplAnd(Map(Enumerable.Range(0, arity), i => MkIs(boxes[i], types[i], true)));
+ var pre = FunctionCall(tok, Requires(ad.Arity), predef.BoxType, Concat(types, Cons(f, Cons<Bpl.Expr>(h, boxes))));
+ var applied = FunctionCall(tok, Apply(ad.Arity), predef.BoxType, Concat(types, Cons(f, Cons<Bpl.Expr>(h, boxes))));
+ var applied_is = MkIs(applied, types[ad.Arity], true);
- var f = BplBoundVar("f", predef.HandleType, bvars);
- var boxes = Map(Enumerable.Range(0, arity), i => BplBoundVar("bx" + i, predef.BoxType, bvars));
-
- var isness = BplAnd(
- Snoc(Map(Enumerable.Range(0, arity), i =>
- BplAnd(MkIs(boxes[i], types[i], true),
- MkIsAlloc(boxes[i], types[i], h, true))),
- BplAnd(MkIs(f, ClassTyCon(ad, types)),
- MkIsAlloc(f, ClassTyCon(ad, types), h))));
+ sink.AddTopLevelDeclaration(new Axiom(tok,
+ BplForall(bvarsOuter, BplTrigger(Is),
+ BplIff(Is,
+ BplForall(bvarsInner, BplTrigger(applied),
+ BplImp(BplAnd(BplAnd(goodHeap, isBoxes), pre), applied_is))))));
+ }
+ /*
+ axiom (forall f: HandleType, t0: Ty, t1: Ty, h: Heap ::
+ { $IsAlloc(f, Tclass._System.___hFunc1(t0, t1), h) }
+ $IsGoodHeap(h)
+ ==>
+ (
+ $IsAlloc(f, Tclass._System.___hFunc1(t0, t1), h)
+ <==>
+ (forall bx0: Box ::
+ { Apply1(t0, t1, f, h, bx0) } { Reads1(t0, t1, f, h, bx0) }
+ $IsAllocBox(bx0, t0, h)
+ && precondition of f(bx0) holds in h
+ ==>
+ (everything in reads set of f(bx0) is allocated in h) &&
+ $IsAllocBox(Apply1(t0, t1, f, h, bx0), t1, h))
+ ));
+ */
+ {
+ var bvarsOuter = new List<Bpl.Variable>();
+ var f = BplBoundVar("f", predef.HandleType, bvarsOuter);
+ var types = Map(Enumerable.Range(0, arity + 1), i => BplBoundVar("t" + i, predef.Ty, bvarsOuter));
+ var h = BplBoundVar("h", predef.HeapType, bvarsOuter);
+ var goodHeap = FunctionCall(tok, BuiltinFunction.IsGoodHeap, null, h);
+ var isAlloc = MkIsAlloc(f, ClassTyCon(ad, types), h);
+ var bvarsInner = new List<Bpl.Variable>();
+ var boxes = Map(Enumerable.Range(0, arity), i => BplBoundVar("bx" + i, predef.BoxType, bvarsInner));
+ var isAllocBoxes = BplAnd(Map(Enumerable.Range(0, arity), i => MkIsAlloc(boxes[i], types[i], h, true)));
+ var pre = FunctionCall(tok, Requires(ad.Arity), predef.BoxType, Concat(types, Cons(f, Cons<Bpl.Expr>(h, boxes))));
var applied = FunctionCall(tok, Apply(ad.Arity), predef.BoxType, Concat(types, Cons(f, Cons<Bpl.Expr>(h, boxes))));
+ var applied_isAlloc = MkIsAlloc(applied, types[ad.Arity], h, true);
- var applied_is = BplAnd(MkIs(applied, types[ad.Arity], true), MkIsAlloc(applied, types[ad.Arity], h, true));
+ // (forall r: ref :: {Reads1(t0, t1, f, h, bx0)[$Box(r)]} r != null && Reads1(t0, t1, f, h, bx0)[$Box(r)] ==> h[r, alloc])
+ var bvarsR = new List<Bpl.Variable>();
+ var r = BplBoundVar("r", predef.RefType, bvarsR);
+ var rNonNull = Bpl.Expr.Neq(r, predef.Null);
+ var reads = FunctionCall(tok, Reads(ad.Arity), predef.BoxType, Concat(types, Cons(f, Cons<Bpl.Expr>(h, boxes))));
+ var rInReads = Bpl.Expr.Select(reads, FunctionCall(tok, BuiltinFunction.Box, null, r));
+ var rAlloc = IsAlloced(tok, h, r);
+ var isAllocReads = BplForall(bvarsR, BplTrigger(rInReads), BplImp(BplAnd(rNonNull, rInReads), rAlloc));
sink.AddTopLevelDeclaration(new Axiom(tok,
- BplForall(bvars,
- new Bpl.Trigger(tok, true, new List<Bpl.Expr> {applied}),
- BplImp(BplAnd(goodHeap, isness), applied_is))));
+ BplForall(bvarsOuter, BplTrigger(isAlloc),
+ BplImp(goodHeap,
+ BplIff(isAlloc,
+ BplForall(bvarsInner,
+ new Bpl.Trigger(tok, true, new List<Bpl.Expr> { applied }, BplTrigger(reads)),
+ BplImp(BplAnd(isAllocBoxes, pre), BplAnd(isAllocReads, applied_isAlloc))))))));
}
}
}
@@ -5823,7 +6097,7 @@ namespace Microsoft.Dafny {
var inner_name = GetClass(td).TypedIdent.Name;
string name = "T" + inner_name;
// Create the type constructor
- {
+ if (td.Name != "object") { // the type constructor for "object" is in DafnyPrelude.bpl
Bpl.Variable tyVarOut = BplFormalVar(null, predef.Ty, false);
List<Bpl.Variable> args = new List<Bpl.Variable>(
Enumerable.Range(0, arity).Select(i =>
@@ -5998,8 +6272,10 @@ namespace Microsoft.Dafny {
// axiom (forall o: Ref :: 0 <= array.Length(o));
Bpl.BoundVariable oVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "o", predef.RefType));
Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(f.tok, oVar);
- Bpl.Expr body = Bpl.Expr.Le(Bpl.Expr.Literal(0), new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(ff), new List<Bpl.Expr> { o }));
- Bpl.Expr qq = new Bpl.ForallExpr(f.tok, new List<Variable> { oVar }, body);
+ var rhs = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(ff), new List<Bpl.Expr> { o });
+ Bpl.Expr body = Bpl.Expr.Le(Bpl.Expr.Literal(0), rhs);
+ var trigger = BplTrigger(rhs);
+ Bpl.Expr qq = new Bpl.ForallExpr(f.tok, new List<Variable> { oVar }, trigger, body);
sink.AddTopLevelDeclaration(new Bpl.Axiom(f.tok, qq));
}
}
@@ -6027,7 +6303,7 @@ namespace Microsoft.Dafny {
var typeParams = TrTypeParamDecls(f.TypeArgs);
var formals = new List<Variable>();
formals.AddRange(MkTyParamFormals(GetTypeParams(f)));
- if (f.IsRecursive) {
+ if (f.IsFuelAware()) {
formals.Add(new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "$ly", predef.LayerType), true));
}
formals.Add(new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "$heap", predef.HeapType), true));
@@ -6136,11 +6412,8 @@ namespace Microsoft.Dafny {
req.Add(Requires(p.E.tok, true, etran.TrExpr(p.E), null, comment));
comment = null;
} else {
- bool splitHappened; // we actually don't care
- foreach (var s in TrSplitExpr(p.E, etran, kind == MethodTranslationKind.InterModuleCall ? 0 : int.MaxValue, true /* kind == MethodTranslationKind.Implementation */, out splitHappened)) {
- if ((kind == MethodTranslationKind.IntraModuleCall || kind == MethodTranslationKind.CoCall) && RefinementToken.IsInherited(s.E.tok, currentModule)) {
- // this precondition was inherited into this module, so just ignore it
- } else if (s.IsOnlyChecked && bodyKind) {
+ foreach (var s in TrSplitExprForMethodSpec(p.E, etran, kind)) {
+ if (s.IsOnlyChecked && bodyKind) {
// don't include in split
} else if (s.IsOnlyFree && !bodyKind) {
// don't include in split -- it would be ignored, anyhow
@@ -6159,8 +6432,7 @@ namespace Microsoft.Dafny {
if (p.IsFree && !DafnyOptions.O.DisallowSoundnessCheating) {
ens.Add(Ensures(p.E.tok, true, etran.TrExpr(p.E), null, null));
} else {
- bool splitHappened; // we actually don't care
- foreach (var s in TrSplitExpr(p.E, etran, kind == MethodTranslationKind.InterModuleCall ? 0 : int.MaxValue, true /* kind == MethodTranslationKind.Implementation */ , out splitHappened)) {
+ foreach (var s in TrSplitExprForMethodSpec(p.E, etran, kind)) {
var post = s.E;
if (kind == MethodTranslationKind.Implementation && RefinementToken.IsInherited(s.E.tok, currentModule)) {
// this postcondition was inherited into this module, so make it into the form "$_reverifyPost ==> s.E"
@@ -6332,7 +6604,7 @@ namespace Microsoft.Dafny {
}
// Make the call
- builder.Add(new Bpl.CallCmd(method.tok, method.FullSanitizedName, ins, outs));
+ builder.Add(Call(method.tok, method.FullSanitizedName, ins, outs));
for (int i = 0; i < m.Outs.Count; i++) {
var bLhs = m.Outs[i];
@@ -6347,7 +6619,7 @@ namespace Microsoft.Dafny {
foreach (var p in m.Ens) {
bool splitHappened; // we actually don't care
foreach (var s in TrSplitExpr(p.E, etran, true, out splitHappened)) {
- var assert = new Bpl.AssertCmd(method.tok, s.E, ErrorMessageAttribute(s.E.tok, "This is the postcondition that may not hold."));
+ var assert = TrAssertCmd(method.tok, s.E, ErrorMessageAttribute(s.E.tok, "This is the postcondition that may not hold."));
assert.ErrorData = "Error: A postcondition of the refined method may not hold.";
builder.Add(assert);
}
@@ -6363,6 +6635,18 @@ namespace Microsoft.Dafny {
Reset();
}
+ private static CallCmd Call(IToken tok, string methodName, List<Expr> ins, List<Bpl.IdentifierExpr> outs) {
+ Contract.Requires(tok != null);
+ Contract.Requires(methodName != null);
+ Contract.Requires(ins != null);
+ Contract.Requires(outs != null);
+
+ CallCmd call;
+ call = new CallCmd(tok, methodName, ins, outs);
+ // CLEMENT enable this: call.ErrorData = "possible violation of function precondition";
+ return call;
+ }
+
private static QKeyValue ErrorMessageAttribute(IToken t, string error) {
var l = new List<object>(1);
l.Add(error);
@@ -6387,7 +6671,7 @@ namespace Microsoft.Dafny {
// parameters of the procedure
List<Variable> inParams = new List<Variable>();
Bpl.Formal layer;
- if (f.IsRecursive) {
+ if (f.IsFuelAware()) {
layer = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "$ly", predef.LayerType), true);
inParams.Add(layer);
} else {
@@ -6576,7 +6860,7 @@ namespace Microsoft.Dafny {
// the frame condition, which is free since it is checked with every heap update and call
boilerplate.Add(new BoilerplateTriple(tok, true, FrameCondition(tok, modifiesClause, isGhostContext, etranPre, etran, etranMod), null, "frame condition"));
// HeapSucc(S1, S2) or HeapSuccGhost(S1, S2)
- Bpl.Expr heapSucc = FunctionCall(tok, isGhostContext ? BuiltinFunction.HeapSuccGhost : BuiltinFunction.HeapSucc, null, etranPre.HeapExpr, etran.HeapExpr);
+ Bpl.Expr heapSucc = HeapSucc(etranPre.HeapExpr, etran.HeapExpr, isGhostContext);
boilerplate.Add(new BoilerplateTriple(tok, true, heapSucc, null, "boilerplate"));
}
return boilerplate;
@@ -6696,7 +6980,7 @@ namespace Microsoft.Dafny {
} else if (type.IsDatatype || type is DatatypeProxy) {
return predef.DatatypeType;
} else if (type is SetType) {
- return predef.SetType(Token.NoToken, predef.BoxType);
+ return predef.SetType(Token.NoToken, ((SetType)type).Finite, predef.BoxType);
} else if (type is MultiSetType) {
return predef.MultiSetType(Token.NoToken, predef.BoxType);
} else if (type is MapType) {
@@ -6820,9 +7104,9 @@ namespace Microsoft.Dafny {
if (assertAsAssume || (RefinementToken.IsInherited(refinesToken, currentModule) && (codeContext == null || !codeContext.MustReverify))) {
// produce an assume instead
- return new Bpl.AssumeCmd(tok, condition, kv);
+ return TrAssumeCmd(tok, condition, kv);
} else {
- var cmd = new Bpl.AssertCmd(ForceCheckToken.Unwrap(tok), condition, kv);
+ var cmd = TrAssertCmd(ForceCheckToken.Unwrap(tok), condition, kv);
cmd.ErrorData = "Error: " + errorMessage;
return cmd;
}
@@ -6839,12 +7123,12 @@ namespace Microsoft.Dafny {
if (RefinementToken.IsInherited(refinesTok, currentModule) && (codeContext == null || !codeContext.MustReverify)) {
// produce a "skip" instead
- return new Bpl.AssumeCmd(tok, Bpl.Expr.True, kv);
+ return TrAssumeCmd(tok, Bpl.Expr.True, kv);
} else {
tok = ForceCheckToken.Unwrap(tok);
var args = new List<object>();
args.Add(Bpl.Expr.Literal(0));
- Bpl.AssertCmd cmd = new Bpl.AssertCmd(tok, condition, new Bpl.QKeyValue(tok, "subsumption", args, kv));
+ Bpl.AssertCmd cmd = TrAssertCmd(tok, condition, new Bpl.QKeyValue(tok, "subsumption", args, kv));
cmd.ErrorData = "Error: " + errorMessage;
return cmd;
}
@@ -6858,9 +7142,9 @@ namespace Microsoft.Dafny {
if (assertAsAssume || (RefinementToken.IsInherited(tok, currentModule) && (codeContext == null || !codeContext.MustReverify))) {
// produce an assume instead
- return new Bpl.AssumeCmd(tok, condition, kv);
+ return TrAssumeCmd(tok, condition, kv);
} else {
- var cmd = new Bpl.AssertCmd(ForceCheckToken.Unwrap(tok), condition, kv);
+ var cmd = TrAssertCmd(ForceCheckToken.Unwrap(tok), condition, kv);
cmd.ErrorData = "Error: " + errorMessage;
return cmd;
}
@@ -6883,6 +7167,7 @@ namespace Microsoft.Dafny {
{
Contract.Requires(tok != null);
Contract.Requires(condition != null);
+ Contract.Ensures(Contract.Result<Bpl.Requires>() != null);
Bpl.Requires req = new Bpl.Requires(ForceCheckToken.Unwrap(tok), free, condition, comment);
if (errorMessage != null) {
req.ErrorData = errorMessage;
@@ -6910,11 +7195,16 @@ namespace Microsoft.Dafny {
Contract.Requires(locals != null);
Contract.Requires(etran != null);
Contract.Requires(codeContext != null && predef != null);
+ Contract.Ensures(fuelContext == Contract.OldValue(fuelContext));
if (stmt is PredicateStmt) {
+ var stmtBuilder = new Bpl.StmtListBuilder();
+ this.fuelContext = FuelSetting.ExpandFuelContext(stmt.Attributes, stmt.Tok, this.fuelContext, this.reporter);
+ var defineFuel = DefineFuelConstant(stmt.Tok, stmt.Attributes, stmtBuilder, etran);
+ var b = defineFuel ? stmtBuilder : builder;
if (stmt is AssertStmt || DafnyOptions.O.DisallowSoundnessCheating) {
- AddComment(builder, stmt, "assert statement");
+ AddComment(b, stmt, "assert statement");
PredicateStmt s = (PredicateStmt)stmt;
- TrStmt_CheckWellformed(s.Expr, builder, locals, etran, false);
+ TrStmt_CheckWellformed(s.Expr, b, locals, etran, false);
IToken enclosingToken = null;
if (Attributes.Contains(stmt.Attributes, "prependAssertToken")) {
enclosingToken = stmt.Tok;
@@ -6923,22 +7213,32 @@ namespace Microsoft.Dafny {
var ss = TrSplitExpr(s.Expr, etran, true, out splitHappened);
if (!splitHappened) {
var tok = enclosingToken == null ? s.Expr.tok : new NestedToken(enclosingToken, s.Expr.tok);
- builder.Add(Assert(tok, etran.TrExpr(s.Expr), "assertion violation", stmt.Tok, etran.TrAttributes(stmt.Attributes, null)));
+ b.Add(Assert(tok, etran.TrExpr(s.Expr), "assertion violation", stmt.Tok, etran.TrAttributes(stmt.Attributes, null)));
} else {
foreach (var split in ss) {
if (split.IsChecked) {
var tok = enclosingToken == null ? split.E.tok : new NestedToken(enclosingToken, split.E.tok);
- builder.Add(AssertNS(tok, split.E, "assertion violation", stmt.Tok, etran.TrAttributes(stmt.Attributes, null))); // attributes go on every split
+ b.Add(AssertNS(tok, split.E, "assertion violation", stmt.Tok, etran.TrAttributes(stmt.Attributes, null))); // attributes go on every split
}
}
- builder.Add(new Bpl.AssumeCmd(stmt.Tok, etran.TrExpr(s.Expr)));
+ if (!defineFuel) {
+ b.Add(TrAssumeCmd(stmt.Tok, etran.TrExpr(s.Expr)));
+ }
+ }
+ if (defineFuel) {
+ var ifCmd = new Bpl.IfCmd(s.Tok, null, b.Collect(s.Tok), null, null);
+ builder.Add(ifCmd);
+ builder.Add(TrAssumeCmd(stmt.Tok, etran.TrExpr(s.Expr)));
}
} else if (stmt is AssumeStmt) {
AddComment(builder, stmt, "assume statement");
AssumeStmt s = (AssumeStmt)stmt;
TrStmt_CheckWellformed(s.Expr, builder, locals, etran, false);
- builder.Add(new Bpl.AssumeCmd(stmt.Tok, etran.TrExpr(s.Expr), etran.TrAttributes(stmt.Attributes, null)));
+ // we need to increase fuel for quantifier expr and functions that contain quantifier expr in the assume context.
+ var existEtran = etran.LayerOffset(1);
+ builder.Add(TrAssumeCmd(stmt.Tok, etran.TrExpr(s.Expr, null, existEtran, existEtran), etran.TrAttributes(stmt.Attributes, null)));
}
+ this.fuelContext = FuelSetting.PopFuelContext();
} else if (stmt is PrintStmt) {
AddComment(builder, stmt, "print statement");
PrintStmt s = (PrintStmt)stmt;
@@ -6994,7 +7294,7 @@ namespace Microsoft.Dafny {
var yc = new Bpl.IdentifierExpr(s.Tok, yieldCountVariable);
var incYieldCount = Bpl.Cmd.SimpleAssign(s.Tok, yc, Bpl.Expr.Binary(s.Tok, Bpl.BinaryOperator.Opcode.Add, yc, Bpl.Expr.Literal(1)));
builder.Add(incYieldCount);
- builder.Add(new Bpl.AssumeCmd(s.Tok, YieldCountAssumption(iter, etran)));
+ builder.Add(TrAssumeCmd(s.Tok, YieldCountAssumption(iter, etran)));
// assume $IsGoodHeap($Heap);
builder.Add(AssumeGoodHeap(s.Tok, etran));
// assert YieldEnsures[subst]; // where 'subst' replaces "old(E)" with "E" being evaluated in $_OldIterHeap
@@ -7013,7 +7313,7 @@ namespace Microsoft.Dafny {
builder.Add(AssertNS(yieldToken, split.E, "possible violation of yield-ensures condition", stmt.Tok, null));
}
}
- builder.Add(new Bpl.AssumeCmd(stmt.Tok, yeEtran.TrExpr(p.E)));
+ builder.Add(TrAssumeCmd(stmt.Tok, yeEtran.TrExpr(p.E)));
}
}
YieldHavoc(iter.tok, iter, builder, etran);
@@ -7078,7 +7378,7 @@ namespace Microsoft.Dafny {
builder.Add(Assert(s.Tok, w, "cannot establish the existence of LHS values that satisfy the such-that predicate"));
}
// End by doing the assume
- builder.Add(new Bpl.AssumeCmd(s.Tok, etran.TrExpr(s.Expr)));
+ builder.Add(TrAssumeCmd(s.Tok, etran.TrExpr(s.Expr)));
builder.Add(CaptureState(s)); // just do one capture state--here, at the very end (that is, don't do one before the assume)
} else if (stmt is UpdateStmt) {
@@ -7134,24 +7434,30 @@ namespace Microsoft.Dafny {
} else if (stmt is IfStmt) {
AddComment(builder, stmt, "if statement");
IfStmt s = (IfStmt)stmt;
- Bpl.Expr guard;
+ Expression guard;
if (s.Guard == null) {
guard = null;
} else {
- TrStmt_CheckWellformed(s.Guard, builder, locals, etran, true);
- guard = etran.TrExpr(s.Guard);
+ guard = s.IsExistentialGuard ? AlphaRename((ExistsExpr)s.Guard, "eg$", this) : s.Guard;
+ TrStmt_CheckWellformed(guard, builder, locals, etran, true);
}
Bpl.StmtListBuilder b = new Bpl.StmtListBuilder();
CurrentIdGenerator.Push();
+ if (s.IsExistentialGuard) {
+ var exists = (ExistsExpr)s.Guard; // the original (that is, not alpha-renamed) guard
+ IntroduceAndAssignExistentialVars(exists, b, builder, locals, etran);
+ }
Bpl.StmtList thn = TrStmt2StmtList(b, s.Thn, locals, etran);
CurrentIdGenerator.Pop();
Bpl.StmtList els;
Bpl.IfCmd elsIf = null;
+ b = new Bpl.StmtListBuilder();
+ if (s.IsExistentialGuard) {
+ b.Add(TrAssumeCmd(guard.tok, Bpl.Expr.Not(etran.TrExpr(guard))));
+ }
if (s.Els == null) {
- b = new Bpl.StmtListBuilder();
els = b.Collect(s.Tok);
} else {
- b = new Bpl.StmtListBuilder();
els = TrStmt2StmtList(b, s.Els, locals, etran);
if (els.BigBlocks.Count == 1) {
Bpl.BigBlock bb = els.BigBlocks[0];
@@ -7161,7 +7467,7 @@ namespace Microsoft.Dafny {
}
}
}
- builder.Add(new Bpl.IfCmd(stmt.Tok, guard, thn, elsIf, els));
+ builder.Add(new Bpl.IfCmd(stmt.Tok, guard == null || s.IsExistentialGuard ? null : etran.TrExpr(guard), thn, elsIf, els));
} else if (stmt is AlternativeStmt) {
AddComment(builder, stmt, "alternative statement");
@@ -7171,6 +7477,8 @@ namespace Microsoft.Dafny {
} else if (stmt is WhileStmt) {
AddComment(builder, stmt, "while statement");
+ this.fuelContext = FuelSetting.ExpandFuelContext(stmt.Attributes, stmt.Tok, this.fuelContext, this.reporter);
+ DefineFuelConstant(stmt.Tok, stmt.Attributes, builder, etran);
var s = (WhileStmt)stmt;
BodyTranslator bodyTr = null;
if (s.Body != null) {
@@ -7181,7 +7489,7 @@ namespace Microsoft.Dafny {
};
}
TrLoop(s, s.Guard, bodyTr, builder, locals, etran);
-
+ this.fuelContext = FuelSetting.PopFuelContext();
} else if (stmt is AlternativeLoopStmt) {
AddComment(builder, stmt, "alternative loop statement");
var s = (AlternativeLoopStmt)stmt;
@@ -7209,11 +7517,11 @@ namespace Microsoft.Dafny {
// havoc $Heap;
builder.Add(new Bpl.HavocCmd(s.Tok, new List<Bpl.IdentifierExpr> { (Bpl.IdentifierExpr/*TODO: this cast is rather dubious*/)etran.HeapExpr }));
// assume $HeapSucc(preModifyHeap, $Heap); OR $HeapSuccGhost
- builder.Add(new Bpl.AssumeCmd(s.Tok, FunctionCall(s.Tok, s.IsGhost ? BuiltinFunction.HeapSuccGhost : BuiltinFunction.HeapSucc, null, preModifyHeap, etran.HeapExpr)));
+ builder.Add(TrAssumeCmd(s.Tok, HeapSucc(preModifyHeap, etran.HeapExpr, s.IsGhost)));
// assume nothing outside the frame was changed
var etranPreLoop = new ExpressionTranslator(this, predef, preModifyHeap);
var updatedFrameEtran = new ExpressionTranslator(etran, modifyFrameName);
- builder.Add(new Bpl.AssumeCmd(s.Tok, FrameConditionUsingDefinedFrame(s.Tok, etranPreLoop, etran, updatedFrameEtran)));
+ builder.Add(TrAssumeCmd(s.Tok, FrameConditionUsingDefinedFrame(s.Tok, etranPreLoop, etran, updatedFrameEtran)));
} else {
// do the body, but with preModifyHeapVar as the governing frame
var updatedFrameEtran = new ExpressionTranslator(etran, modifyFrameName);
@@ -7223,6 +7531,8 @@ namespace Microsoft.Dafny {
} else if (stmt is ForallStmt) {
var s = (ForallStmt)stmt;
+ this.fuelContext = FuelSetting.ExpandFuelContext(stmt.Attributes, stmt.Tok, this.fuelContext, this.reporter);
+
if (s.Kind == ForallStmt.ParBodyKind.Assign) {
AddComment(builder, stmt, "forall statement (assign)");
Contract.Assert(s.Ens.Count == 0);
@@ -7232,6 +7542,7 @@ namespace Microsoft.Dafny {
var s0 = (AssignStmt)s.S0;
var definedness = new Bpl.StmtListBuilder();
var updater = new Bpl.StmtListBuilder();
+ DefineFuelConstant(stmt.Tok, stmt.Attributes, definedness, etran);
TrForallAssign(s, s0, definedness, updater, locals, etran);
// All done, so put the two pieces together
builder.Add(new Bpl.IfCmd(s.Tok, null, definedness.Collect(s.Tok), null, updater.Collect(s.Tok)));
@@ -7247,8 +7558,9 @@ namespace Microsoft.Dafny {
} else {
var s0 = (CallStmt)s.S0;
var definedness = new Bpl.StmtListBuilder();
+ DefineFuelConstant(stmt.Tok, stmt.Attributes, definedness, etran);
var exporter = new Bpl.StmtListBuilder();
- TrForallStmtCall(s.Tok, s.BoundVars, s.Range, null, s0, definedness, exporter, locals, etran);
+ TrForallStmtCall(s.Tok, s.BoundVars, s.Range, null, s.ForallExpressions, s0, definedness, exporter, locals, etran);
// All done, so put the two pieces together
builder.Add(new Bpl.IfCmd(s.Tok, null, definedness.Collect(s.Tok), null, exporter.Collect(s.Tok)));
builder.Add(CaptureState(stmt));
@@ -7258,6 +7570,7 @@ namespace Microsoft.Dafny {
AddComment(builder, stmt, "forall statement (proof)");
var definedness = new Bpl.StmtListBuilder();
var exporter = new Bpl.StmtListBuilder();
+ DefineFuelConstant(stmt.Tok, stmt.Attributes, definedness, etran);
TrForallProof(s, definedness, exporter, locals, etran);
// All done, so put the two pieces together
builder.Add(new Bpl.IfCmd(s.Tok, null, definedness.Collect(s.Tok), null, exporter.Collect(s.Tok)));
@@ -7266,7 +7579,7 @@ namespace Microsoft.Dafny {
} else {
Contract.Assert(false); // unexpected kind
}
-
+ this.fuelContext = FuelSetting.PopFuelContext();
} else if (stmt is CalcStmt) {
/* Translate into:
if (*) {
@@ -7292,6 +7605,8 @@ namespace Microsoft.Dafny {
var s = (CalcStmt)stmt;
Contract.Assert(s.Steps.Count == s.Hints.Count); // established by the resolver
AddComment(builder, stmt, "calc statement");
+ this.fuelContext = FuelSetting.ExpandFuelContext(stmt.Attributes, stmt.Tok, this.fuelContext, this.reporter);
+ DefineFuelConstant(stmt.Tok, stmt.Attributes, builder, etran);
CurrentIdGenerator.Push(); // put the entire calc statement within its own sub-branch
if (s.Lines.Count > 0) {
Bpl.IfCmd ifCmd = null;
@@ -7310,7 +7625,7 @@ namespace Microsoft.Dafny {
if (s.Steps[i] is BinaryExpr && (((BinaryExpr)s.Steps[i]).ResolvedOp == BinaryExpr.ResolvedOpcode.Imp)) {
// assume line<i>:
AddComment(b, stmt, "assume lhs");
- b.Add(new Bpl.AssumeCmd(s.Tok, etran.TrExpr(CalcStmt.Lhs(s.Steps[i]))));
+ b.Add(TrAssumeCmd(s.Tok, etran.TrExpr(CalcStmt.Lhs(s.Steps[i]))));
}
// hint:
AddComment(b, stmt, "Hint" + i.ToString());
@@ -7338,7 +7653,7 @@ namespace Microsoft.Dafny {
}
}
}
- b.Add(new Bpl.AssumeCmd(s.Tok, Bpl.Expr.False));
+ b.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False));
ifCmd = new Bpl.IfCmd(s.Tok, null, b.Collect(s.Tok), ifCmd, null);
CurrentIdGenerator.Pop();
}
@@ -7347,23 +7662,23 @@ namespace Microsoft.Dafny {
AddComment(b, stmt, "assert wf[initial]");
Contract.Assert(s.Result != null); // established by the resolver
TrStmt_CheckWellformed(CalcStmt.Lhs(s.Result), b, locals, etran, false);
- b.Add(new Bpl.AssumeCmd(s.Tok, Bpl.Expr.False));
+ b.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False));
ifCmd = new Bpl.IfCmd(s.Tok, null, b.Collect(s.Tok), ifCmd, null);
builder.Add(ifCmd);
// assume result:
if (s.Steps.Count > 1) {
- builder.Add(new Bpl.AssumeCmd(s.Tok, etran.TrExpr(s.Result)));
+ builder.Add(TrAssumeCmd(s.Tok, etran.TrExpr(s.Result)));
}
}
CurrentIdGenerator.Pop();
-
+ this.fuelContext = FuelSetting.PopFuelContext();
} else if (stmt is MatchStmt) {
var s = (MatchStmt)stmt;
TrStmt_CheckWellformed(s.Source, builder, locals, etran, true);
Bpl.Expr source = etran.TrExpr(s.Source);
var b = new Bpl.StmtListBuilder();
- b.Add(new Bpl.AssumeCmd(stmt.Tok, Bpl.Expr.False));
+ b.Add(TrAssumeCmd(stmt.Tok, Bpl.Expr.False));
Bpl.StmtList els = b.Collect(stmt.Tok);
Bpl.IfCmd ifCmd = null;
foreach (var missingCtor in s.MissingCases) {
@@ -7432,12 +7747,57 @@ namespace Microsoft.Dafny {
if (s.Update != null) {
TrStmt(s.Update, builder, locals, etran);
}
-
+ } else if (stmt is LetStmt) {
+ var s = (LetStmt)stmt;
+ foreach (var bv in s.BoundVars) {
+ Bpl.LocalVariable bvar = new Bpl.LocalVariable(bv.Tok, new Bpl.TypedIdent(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), TrType(bv.Type)));
+ locals.Add(bvar);
+ var bIe = new Bpl.IdentifierExpr(bvar.tok, bvar);
+ builder.Add(new Bpl.HavocCmd(bv.Tok, new List<Bpl.IdentifierExpr> { bIe }));
+ Bpl.Expr wh = GetWhereClause(bv.Tok, bIe, bv.Type, etran);
+ if (wh != null) {
+ builder.Add(TrAssumeCmd(bv.Tok, wh));
+ }
+ }
+ Contract.Assert(s.LHSs.Count == s.RHSs.Count); // checked by resolution
+ var varNameGen = CurrentIdGenerator.NestedFreshIdGenerator("let#");
+ for (int i = 0; i < s.LHSs.Count; i++) {
+ var pat = s.LHSs[i];
+ var rhs = s.RHSs[i];
+ var nm = varNameGen.FreshId(string.Format("#{0}#", i));
+ var r = new Bpl.LocalVariable(pat.tok, new Bpl.TypedIdent(pat.tok, nm, TrType(rhs.Type)));
+ locals.Add(r);
+ var rIe = new Bpl.IdentifierExpr(pat.tok, r);
+ TrStmt_CheckWellformed(s.RHSs[i], builder, locals, etran, false);
+ CheckWellformedWithResult(s.RHSs[i], new WFOptions(null, false, false), rIe, pat.Expr.Type, locals, builder, etran);
+ CheckCasePatternShape(pat, rIe, builder);
+ builder.Add(TrAssumeCmd(pat.tok, Bpl.Expr.Eq(etran.TrExpr(pat.Expr), rIe)));
+ }
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement
}
}
+ private void IntroduceAndAssignExistentialVars(ExistsExpr exists, Bpl.StmtListBuilder builder, Bpl.StmtListBuilder builderOutsideIfConstruct, List<Variable> locals, ExpressionTranslator etran) {
+ Contract.Requires(exists != null);
+ Contract.Requires(exists.Range == null);
+ Contract.Requires(builder != null);
+ Contract.Requires(builderOutsideIfConstruct != null);
+ Contract.Requires(locals != null);
+ Contract.Requires(etran != null);
+ // declare and havoc the bound variables of 'exists' as local variables
+ var iesForHavoc = new List<Bpl.IdentifierExpr>();
+ foreach (var bv in exists.BoundVars) {
+ Bpl.Type varType = TrType(bv.Type);
+ Bpl.Expr wh = GetWhereClause(bv.Tok, new Bpl.IdentifierExpr(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), varType), bv.Type, etran);
+ Bpl.Variable local = new Bpl.LocalVariable(bv.Tok, new Bpl.TypedIdent(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh));
+ locals.Add(local);
+ iesForHavoc.Add(new Bpl.IdentifierExpr(local.tok, local));
+ }
+ builderOutsideIfConstruct.Add(new Bpl.HavocCmd(exists.tok, iesForHavoc));
+ builder.Add(TrAssumeCmd(exists.tok, etran.TrExpr(exists.Term)));
+ }
+
void TrStmtList(List<Statement> stmts, Bpl.StmtListBuilder builder, List<Variable> locals, ExpressionTranslator etran) {
Contract.Requires(stmts != null);
Contract.Requires(builder != null);
@@ -7452,6 +7812,46 @@ namespace Microsoft.Dafny {
}
/// <summary>
+ /// Returns an expression like 'exists' but where the bound variables have been renamed to have
+ /// 'prefix' as a prefix to their previous names.
+ /// Assumes the expression has been resolved.
+ /// </summary>
+ public static Expression AlphaRename(ExistsExpr exists, string prefix, Translator translator) {
+ Contract.Requires(exists != null);
+ Contract.Requires(prefix != null);
+ Contract.Requires(translator != null);
+
+ if (exists.SplitQuantifier != null) {
+ // TODO: what to do? Substitute(exists.SplitQuantifierExpression);
+ }
+
+ var substMap = new Dictionary<IVariable, Expression>();
+ var var4var = new Dictionary<BoundVar, BoundVar>();
+ var bvars = new List<BoundVar>();
+ foreach (var bv in exists.BoundVars) {
+ var newBv = new BoundVar(bv.tok, prefix + bv.Name, bv.Type);
+ bvars.Add(newBv);
+ var4var.Add(bv, newBv);
+ var ie = new IdentifierExpr(newBv.tok, newBv.Name);
+ ie.Var = newBv; // resolve here
+ ie.Type = newBv.Type; // resolve here
+ substMap.Add(bv, ie);
+ }
+ var s = new Substituter(null, substMap, new Dictionary<TypeParameter, Type>(), translator);
+ var range = exists.Range == null ? null : s.Substitute(exists.Range);
+ var term = s.Substitute(exists.Term);
+ var attrs = s.SubstAttributes(exists.Attributes);
+ var ex = new ExistsExpr(exists.tok, exists.TypeArgs, bvars, range, term, attrs);
+ if (exists.Bounds != null) {
+ ex.Bounds = exists.Bounds.ConvertAll(bound => s.SubstituteBoundedPool(bound));
+ }
+ if (exists.MissingBounds != null) {
+ ex.MissingBounds = exists.MissingBounds.ConvertAll(bv => var4var[bv]);
+ }
+ return ex;
+ }
+
+ /// <summary>
/// Generate:
/// havoc Heap \ {this} \ _reads \ _new;
/// assume this.Valid();
@@ -7477,7 +7877,7 @@ namespace Microsoft.Dafny {
new List<Bpl.IdentifierExpr>()));
// assume YieldRequires;
foreach (var p in iter.YieldRequires) {
- builder.Add(new Bpl.AssumeCmd(tok, etran.TrExpr(p.E)));
+ builder.Add(TrAssumeCmd(tok, etran.TrExpr(p.E)));
}
// $_OldIterHeap := Heap;
builder.Add(Bpl.Cmd.SimpleAssign(tok, new Bpl.IdentifierExpr(tok, "$_OldIterHeap", predef.HeapType), etran.HeapExpr));
@@ -7550,7 +7950,7 @@ namespace Microsoft.Dafny {
}
}
} else if (xType is SetType) {
- var empty = new SetDisplayExpr(x.tok, new List<Expression>());
+ var empty = new SetDisplayExpr(x.tok, ((SetType)xType).Finite, new List<Expression>());
empty.Type = xType;
yield return empty;
} else if (xType is MultiSetType) {
@@ -7572,39 +7972,37 @@ namespace Microsoft.Dafny {
}
var missingBounds = new List<BoundVar>();
- var bounds = Resolver.DiscoverBounds(x.tok, new List<BoundVar>() { x }, expr, true, true, missingBounds);
- if (missingBounds.Count == 0) {
- foreach (var bound in bounds) {
- if (bound is ComprehensionExpr.IntBoundedPool) {
- var bnd = (ComprehensionExpr.IntBoundedPool)bound;
- if (bnd.LowerBound != null) yield return bnd.LowerBound;
- if (bnd.UpperBound != null) yield return Expression.CreateDecrement(bnd.UpperBound, 1);
- } else if (bound is ComprehensionExpr.SubSetBoundedPool) {
- var bnd = (ComprehensionExpr.SubSetBoundedPool)bound;
- yield return bnd.UpperBound;
- } else if (bound is ComprehensionExpr.SuperSetBoundedPool) {
- var bnd = (ComprehensionExpr.SuperSetBoundedPool)bound;
- yield return bnd.LowerBound;
- } else if (bound is ComprehensionExpr.SetBoundedPool) {
- var st = ((ComprehensionExpr.SetBoundedPool)bound).Set.Resolved;
- if (st is DisplayExpression) {
- var display = (DisplayExpression)st;
- foreach (var el in display.Elements) {
- yield return el;
- }
- } else if (st is MapDisplayExpr) {
- var display = (MapDisplayExpr)st;
- foreach (var maplet in display.Elements) {
- yield return maplet.A;
- }
+ var bounds = Resolver.DiscoverAllBounds_SingleVar(x, expr);
+ foreach (var bound in bounds) {
+ if (bound is ComprehensionExpr.IntBoundedPool) {
+ var bnd = (ComprehensionExpr.IntBoundedPool)bound;
+ if (bnd.LowerBound != null) yield return bnd.LowerBound;
+ if (bnd.UpperBound != null) yield return Expression.CreateDecrement(bnd.UpperBound, 1);
+ } else if (bound is ComprehensionExpr.SubSetBoundedPool) {
+ var bnd = (ComprehensionExpr.SubSetBoundedPool)bound;
+ yield return bnd.UpperBound;
+ } else if (bound is ComprehensionExpr.SuperSetBoundedPool) {
+ var bnd = (ComprehensionExpr.SuperSetBoundedPool)bound;
+ yield return bnd.LowerBound;
+ } else if (bound is ComprehensionExpr.SetBoundedPool) {
+ var st = ((ComprehensionExpr.SetBoundedPool)bound).Set.Resolved;
+ if (st is DisplayExpression) {
+ var display = (DisplayExpression)st;
+ foreach (var el in display.Elements) {
+ yield return el;
}
- } else if (bound is ComprehensionExpr.SeqBoundedPool) {
- var sq = ((ComprehensionExpr.SeqBoundedPool)bound).Seq.Resolved;
- var display = sq as DisplayExpression;
- if (display != null) {
- foreach (var el in display.Elements) {
- yield return el;
- }
+ } else if (st is MapDisplayExpr) {
+ var display = (MapDisplayExpr)st;
+ foreach (var maplet in display.Elements) {
+ yield return maplet.A;
+ }
+ }
+ } else if (bound is ComprehensionExpr.SeqBoundedPool) {
+ var sq = ((ComprehensionExpr.SeqBoundedPool)bound).Seq.Resolved;
+ var display = sq as DisplayExpression;
+ if (display != null) {
+ foreach (var el in display.Elements) {
+ yield return el;
}
}
}
@@ -7694,7 +8092,7 @@ namespace Microsoft.Dafny {
var substMap = SetupBoundVarsAsLocals(s.BoundVars, definedness, locals, etran);
Expression range = Substitute(s.Range, null, substMap);
TrStmt_CheckWellformed(range, definedness, locals, etran, false);
- definedness.Add(new Bpl.AssumeCmd(s.Range.tok, etran.TrExpr(range)));
+ definedness.Add(TrAssumeCmd(s.Range.tok, etran.TrExpr(range)));
var lhs = Substitute(s0.Lhs.Resolved, null, substMap);
TrStmt_CheckWellformed(lhs, definedness, locals, etran, false);
@@ -7730,7 +8128,7 @@ namespace Microsoft.Dafny {
var substMapPrime = SetupBoundVarsAsLocals(s.BoundVars, definedness, locals, etran);
var lhsPrime = Substitute(s0.Lhs.Resolved, null, substMapPrime);
range = Substitute(s.Range, null, substMapPrime);
- definedness.Add(new Bpl.AssumeCmd(range.tok, etran.TrExpr(range)));
+ definedness.Add(TrAssumeCmd(range.tok, etran.TrExpr(range)));
// assume !(x == x' && y == y');
Bpl.Expr eqs = Bpl.Expr.True;
foreach (var bv in s.BoundVars) {
@@ -7739,7 +8137,7 @@ namespace Microsoft.Dafny {
// TODO: in the following line, is the term equality okay, or does it have to include things like Set#Equal sometimes too?
eqs = BplAnd(eqs, Bpl.Expr.Eq(etran.TrExpr(x), etran.TrExpr(xPrime)));
}
- definedness.Add(new Bpl.AssumeCmd(s.Tok, Bpl.Expr.Not(eqs)));
+ definedness.Add(TrAssumeCmd(s.Tok, Bpl.Expr.Not(eqs)));
Bpl.Expr objPrime, FPrime;
GetObjFieldDetails(lhsPrime, etran, out objPrime, out FPrime);
var Rhs = ((ExprRhs)s0.Rhs).Expr;
@@ -7752,7 +8150,7 @@ namespace Microsoft.Dafny {
"left-hand sides for different forall-statement bound variables may refer to the same location"));
}
- definedness.Add(new Bpl.AssumeCmd(s.Tok, Bpl.Expr.False));
+ definedness.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False));
// Now for the translation of the update itself
@@ -7760,7 +8158,7 @@ namespace Microsoft.Dafny {
var prevEtran = new ExpressionTranslator(this, predef, prevHeap);
updater.Add(Bpl.Cmd.SimpleAssign(s.Tok, prevHeap, etran.HeapExpr));
updater.Add(new Bpl.HavocCmd(s.Tok, new List<Bpl.IdentifierExpr> { (Bpl.IdentifierExpr/*TODO: this cast is rather dubious*/)etran.HeapExpr }));
- updater.Add(new Bpl.AssumeCmd(s.Tok, FunctionCall(s.Tok, BuiltinFunction.HeapSucc, null, prevHeap, etran.HeapExpr)));
+ updater.Add(TrAssumeCmd(s.Tok, HeapSucc(prevHeap, etran.HeapExpr)));
// Here comes:
// assume (forall<alpha> o: ref, f: Field alpha ::
@@ -7782,71 +8180,23 @@ namespace Microsoft.Dafny {
GetObjFieldDetails(s0.Lhs.Resolved, prevEtran, out xObj, out xField);
xBody = BplAnd(xBody, Bpl.Expr.Eq(o, xObj));
xBody = BplAnd(xBody, Bpl.Expr.Eq(f, xField));
- Bpl.Expr xObjField = new Bpl.ExistsExpr(s.Tok, xBvars, xBody);
+ //TRIG (exists k#2: int :: (k#2 == LitInt(0 - 3) || k#2 == LitInt(4)) && $o == read($prevHeap, this, _module.MyClass.arr) && $f == MultiIndexField(IndexField(i#0), j#0))
+ Bpl.Expr xObjField = new Bpl.ExistsExpr(s.Tok, xBvars, xBody); // LL_TRIGGER
Bpl.Expr body = Bpl.Expr.Or(Bpl.Expr.Eq(heapOF, oldHeapOF), xObjField);
var tr = new Trigger(s.Tok, true, new List<Expr>() { heapOF });
Bpl.Expr qq = new Bpl.ForallExpr(s.Tok, new List<TypeVariable> { alpha }, new List<Variable> { oVar, fVar }, null, tr, body);
- updater.Add(new Bpl.AssumeCmd(s.Tok, qq));
+ updater.Add(TrAssumeCmd(s.Tok, qq));
- if (s0.Rhs is ExprRhs) {
- Expression Fi = null;
- Func<Expression,Expression> lhsBuilder = null;
- lhs = s0.Lhs.Resolved;
- var i = s.BoundVars[0];
- if (s.BoundVars.Count == 1) {
- //var lhsContext = null;
- // Detect the following cases:
- // 0: forall i | R(i) { F(i).f := E(i); }
- // 1: forall i | R(i) { A[F(i)] := E(i); }
- // 2: forall i | R(i) { F(i)[N] := E(i); }
- if (lhs is MemberSelectExpr) {
- var ll = (MemberSelectExpr)lhs;
- Fi = ll.Obj;
- lhsBuilder = e => { var l = new MemberSelectExpr(ll.tok, e, ll.MemberName); l.Member = ll.Member; l.Type = ll.Type; return l; };
- } else if (lhs is SeqSelectExpr) {
- var ll = (SeqSelectExpr)lhs;
- Contract.Assert(ll.SelectOne);
- if (!ContainsFreeVariable(ll.Seq, false, i)) {
- Fi = ll.E0;
- lhsBuilder = e => { var l = new SeqSelectExpr(ll.tok, true, ll.Seq, e, null); l.Type = ll.Type; return l; };
- } else if (!ContainsFreeVariable(ll.E0, false, i)) {
- Fi = ll.Seq;
- lhsBuilder = e => { var l = new SeqSelectExpr(ll.tok, true, e, ll.E0, null); l.Type = ll.Type; return l; };
- }
- }
- }
- var rhs = ((ExprRhs)s0.Rhs).Expr;
- bool usedInversion = false;
- if (Fi != null) {
- var j = new BoundVar(i.tok, i.Name + "#inv", Fi.Type);
- var jj = Expression.CreateIdentExpr(j);
- var jList = new List<BoundVar>() { j };
- var vals = InvertExpression(i, j, s.Range, Fi);
-#if DEBUG_PRINT
- Console.WriteLine("DEBUG: Trying to invert:");
- Console.WriteLine("DEBUG: " + Printer.ExprToString(s.Range) + " && " + j.Name + " == " + Printer.ExprToString(Fi));
- if (vals == null) {
- Console.WriteLine("DEBUG: Can't");
- } else {
- Console.WriteLine("DEBUG: The inverse is the disjunction of the following:");
- foreach (var val in vals) {
- Console.WriteLine("DEBUG: " + Printer.ExprToString(val.Range) + " && " + Printer.ExprToString(val.FInverse) + " == " + i.Name);
- }
- }
-#endif
- if (vals != null) {
- foreach (var val in vals) {
- qq = TrForall_NewValueAssumption(s.Tok, jList, val.Range, lhsBuilder(jj), Substitute(rhs, i, val.FInverse), true, etran, prevEtran);
- updater.Add(new Bpl.AssumeCmd(s.Tok, qq));
- }
- usedInversion = true;
- }
- }
- if (!usedInversion) {
- qq = TrForall_NewValueAssumption(s.Tok, s.BoundVars, s.Range, lhs, rhs, false, etran, prevEtran);
- updater.Add(new Bpl.AssumeCmd(s.Tok, qq));
+ if (s.ForallExpressions != null) {
+ foreach (ForallExpr expr in s.ForallExpressions) {
+ BinaryExpr term = (BinaryExpr)expr.Term;
+ Contract.Assert(term != null);
+ var e0 = Substitute(((BinaryExpr)term).E0.Resolved, null, substMap);
+ var e1 = Substitute(((BinaryExpr)term).E1, null, substMap);
+ qq = TrForall_NewValueAssumption(expr.tok, expr.BoundVars, expr.Range, e0, e1, expr.Attributes, etran, prevEtran);
+ updater.Add(TrAssumeCmd(s.Tok, qq));
}
- }
+ }
}
/// <summary>
@@ -7860,7 +8210,7 @@ namespace Microsoft.Dafny {
/// G is rhs
/// If lhsAsTrigger is true, then use the LHS of the equality above as the trigger; otherwise, don't specify any trigger.
/// </summary>
- private Bpl.Expr TrForall_NewValueAssumption(IToken tok, List<BoundVar> boundVars, Expression range, Expression lhs, Expression rhs, bool lhsAsTrigger, ExpressionTranslator etran, ExpressionTranslator prevEtran) {
+ private Bpl.Expr TrForall_NewValueAssumption(IToken tok, List<BoundVar> boundVars, Expression range, Expression lhs, Expression rhs, Attributes attributes, ExpressionTranslator etran, ExpressionTranslator prevEtran) {
Contract.Requires(tok != null);
Contract.Requires(boundVars != null);
Contract.Requires(range != null);
@@ -7880,159 +8230,27 @@ namespace Microsoft.Dafny {
Type lhsType = lhs is MemberSelectExpr ? ((MemberSelectExpr)lhs).Type : null;
g = CondApplyBox(rhs.tok, g, rhs.Type, lhsType);
- Trigger tr = lhsAsTrigger ? new Trigger(tok, true, new List<Bpl.Expr>() { xHeapOF }) : null;
- return new Bpl.ForallExpr(tok, xBvars, tr, Bpl.Expr.Imp(xAnte, Bpl.Expr.Eq(xHeapOF, g)));
- }
-
- class ForallStmtTranslationValues
- {
- public readonly Expression Range;
- public readonly Expression FInverse;
- public ForallStmtTranslationValues(Expression range, Expression fInverse) {
- Contract.Requires(range != null);
- Contract.Requires(fInverse != null);
- Range = range;
- FInverse = fInverse;
- }
- public ForallStmtTranslationValues Subst(IVariable j, Expression e, Translator translator) {
- Contract.Requires(j != null);
- Contract.Requires(e != null);
- Contract.Requires(translator != null);
- var substMap = new Dictionary<IVariable, Expression>();
- substMap.Add(j, e);
- var v = new ForallStmtTranslationValues(translator.Substitute(Range, null, substMap), translator.Substitute(FInverse, null, substMap));
- return v;
- }
- }
-
- /// <summary>
- /// Find piecewise inverse of F under R. More precisely, find lists of expressions P and F-1
- /// such that
- /// R(i) && j == F(i)
- /// holds iff the disjunction of the following predicates holds:
- /// P_0(j) && F-1_0(j) == i
- /// ...
- /// P_{n-1}(j) && F-1_{n-1}(j) == i
- /// If no such disjunction is found, return null.
- /// If such a disjunction is found, return for each disjunct:
- /// * The predicate P_k(j), which is an expression that may have free occurrences of j (but no free occurrences of i)
- /// * The expression F-1_k(j), which also may have free occurrences of j but not of i
- /// </summary>
- private List<ForallStmtTranslationValues> InvertExpression(BoundVar i, BoundVar j, Expression R, Expression F) {
- Contract.Requires(i != null);
- Contract.Requires(j != null);
- Contract.Requires(R != null);
- Contract.Requires(F != null);
- var vals = new List<ForallStmtTranslationValues>(InvertExpressionIter(i, j, R, F));
- if (vals.Count == 0) {
- return null;
- } else {
- return vals;
- }
- }
- /// <summary>
- /// See InvertExpression.
- /// </summary>
- private IEnumerable<ForallStmtTranslationValues> InvertExpressionIter(BoundVar i, BoundVar j, Expression R, Expression F) {
- Contract.Requires(i != null);
- Contract.Requires(j != null);
- Contract.Requires(R != null);
- Contract.Requires(F != null);
- F = F.Resolved;
- if (!ContainsFreeVariable(F, false, i)) {
- // We're looking at R(i) && j == K.
- // We cannot invert j == K, but if we're lucky, R(i) contains a conjunct i==G.
- Expression r = Expression.CreateBoolLiteral(R.tok, true);
- Expression G = null;
- foreach (var c in Expression.Conjuncts(R)) {
- if (G == null && c is BinaryExpr) {
- var bin = (BinaryExpr)c;
- if (BinaryExpr.IsEqualityOp(bin.ResolvedOp)) {
- var id = bin.E0.Resolved as IdentifierExpr;
- if (id != null && id.Var == i) {
- G = bin.E1;
- continue;
- }
- id = bin.E1.Resolved as IdentifierExpr;
- if (id != null && id.Var == i) {
- G = bin.E0;
- continue;
- }
+ Bpl.Trigger tr = null;
+ var argsEtran = etran.WithNoLits();
+ foreach (var aa in attributes.AsEnumerable()) {
+ if (aa.Name == "trigger") {
+ List<Bpl.Expr> tt = new List<Bpl.Expr>();
+ foreach (var arg in aa.Args) {
+ if (arg == lhs) {
+ tt.Add(xHeapOF);
+ } else {
+ tt.Add(argsEtran.TrExpr(arg));
}
}
- r = Expression.CreateAnd(r, c);
- }
- if (G != null) {
- var jIsK = Expression.CreateEq(Expression.CreateIdentExpr(j), F, j.Type);
- var rr = Substitute(r, i, G);
- yield return new ForallStmtTranslationValues(Expression.CreateAnd(rr, jIsK), G);
- }
- } else if (F is IdentifierExpr) {
- var e = (IdentifierExpr)F;
- if (e.Var == i) {
- // We're looking at R(i) && j == i, which is particularly easy to invert: R(j) && j == i
- var jj = Expression.CreateIdentExpr(j);
- yield return new ForallStmtTranslationValues(Substitute(R, i, jj), jj);
- }
- } else if (F is BinaryExpr) {
- var bin = (BinaryExpr)F;
- if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Add && (bin.E0.Type.IsIntegerType || bin.E0.Type.IsRealType)) {
- if (!ContainsFreeVariable(bin.E1, false, i)) {
- // We're looking at: R(i) && j == f(i) + K.
- // By a recursive call, we'll ask to invert: R(i) && j' == f(i).
- // For each P_0(j') && f-1_0(j') == i we get back, we yield:
- // P_0(j - K) && f-1_0(j - K) == i
- var jMinusK = Expression.CreateSubtract(Expression.CreateIdentExpr(j), bin.E1);
- foreach (var val in InvertExpression(i, j, R, bin.E0)) {
- yield return val.Subst(j, jMinusK, this);
- }
- } else if (!ContainsFreeVariable(bin.E0, false, i)) {
- // We're looking at: R(i) && j == K + f(i)
- // Do as in previous case, but with operands reversed.
- var jMinusK = Expression.CreateSubtract(Expression.CreateIdentExpr(j), bin.E0);
- foreach (var val in InvertExpression(i, j, R, bin.E1)) {
- yield return val.Subst(j, jMinusK, this);
- }
- }
- } else if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Sub && (bin.E0.Type.IsIntegerType || bin.E0.Type.IsRealType)) {
- if (!ContainsFreeVariable(bin.E1, false, i)) {
- // We're looking at: R(i) && j == f(i) - K
- // Recurse on f(i) and then replace j := j + K
- var jPlusK = Expression.CreateAdd(Expression.CreateIdentExpr(j), bin.E1);
- foreach (var val in InvertExpression(i, j, R, bin.E0)) {
- yield return val.Subst(j, jPlusK, this);
- }
- } else if (!ContainsFreeVariable(bin.E0, false, i)) {
- // We're looking at: R(i) && j == K - f(i)
- // Recurse on f(i) and then replace j := K - j
- var kMinusJ = Expression.CreateAdd(Expression.CreateIdentExpr(j), bin.E0);
- foreach (var val in InvertExpression(i, j, R, bin.E1)) {
- yield return val.Subst(j, kMinusJ, this);
- }
- }
- }
- } else if (F is ITEExpr) {
- var ife = (ITEExpr)F;
- // We're looking at R(i) && j == if A(i) then B(i) else C(i), which is equivalent to the disjunction of:
- // R(i) && A(i) && j == B(i)
- // R(i) && !A(i) && j == C(i)
- // We recurse on each one, yielding the results
- var r = Expression.CreateAnd(R, ife.Test);
- var valsThen = InvertExpression(i, j, r, ife.Thn);
- if (valsThen != null) {
- r = Expression.CreateAnd(R, Expression.CreateNot(ife.tok, ife.Test));
- var valsElse = InvertExpression(i, j, r, ife.Els);
- if (valsElse != null) {
- foreach (var val in valsThen) { yield return val; }
- foreach (var val in valsElse) { yield return val; }
- }
+ tr = new Bpl.Trigger(tok, true, tt, tr);
}
}
+ return new Bpl.ForallExpr(tok, xBvars, tr, Bpl.Expr.Imp(xAnte, Bpl.Expr.Eq(xHeapOF, g)));
}
delegate Bpl.Expr ExpressionConverter(Dictionary<IVariable, Expression> substMap, ExpressionTranslator etran);
- void TrForallStmtCall(IToken tok, List<BoundVar> boundVars, Expression range, ExpressionConverter additionalRange, CallStmt s0,
+ void TrForallStmtCall(IToken tok, List<BoundVar> boundVars, Expression range, ExpressionConverter additionalRange, List<Expression> forallExpressions, CallStmt s0,
Bpl.StmtListBuilder definedness, Bpl.StmtListBuilder exporter, List<Variable> locals, ExpressionTranslator etran) {
Contract.Requires(tok != null);
Contract.Requires(boundVars != null);
@@ -8079,18 +8297,18 @@ namespace Microsoft.Dafny {
havocIds.Add(new Bpl.IdentifierExpr(tok, bv));
}
definedness.Add(new Bpl.HavocCmd(tok, havocIds));
- definedness.Add(new Bpl.AssumeCmd(tok, ante));
+ definedness.Add(TrAssumeCmd(tok, ante));
}
TrStmt_CheckWellformed(range, definedness, locals, etran, false);
- definedness.Add(new Bpl.AssumeCmd(range.tok, etran.TrExpr(range)));
+ definedness.Add(TrAssumeCmd(range.tok, etran.TrExpr(range)));
if (additionalRange != null) {
var es = additionalRange(new Dictionary<IVariable, Expression>(), etran);
- definedness.Add(new Bpl.AssumeCmd(es.tok, es));
+ definedness.Add(TrAssumeCmd(es.tok, es));
}
TrStmt(s0, definedness, locals, etran);
- definedness.Add(new Bpl.AssumeCmd(tok, Bpl.Expr.False));
+ definedness.Add(TrAssumeCmd(tok, Bpl.Expr.False));
}
// Now for the other branch, where the postcondition of the call is exported.
@@ -8107,7 +8325,7 @@ namespace Microsoft.Dafny {
Contract.Assert(s0.Method.Mod.Expressions.Count == 0); // checked by the resolver
foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(tok, new List<FrameExpression>(), s0.IsGhost, initEtran, etran, initEtran)) {
if (tri.IsFree) {
- exporter.Add(new Bpl.AssumeCmd(tok, tri.Expr));
+ exporter.Add(TrAssumeCmd(tok, tri.Expr));
}
}
if (codeContext is IteratorDecl) {
@@ -8115,35 +8333,56 @@ namespace Microsoft.Dafny {
RecordNewObjectsIn_New(tok, iter, initHeap, heapIdExpr, exporter, locals, etran);
}
- var bvars = new List<Variable>();
- Dictionary<IVariable, Expression> substMap;
- var ante = initEtran.TrBoundVariablesRename(boundVars, bvars, out substMap);
- ante = BplAnd(ante, initEtran.TrExpr(Substitute(range, null, substMap)));
- if (additionalRange != null) {
- ante = BplAnd(ante, additionalRange(substMap, initEtran));
- }
-
- // Note, in the following, we need to do a bit of a song and dance. The actual arguements of the
+ // Note, in the following, we need to do a bit of a song and dance. The actual arguments of the
// call should be translated using "initEtran", whereas the method postcondition should be translated
// using "callEtran". To accomplish this, we translate the argument and then tuck the resulting
// Boogie expressions into BoogieExprWrappers that are used in the DafnyExpr-to-DafnyExpr substitution.
- // TODO
- var argsSubstMap = new Dictionary<IVariable, Expression>(); // maps formal arguments to actuals
- Contract.Assert(s0.Method.Ins.Count == s0.Args.Count);
- for (int i = 0; i < s0.Method.Ins.Count; i++) {
- var arg = Substitute(s0.Args[i], null, substMap, s0.MethodSelect.TypeArgumentSubstitutions()); // substitute the renamed bound variables for the declared ones
- argsSubstMap.Add(s0.Method.Ins[i], new BoogieWrapper(initEtran.TrExpr(arg), s0.Args[i].Type));
- }
- var receiver = new BoogieWrapper(initEtran.TrExpr(Substitute(s0.Receiver, null, substMap, s0.MethodSelect.TypeArgumentSubstitutions())), s0.Receiver.Type);
- var callEtran = new ExpressionTranslator(this, predef, etran.HeapExpr, initHeap);
- Bpl.Expr post = Bpl.Expr.True;
- foreach (var ens in s0.Method.Ens) {
- var p = Substitute(ens.E, receiver, argsSubstMap, s0.MethodSelect.TypeArgumentSubstitutions()); // substitute the call's actuals for the method's formals
- post = BplAnd(post, callEtran.TrExpr(p));
- }
-
- Bpl.Expr qq = new Bpl.ForallExpr(tok, bvars, Bpl.Expr.Imp(ante, post));
- exporter.Add(new Bpl.AssumeCmd(tok, qq));
+ // TODO
+ Bpl.Expr qq;
+ if (forallExpressions != null) {
+ var callEtran = new ExpressionTranslator(this, predef, etran.HeapExpr, initHeap);
+ foreach (Expression expr in forallExpressions) {
+ Dictionary<IVariable, Expression> substMap = new Dictionary<IVariable, Expression>();
+ var e = Substitute(expr, null, substMap, null);
+ var argsSubstMap = new Dictionary<IVariable, Expression>();
+ Contract.Assert(s0.Method.Ins.Count == s0.Args.Count);
+ for (int i = 0; i < s0.Method.Ins.Count; i++) {
+ var arg = Substitute(s0.Args[i], null, substMap, s0.MethodSelect.TypeArgumentSubstitutions()); // substitute the renamed bound variables for the declared ones
+ argsSubstMap.Add(s0.Method.Ins[i], new BoogieWrapper(initEtran.TrExpr(arg), s0.Args[i].Type));
+ }
+ var receiver = new BoogieWrapper(initEtran.TrExpr(Substitute(s0.Receiver, null, substMap, s0.MethodSelect.TypeArgumentSubstitutions())), s0.Receiver.Type);
+ var p = Substitute(e, receiver, argsSubstMap, s0.MethodSelect.TypeArgumentSubstitutions()); // substitute the call's actuals for the method's formals
+ qq = callEtran.TrExpr(p, initEtran, null, null);
+ exporter.Add(TrAssumeCmd(tok, qq));
+ }
+ } else {
+ var bvars = new List<Variable>();
+ Dictionary<IVariable, Expression> substMap;
+ var ante = initEtran.TrBoundVariablesRename(boundVars, bvars, out substMap);
+ var argsSubstMap = new Dictionary<IVariable, Expression>(); // maps formal arguments to actuals
+ Contract.Assert(s0.Method.Ins.Count == s0.Args.Count);
+ for (int i = 0; i < s0.Method.Ins.Count; i++) {
+ var arg = Substitute(s0.Args[i], null, substMap, s0.MethodSelect.TypeArgumentSubstitutions()); // substitute the renamed bound variables for the declared ones
+ argsSubstMap.Add(s0.Method.Ins[i], new BoogieWrapper(initEtran.TrExpr(arg), s0.Args[i].Type));
+ }
+ var callEtran = new ExpressionTranslator(this, predef, etran.HeapExpr, initHeap);
+ ante = BplAnd(ante, initEtran.TrExpr(Substitute(range, null, substMap)));
+ if (additionalRange != null) {
+ ante = BplAnd(ante, additionalRange(substMap, initEtran));
+ }
+ var receiver = new BoogieWrapper(initEtran.TrExpr(Substitute(s0.Receiver, null, substMap, s0.MethodSelect.TypeArgumentSubstitutions())), s0.Receiver.Type);
+ Bpl.Expr post = Bpl.Expr.True;
+ foreach (var ens in s0.Method.Ens) {
+ var p = Substitute(ens.E, receiver, argsSubstMap, s0.MethodSelect.TypeArgumentSubstitutions()); // substitute the call's actuals for the method's formals
+ post = BplAnd(post, callEtran.TrExpr(p));
+ }
+
+ // TRIG (forall $ih#s0#0: Seq Box :: $Is($ih#s0#0, TSeq(TChar)) && $IsAlloc($ih#s0#0, TSeq(TChar), $initHeapForallStmt#0) && Seq#Length($ih#s0#0) != 0 && Seq#Rank($ih#s0#0) < Seq#Rank(s#0) ==> (forall i#2: int :: true ==> LitInt(0) <= i#2 && i#2 < Seq#Length($ih#s0#0) ==> char#ToInt(_module.CharChar.MinChar($LS($LZ), $Heap, this, $ih#s0#0)) <= char#ToInt($Unbox(Seq#Index($ih#s0#0, i#2)): char)))
+ // TRIG (forall $ih#pat0#0: Seq Box, $ih#a0#0: Seq Box :: $Is($ih#pat0#0, TSeq(_module._default.Same0$T)) && $IsAlloc($ih#pat0#0, TSeq(_module._default.Same0$T), $initHeapForallStmt#0) && $Is($ih#a0#0, TSeq(_module._default.Same0$T)) && $IsAlloc($ih#a0#0, TSeq(_module._default.Same0$T), $initHeapForallStmt#0) && Seq#Length($ih#pat0#0) <= Seq#Length($ih#a0#0) && Seq#SameUntil($ih#pat0#0, $ih#a0#0, Seq#Length($ih#pat0#0)) && (Seq#Rank($ih#pat0#0) < Seq#Rank(pat#0) || (Seq#Rank($ih#pat0#0) == Seq#Rank(pat#0) && Seq#Rank($ih#a0#0) < Seq#Rank(a#0))) ==> _module.__default.IsRelaxedPrefixAux(_module._default.Same0$T, $LS($LZ), $Heap, $ih#pat0#0, $ih#a0#0, LitInt(1)))'
+ // TRIG (forall $ih#m0#0: DatatypeType, $ih#n0#0: DatatypeType :: $Is($ih#m0#0, Tclass._module.Nat()) && $IsAlloc($ih#m0#0, Tclass._module.Nat(), $initHeapForallStmt#0) && $Is($ih#n0#0, Tclass._module.Nat()) && $IsAlloc($ih#n0#0, Tclass._module.Nat(), $initHeapForallStmt#0) && Lit(true) && (DtRank($ih#m0#0) < DtRank(m#0) || (DtRank($ih#m0#0) == DtRank(m#0) && DtRank($ih#n0#0) < DtRank(n#0))) ==> _module.__default.mult($LS($LZ), $Heap, $ih#m0#0, _module.__default.plus($LS($LZ), $Heap, $ih#n0#0, $ih#n0#0)) == _module.__default.mult($LS($LZ), $Heap, _module.__default.plus($LS($LZ), $Heap, $ih#m0#0, $ih#m0#0), $ih#n0#0))
+ qq = new Bpl.ForallExpr(tok, bvars, Bpl.Expr.Imp(ante, post)); // SMART_TRIGGER
+ exporter.Add(TrAssumeCmd(tok, qq));
+ }
}
}
@@ -8157,7 +8396,7 @@ namespace Microsoft.Dafny {
Contract.Requires(locals != null);
Contract.Requires(etran != null);
// Add all newly allocated objects to the set this._new
- var updatedSet = new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, CurrentIdGenerator.FreshId("$iter_newUpdate"), predef.SetType(iter.tok, predef.BoxType)));
+ var updatedSet = new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, CurrentIdGenerator.FreshId("$iter_newUpdate"), predef.SetType(iter.tok, true, predef.BoxType)));
locals.Add(updatedSet);
var updatedSetIE = new Bpl.IdentifierExpr(iter.tok, updatedSet);
// call $iter_newUpdate := $IterCollectNewObjects(initHeap, $Heap, this, _new);
@@ -8209,10 +8448,10 @@ namespace Microsoft.Dafny {
havocIds.Add(new Bpl.IdentifierExpr(s.Tok, bv));
}
definedness.Add(new Bpl.HavocCmd(s.Tok, havocIds));
- definedness.Add(new Bpl.AssumeCmd(s.Tok, typeAntecedent));
+ definedness.Add(TrAssumeCmd(s.Tok, typeAntecedent));
}
TrStmt_CheckWellformed(s.Range, definedness, locals, etran, false);
- definedness.Add(new Bpl.AssumeCmd(s.Range.tok, etran.TrExpr(s.Range)));
+ definedness.Add(TrAssumeCmd(s.Range.tok, etran.TrExpr(s.Range)));
if (s.Body != null) {
TrStmt(s.Body, definedness, locals, etran);
@@ -8230,7 +8469,7 @@ namespace Microsoft.Dafny {
}
}
- definedness.Add(new Bpl.AssumeCmd(s.Tok, Bpl.Expr.False));
+ definedness.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False));
// Now for the other branch, where the ensures clauses are exported.
@@ -8244,28 +8483,18 @@ namespace Microsoft.Dafny {
exporter.Add(new Bpl.HavocCmd(s.Tok, new List<Bpl.IdentifierExpr> { (Bpl.IdentifierExpr/*TODO: this cast is rather dubious*/)etran.HeapExpr, etran.Tick() }));
foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(s.Tok, new List<FrameExpression>(), s.IsGhost, initEtran, etran, initEtran)) {
if (tri.IsFree) {
- exporter.Add(new Bpl.AssumeCmd(s.Tok, tri.Expr));
+ exporter.Add(TrAssumeCmd(s.Tok, tri.Expr));
}
}
- var bvars = new List<Variable>();
- Dictionary<IVariable, Expression> substMap;
- var ante = initEtran.TrBoundVariablesRename(s.BoundVars, bvars, out substMap);
- var range = Substitute(s.Range, null, substMap);
- ante = BplAnd(ante, initEtran.TrExpr(range));
-
- Bpl.Expr post = Bpl.Expr.True;
- foreach (var ens in s.Ens) {
- var p = Substitute(ens.E, null, substMap);
- post = BplAnd(post, etran.TrExpr(p));
- }
-
- Bpl.Expr qq = Bpl.Expr.Imp(ante, post);
- if (bvars.Count != 0) {
- var triggers = TrTrigger(etran, s.Attributes, s.Tok, substMap);
- qq = new Bpl.ForallExpr(s.Tok, bvars, triggers, qq);
+ Dictionary<IVariable, Expression> substMap = new Dictionary<IVariable, Expression>();
+ var p = Substitute(s.ForallExpressions[0], null, substMap);
+ Bpl.Expr qq = etran.TrExpr(p, initEtran, null, null);
+ if (s.BoundVars.Count != 0) {
+ exporter.Add(TrAssumeCmd(s.Tok, qq));
+ } else {
+ exporter.Add(TrAssumeCmd(s.Tok, ((Bpl.ForallExpr)qq).Body));
}
- exporter.Add(new Bpl.AssumeCmd(s.Tok, qq));
}
private string GetObjFieldDetails(Expression lhs, ExpressionTranslator etran, out Bpl.Expr obj, out Bpl.Expr F) {
@@ -8289,6 +8518,15 @@ namespace Microsoft.Dafny {
return description;
}
+ Bpl.AssumeCmd TrAssumeCmd(IToken tok, Bpl.Expr expr, Bpl.QKeyValue attributes = null) {
+ var lit = RemoveLit(expr);
+ return attributes == null ? new Bpl.AssumeCmd(tok, lit) : new Bpl.AssumeCmd(tok, lit, attributes);
+ }
+
+ Bpl.AssertCmd TrAssertCmd(IToken tok, Bpl.Expr expr, Bpl.QKeyValue attributes = null) {
+ var lit = RemoveLit(expr);
+ return attributes == null ? new Bpl.AssertCmd(tok, lit) : new Bpl.AssertCmd(tok, lit, attributes);
+ }
delegate void BodyTranslator(Bpl.StmtListBuilder builder, ExpressionTranslator etran);
@@ -8337,11 +8575,11 @@ namespace Microsoft.Dafny {
Bpl.StmtListBuilder invDefinednessBuilder = new Bpl.StmtListBuilder();
foreach (MaybeFreeExpression loopInv in s.Invariants) {
TrStmt_CheckWellformed(loopInv.E, invDefinednessBuilder, locals, etran, false);
- invDefinednessBuilder.Add(new Bpl.AssumeCmd(loopInv.E.tok, etran.TrExpr(loopInv.E)));
+ invDefinednessBuilder.Add(TrAssumeCmd(loopInv.E.tok, etran.TrExpr(loopInv.E)));
- invariants.Add(new Bpl.AssumeCmd(loopInv.E.tok, Bpl.Expr.Imp(w, CanCallAssumption(loopInv.E, etran))));
+ invariants.Add(TrAssumeCmd(loopInv.E.tok, Bpl.Expr.Imp(w, CanCallAssumption(loopInv.E, etran))));
if (loopInv.IsFree && !DafnyOptions.O.DisallowSoundnessCheating) {
- invariants.Add(new Bpl.AssumeCmd(loopInv.E.tok, Bpl.Expr.Imp(w, etran.TrExpr(loopInv.E))));
+ invariants.Add(TrAssumeCmd(loopInv.E.tok, Bpl.Expr.Imp(w, etran.TrExpr(loopInv.E))));
} else {
bool splitHappened;
var ss = TrSplitExpr(loopInv.E, etran, false, out splitHappened);
@@ -8354,7 +8592,7 @@ namespace Microsoft.Dafny {
if (split.IsChecked) {
invariants.Add(Assert(split.E.tok, wInv, "loop invariant violation")); // TODO: it would be fine to have this use {:subsumption 0}
} else {
- invariants.Add(new Bpl.AssumeCmd(split.E.tok, wInv));
+ invariants.Add(TrAssumeCmd(split.E.tok, wInv));
}
}
}
@@ -8370,14 +8608,14 @@ namespace Microsoft.Dafny {
// include boilerplate invariants
foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(s.Tok, modifiesClause, s.IsGhost, etranPreLoop, etran, etran.Old)) {
if (tri.IsFree) {
- invariants.Add(new Bpl.AssumeCmd(s.Tok, tri.Expr));
+ invariants.Add(TrAssumeCmd(s.Tok, tri.Expr));
} else {
Contract.Assert(tri.ErrorMessage != null); // follows from BoilerplateTriple invariant
invariants.Add(Assert(s.Tok, tri.Expr, tri.ErrorMessage));
}
}
// add a free invariant which says that the heap hasn't changed outside of the modifies clause.
- invariants.Add(new Bpl.AssumeCmd(s.Tok, FrameConditionUsingDefinedFrame(s.Tok, etranPreLoop, etran, updatedFrameEtran)));
+ invariants.Add(TrAssumeCmd(s.Tok, FrameConditionUsingDefinedFrame(s.Tok, etranPreLoop, etran, updatedFrameEtran)));
}
// include a free invariant that says that all completed iterations so far have only decreased the termination metric
@@ -8391,13 +8629,13 @@ namespace Microsoft.Dafny {
decrs.Add(etran.TrExpr(e));
}
Bpl.Expr decrCheck = DecreasesCheck(toks, types, types, decrs, initDecr, null, null, true, false);
- invariants.Add(new Bpl.AssumeCmd(s.Tok, decrCheck));
+ invariants.Add(TrAssumeCmd(s.Tok, decrCheck));
}
Bpl.StmtListBuilder loopBodyBuilder = new Bpl.StmtListBuilder();
loopBodyBuilder.Add(CaptureState(s.Tok, true, "after some loop iterations"));
// as the first thing inside the loop, generate: if (!w) { CheckWellformed(inv); assume false; }
- invDefinednessBuilder.Add(new Bpl.AssumeCmd(s.Tok, Bpl.Expr.False));
+ invDefinednessBuilder.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False));
loopBodyBuilder.Add(new Bpl.IfCmd(s.Tok, Bpl.Expr.Not(w), invDefinednessBuilder.Collect(s.Tok), null, null));
// generate: CheckWellformed(guard); if (!guard) { break; }
Bpl.Expr guard = null;
@@ -8438,13 +8676,13 @@ namespace Microsoft.Dafny {
loopBodyBuilder.Add(Assert(s.Tok, decrCheck, msg));
}
} else {
- loopBodyBuilder.Add(new Bpl.AssumeCmd(s.Tok, Bpl.Expr.False));
+ loopBodyBuilder.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False));
// todo(maria): havoc stuff
}
// Finally, assume the well-formedness of the invariant (which has been checked once and for all above), so that the check
// of invariant-maintenance can use the appropriate canCall predicates.
foreach (MaybeFreeExpression loopInv in s.Invariants) {
- loopBodyBuilder.Add(new Bpl.AssumeCmd(loopInv.E.tok, CanCallAssumption(loopInv.E, etran)));
+ loopBodyBuilder.Add(TrAssumeCmd(loopInv.E.tok, CanCallAssumption(loopInv.E, etran)));
}
Bpl.StmtList body = loopBodyBuilder.Collect(s.Tok);
@@ -8454,7 +8692,7 @@ namespace Microsoft.Dafny {
void TrAlternatives(List<GuardedAlternative> alternatives, Bpl.Cmd elseCase0, Bpl.StructuredCmd elseCase1,
Bpl.StmtListBuilder builder, List<Variable> locals, ExpressionTranslator etran) {
Contract.Requires(alternatives != null);
- Contract.Requires((elseCase0 != null) == (elseCase1 == null)); // ugly way of doing a type union
+ Contract.Requires((elseCase0 == null) != (elseCase1 == null)); // ugly way of doing a type union
Contract.Requires(builder != null);
Contract.Requires(locals != null);
Contract.Requires(etran != null);
@@ -8468,15 +8706,18 @@ namespace Microsoft.Dafny {
return;
}
+ // alpha-rename any existential guards
+ var guards = alternatives.ConvertAll(alt => alt.IsExistentialGuard ? AlphaRename((ExistsExpr)alt.Guard, "eg$", this) : alt.Guard);
+
// build the negation of the disjunction of all guards (that is, the conjunction of their negations)
Bpl.Expr noGuard = Bpl.Expr.True;
- foreach (var alternative in alternatives) {
- noGuard = BplAnd(noGuard, Bpl.Expr.Not(etran.TrExpr(alternative.Guard)));
+ foreach (var g in guards) {
+ noGuard = BplAnd(noGuard, Bpl.Expr.Not(etran.TrExpr(g)));
}
var b = new Bpl.StmtListBuilder();
var elseTok = elseCase0 != null ? elseCase0.tok : elseCase1.tok;
- b.Add(new Bpl.AssumeCmd(elseTok, noGuard));
+ b.Add(TrAssumeCmd(elseTok, noGuard));
if (elseCase0 != null) {
b.Add(elseCase0);
} else {
@@ -8490,8 +8731,13 @@ namespace Microsoft.Dafny {
CurrentIdGenerator.Push();
var alternative = alternatives[i];
b = new Bpl.StmtListBuilder();
- TrStmt_CheckWellformed(alternative.Guard, b, locals, etran, true);
- b.Add(new AssumeCmd(alternative.Guard.tok, etran.TrExpr(alternative.Guard)));
+ TrStmt_CheckWellformed(guards[i], b, locals, etran, true);
+ if (alternative.IsExistentialGuard) {
+ var exists = (ExistsExpr)alternative.Guard; // the original (that is, not alpha-renamed) guard
+ IntroduceAndAssignExistentialVars(exists, b, builder, locals, etran);
+ } else {
+ b.Add(new AssumeCmd(alternative.Guard.tok, etran.TrExpr(alternative.Guard)));
+ }
foreach (var s in alternative.Body) {
TrStmt(s, b, locals, etran);
}
@@ -8693,7 +8939,7 @@ namespace Microsoft.Dafny {
Contract.Assert(codeContext != null);
List<Expression> contextDecreases = codeContext.Decreases.Expressions;
List<Expression> calleeDecreases = callee.Decreases.Expressions;
- CheckCallTermination(tok, contextDecreases, calleeDecreases, null, receiver, substMap, etran, etran.Old, builder, codeContext.InferredDecreases, null);
+ CheckCallTermination(tok, contextDecreases, calleeDecreases, null, receiver, substMap, tySubst, etran, etran.Old, builder, codeContext.InferredDecreases, null);
}
// Create variables to hold the output parameters of the call, so that appropriate unboxes can be introduced.
@@ -8716,7 +8962,7 @@ namespace Microsoft.Dafny {
builder.Add(new CommentCmd("ProcessCallStmt: Make the call"));
// Make the call
- Bpl.CallCmd call = new Bpl.CallCmd(tok, MethodName(callee, kind), ins, outs);
+ Bpl.CallCmd call = Call(tok, MethodName(callee, kind), ins, outs);
if (module != currentModule && RefinementToken.IsInherited(tok, currentModule) && (codeContext == null || !codeContext.MustReverify)) {
// The call statement is inherited, so the refined module already checked that the precondition holds. Note,
// preconditions are not allowed to be strengthened, except if they use a predicate whose body has been strengthened.
@@ -8740,7 +8986,7 @@ namespace Microsoft.Dafny {
// the out-parameter.
Bpl.Cmd cmd = new Bpl.HavocCmd(bLhs.tok, new List<Bpl.IdentifierExpr> { bLhs });
builder.Add(cmd);
- cmd = new Bpl.AssumeCmd(bLhs.tok, Bpl.Expr.Eq(bLhs, FunctionCall(bLhs.tok, BuiltinFunction.Unbox, TrType(LhsTypes[i]), tmpVarIdE)));
+ cmd = TrAssumeCmd(bLhs.tok, Bpl.Expr.Eq(bLhs, FunctionCall(bLhs.tok, BuiltinFunction.Unbox, TrType(LhsTypes[i]), tmpVarIdE)));
builder.Add(cmd);
}
}
@@ -8768,7 +9014,7 @@ namespace Microsoft.Dafny {
builder.Add(new Bpl.HavocCmd(bv.tok, new List<Bpl.IdentifierExpr> { bIe }));
Bpl.Expr wh = GetWhereClause(bv.tok, bIe, local.Type, etran);
if (wh != null) {
- builder.Add(new Bpl.AssumeCmd(bv.tok, wh));
+ builder.Add(TrAssumeCmd(bv.tok, wh));
}
}
return substMap;
@@ -8804,6 +9050,7 @@ namespace Microsoft.Dafny {
void CheckCallTermination(IToken tok, List<Expression> contextDecreases, List<Expression> calleeDecreases,
Bpl.Expr allowance,
Expression receiverReplacement, Dictionary<IVariable,Expression> substMap,
+ Dictionary<TypeParameter, Type> typeMap,
ExpressionTranslator etranCurrent, ExpressionTranslator etranInitial, Bpl.StmtListBuilder builder, bool inferredDecreases, string hint) {
Contract.Requires(tok != null);
Contract.Requires(cce.NonNullElements(contextDecreases));
@@ -8835,7 +9082,7 @@ namespace Microsoft.Dafny {
tok = new ForceCheckToken(tok);
}
for (int i = 0; i < N; i++) {
- Expression e0 = Substitute(calleeDecreases[i], receiverReplacement, substMap);
+ Expression e0 = Substitute(calleeDecreases[i], receiverReplacement, substMap, typeMap);
Expression e1 = contextDecreases[i];
if (!CompatibleDecreasesTypes(e0.Type, e1.Type)) {
N = i;
@@ -9049,9 +9296,9 @@ namespace Microsoft.Dafny {
less = Bpl.Expr.Gt(e0, e1);
atmost = Bpl.Expr.Ge(e0, e1);
- } else if (ty0 is SetType || (ty0 is MapType && ((MapType)ty0).Finite)) {
+ } else if ((ty0 is SetType && ((SetType)ty0).Finite) || (ty0 is MapType && ((MapType)ty0).Finite)) {
Bpl.Expr b0, b1;
- if (ty0 is SetType) {
+ if (ty0 is SetType && ((SetType)ty0).Finite) {
b0 = e0;
b1 = e1;
} else if (ty0 is MapType && ((MapType)ty0).Finite) {
@@ -9130,40 +9377,41 @@ namespace Microsoft.Dafny {
Contract.Requires(type != null);
Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
- type = type.NormalizeExpand();
-
- if (type is SetType) {
- return FunctionCall(Token.NoToken, "TSet", predef.Ty, TypeToTy(((CollectionType)type).Arg));
- } else if (type is MultiSetType) {
- return FunctionCall(Token.NoToken, "TMultiSet", predef.Ty, TypeToTy(((CollectionType)type).Arg));
- } else if (type is SeqType) {
- return FunctionCall(Token.NoToken, "TSeq", predef.Ty, TypeToTy(((CollectionType)type).Arg));
- } else if (type is MapType) {
- bool finite = ((MapType)type).Finite;
+ var normType = type.NormalizeExpand();
+
+ if (normType is SetType) {
+ bool finite = ((SetType)normType).Finite;
+ return FunctionCall(Token.NoToken, finite ? "TSet" : "TISet", predef.Ty, TypeToTy(((CollectionType)normType).Arg));
+ } else if (normType is MultiSetType) {
+ return FunctionCall(Token.NoToken, "TMultiSet", predef.Ty, TypeToTy(((CollectionType)normType).Arg));
+ } else if (normType is SeqType) {
+ return FunctionCall(Token.NoToken, "TSeq", predef.Ty, TypeToTy(((CollectionType)normType).Arg));
+ } else if (normType is MapType) {
+ bool finite = ((MapType)normType).Finite;
return FunctionCall(Token.NoToken, finite ? "TMap" : "TIMap", predef.Ty,
- TypeToTy(((MapType)type).Domain),
- TypeToTy(((MapType)type).Range));
- } else if (type is BoolType) {
+ TypeToTy(((MapType)normType).Domain),
+ TypeToTy(((MapType)normType).Range));
+ } else if (normType is BoolType) {
return new Bpl.IdentifierExpr(Token.NoToken, "TBool", predef.Ty);
- } else if (type is CharType) {
+ } else if (normType is CharType) {
return new Bpl.IdentifierExpr(Token.NoToken, "TChar", predef.Ty);
- } else if (type is RealType) {
+ } else if (normType is RealType) {
return new Bpl.IdentifierExpr(Token.NoToken, "TReal", predef.Ty);
- } else if (type is NatType) {
+ } else if (normType is NatType) {
// (Nat needs to come before Int)
return new Bpl.IdentifierExpr(Token.NoToken, "TNat", predef.Ty);
- } else if (type is IntType) {
+ } else if (normType is IntType) {
return new Bpl.IdentifierExpr(Token.NoToken, "TInt", predef.Ty);
- } else if (type.IsTypeParameter) {
- return trTypeParam(type.AsTypeParameter, type.TypeArgs);
- } else if (type is ObjectType) {
+ } else if (normType.IsTypeParameter) {
+ return trTypeParam(normType.AsTypeParameter, normType.TypeArgs);
+ } else if (normType is ObjectType) {
return ClassTyCon(program.BuiltIns.ObjectDecl, new List<Bpl.Expr>());
- } else if (type is UserDefinedType) {
+ } else if (normType is UserDefinedType) {
// Classes, (co-)datatypes
- var args = type.TypeArgs.ConvertAll(TypeToTy);
- return ClassTyCon(((UserDefinedType)type), args);
- } else if (type is ParamTypeProxy) {
- return trTypeParam(((ParamTypeProxy)type).orig, null);
+ var args = normType.TypeArgs.ConvertAll(TypeToTy);
+ return ClassTyCon(((UserDefinedType)normType), args);
+ } else if (normType is ParamTypeProxy) {
+ return trTypeParam(((ParamTypeProxy)normType).orig, null);
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
}
@@ -9266,19 +9514,19 @@ namespace Microsoft.Dafny {
Contract.Requires(etran != null);
Contract.Requires(predef != null);
- type = type.NormalizeExpand();
- if (type is TypeProxy) {
+ var normType = type.NormalizeExpand();
+ if (normType is TypeProxy) {
// Unresolved proxy
// Omit where clause (in other places, unresolved proxies are treated as a reference type; we could do that here too, but
// we might as well leave out the where clause altogether).
return null;
}
- if (type is NatType) {
+ if (normType is NatType) {
// nat:
// 0 <= x
return Bpl.Expr.Le(Bpl.Expr.Literal(0), x);
- } else if (type is BoolType || type is IntType || type is RealType) {
+ } else if (normType is BoolType || normType is IntType || normType is RealType) {
// nothing to do
return null;
/* } else if (type is ArrowType) {
@@ -9286,7 +9534,7 @@ namespace Microsoft.Dafny {
return null;
*/
} else {
- return BplAnd(MkIs(x, type), MkIsAlloc(x, type, etran.HeapExpr));
+ return BplAnd(MkIs(x, normType), MkIsAlloc(x, normType, etran.HeapExpr));
}
}
@@ -9692,7 +9940,7 @@ namespace Microsoft.Dafny {
} else if (rhs is HavocRhs) {
builder.Add(new Bpl.HavocCmd(tok, new List<Bpl.IdentifierExpr> { bLhs }));
var isNat = CheckSubrange_Expr(tok, bLhs, rhsTypeConstraint);
- builder.Add(new Bpl.AssumeCmd(tok, isNat));
+ builder.Add(TrAssumeCmd(tok, isNat));
return CondApplyBox(tok, bLhs, rhsTypeConstraint, lhsType);
} else {
// x := new Something
@@ -9717,13 +9965,13 @@ namespace Microsoft.Dafny {
Bpl.Expr nwNotNull = Bpl.Expr.Neq(nw, predef.Null);
Bpl.Expr rightType;
rightType = etran.GoodRef_(tok, nw, tRhs.Type, true);
- builder.Add(new Bpl.AssumeCmd(tok, Bpl.Expr.And(nwNotNull, rightType)));
+ builder.Add(TrAssumeCmd(tok, Bpl.Expr.And(nwNotNull, rightType)));
if (tRhs.ArrayDimensions != null) {
int i = 0;
foreach (Expression dim in tRhs.ArrayDimensions) {
// assume Array#Length($nw, i) == arraySize;
Bpl.Expr arrayLength = ArrayLength(tok, nw, tRhs.ArrayDimensions.Count, i);
- builder.Add(new Bpl.AssumeCmd(tok, Bpl.Expr.Eq(arrayLength, etran.TrExpr(dim))));
+ builder.Add(TrAssumeCmd(tok, Bpl.Expr.Eq(arrayLength, etran.TrExpr(dim))));
i++;
}
}
@@ -9829,7 +10077,7 @@ namespace Microsoft.Dafny {
Contract.Requires(etran != null);
Contract.Ensures(Contract.Result<AssumeCmd>() != null);
- return new Bpl.AssumeCmd(tok, FunctionCall(tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr));
+ return TrAssumeCmd(tok, FunctionCall(tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr));
}
/// <summary>
@@ -9862,7 +10110,7 @@ namespace Microsoft.Dafny {
var FVs = new HashSet<IVariable>();
bool usesHeap = false, usesOldHeap = false;
Type usesThis = null;
- ComputeFreeVariables(e.RHSs[0], FVs, ref usesHeap, ref usesOldHeap, ref usesThis, false);
+ ComputeFreeVariables(e.RHSs[0], FVs, ref usesHeap, ref usesOldHeap, ref usesThis);
foreach (var bv in e.BoundVars) {
FVs.Remove(bv);
}
@@ -10152,6 +10400,293 @@ namespace Microsoft.Dafny {
}
}
+ internal class FuelSettingPair
+ {
+ public int low;
+ public int high;
+
+ public FuelSettingPair(int low = (int)FuelSetting.FuelAmount.LOW, int high = (int)FuelSetting.FuelAmount.HIGH) {
+ this.low = low;
+ this.high = high;
+ }
+ }
+
+ // C#'s version of a type alias
+ internal class FuelContext : Dictionary<Function, FuelSettingPair> { }
+
+ internal class FuelConstant
+ {
+ public Function f;
+ public Bpl.Expr baseFuel;
+ public Bpl.Expr startFuel;
+ public Bpl.Expr startFuelAssert;
+
+ public FuelConstant(Function f, Bpl.Expr baseFuel, Bpl.Expr startFuel, Bpl.Expr startFuelAssert) {
+ this.f = f;
+ this.baseFuel = baseFuel;
+ this.startFuel = startFuel;
+ this.startFuelAssert = startFuelAssert;
+ }
+
+ public Bpl.Expr MoreFuel(Bpl.Program sink, PredefinedDecls predef, FreshIdGenerator idGen) {
+ string uniqueId = idGen.FreshId("MoreFuel_" + f.FullName);
+ Bpl.Constant moreFuel = new Bpl.Constant(f.tok, new Bpl.TypedIdent(f.tok, uniqueId, predef.LayerType), false);
+ sink.AddTopLevelDeclaration(moreFuel);
+ Bpl.Expr moreFuel_expr = new Bpl.IdentifierExpr(f.tok, moreFuel);
+ return moreFuel_expr;
+ }
+ }
+
+ internal class FuelSetting
+ {
+ public enum FuelAmount { NONE, LOW, HIGH };
+ public static Stack<FuelContext> SavedContexts = new Stack<FuelContext>();
+
+ public static FuelSettingPair FuelAttrib(Function f, out bool found) {
+ Contract.Requires(f != null);
+ Contract.Ensures(Contract.Result<FuelSettingPair>() != null);
+ FuelSettingPair setting = new FuelSettingPair();
+ found = false;
+
+ if (f.Attributes != null) {
+ List<Expression> args = Attributes.FindExpressions(f.Attributes, "fuel");
+ if (args != null) {
+ found = true;
+ if (args.Count >= 2) {
+ LiteralExpr literalLow = args[0] as LiteralExpr;
+ LiteralExpr literalHigh = args[1] as LiteralExpr;
+
+ if (literalLow != null && literalLow.Value is BigInteger && literalHigh != null && literalHigh.Value is BigInteger) {
+ setting.low = (int)((BigInteger)literalLow.Value);
+ setting.high = (int)((BigInteger)literalHigh.Value);
+ }
+ } else if (args.Count >= 1) {
+ LiteralExpr literal = args[0] as LiteralExpr;
+ if (literal != null && literal.Value is BigInteger) {
+ setting.low = (int)((BigInteger)literal.Value);
+ setting.high = setting.low + 1;
+ }
+ }
+ }
+ }
+
+ return setting;
+ }
+
+ public int amount; // Amount of fuel above that represented by start
+ private Bpl.Expr start; // Starting fuel argument (null indicates LZ)
+ private Translator translator;
+
+ public FuelSetting(Translator translator, int amount, Bpl.Expr start = null) {
+ this.translator = translator;
+ this.amount = amount;
+ this.start = start;
+ }
+
+ public FuelSetting Offset(int offset) {
+ return new FuelSetting(translator, this.amount + offset, start);
+ }
+
+ public Bpl.Expr LayerZero() {
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+ return new Bpl.IdentifierExpr(Token.NoToken, "$LZ", translator.predef.LayerType);
+ }
+
+ public Bpl.Expr LayerN(int n) {
+ Contract.Requires(0 <= n);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+ return translator.LayerSucc(LayerZero(), n);
+ }
+
+ public Bpl.Expr LayerN(int n, Bpl.Expr baseLayer) {
+ Contract.Requires(0 <= n);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+ return translator.LayerSucc(baseLayer, n);
+ }
+
+ private Bpl.Expr ToExpr(int amount) {
+ if (start == null) {
+ return LayerN(amount);
+ } else {
+ return translator.LayerSucc(start, amount);
+ }
+ }
+
+ public Bpl.Expr ToExpr() {
+ return this.ToExpr(this.amount);
+ }
+
+ /// <summary>
+ /// Get the fuel value for this function, given the ambient environment (represented by the fuel setting)
+ /// the function itself, and the function call's context (if any)
+ /// </summary>
+ public Bpl.Expr GetFunctionFuel(Function f) {
+ Contract.Requires(f != null);
+ if (this.amount == (int)FuelAmount.NONE) {
+ return this.ToExpr();
+ } else {
+ FuelSettingPair setting = null;
+ var found = translator.fuelContext.TryGetValue(f, out setting);
+
+ if (!found) { // If the context doesn't define fuel for this function, check for a fuel attribute (which supplies a default value if none is found)
+ setting = FuelAttrib(f, out found);
+ }
+
+ FuelConstant fuelConstant = translator.functionFuel.Find(x => x.f == f);
+ if (this.amount == (int)FuelAmount.LOW) {
+ return GetFunctionFuel(setting.low, found, fuelConstant);
+ } else if (this.amount == (int)FuelAmount.HIGH) {
+ return GetFunctionFuel(setting.high, found, fuelConstant);
+ } else {
+ Contract.Assert(false); // Should not reach here
+ return null;
+ }
+ }
+ }
+
+ private Bpl.Expr GetFunctionFuel(int amount, bool hasFuel, FuelConstant fuelConstant) {
+ if (fuelConstant != null) {
+ if (hasFuel) {
+ // it has fuel context
+ return LayerN(amount, fuelConstant.baseFuel);
+ } else {
+ // startfuel
+ if (amount == (int)FuelAmount.LOW) {
+ return fuelConstant.startFuel;
+ } else {
+ return fuelConstant.startFuelAssert;
+ }
+ }
+ } else {
+ return ToExpr(amount);
+ }
+ }
+
+ /// <summary>
+ /// Finds all fuel related attributes of the form {:fuel function low [high]}
+ /// Adds the setting to the context _if_ the context does not already have a setting for that function.
+ /// In other words, it should be called in order from most to least specific contenxt scope.
+ /// </summary>
+ public static void FindFuelAttributes(Attributes attribs, FuelContext fuelContext) {
+ Function f = null;
+ FuelSettingPair setting = null;
+
+ if (attribs != null) {
+ List<List<Expression>> results = Attributes.FindAllExpressions(attribs, "fuel");
+
+ if (results != null) {
+ foreach (List<Expression> args in results) {
+ if (args != null && args.Count >= 2) {
+ // Try to extract the function from the first argument
+ MemberSelectExpr selectExpr = args[0].Resolved as MemberSelectExpr;
+ if (selectExpr != null) {
+ f = selectExpr.Member as Function;
+ }
+
+ // Try to extract the lower fuel setting
+ LiteralExpr literalLow = args[1] as LiteralExpr;
+ if (literalLow != null && literalLow.Value is BigInteger) {
+ setting = new FuelSettingPair();
+ setting.low = (int)((BigInteger)literalLow.Value);
+ }
+
+ // The user may supply an additional high argument; if not, it defaults to low + 1
+ if (f != null && args.Count >= 3) {
+ LiteralExpr literalHigh = args[2] as LiteralExpr;
+ if (setting != null && literalHigh != null && literalHigh.Value is BigInteger) {
+ setting.high = (int)((BigInteger)literalHigh.Value);
+ if (!fuelContext.ContainsKey(f)) {
+ fuelContext.Add(f, setting);
+ }
+ }
+ } else if (f != null && setting != null) {
+ setting.high = setting.low + 1;
+ if (!fuelContext.ContainsKey(f)) {
+ fuelContext.Add(f, setting);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Extend the given context with fuel information from the declaration itself, and enclosing modules
+ /// </summary>
+ private static void AddFuelContext(FuelContext context, TopLevelDecl decl) {
+ FindFuelAttributes(decl.Attributes, context);
+
+ var module = decl.Module;
+ while (module != null) {
+ FindFuelAttributes(module.Attributes, context);
+ module = module.Module;
+ }
+ }
+
+ /// <summary>
+ /// Creates a summary of all fuel settings in scope, starting from the given class declaration
+ /// </summary>
+ public static FuelContext NewFuelContext(TopLevelDecl decl) {
+ FuelContext context = new FuelContext();
+ AddFuelContext(context, decl);
+ return context;
+ }
+
+ /// <summary>
+ /// Creates a summary of all fuel settings in scope, starting from the given member declaration
+ /// </summary>
+ public static FuelContext NewFuelContext(MemberDecl decl) {
+ FuelContext context = new FuelContext();
+
+ FindFuelAttributes(decl.Attributes, context);
+ AddFuelContext(context, decl.EnclosingClass);
+
+ return context;
+ }
+
+ /// <summary>
+ /// Extends the given fuel context with any new fuel settings found in attribs
+ /// </summary>
+ public static FuelContext ExpandFuelContext(Attributes attribs, IToken tok, FuelContext oldFuelContext, ErrorReporter reporter) {
+ Contract.Ensures(SavedContexts.Count == Contract.OldValue(SavedContexts.Count) + 1);
+ FuelContext newContext = new FuelContext();
+ FindFuelAttributes(attribs, newContext);
+ if (newContext.Count > 0) {
+ // first make sure that the fuel only increase relative to the oldContext
+ foreach (var pair in newContext) {
+ FuelSettingPair newSetting = pair.Value;
+ FuelSettingPair oldSetting;
+ var found = oldFuelContext.TryGetValue(pair.Key, out oldSetting);
+ if (!found) { // the default is {:fuel, 1, 2}
+ oldSetting = new FuelSettingPair();
+ }
+ // make sure that the fuel can only increase within a given scope
+ if (newSetting.low < oldSetting.low || newSetting.high < oldSetting.high) {
+ reporter.Error(MessageSource.Translator, tok, "Fuel can only increase within a given scope.");
+ }
+ }
+ // add oldContext to newContext if it doesn't exist already
+ foreach (var pair in oldFuelContext) {
+ if (!newContext.ContainsKey(pair.Key)) { // Local setting takes precedence over old context
+ newContext.Add(pair.Key, pair.Value);
+ }
+ }
+ } else {
+ newContext = oldFuelContext;
+ }
+ SavedContexts.Push(oldFuelContext);
+
+ return newContext;
+ }
+
+ public static FuelContext PopFuelContext() {
+ Contract.Requires(SavedContexts.Count > 0);
+ return SavedContexts.Pop();
+ }
+
+ }
+
internal class ExpressionTranslator
{
public readonly Bpl.Expr HeapExpr;
@@ -10160,10 +10695,10 @@ namespace Microsoft.Dafny {
public readonly string This;
public readonly string modifiesFrame; // the name of the context's frame variable.
readonly Function applyLimited_CurrentFunction;
- public readonly Bpl.Expr layerInterCluster;
- public readonly Bpl.Expr layerIntraCluster = null; // a value of null says to do the same as for inter-cluster calls
+ public readonly FuelSetting layerInterCluster;
+ public readonly FuelSetting layerIntraCluster = null; // a value of null says to do the same as for inter-cluster calls
public int Statistics_CustomLayerFunctionCount = 0;
- public readonly bool ProducingCoCertificates = false;
+ public readonly bool stripLits = false;
[ContractInvariantMethod]
void ObjectInvariant()
{
@@ -10178,40 +10713,16 @@ namespace Microsoft.Dafny {
}
/// <summary>
- /// This is a general constructor, but takes the layerInterCluster as an int.
- /// </summary>
- ExpressionTranslator(Translator translator, PredefinedDecls predef, Bpl.Expr heap, string thisVar,
- Function applyLimited_CurrentFunction, int layerInterCluster, Bpl.Expr layerIntraCluster, string modifiesFrame) {
-
- Contract.Requires(translator != null);
- Contract.Requires(predef != null);
- Contract.Requires(heap != null);
- Contract.Requires(thisVar != null);
- Contract.Requires(0 <= layerInterCluster);
- Contract.Requires(modifiesFrame != null);
-
- this.translator = translator;
- this.predef = predef;
- this.HeapExpr = heap;
- this.This = thisVar;
- this.applyLimited_CurrentFunction = applyLimited_CurrentFunction;
- this.layerInterCluster = LayerN(layerInterCluster);
- this.layerIntraCluster = layerIntraCluster;
- this.modifiesFrame = modifiesFrame;
- }
-
- /// <summary>
/// This is the most general constructor. It is private and takes all the parameters. Whenever
/// one ExpressionTranslator is constructed from another, unchanged parameters are just copied in.
/// </summary>
ExpressionTranslator(Translator translator, PredefinedDecls predef, Bpl.Expr heap, string thisVar,
- Function applyLimited_CurrentFunction, Bpl.Expr layerInterCluster, Bpl.Expr layerIntraCluster, string modifiesFrame) {
+ Function applyLimited_CurrentFunction, FuelSetting layerInterCluster, FuelSetting layerIntraCluster, string modifiesFrame, bool stripLits) {
Contract.Requires(translator != null);
Contract.Requires(predef != null);
Contract.Requires(heap != null);
- Contract.Requires(thisVar != null);
- Contract.Requires(layerInterCluster != null);
+ Contract.Requires(thisVar != null);
Contract.Requires(modifiesFrame != null);
this.translator = translator;
@@ -10226,6 +10737,7 @@ namespace Microsoft.Dafny {
this.layerIntraCluster = layerIntraCluster;
}
this.modifiesFrame = modifiesFrame;
+ this.stripLits = stripLits;
}
public ExpressionTranslator(Translator translator, PredefinedDecls predef, IToken heapToken)
@@ -10255,7 +10767,7 @@ namespace Microsoft.Dafny {
}
public ExpressionTranslator(Translator translator, PredefinedDecls predef, Bpl.Expr heap, string thisVar)
- : this(translator, predef, heap, thisVar, null, 1, null, "$_Frame") {
+ : this(translator, predef, heap, thisVar, null, new FuelSetting(translator, 1), null, "$_Frame", false) {
Contract.Requires(translator != null);
Contract.Requires(predef != null);
Contract.Requires(heap != null);
@@ -10263,14 +10775,14 @@ namespace Microsoft.Dafny {
}
public ExpressionTranslator(ExpressionTranslator etran, Bpl.Expr heap)
- : this(etran.translator, etran.predef, heap, etran.This, etran.applyLimited_CurrentFunction, etran.layerInterCluster, etran.layerIntraCluster, etran.modifiesFrame)
+ : this(etran.translator, etran.predef, heap, etran.This, etran.applyLimited_CurrentFunction, etran.layerInterCluster, etran.layerIntraCluster, etran.modifiesFrame, etran.stripLits)
{
Contract.Requires(etran != null);
Contract.Requires(heap != null);
}
public ExpressionTranslator(ExpressionTranslator etran, string modifiesFrame)
- : this(etran.translator, etran.predef, etran.HeapExpr, etran.This, etran.applyLimited_CurrentFunction, etran.layerInterCluster, etran.layerIntraCluster, modifiesFrame) {
+ : this(etran.translator, etran.predef, etran.HeapExpr, etran.This, etran.applyLimited_CurrentFunction, etran.layerInterCluster, etran.layerIntraCluster, modifiesFrame, etran.stripLits) {
Contract.Requires(etran != null);
Contract.Requires(modifiesFrame != null);
}
@@ -10281,7 +10793,7 @@ namespace Microsoft.Dafny {
Contract.Ensures(Contract.Result<ExpressionTranslator>() != null);
if (oldEtran == null) {
- oldEtran = new ExpressionTranslator(translator, predef, new Bpl.OldExpr(HeapExpr.tok, HeapExpr), This, applyLimited_CurrentFunction, layerInterCluster, layerIntraCluster, modifiesFrame);
+ oldEtran = new ExpressionTranslator(translator, predef, new Bpl.OldExpr(HeapExpr.tok, HeapExpr), This, applyLimited_CurrentFunction, layerInterCluster, layerIntraCluster, modifiesFrame, stripLits);
oldEtran.oldEtran = oldEtran;
}
return oldEtran;
@@ -10299,7 +10811,12 @@ namespace Microsoft.Dafny {
Contract.Requires(layerArgument != null);
Contract.Ensures(Contract.Result<ExpressionTranslator>() != null);
- return new ExpressionTranslator(translator, predef, HeapExpr, This, null, layerArgument, layerArgument, modifiesFrame);
+ return new ExpressionTranslator(translator, predef, HeapExpr, This, null, new FuelSetting(translator, 0, layerArgument), new FuelSetting(translator, 0, layerArgument), modifiesFrame, stripLits);
+ }
+
+ public ExpressionTranslator WithNoLits() {
+ Contract.Ensures(Contract.Result<ExpressionTranslator>() != null);
+ return new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, layerInterCluster, layerIntraCluster, modifiesFrame, true);
}
public ExpressionTranslator LimitedFunctions(Function applyLimited_CurrentFunction, Bpl.Expr layerArgument) {
@@ -10307,16 +10824,16 @@ namespace Microsoft.Dafny {
Contract.Requires(layerArgument != null);
Contract.Ensures(Contract.Result<ExpressionTranslator>() != null);
- return new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, /* layerArgument */ layerInterCluster, layerArgument, modifiesFrame);
+ return new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, /* layerArgument */ layerInterCluster, new FuelSetting(translator, 0, layerArgument), modifiesFrame, stripLits);
}
public ExpressionTranslator LayerOffset(int offset) {
Contract.Requires(0 <= offset);
Contract.Ensures(Contract.Result<ExpressionTranslator>() != null);
- var et = new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, translator.LayerSucc(layerInterCluster, offset), layerIntraCluster, modifiesFrame);
+ var et = new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, layerInterCluster.Offset(offset), layerIntraCluster, modifiesFrame, stripLits);
if (this.oldEtran != null) {
- var etOld = new ExpressionTranslator(translator, predef, Old.HeapExpr, This, applyLimited_CurrentFunction, translator.LayerSucc(layerInterCluster, offset), layerIntraCluster, modifiesFrame);
+ var etOld = new ExpressionTranslator(translator, predef, Old.HeapExpr, This, applyLimited_CurrentFunction, layerInterCluster.Offset(offset), layerIntraCluster, modifiesFrame, stripLits);
etOld.oldEtran = etOld;
et.oldEtran = etOld;
}
@@ -10355,17 +10872,6 @@ namespace Microsoft.Dafny {
}
}
- public Bpl.Expr LayerZero() {
- Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
- return new Bpl.IdentifierExpr(Token.NoToken, "$LZ", predef.LayerType);
- }
-
- public Bpl.Expr LayerN(int n) {
- Contract.Requires(0 <= n);
- Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
- return translator.LayerSucc(LayerZero(), n);
- }
-
public Bpl.IdentifierExpr ModuleContextHeight() {
Contract.Ensures(Contract.Result<Bpl.IdentifierExpr>().Type != null);
return new Bpl.IdentifierExpr(Token.NoToken, "$ModuleContextHeight", Bpl.Type.Int);
@@ -10399,13 +10905,24 @@ namespace Microsoft.Dafny {
return translator.Substitute(e.Body, null, substMap);
}
+ public Expr MaybeLit(Expr expr, Bpl.Type type) {
+ return stripLits ? expr : translator.Lit(expr, type);
+ }
+
+ public Expr MaybeLit(Expr expr) {
+ return stripLits ? expr : translator.Lit(expr);
+ }
+
/// <summary>
/// Translates Dafny expression "expr" into a Boogie expression. If the type of "expr" can be a boolean, then the
/// token (source location) of the resulting expression is filled in (it wouldn't hurt if the token were always
/// filled in, but it is really necessary for anything that may show up in a Boogie assert, since that location may
/// then show up in an error message).
/// </summary>
- public Bpl.Expr TrExpr(Expression expr)
+ public Bpl.Expr TrExpr(Expression expr) {
+ return TrExpr(expr, null, null, null);
+ }
+ public Bpl.Expr TrExpr(Expression expr, ExpressionTranslator initEtran, ExpressionTranslator funcEtran, ExpressionTranslator existEtran)
{
Contract.Requires(expr != null);
Contract.Requires(predef != null);
@@ -10415,7 +10932,7 @@ namespace Microsoft.Dafny {
if (e.Value == null) {
return predef.Null;
} else if (e.Value is bool) {
- return translator.Lit(new Bpl.LiteralExpr(e.tok, (bool)e.Value));
+ return MaybeLit(new Bpl.LiteralExpr(e.tok, (bool)e.Value));
} else if (e is CharLiteralExpr) {
// we expect e.Value to be a string representing exactly one char
Bpl.Expr rawElement = null; // assignment to please compiler's definite assignment rule
@@ -10424,7 +10941,7 @@ namespace Microsoft.Dafny {
rawElement = translator.FunctionCall(expr.tok, BuiltinFunction.CharFromInt, null, Bpl.Expr.Literal((int)ch));
}
Contract.Assert(rawElement != null); // there should have been an iteration of the loop above
- return translator.Lit(rawElement, predef.CharType);
+ return MaybeLit(rawElement, predef.CharType);
} else if (e is StringLiteralExpr) {
var str = (StringLiteralExpr)e;
Bpl.Expr seq = translator.FunctionCall(expr.tok, BuiltinFunction.SeqEmpty, predef.BoxType);
@@ -10433,11 +10950,11 @@ namespace Microsoft.Dafny {
Bpl.Expr elt = BoxIfNecessary(expr.tok, rawElement, Type.Char);
seq = translator.FunctionCall(expr.tok, BuiltinFunction.SeqBuild, predef.BoxType, seq, elt);
}
- return translator.Lit(seq, translator.TrType(new SeqType(Type.Char)));
+ return MaybeLit(seq, translator.TrType(new SeqType(Type.Char)));
} else if (e.Value is BigInteger) {
- return translator.Lit(Bpl.Expr.Literal(Microsoft.Basetypes.BigNum.FromBigInt((BigInteger)e.Value)));
+ return MaybeLit(Bpl.Expr.Literal(Microsoft.Basetypes.BigNum.FromBigInt((BigInteger)e.Value)));
} else if (e.Value is Basetypes.BigDec) {
- return translator.Lit(Bpl.Expr.Literal((Basetypes.BigDec)e.Value));
+ return MaybeLit(Bpl.Expr.Literal((Basetypes.BigDec)e.Value));
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected literal
}
@@ -10469,16 +10986,16 @@ namespace Microsoft.Dafny {
args.Add(Old.HeapExpr);
}
foreach (var arg in e.Args) {
- args.Add(TrExpr(arg));
+ args.Add(TrExpr(arg, initEtran, funcEtran, existEtran));
}
return new Bpl.NAryExpr(e.tok, new Bpl.FunctionCall(id), args);
} else if (expr is SetDisplayExpr) {
SetDisplayExpr e = (SetDisplayExpr)expr;
- Bpl.Expr s = translator.FunctionCall(expr.tok, BuiltinFunction.SetEmpty, predef.BoxType);
+ Bpl.Expr s = translator.FunctionCall(expr.tok, e.Finite ? BuiltinFunction.SetEmpty : BuiltinFunction.ISetEmpty, predef.BoxType);
foreach (Expression ee in e.Elements) {
- Bpl.Expr ss = BoxIfNecessary(expr.tok, TrExpr(ee), cce.NonNull(ee.Type));
- s = translator.FunctionCall(expr.tok, BuiltinFunction.SetUnionOne, predef.BoxType, s, ss);
+ Bpl.Expr ss = BoxIfNecessary(expr.tok, TrExpr(ee, initEtran, funcEtran, existEtran), cce.NonNull(ee.Type));
+ s = translator.FunctionCall(expr.tok, e.Finite ? BuiltinFunction.SetUnionOne : BuiltinFunction.ISetUnionOne, predef.BoxType, s, ss);
}
return s;
@@ -10486,7 +11003,7 @@ namespace Microsoft.Dafny {
MultiSetDisplayExpr e = (MultiSetDisplayExpr)expr;
Bpl.Expr s = translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetEmpty, predef.BoxType);
foreach (Expression ee in e.Elements) {
- Bpl.Expr ss = BoxIfNecessary(expr.tok, TrExpr(ee), cce.NonNull(ee.Type));
+ Bpl.Expr ss = BoxIfNecessary(expr.tok, TrExpr(ee, initEtran, funcEtran, existEtran), cce.NonNull(ee.Type));
s = translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetUnionOne, predef.BoxType, s, ss);
}
return s;
@@ -10497,13 +11014,14 @@ namespace Microsoft.Dafny {
Bpl.Expr s = translator.FunctionCall(expr.tok, BuiltinFunction.SeqEmpty, predef.BoxType);
bool isLit = true;
foreach (Expression ee in e.Elements) {
- var rawElement = TrExpr(ee);
+ var rawElement = TrExpr(ee, initEtran, funcEtran, existEtran);
isLit = isLit && translator.IsLit(rawElement);
Bpl.Expr elt = BoxIfNecessary(expr.tok, rawElement, ee.Type);
s = translator.FunctionCall(expr.tok, BuiltinFunction.SeqBuild, predef.BoxType, s, elt);
}
if (isLit) {
- s = translator.Lit(s, predef.BoxType);
+ // Lit-lifting: All elements are lit, so the sequence is Lit too
+ s = MaybeLit(s, predef.BoxType);
}
return s;
@@ -10512,8 +11030,8 @@ namespace Microsoft.Dafny {
Bpl.Type maptype = predef.MapType(expr.tok, e.Finite, predef.BoxType, predef.BoxType);
Bpl.Expr s = translator.FunctionCall(expr.tok, e.Finite ? BuiltinFunction.MapEmpty : BuiltinFunction.IMapEmpty, predef.BoxType);
foreach (ExpressionPair p in e.Elements) {
- Bpl.Expr elt = BoxIfNecessary(expr.tok, TrExpr(p.A), cce.NonNull(p.A.Type));
- Bpl.Expr elt2 = BoxIfNecessary(expr.tok, TrExpr(p.B), cce.NonNull(p.B.Type));
+ Bpl.Expr elt = BoxIfNecessary(expr.tok, TrExpr(p.A, initEtran, funcEtran, existEtran), cce.NonNull(p.A.Type));
+ Bpl.Expr elt2 = BoxIfNecessary(expr.tok, TrExpr(p.B, initEtran, funcEtran, existEtran), cce.NonNull(p.B.Type));
s = translator.FunctionCall(expr.tok, e.Finite ? "Map#Build" : "IMap#Build", maptype, s, elt, elt2);
}
return s;
@@ -10522,7 +11040,7 @@ namespace Microsoft.Dafny {
var e = (MemberSelectExpr)expr;
return e.MemberSelectCase(
field => {
- Bpl.Expr obj = TrExpr(e.Obj);
+ Bpl.Expr obj = TrExpr(e.Obj, initEtran, funcEtran, existEtran);
Bpl.Expr result;
if (field.IsMutable) {
result = ReadHeap(expr.tok, HeapExpr, obj, new Bpl.IdentifierExpr(expr.tok, translator.GetField(field)));
@@ -10532,15 +11050,15 @@ namespace Microsoft.Dafny {
new List<Bpl.Expr> {obj});
result = translator.CondApplyUnbox(expr.tok, result, field.Type, expr.Type);
if (translator.IsLit(obj)) {
- result = translator.Lit(result, translator.TrType(expr.Type));
+ result = MaybeLit(result, translator.TrType(expr.Type));
}
return result;
}
},
fn => {
var args = e.TypeApplication.ConvertAll(translator.TypeToTy);
- if (fn.IsRecursive) {
- args.Add(layerInterCluster);
+ if (fn.IsFuelAware()) {
+ args.Add(this.layerInterCluster.GetFunctionFuel(fn));
}
if (!fn.IsStatic) {
args.Add(/* translator.BoxIfUnboxed */(TrExpr(e.Obj)/*, e.Type */));
@@ -10549,7 +11067,7 @@ namespace Microsoft.Dafny {
});
} else if (expr is SeqSelectExpr) {
SeqSelectExpr e = (SeqSelectExpr)expr;
- Bpl.Expr seq = TrExpr(e.Seq);
+ Bpl.Expr seq = TrExpr(e.Seq, initEtran, funcEtran, existEtran);
var seqType = e.Seq.Type.NormalizeExpand();
Type elmtType = null;
Type domainType = null;
@@ -10569,14 +11087,14 @@ namespace Microsoft.Dafny {
} else { Contract.Assert(false); }
Bpl.Type elType = translator.TrType(elmtType);
Bpl.Type dType = translator.TrType(domainType);
- Bpl.Expr e0 = e.E0 == null ? null : TrExpr(e.E0);
- Bpl.Expr e1 = e.E1 == null ? null : TrExpr(e.E1);
+ Bpl.Expr e0 = e.E0 == null ? null : TrExpr(e.E0, initEtran, funcEtran, existEtran);
+ Bpl.Expr e1 = e.E1 == null ? null : TrExpr(e.E1, initEtran, funcEtran, existEtran);
if (e.SelectOne) {
Contract.Assert(e1 == null);
Bpl.Expr x;
if (seqType.IsArrayType) {
Bpl.Expr fieldName = translator.FunctionCall(expr.tok, BuiltinFunction.IndexField, null, e0);
- x = ReadHeap(expr.tok, HeapExpr, TrExpr(e.Seq), fieldName);
+ x = ReadHeap(expr.tok, HeapExpr, TrExpr(e.Seq, initEtran, funcEtran, existEtran), fieldName);
} else if (seqType is SeqType) {
x = translator.FunctionCall(expr.tok, BuiltinFunction.SeqIndex, predef.BoxType, seq, e0);
} else if (seqType is MapType) {
@@ -10585,7 +11103,7 @@ namespace Microsoft.Dafny {
x = translator.FunctionCall(expr.tok, f, predef.MapType(e.tok, finite, predef.BoxType, predef.BoxType), seq);
x = Bpl.Expr.Select(x, BoxIfNecessary(e.tok, e0, domainType));
} else if (seqType is MultiSetType) {
- x = Bpl.Expr.SelectTok(expr.tok, TrExpr(e.Seq), BoxIfNecessary(expr.tok, e0, domainType));
+ x = Bpl.Expr.SelectTok(expr.tok, TrExpr(e.Seq, initEtran, funcEtran, existEtran), BoxIfNecessary(expr.tok, e0, domainType));
} else { Contract.Assert(false); x = null; }
if (!ModeledAsBoxType(elmtType) && !(seqType is MultiSetType)) {
x = translator.FunctionCall(expr.tok, BuiltinFunction.Unbox, elType, x);
@@ -10607,7 +11125,7 @@ namespace Microsoft.Dafny {
// if e0 == null && e1 == null, then we have the identity operation seq[..] == seq;
if (isLit && (e0 != null || e1 != null)) {
// Lit-lift the expression
- seq = translator.Lit(seq, translator.TrType(expr.Type));
+ seq = MaybeLit(seq, translator.TrType(expr.Type));
}
return seq;
}
@@ -10616,32 +11134,32 @@ namespace Microsoft.Dafny {
SeqUpdateExpr e = (SeqUpdateExpr)expr;
if (e.ResolvedUpdateExpr != null)
{
- return TrExpr(e.ResolvedUpdateExpr);
+ return TrExpr(e.ResolvedUpdateExpr, initEtran, funcEtran, existEtran);
}
else
{
- Bpl.Expr seq = TrExpr(e.Seq);
+ Bpl.Expr seq = TrExpr(e.Seq, initEtran, funcEtran, existEtran);
var seqType = e.Seq.Type.NormalizeExpand();
if (seqType is SeqType)
{
Type elmtType = cce.NonNull((SeqType)seqType).Arg;
- Bpl.Expr index = TrExpr(e.Index);
- Bpl.Expr val = BoxIfNecessary(expr.tok, TrExpr(e.Value), elmtType);
+ Bpl.Expr index = TrExpr(e.Index, initEtran, funcEtran, existEtran);
+ Bpl.Expr val = BoxIfNecessary(expr.tok, TrExpr(e.Value, initEtran, funcEtran, existEtran), elmtType);
return translator.FunctionCall(expr.tok, BuiltinFunction.SeqUpdate, predef.BoxType, seq, index, val);
}
else if (seqType is MapType)
{
MapType mt = (MapType)seqType;
Bpl.Type maptype = predef.MapType(expr.tok, mt.Finite, predef.BoxType, predef.BoxType);
- Bpl.Expr index = BoxIfNecessary(expr.tok, TrExpr(e.Index), mt.Domain);
- Bpl.Expr val = BoxIfNecessary(expr.tok, TrExpr(e.Value), mt.Range);
+ Bpl.Expr index = BoxIfNecessary(expr.tok, TrExpr(e.Index, initEtran, funcEtran, existEtran), mt.Domain);
+ Bpl.Expr val = BoxIfNecessary(expr.tok, TrExpr(e.Value, initEtran, funcEtran, existEtran), mt.Range);
return translator.FunctionCall(expr.tok, mt.Finite ? "Map#Build" : "IMap#Build", maptype, seq, index, val);
}
else if (seqType is MultiSetType)
{
Type elmtType = cce.NonNull((MultiSetType)seqType).Arg;
- Bpl.Expr index = BoxIfNecessary(expr.tok, TrExpr(e.Index), elmtType);
- Bpl.Expr val = TrExpr(e.Value);
+ Bpl.Expr index = BoxIfNecessary(expr.tok, TrExpr(e.Index, initEtran, funcEtran, existEtran), elmtType);
+ Bpl.Expr val = TrExpr(e.Value, initEtran, funcEtran, existEtran);
return Bpl.Expr.StoreTok(expr.tok, seq, index, val);
}
else
@@ -10657,7 +11175,7 @@ namespace Microsoft.Dafny {
Bpl.Type elType = translator.TrType(elmtType);
Bpl.Expr fieldName = GetArrayIndexFieldName(expr.tok, e.Indices);
- Bpl.Expr x = ReadHeap(expr.tok, HeapExpr, TrExpr(e.Array), fieldName);
+ Bpl.Expr x = ReadHeap(expr.tok, HeapExpr, TrExpr(e.Array, initEtran, funcEtran, existEtran), fieldName);
if (!ModeledAsBoxType(elmtType)) {
x = translator.FunctionCall(expr.tok, BuiltinFunction.Unbox, elType, x);
}
@@ -10681,67 +11199,82 @@ namespace Microsoft.Dafny {
Function = fn,
Type = e.Type,
TypeArgumentSubstitutions = Util.Dict(GetTypeParams(fn), mem.TypeApplication)
- });
+ }, initEtran, funcEtran, existEtran);
}
}
- Func<Expression, Bpl.Expr> TrArg = arg => translator.BoxIfUnboxed(TrExpr(arg), arg.Type);
+ Func<Expression, Bpl.Expr> TrArg = arg => translator.BoxIfUnboxed(TrExpr(arg, initEtran, funcEtran, existEtran), arg.Type);
var applied = translator.FunctionCall(expr.tok, translator.Apply(arity), predef.BoxType,
Concat(Map(tt.TypeArgs,translator.TypeToTy),
- Cons(TrExpr(e.Function), Cons(HeapExpr, e.Args.ConvertAll(arg => TrArg(arg))))));
+ Cons(TrExpr(e.Function, initEtran, funcEtran, existEtran), Cons(HeapExpr, e.Args.ConvertAll(arg => TrArg(arg))))));
return translator.UnboxIfBoxed(applied, tt.Result);
} else if (expr is FunctionCallExpr) {
FunctionCallExpr e = (FunctionCallExpr)expr;
Bpl.Expr layerArgument;
- if (e.Function.IsRecursive) {
+ var etran = this;
+ if (e.Function.ContainsQuantifier && funcEtran != null) {
+ etran = funcEtran;
+ }
+ if (e.Function.IsFuelAware()) {
Statistics_CustomLayerFunctionCount++;
ModuleDefinition module = e.Function.EnclosingClass.Module;
- if (this.applyLimited_CurrentFunction != null &&
- this.layerIntraCluster != null &&
+ if (etran.applyLimited_CurrentFunction != null &&
+ etran.layerIntraCluster != null &&
ModuleDefinition.InSameSCC(e.Function, applyLimited_CurrentFunction)) {
- layerArgument = this.layerIntraCluster;
+ layerArgument = etran.layerIntraCluster.GetFunctionFuel(e.Function);
} else {
- layerArgument = this.layerInterCluster;
+ layerArgument = etran.layerInterCluster.GetFunctionFuel(e.Function);
}
} else {
layerArgument = null;
}
var ty = translator.TrType(e.Type);
- var id = new Bpl.IdentifierExpr(e.tok, e.Function.FullSanitizedName, ty);
- bool returnLit;
- var args = FunctionInvocationArguments(e, layerArgument, out returnLit);
+ var name = e.Function.FullSanitizedName;
+ if (DafnyOptions.O.IronDafny) {
+ name = e.Function.FullSanitizedRefinementName;
+ }
+ var id = new Bpl.IdentifierExpr(e.tok, name, ty);
+ bool argsAreLit;
+ var args = FunctionInvocationArguments(e, layerArgument, out argsAreLit);
Expr result = new Bpl.NAryExpr(e.tok, new Bpl.FunctionCall(id), args);
result = translator.CondApplyUnbox(e.tok, result, e.Function.ResultType, e.Type);
- if (returnLit && Translator.FunctionBodyIsAvailable(e.Function, translator.currentModule)) {
- result = translator.Lit(result, ty);
+
+ bool callIsLit = argsAreLit
+ && Translator.FunctionBodyIsAvailable(e.Function, translator.currentModule, true)
+ && !e.Function.Reads.Any(); // Function could depend on external values
+ if (callIsLit) {
+ result = MaybeLit(result, ty);
}
+
return result;
} else if (expr is DatatypeValue) {
DatatypeValue dtv = (DatatypeValue)expr;
Contract.Assert(dtv.Ctor != null); // since dtv has been successfully resolved
List<Bpl.Expr> args = new List<Bpl.Expr>();
- bool isLit = true;
+
+ bool argsAreLit = true;
for (int i = 0; i < dtv.Arguments.Count; i++) {
Expression arg = dtv.Arguments[i];
Type t = dtv.Ctor.Formals[i].Type;
- var bArg = TrExpr(arg);
- isLit = isLit && translator.IsLit(bArg);
+ var bArg = TrExpr(arg, initEtran, funcEtran, existEtran);
+ argsAreLit = argsAreLit && translator.IsLit(bArg);
args.Add(translator.CondApplyBox(expr.tok, bArg, cce.NonNull(arg.Type), t));
}
Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(dtv.tok, dtv.Ctor.FullName, predef.DatatypeType);
Bpl.Expr ret = new Bpl.NAryExpr(dtv.tok, new Bpl.FunctionCall(id), args);
- if (isLit) {
- ret = translator.Lit(ret, predef.DatatypeType);
+ if (argsAreLit) {
+ // If all arguments are Lit, so is the whole expression
+ ret = MaybeLit(ret, predef.DatatypeType);
}
return ret;
} else if (expr is OldExpr) {
OldExpr e = (OldExpr)expr;
- return Old.TrExpr(e.E);
+ return Old.TrExpr(e.E, initEtran, funcEtran, existEtran);
} else if (expr is MultiSetFormingExpr) {
MultiSetFormingExpr e = (MultiSetFormingExpr)expr;
@@ -10756,17 +11289,17 @@ namespace Microsoft.Dafny {
} else if (expr is UnaryOpExpr) {
var e = (UnaryOpExpr)expr;
- Bpl.Expr arg = TrExpr(e.E);
+ Bpl.Expr arg = TrExpr(e.E, initEtran, funcEtran, existEtran);
switch (e.Op) {
case UnaryOpExpr.Opcode.Lit:
- return translator.Lit(arg);
+ return MaybeLit(arg);
case UnaryOpExpr.Opcode.Not:
return Bpl.Expr.Unary(expr.tok, UnaryOperator.Opcode.Not, arg);
case UnaryOpExpr.Opcode.Cardinality:
var eType = e.E.Type.NormalizeExpand();
if (eType is SeqType) {
return translator.FunctionCall(expr.tok, BuiltinFunction.SeqLength, null, arg);
- } else if (eType is SetType) {
+ } else if (eType is SetType && ((SetType)eType).Finite) {
return translator.FunctionCall(expr.tok, BuiltinFunction.SetCard, null, arg);
} else if (eType is MultiSetType) {
return translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetCard, null, arg);
@@ -10784,29 +11317,37 @@ namespace Microsoft.Dafny {
Bpl.Expr o = new Bpl.IdentifierExpr(expr.tok, oVar);
Bpl.Expr oNotNull = Bpl.Expr.Neq(o, predef.Null);
Bpl.Expr oInSet = TrInSet(expr.tok, o, e.E, ((SetType)eeType).Arg);
- Bpl.Expr oIsFresh = Bpl.Expr.Not(Old.IsAlloced(expr.tok, o));
+ Bpl.Expr oNotFresh = Old.IsAlloced(expr.tok, o);
+ Bpl.Expr oIsFresh = Bpl.Expr.Not(oNotFresh);
Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(oNotNull, oInSet), oIsFresh);
- return new Bpl.ForallExpr(expr.tok, new List<Variable> { oVar }, body);
+ // TRIGGERS: Does this make sense? VSI-Benchmarks\b7
+ // TRIG (forall $o: ref :: $o != null && read($Heap, this, _module.List.Repr)[$Box($o)] && $o != this ==> !read(old($Heap), $o, alloc))
+ // TRIG (forall $o: ref :: $o != null && read($Heap, this, _module.Stream.footprint)[$Box($o)] && $o != this ==> !read(old($Heap), $o, alloc))
+ var trigger = BplTrigger(oNotFresh); // NEW_TRIGGER
+ return new Bpl.ForallExpr(expr.tok, new List<Variable> { oVar }, trigger, body);
} else if (eeType is SeqType) {
// generate: (forall $i: int :: 0 <= $i && $i < Seq#Length(X) && Unbox(Seq#Index(X,$i)) != null ==> !old($Heap)[Unbox(Seq#Index(X,$i)),alloc])
- // TODO: trigger?
Bpl.Variable iVar = new Bpl.BoundVariable(expr.tok, new Bpl.TypedIdent(expr.tok, "$i", Bpl.Type.Int));
Bpl.Expr i = new Bpl.IdentifierExpr(expr.tok, iVar);
- Bpl.Expr iBounds = translator.InSeqRange(expr.tok, i, TrExpr(e.E), true, null, false);
- Bpl.Expr XsubI = translator.FunctionCall(expr.tok, BuiltinFunction.SeqIndex, predef.RefType, TrExpr(e.E), i);
+ Bpl.Expr iBounds = translator.InSeqRange(expr.tok, i, TrExpr(e.E, initEtran, funcEtran, existEtran), true, null, false);
+ Bpl.Expr XsubI = translator.FunctionCall(expr.tok, BuiltinFunction.SeqIndex, predef.RefType, TrExpr(e.E, initEtran, funcEtran, existEtran), i);
XsubI = translator.FunctionCall(expr.tok, BuiltinFunction.Unbox, predef.RefType, XsubI);
- Bpl.Expr oIsFresh = Bpl.Expr.Not(Old.IsAlloced(expr.tok, XsubI));
+ Bpl.Expr oNotFresh = Old.IsAlloced(expr.tok, XsubI);
+ Bpl.Expr oIsFresh = Bpl.Expr.Not(oNotFresh);
Bpl.Expr xsubiNotNull = Bpl.Expr.Neq(XsubI, predef.Null);
Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(iBounds, xsubiNotNull), oIsFresh);
+ //TRIGGERS: Does this make sense? dafny0\SmallTests
+ // BROKEN // NEW_TRIGGER
+ //TRIG (forall $i: int :: 0 <= $i && $i < Seq#Length(Q#0) && $Unbox(Seq#Index(Q#0, $i)): ref != null ==> !read(old($Heap), $Unbox(Seq#Index(Q#0, $i)): ref, alloc))
return new Bpl.ForallExpr(expr.tok, new List<Variable> { iVar }, body);
} else if (eeType.IsDatatype) {
// translator.FunctionCall(e.tok, BuiltinFunction.DtAlloc, null, TrExpr(e.E), Old.HeapExpr);
- Bpl.Expr alloc = translator.MkIsAlloc(TrExpr(e.E), eeType, Old.HeapExpr);
+ Bpl.Expr alloc = translator.MkIsAlloc(TrExpr(e.E, initEtran, funcEtran, existEtran), eeType, Old.HeapExpr);
return Bpl.Expr.Unary(expr.tok, UnaryOperator.Opcode.Not, alloc);
} else {
// generate: x != null && !old($Heap)[x]
- Bpl.Expr oNull = Bpl.Expr.Neq(TrExpr(e.E), predef.Null);
- Bpl.Expr oIsFresh = Bpl.Expr.Not(Old.IsAlloced(expr.tok, TrExpr(e.E)));
+ Bpl.Expr oNull = Bpl.Expr.Neq(TrExpr(e.E, initEtran, funcEtran, existEtran), predef.Null);
+ Bpl.Expr oIsFresh = Bpl.Expr.Not(Old.IsAlloced(expr.tok, TrExpr(e.E, initEtran, funcEtran, existEtran)));
return Bpl.Expr.Binary(expr.tok, BinaryOperator.Opcode.And, oNull, oIsFresh);
}
default:
@@ -10826,14 +11367,14 @@ namespace Microsoft.Dafny {
ct = BuiltinFunction.RealToInt;
} else {
Contract.Assert(fromInt == toInt);
- return TrExpr(e.E);
+ return TrExpr(e.E, initEtran, funcEtran, existEtran);
}
- return translator.FunctionCall(e.tok, ct, null, TrExpr(e.E));
+ return translator.FunctionCall(e.tok, ct, null, TrExpr(e.E, initEtran, funcEtran, existEtran));
} else if (expr is BinaryExpr) {
BinaryExpr e = (BinaryExpr)expr;
bool isReal = e.E0.Type.IsNumericBased(Type.NumericPersuation.Real);
- Bpl.Expr e0 = TrExpr(e.E0);
+ Bpl.Expr e0 = TrExpr(e.E0, initEtran, funcEtran, existEtran);
if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.InSet) {
return TrInSet(expr.tok, e0, e.E1, cce.NonNull(e.E0.Type)); // let TrInSet translate e.E1
} else if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.NotInSet) {
@@ -10845,7 +11386,7 @@ namespace Microsoft.Dafny {
Bpl.Expr arg = TrInMultiSet(expr.tok, e0, e.E1, cce.NonNull(e.E0.Type)); // let TrInMultiSet translate e.E1
return Bpl.Expr.Unary(expr.tok, UnaryOperator.Opcode.Not, arg);
}
- Bpl.Expr e1 = TrExpr(e.E1);
+ Bpl.Expr e1 = TrExpr(e.E1, initEtran, funcEtran, existEtran);
BinaryOperator.Opcode bOpcode;
Bpl.Type typ;
var oe0 = e0;
@@ -10882,7 +11423,7 @@ namespace Microsoft.Dafny {
if (cot != null) {
var e0args = e.E0.Type.NormalizeExpand().TypeArgs;
var e1args = e.E1.Type.NormalizeExpand().TypeArgs;
- return translator.CoEqualCall(cot, e0args, e1args, null, LayerN(2), e0, e1, expr.tok);
+ return translator.CoEqualCall(cot, e0args, e1args, null, this.layerInterCluster.LayerN((int)FuelSetting.FuelAmount.HIGH), e0, e1, expr.tok);
}
typ = Bpl.Type.Bool;
bOpcode = BinaryOperator.Opcode.Eq; break;
@@ -10891,7 +11432,7 @@ namespace Microsoft.Dafny {
if (cotx != null) {
var e0args = e.E0.Type.NormalizeExpand().TypeArgs;
var e1args = e.E1.Type.NormalizeExpand().TypeArgs;
- var x = translator.CoEqualCall(cotx, e0args, e1args, null, LayerN(2), e0, e1, expr.tok);
+ var x = translator.CoEqualCall(cotx, e0args, e1args, null, this.layerInterCluster.LayerN((int)FuelSetting.FuelAmount.HIGH), e0, e1, expr.tok);
return Bpl.Expr.Unary(expr.tok, UnaryOperator.Opcode.Not, x);
}
typ = Bpl.Type.Bool;
@@ -10902,7 +11443,7 @@ namespace Microsoft.Dafny {
bOpcode = BinaryOperator.Opcode.Lt;
break;
} else {
- return translator.FunctionCall(expr.tok, "INTERNAL_lt_boogie", Bpl.Type.Int, e0, e1);
+ return TrToFunctionCall(expr.tok, "INTERNAL_lt_boogie", Bpl.Type.Bool, e0, e1, liftLit);
}
case BinaryExpr.ResolvedOpcode.Le:
@@ -10912,7 +11453,7 @@ namespace Microsoft.Dafny {
bOpcode = BinaryOperator.Opcode.Le;
break;
} else {
- return translator.FunctionCall(expr.tok, "INTERNAL_le_boogie", Bpl.Type.Int, e0, e1);
+ return TrToFunctionCall(expr.tok, "INTERNAL_le_boogie", Bpl.Type.Bool, e0, e1, false);
}
case BinaryExpr.ResolvedOpcode.Ge:
keepLits = true;
@@ -10921,7 +11462,7 @@ namespace Microsoft.Dafny {
bOpcode = BinaryOperator.Opcode.Ge;
break;
} else {
- return translator.FunctionCall(expr.tok, "INTERNAL_ge_boogie", Bpl.Type.Int, e0, e1);
+ return TrToFunctionCall(expr.tok, "INTERNAL_ge_boogie", Bpl.Type.Bool, e0, e1, false);
}
case BinaryExpr.ResolvedOpcode.Gt:
if (isReal || !DafnyOptions.O.DisableNLarith) {
@@ -10929,7 +11470,7 @@ namespace Microsoft.Dafny {
bOpcode = BinaryOperator.Opcode.Gt;
break;
} else {
- return translator.FunctionCall(expr.tok, "INTERNAL_gt_boogie", Bpl.Type.Int, e0, e1);
+ return TrToFunctionCall(expr.tok, "INTERNAL_gt_boogie", Bpl.Type.Bool, e0, e1, liftLit);
}
case BinaryExpr.ResolvedOpcode.Add:
if (!DafnyOptions.O.DisableNLarith) {
@@ -10941,7 +11482,7 @@ namespace Microsoft.Dafny {
bOpcode = BinaryOperator.Opcode.Add;
break;
} else {
- return translator.FunctionCall(expr.tok, "INTERNAL_add_boogie", Bpl.Type.Int, e0, e1);
+ return TrToFunctionCall(expr.tok, "INTERNAL_add_boogie", Bpl.Type.Int, e0, e1, liftLit);
}
}
case BinaryExpr.ResolvedOpcode.Sub:
@@ -10954,7 +11495,7 @@ namespace Microsoft.Dafny {
bOpcode = BinaryOperator.Opcode.Sub;
break;
} else {
- return translator.FunctionCall(expr.tok, "INTERNAL_sub_boogie", Bpl.Type.Int, e0, e1);
+ return TrToFunctionCall(expr.tok, "INTERNAL_sub_boogie", Bpl.Type.Int, e0, e1, liftLit);
}
}
case BinaryExpr.ResolvedOpcode.Mul:
@@ -10967,7 +11508,7 @@ namespace Microsoft.Dafny {
bOpcode = BinaryOperator.Opcode.Mul;
break;
} else {
- return translator.FunctionCall(expr.tok, "INTERNAL_mul_boogie", Bpl.Type.Int, e0, e1);
+ return TrToFunctionCall(expr.tok, "INTERNAL_mul_boogie", Bpl.Type.Int, e0, e1, liftLit);
}
}
@@ -10980,7 +11521,7 @@ namespace Microsoft.Dafny {
typ = Bpl.Type.Int;
bOpcode = BinaryOperator.Opcode.Div; break;
} else {
- return translator.FunctionCall(expr.tok, "INTERNAL_div_boogie", Bpl.Type.Int, e0, e1);
+ return TrToFunctionCall(expr.tok, "INTERNAL_div_boogie", Bpl.Type.Int, e0, e1, liftLit);
}
}
case BinaryExpr.ResolvedOpcode.Mod:
@@ -10993,7 +11534,7 @@ namespace Microsoft.Dafny {
bOpcode = BinaryOperator.Opcode.Mod;
break;
} else {
- return translator.FunctionCall(expr.tok, "INTERNAL_mod_boogie", Bpl.Type.Int, e0, e1);
+ return TrToFunctionCall(expr.tok, "INTERNAL_mod_boogie", Bpl.Type.Int, e0, e1, liftLit);
}
}
@@ -11017,31 +11558,55 @@ namespace Microsoft.Dafny {
return Bpl.Expr.Binary(expr.tok, bOp, operand0, operand1);
}
- case BinaryExpr.ResolvedOpcode.SetEq:
- return translator.FunctionCall(expr.tok, BuiltinFunction.SetEqual, null, e0, e1);
- case BinaryExpr.ResolvedOpcode.SetNeq:
- return Bpl.Expr.Unary(expr.tok, UnaryOperator.Opcode.Not, translator.FunctionCall(expr.tok, BuiltinFunction.SetEqual, null, e0, e1));
- case BinaryExpr.ResolvedOpcode.ProperSubset:
+ case BinaryExpr.ResolvedOpcode.SetEq: {
+ bool finite = e.E1.Type.AsSetType.Finite;
+ var f = finite ? BuiltinFunction.SetEqual : BuiltinFunction.ISetEqual;
+ return translator.FunctionCall(expr.tok, f, null, e0, e1);
+ }
+ case BinaryExpr.ResolvedOpcode.SetNeq: {
+ bool finite = e.E1.Type.AsSetType.Finite;
+ var f = finite ? BuiltinFunction.SetEqual : BuiltinFunction.ISetEqual;
+ return Bpl.Expr.Unary(expr.tok, UnaryOperator.Opcode.Not, translator.FunctionCall(expr.tok, f, null, e0, e1));
+ }
+ case BinaryExpr.ResolvedOpcode.ProperSubset: {
return translator.ProperSubset(expr.tok, e0, e1);
- case BinaryExpr.ResolvedOpcode.Subset:
- return translator.FunctionCall(expr.tok, BuiltinFunction.SetSubset, null, e0, e1);
- case BinaryExpr.ResolvedOpcode.Superset:
- return translator.FunctionCall(expr.tok, BuiltinFunction.SetSubset, null, e1, e0);
+ }
+ case BinaryExpr.ResolvedOpcode.Subset: {
+ bool finite = e.E1.Type.AsSetType.Finite;
+ var f = finite ? BuiltinFunction.SetSubset : BuiltinFunction.ISetSubset;
+ return translator.FunctionCall(expr.tok, f, null, e0, e1);
+ }
+ case BinaryExpr.ResolvedOpcode.Superset: {
+ bool finite = e.E1.Type.AsSetType.Finite;
+ var f = finite ? BuiltinFunction.SetSubset : BuiltinFunction.ISetSubset;
+ return translator.FunctionCall(expr.tok, f, null, e1, e0);
+ }
case BinaryExpr.ResolvedOpcode.ProperSuperset:
return translator.ProperSubset(expr.tok, e1, e0);
- case BinaryExpr.ResolvedOpcode.Disjoint:
- return translator.FunctionCall(expr.tok, BuiltinFunction.SetDisjoint, null, e0, e1);
+ case BinaryExpr.ResolvedOpcode.Disjoint: {
+ bool finite = e.E1.Type.AsSetType.Finite;
+ var f = finite ? BuiltinFunction.SetDisjoint : BuiltinFunction.ISetDisjoint;
+ return translator.FunctionCall(expr.tok, f, null, e0, e1);
+ }
case BinaryExpr.ResolvedOpcode.InSet:
Contract.Assert(false); throw new cce.UnreachableException(); // this case handled above
case BinaryExpr.ResolvedOpcode.NotInSet:
Contract.Assert(false); throw new cce.UnreachableException(); // this case handled above
- case BinaryExpr.ResolvedOpcode.Union:
- return translator.FunctionCall(expr.tok, BuiltinFunction.SetUnion, translator.TrType(expr.Type.AsSetType.Arg), e0, e1);
- case BinaryExpr.ResolvedOpcode.Intersection:
- return translator.FunctionCall(expr.tok, BuiltinFunction.SetIntersection, translator.TrType(expr.Type.AsSetType.Arg), e0, e1);
- case BinaryExpr.ResolvedOpcode.SetDifference:
- return translator.FunctionCall(expr.tok, BuiltinFunction.SetDifference, translator.TrType(expr.Type.AsSetType.Arg), e0, e1);
-
+ case BinaryExpr.ResolvedOpcode.Union: {
+ bool finite = e.E1.Type.AsSetType.Finite;
+ var f = finite ? BuiltinFunction.SetUnion : BuiltinFunction.ISetUnion;
+ return translator.FunctionCall(expr.tok, f, translator.TrType(expr.Type.AsSetType.Arg), e0, e1);
+ }
+ case BinaryExpr.ResolvedOpcode.Intersection: {
+ bool finite = e.E1.Type.AsSetType.Finite;
+ var f = finite ? BuiltinFunction.SetIntersection : BuiltinFunction.ISetIntersection;
+ return translator.FunctionCall(expr.tok, f, translator.TrType(expr.Type.AsSetType.Arg), e0, e1);
+ }
+ case BinaryExpr.ResolvedOpcode.SetDifference: {
+ bool finite = e.E1.Type.AsSetType.Finite;
+ var f = finite ? BuiltinFunction.SetDifference : BuiltinFunction.ISetDifference;
+ return translator.FunctionCall(expr.tok, f, translator.TrType(expr.Type.AsSetType.Arg), e0, e1);
+ }
case BinaryExpr.ResolvedOpcode.MultiSetEq:
return translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetEqual, null, e0, e1);
case BinaryExpr.ResolvedOpcode.MultiSetNeq:
@@ -11133,14 +11698,14 @@ namespace Microsoft.Dafny {
var ae1 = keepLits ? oe1 : e1;
Bpl.Expr re = Bpl.Expr.Binary(expr.tok, bOpcode, ae0, ae1);
if (liftLit) {
- re = translator.Lit(re, typ);
+ re = MaybeLit(re, typ);
}
return re;
} else if (expr is TernaryExpr) {
var e = (TernaryExpr)expr;
- var e0 = TrExpr(e.E0);
- var e1 = TrExpr(e.E1);
- var e2 = TrExpr(e.E2);
+ var e0 = TrExpr(e.E0, initEtran, funcEtran, existEtran);
+ var e1 = TrExpr(e.E1, initEtran, funcEtran, existEtran);
+ var e2 = TrExpr(e.E2, initEtran, funcEtran, existEtran);
switch (e.Op) {
case TernaryExpr.Opcode.PrefixEqOp:
case TernaryExpr.Opcode.PrefixNeqOp:
@@ -11148,7 +11713,7 @@ namespace Microsoft.Dafny {
var e2type = e.E2.Type.NormalizeExpand();
var cot = e1type.AsCoDatatype;
Contract.Assert(cot != null); // the argument types of prefix equality (and prefix disequality) are codatatypes
- var r = translator.CoEqualCall(cot, e1type.TypeArgs, e2type.TypeArgs, e0, LayerN(2), e1, e2);
+ var r = translator.CoEqualCall(cot, e1type.TypeArgs, e2type.TypeArgs, e0, this.layerInterCluster.LayerN((int)FuelSetting.FuelAmount.HIGH), e1, e2);
if (e.Op == TernaryExpr.Opcode.PrefixEqOp) {
return r;
} else {
@@ -11160,65 +11725,70 @@ namespace Microsoft.Dafny {
} else if (expr is LetExpr) {
var e = (LetExpr)expr;
if (e.Exact) {
- return TrExpr(GetSubstitutedBody(e));
+ return TrExpr(GetSubstitutedBody(e), initEtran, funcEtran, existEtran);
} else {
var d = translator.LetDesugaring(e);
- return TrExpr(d);
+ return TrExpr(d, initEtran, funcEtran, existEtran);
}
} else if (expr is NamedExpr) {
- return TrExpr(((NamedExpr)expr).Body);
+ return TrExpr(((NamedExpr)expr).Body, initEtran, funcEtran, existEtran);
} else if (expr is QuantifierExpr) {
QuantifierExpr e = (QuantifierExpr)expr;
- List<Variable> tyvars = translator.MkTyParamBinders(e.TypeArgs);
- List<Variable> bvars = new List<Variable>();
- var initEtran = this;
- var bodyEtran = this;
- bool _scratch = true;
+ if (e.SplitQuantifier != null) {
+ return TrExpr(e.SplitQuantifierExpression, initEtran, existEtran, existEtran);
+ } else {
+ List<Variable> tyvars = translator.MkTyParamBinders(e.TypeArgs);
+ List<Variable> bvars = new List<Variable>();
- Bpl.Expr antecedent = Bpl.Expr.True;
+ var etran = initEtran ?? this;
+ var bodyEtran = this;
+ existEtran = e is ForallExpr ? this : existEtran;
+ bool _scratch = true;
- if (Attributes.ContainsBool(e.Attributes, "layerQuantifier", ref _scratch)) {
- // If this is a layer quantifier, quantify over layers here, and use $LS(ly) layers in the translation of the body
- var ly = BplBoundVar(e.Refresh("q$ly#", translator.CurrentIdGenerator), predef.LayerType, bvars);
- Expr layer = translator.LayerSucc(ly);
- bodyEtran = new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, layer, layer, modifiesFrame);
- }
- if (Attributes.ContainsBool(e.Attributes, "heapQuantifier", ref _scratch)) {
- var h = BplBoundVar(e.Refresh("q$heap#", translator.CurrentIdGenerator), predef.HeapType, bvars);
- bodyEtran = new ExpressionTranslator(bodyEtran, h);
- antecedent = BplAnd(new List<Bpl.Expr> {
+ Bpl.Expr antecedent = Bpl.Expr.True;
+
+ if (Attributes.ContainsBool(e.Attributes, "layerQuantifier", ref _scratch)) {
+ // If this is a layer quantifier, quantify over layers here, and use $LS(ly) layers in the translation of the body
+ var ly = BplBoundVar(e.Refresh("q$ly#", translator.CurrentIdGenerator), predef.LayerType, bvars);
+ bodyEtran = new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, new FuelSetting(translator, 1, ly), new FuelSetting(translator, 1, ly), modifiesFrame, stripLits);
+ }
+ if (Attributes.ContainsBool(e.Attributes, "heapQuantifier", ref _scratch)) {
+ var h = BplBoundVar(e.Refresh("q$heap#", translator.CurrentIdGenerator), predef.HeapType, bvars);
+ bodyEtran = new ExpressionTranslator(bodyEtran, h);
+ antecedent = BplAnd(new List<Bpl.Expr> {
antecedent,
translator.FunctionCall(e.tok, BuiltinFunction.IsGoodHeap, null, h),
- translator.HeapSameOrSucc(initEtran.HeapExpr, h)
+ translator.HeapSameOrSucc(etran.HeapExpr, h)
});
- }
+ }
- antecedent = BplAnd(antecedent, bodyEtran.TrBoundVariables(e.BoundVars, bvars));
+ antecedent = BplAnd(antecedent, etran.TrBoundVariables(e.BoundVars, bvars));
- Bpl.QKeyValue kv = TrAttributes(e.Attributes, "trigger");
- Bpl.Trigger tr = null;
- for (Attributes aa = e.Attributes; aa != null; aa = aa.Prev) {
- if (aa.Name == "trigger") {
- List<Bpl.Expr> tt = new List<Bpl.Expr>();
- foreach (var arg in aa.Args) {
- tt.Add(bodyEtran.TrExpr(arg));
+ Bpl.QKeyValue kv = TrAttributes(e.Attributes, "trigger");
+ Bpl.Trigger tr = null;
+ var argsEtran = bodyEtran.WithNoLits();
+ foreach (var aa in e.Attributes.AsEnumerable()) {
+ if (aa.Name == "trigger") {
+ List<Bpl.Expr> tt = new List<Bpl.Expr>();
+ foreach (var arg in aa.Args) {
+ tt.Add(argsEtran.TrExpr(arg, initEtran, existEtran, existEtran));
+ }
+ tr = new Bpl.Trigger(expr.tok, true, tt, tr);
}
- tr = new Bpl.Trigger(expr.tok, true, tt, tr);
}
- }
- if (e.Range != null) {
- antecedent = BplAnd(antecedent, bodyEtran.TrExpr(e.Range));
- }
- Bpl.Expr body = bodyEtran.TrExpr(e.Term);
+ if (e.Range != null) {
+ antecedent = BplAnd(antecedent, etran.TrExpr(e.Range, initEtran, existEtran, existEtran));
+ }
+ Bpl.Expr body = bodyEtran.TrExpr(e.Term, initEtran, existEtran, existEtran);
- if (e is ForallExpr) {
- return new Bpl.ForallExpr(expr.tok, new List<TypeVariable>(), Concat(tyvars,bvars), kv, tr, Bpl.Expr.Imp(antecedent, body));
- } else {
- Contract.Assert(e is ExistsExpr);
- return new Bpl.ExistsExpr(expr.tok, new List<TypeVariable>(), Concat(tyvars,bvars), kv, tr, Bpl.Expr.And(antecedent, body));
+ if (e is ForallExpr) {
+ return new Bpl.ForallExpr(expr.tok, new List<TypeVariable>(), Concat(tyvars, bvars), kv, tr, Bpl.Expr.Imp(antecedent, body));
+ } else {
+ Contract.Assert(e is ExistsExpr);
+ return new Bpl.ExistsExpr(expr.tok, new List<TypeVariable>(), Concat(tyvars, bvars), kv, tr, Bpl.Expr.And(antecedent, body));
+ }
}
-
} else if (expr is SetComprehension) {
var e = (SetComprehension)expr;
// Translate "set xs | R :: T" into "lambda y: BoxType :: (exists xs :: CorrectType(xs) && R && y==Box(T))".
@@ -11229,8 +11799,8 @@ namespace Microsoft.Dafny {
var yVar = new Bpl.BoundVariable(expr.tok, new Bpl.TypedIdent(expr.tok, translator.CurrentIdGenerator.FreshId("$y#"), predef.BoxType));
Bpl.Expr y = new Bpl.IdentifierExpr(expr.tok, yVar);
- var eq = Bpl.Expr.Eq(y, BoxIfNecessary(expr.tok, TrExpr(e.Term), e.Term.Type));
- var ebody = Bpl.Expr.And(BplAnd(typeAntecedent, TrExpr(e.Range)), eq);
+ var eq = Bpl.Expr.Eq(y, BoxIfNecessary(expr.tok, TrExpr(e.Term, initEtran, funcEtran, existEtran), e.Term.Type));
+ var ebody = Bpl.Expr.And(BplAnd(typeAntecedent, TrExpr(e.Range, initEtran, funcEtran, existEtran)), eq);
var triggers = translator.TrTrigger(this, e.Attributes, e.tok);
var exst = new Bpl.ExistsExpr(expr.tok, bvars, triggers, ebody);
@@ -11257,9 +11827,9 @@ namespace Microsoft.Dafny {
Dictionary<IVariable, Expression> subst = new Dictionary<IVariable,Expression>();
subst.Add(e.BoundVars[0], new BoogieWrapper(unboxy,e.BoundVars[0].Type));
- var ebody = BplAnd(typeAntecedent ?? Bpl.Expr.True, TrExpr(translator.Substitute(e.Range, null, subst)));
+ var ebody = BplAnd(typeAntecedent ?? Bpl.Expr.True, TrExpr(translator.Substitute(e.Range, null, subst), initEtran, funcEtran, existEtran));
Bpl.Expr l1 = new Bpl.LambdaExpr(e.tok, new List<TypeVariable>(), new List<Variable> { yVar }, kv, ebody);
- ebody = TrExpr(translator.Substitute(e.Term, null, subst));
+ ebody = TrExpr(translator.Substitute(e.Term, null, subst), initEtran, funcEtran, existEtran);
Bpl.Expr l2 = new Bpl.LambdaExpr(e.tok, new List<TypeVariable>(), new List<Variable> { yVar }, kv, BoxIfNecessary(expr.tok, ebody, e.Term.Type));
bool finite = e.Finite;
@@ -11268,41 +11838,49 @@ namespace Microsoft.Dafny {
} else if (expr is LambdaExpr) {
var e = (LambdaExpr)expr;
-
return TrLambdaExpr(e);
+
} else if (expr is StmtExpr) {
var e = (StmtExpr)expr;
- return TrExpr(e.E);
+ return TrExpr(e.E, initEtran, funcEtran, existEtran);
} else if (expr is ITEExpr) {
ITEExpr e = (ITEExpr)expr;
- var g = translator.RemoveLit(TrExpr(e.Test));
- var thn = translator.RemoveLit(TrExpr(e.Thn));
- var els = translator.RemoveLit(TrExpr(e.Els));
+ var g = translator.RemoveLit(TrExpr(e.Test, initEtran, funcEtran, existEtran));
+ var thn = translator.RemoveLit(TrExpr(e.Thn, initEtran, funcEtran, existEtran));
+ var els = translator.RemoveLit(TrExpr(e.Els, initEtran, funcEtran, existEtran));
return new NAryExpr(expr.tok, new IfThenElse(expr.tok), new List<Bpl.Expr> { g, thn, els });
} else if (expr is MatchExpr) {
var e = (MatchExpr)expr;
var ite = DesugarMatchExpr(e);
- return TrExpr(ite);
+ return TrExpr(ite, initEtran, funcEtran, existEtran);
} else if (expr is ConcreteSyntaxExpression) {
var e = (ConcreteSyntaxExpression)expr;
- return TrExpr(e.ResolvedExpression);
+ return TrExpr(e.ResolvedExpression, initEtran, funcEtran, existEtran);
} else if (expr is BoxingCastExpr) {
BoxingCastExpr e = (BoxingCastExpr)expr;
- return translator.CondApplyBox(e.tok, TrExpr(e.E), e.FromType, e.ToType);
+ return translator.CondApplyBox(e.tok, TrExpr(e.E, initEtran, funcEtran, existEtran), e.FromType, e.ToType);
} else if (expr is UnboxingCastExpr) {
UnboxingCastExpr e = (UnboxingCastExpr)expr;
- return translator.CondApplyUnbox(e.tok, TrExpr(e.E), e.FromType, e.ToType);
+ return translator.CondApplyUnbox(e.tok, TrExpr(e.E, initEtran, funcEtran, existEtran), e.FromType, e.ToType);
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
}
}
+ private Expr TrToFunctionCall(IToken tok, string function, Bpl.Type returnType, Bpl.Expr e0, Bpl.Expr e1, bool liftLit) {
+ Bpl.Expr re = translator.FunctionCall(tok, function, returnType, e0, e1);
+ if (liftLit) {
+ re = MaybeLit(re, returnType);
+ }
+ return re;
+ }
+
private Expr TrLambdaExpr(LambdaExpr e) {
var bvars = new List<Bpl.Variable>();
var bargs = new List<Bpl.Expr>();
@@ -11340,22 +11918,19 @@ namespace Microsoft.Dafny {
}
var rdvars = new List<Bpl.Variable>();
- var o = translator.UnboxIfBoxed(BplBoundVar(varNameGen.FreshId("#o#"), predef.BoxType, rdvars), new ObjectType());
-
- Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.Neq(o, predef.Null), et.IsAlloced(e.tok, o));
- Bpl.Expr consequent = translator.InRWClause(e.tok, o, null, e.Reads.ConvertAll(su.SubstFrameExpr), et, null, null);
- Bpl.Expr rdbody =
- new Bpl.LambdaExpr(e.tok, new List<TypeVariable>(), rdvars, null,
- BplImp(ante, consequent));
+ var o = BplBoundVar(varNameGen.FreshId("#o#"), predef.RefType, rdvars);
+ Bpl.Expr rdbody = new Bpl.LambdaExpr(e.tok, new List<TypeVariable>(), rdvars, null,
+ translator.InRWClause(e.tok, o, null, e.Reads.ConvertAll(su.SubstFrameExpr), et, null, null));
+ rdbody = translator.FunctionCall(e.tok, "SetRef_to_SetBox", predef.SetType(e.tok, true, predef.BoxType), rdbody);
- return translator.Lit(
+ return MaybeLit(
translator.FunctionCall(e.tok, BuiltinFunction.AtLayer, predef.HandleType,
new Bpl.LambdaExpr(e.tok, new List<TypeVariable>(), lvars, null,
translator.FunctionCall(e.tok, translator.Handle(e.BoundVars.Count), predef.BoxType,
new Bpl.LambdaExpr(e.tok, new List<TypeVariable>(), bvars, null, ebody),
new Bpl.LambdaExpr(e.tok, new List<TypeVariable>(), bvars, null, reqbody),
new Bpl.LambdaExpr(e.tok, new List<TypeVariable>(), bvars, null, rdbody))),
- layerIntraCluster ?? layerInterCluster),
+ layerIntraCluster != null ? layerIntraCluster.ToExpr() : layerInterCluster.ToExpr()),
predef.HandleType);
}
@@ -11433,6 +12008,20 @@ namespace Microsoft.Dafny {
return typeAntecedent;
}
+ public List<Tuple<Bpl.Variable, Bpl.Expr>> TrBoundVariables_SeparateWhereClauses(List<BoundVar/*!*/> boundVars) {
+ Contract.Requires(boundVars != null);
+ Contract.Ensures(Contract.Result<List<Tuple<Bpl.Variable, Bpl.Expr>>>() != null);
+
+ var varsAndAntecedents = new List<Tuple<Bpl.Variable, Bpl.Expr>>();
+ foreach (BoundVar bv in boundVars) {
+ var tid = new Bpl.TypedIdent(bv.tok, bv.AssignUniqueName(translator.currentDeclaration.IdGenerator), translator.TrType(bv.Type));
+ var bvar = new Bpl.BoundVariable(bv.tok, tid);
+ var wh = translator.GetWhereClause(bv.tok, new Bpl.IdentifierExpr(bv.tok, bvar), bv.Type, this);
+ varsAndAntecedents.Add(Tuple.Create<Bpl.Variable, Bpl.Expr>(bvar, wh));
+ }
+ return varsAndAntecedents;
+ }
+
public Bpl.Expr TrBoundVariablesRename(List<BoundVar> boundVars, List<Variable> bvars, out Dictionary<IVariable, Expression> substMap) {
Contract.Requires(boundVars != null);
Contract.Requires(bvars != null);
@@ -11456,11 +12045,11 @@ namespace Microsoft.Dafny {
}
public List<Expr> FunctionInvocationArguments(FunctionCallExpr e, Bpl.Expr layerArgument) {
- bool returnLit;
- return FunctionInvocationArguments(e, layerArgument, out returnLit);
+ bool dummy;
+ return FunctionInvocationArguments(e, layerArgument, out dummy);
}
- public List<Expr> FunctionInvocationArguments(FunctionCallExpr e, Bpl.Expr layerArgument, out bool returnLit) {
+ public List<Expr> FunctionInvocationArguments(FunctionCallExpr e, Bpl.Expr layerArgument, out bool argsAreLit) {
Contract.Requires(e != null);
Contract.Ensures(Contract.Result<List<Bpl.Expr>>() != null);
@@ -11479,12 +12068,12 @@ namespace Microsoft.Dafny {
if (!e.Function.IsStatic) {
args.Add(TrExpr(e.Receiver));
}
- returnLit = true;
+ argsAreLit = true;
for (int i = 0; i < e.Args.Count; i++) {
Expression ee = e.Args[i];
Type t = e.Function.Formals[i].Type;
Expr tr_ee = TrExpr(ee);
- returnLit = returnLit && translator.IsLit(tr_ee);
+ argsAreLit = argsAreLit && translator.IsLit(tr_ee);
args.Add(translator.CondApplyBox(e.tok, tr_ee, cce.NonNull(ee.Type), t));
}
return args;
@@ -11640,13 +12229,13 @@ namespace Microsoft.Dafny {
public Bpl.QKeyValue TrAttributes(Attributes attrs, string skipThisAttribute) {
Bpl.QKeyValue kv = null;
- for ( ; attrs != null; attrs = attrs.Prev) {
- if (attrs.Name == skipThisAttribute
- || attrs.Name == "axiom") { // Dafny's axiom attribute clashes with Boogie's axiom keyword
+ foreach (var attr in attrs.AsEnumerable()) {
+ if (attr.Name == skipThisAttribute
+ || attr.Name == "axiom") { // Dafny's axiom attribute clashes with Boogie's axiom keyword
continue;
}
List<object> parms = new List<object>();
- foreach (var arg in attrs.Args) {
+ foreach (var arg in attr.Args) {
var s = arg.AsStringLiteral();
if (s != null) {
// pass string literals down to Boogie as string literals, not as their expression translation
@@ -11657,7 +12246,7 @@ namespace Microsoft.Dafny {
parms.Add(e);
}
}
- kv = new Bpl.QKeyValue(Token.NoToken, attrs.Name, parms, kv);
+ kv = new Bpl.QKeyValue(Token.NoToken, attr.Name, parms, kv);
}
return kv;
}
@@ -11718,6 +12307,15 @@ namespace Microsoft.Dafny {
SetSubset,
SetDisjoint,
+ ISetEmpty,
+ ISetUnionOne,
+ ISetUnion,
+ ISetIntersection,
+ ISetDifference,
+ ISetEqual,
+ ISetSubset,
+ ISetDisjoint,
+
MultiSetCard,
MultiSetEmpty,
MultiSetUnionOne,
@@ -11771,6 +12369,7 @@ namespace Microsoft.Dafny {
IntToReal,
IsGoodHeap,
+ IsHeapAnchor,
HeapSucc,
HeapSuccGhost,
@@ -11897,25 +12496,25 @@ namespace Microsoft.Dafny {
case BuiltinFunction.SetEmpty: {
Contract.Assert(args.Length == 0);
Contract.Assert(typeInstantiation != null);
- Bpl.Type resultType = predef.SetType(tok, typeInstantiation);
+ Bpl.Type resultType = predef.SetType(tok, true, typeInstantiation);
return Bpl.Expr.CoerceType(tok, FunctionCall(tok, "Set#Empty", resultType, args), resultType);
}
case BuiltinFunction.SetUnionOne:
Contract.Assert(args.Length == 2);
Contract.Assert(typeInstantiation != null);
- return FunctionCall(tok, "Set#UnionOne", predef.SetType(tok, typeInstantiation), args);
+ return FunctionCall(tok, "Set#UnionOne", predef.SetType(tok, true, typeInstantiation), args);
case BuiltinFunction.SetUnion:
Contract.Assert(args.Length == 2);
Contract.Assert(typeInstantiation != null);
- return FunctionCall(tok, "Set#Union", predef.SetType(tok, typeInstantiation), args);
+ return FunctionCall(tok, "Set#Union", predef.SetType(tok, true, typeInstantiation), args);
case BuiltinFunction.SetIntersection:
Contract.Assert(args.Length == 2);
Contract.Assert(typeInstantiation != null);
- return FunctionCall(tok, "Set#Intersection", predef.SetType(tok, typeInstantiation), args);
+ return FunctionCall(tok, "Set#Intersection", predef.SetType(tok, true, typeInstantiation), args);
case BuiltinFunction.SetDifference:
Contract.Assert(args.Length == 2);
Contract.Assert(typeInstantiation != null);
- return FunctionCall(tok, "Set#Difference", predef.SetType(tok, typeInstantiation), args);
+ return FunctionCall(tok, "Set#Difference", predef.SetType(tok, true, typeInstantiation), args);
case BuiltinFunction.SetEqual:
Contract.Assert(args.Length == 2);
Contract.Assert(typeInstantiation == null);
@@ -11928,7 +12527,40 @@ namespace Microsoft.Dafny {
Contract.Assert(args.Length == 2);
Contract.Assert(typeInstantiation == null);
return FunctionCall(tok, "Set#Disjoint", Bpl.Type.Bool, args);
-
+ case BuiltinFunction.ISetEmpty: {
+ Contract.Assert(args.Length == 0);
+ Contract.Assert(typeInstantiation != null);
+ Bpl.Type resultType = predef.SetType(tok, false, typeInstantiation);
+ return Bpl.Expr.CoerceType(tok, FunctionCall(tok, "ISet#Empty", resultType, args), resultType);
+ }
+ case BuiltinFunction.ISetUnionOne:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "ISet#UnionOne", predef.SetType(tok, false, typeInstantiation), args);
+ case BuiltinFunction.ISetUnion:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "ISet#Union", predef.SetType(tok, false, typeInstantiation), args);
+ case BuiltinFunction.ISetIntersection:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "ISet#Intersection", predef.SetType(tok, false, typeInstantiation), args);
+ case BuiltinFunction.ISetDifference:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "ISet#Difference", predef.SetType(tok, false, typeInstantiation), args);
+ case BuiltinFunction.ISetEqual:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "ISet#Equal", Bpl.Type.Bool, args);
+ case BuiltinFunction.ISetSubset:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "ISet#Subset", Bpl.Type.Bool, args);
+ case BuiltinFunction.ISetDisjoint:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "ISet#Disjoint", Bpl.Type.Bool, args);
case BuiltinFunction.MultiSetCard:
Contract.Assert(args.Length == 1);
Contract.Assert(typeInstantiation == null);
@@ -12118,6 +12750,10 @@ namespace Microsoft.Dafny {
Contract.Assert(args.Length == 1);
Contract.Assert(typeInstantiation == null);
return FunctionCall(tok, "$IsGoodHeap", Bpl.Type.Bool, args);
+ case BuiltinFunction.IsHeapAnchor:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "$IsHeapAnchor", Bpl.Type.Bool, args);
case BuiltinFunction.HeapSucc:
Contract.Assert(args.Length == 2);
Contract.Assert(typeInstantiation == null);
@@ -12271,36 +12907,39 @@ namespace Microsoft.Dafny {
Contract.Ensures(Contract.Result<List<SplitExprInfo>>() != null);
var splits = new List<SplitExprInfo>();
- splitHappened = TrSplitExpr(expr, splits, true, int.MaxValue, apply_induction, etran);
+ splitHappened = TrSplitExpr(expr, splits, true, int.MaxValue, true, apply_induction, etran);
return splits;
}
- List<SplitExprInfo/*!*/>/*!*/ TrSplitExpr(Expression expr, ExpressionTranslator etran, int heightLimit, bool apply_induction, out bool splitHappened)
+ List<SplitExprInfo> TrSplitExprForMethodSpec(Expression expr, ExpressionTranslator etran, MethodTranslationKind kind)
{
Contract.Requires(expr != null);
Contract.Requires(etran != null);
Contract.Ensures(Contract.Result<List<SplitExprInfo>>() != null);
var splits = new List<SplitExprInfo>();
- splitHappened = TrSplitExpr(expr, splits, true, heightLimit, apply_induction, etran);
+ var apply_induction = true;/*kind == MethodTranslationKind.Implementation*/;
+ bool splitHappened; // we don't actually care
+ splitHappened = TrSplitExpr(expr, splits, true, int.MaxValue, kind != MethodTranslationKind.InterModuleCall, apply_induction, etran);
return splits;
}
Bpl.Trigger TrTrigger(ExpressionTranslator etran, Attributes attribs, IToken tok, Dictionary<IVariable, Expression> substMap = null)
{
+ Contract.Requires(etran != null);
+ Contract.Requires(tok != null);
+ var argsEtran = etran.WithNoLits();
Bpl.Trigger tr = null;
- for (Attributes aa = attribs; aa != null; aa = aa.Prev) {
- if (aa.Name == "trigger") {
- List<Bpl.Expr> tt = new List<Bpl.Expr>();
- foreach (var arg in aa.Args) {
- if (substMap == null) {
- tt.Add(etran.TrExpr(arg));
- } else {
- tt.Add(etran.TrExpr(Substitute(arg, null, substMap)));
- }
+ foreach (var trigger in attribs.AsEnumerable().Where(aa => aa.Name == "trigger").Select(aa => aa.Args)) {
+ List<Bpl.Expr> tt = new List<Bpl.Expr>();
+ foreach (var arg in trigger) {
+ if (substMap == null) {
+ tt.Add(argsEtran.TrExpr(arg));
+ } else {
+ tt.Add(argsEtran.TrExpr(Substitute(arg, null, substMap)));
}
- tr = new Bpl.Trigger(tok, true, tt, tr);
}
+ tr = new Bpl.Trigger(tok, true, tt, tr);
}
return tr;
}
@@ -12311,7 +12950,7 @@ namespace Microsoft.Dafny {
/// if its body is available in the current context and its height is less than "heightLimit" (if "heightLimit" is
/// passed in as 0, then no functions will be inlined).
/// </summary>
- bool TrSplitExpr(Expression expr, List<SplitExprInfo/*!*/>/*!*/ splits, bool position, int heightLimit, bool apply_induction, ExpressionTranslator etran) {
+ bool TrSplitExpr(Expression expr, List<SplitExprInfo/*!*/>/*!*/ splits, bool position, int heightLimit, bool inlineProtectedFunctions, bool apply_induction, ExpressionTranslator etran) {
Contract.Requires(expr != null);
Contract.Requires(expr.Type.IsBoolType || (expr is BoxingCastExpr && ((BoxingCastExpr)expr).E.Type.IsBoolType));
Contract.Requires(splits != null);
@@ -12320,7 +12959,7 @@ namespace Microsoft.Dafny {
if (expr is BoxingCastExpr) {
var bce = (BoxingCastExpr)expr;
var ss = new List<SplitExprInfo>();
- if (TrSplitExpr(bce.E, ss, position, heightLimit, apply_induction, etran)) {
+ if (TrSplitExpr(bce.E, ss, position, heightLimit, inlineProtectedFunctions, apply_induction, etran)) {
foreach (var s in ss) {
splits.Add(new SplitExprInfo(s.Kind, CondApplyBox(s.E.tok, s.E, bce.FromType, bce.ToType)));
}
@@ -12329,22 +12968,22 @@ namespace Microsoft.Dafny {
} else if (expr is ConcreteSyntaxExpression) {
var e = (ConcreteSyntaxExpression)expr;
- return TrSplitExpr(e.ResolvedExpression, splits, position, heightLimit, apply_induction, etran);
+ return TrSplitExpr(e.ResolvedExpression, splits, position, heightLimit, inlineProtectedFunctions, apply_induction, etran);
} else if (expr is LetExpr) {
var e = (LetExpr)expr;
if (e.Exact) {
- return TrSplitExpr(etran.GetSubstitutedBody(e), splits, position, heightLimit, apply_induction, etran);
+ return TrSplitExpr(etran.GetSubstitutedBody(e), splits, position, heightLimit, inlineProtectedFunctions, apply_induction, etran);
} else {
var d = LetDesugaring(e);
- return TrSplitExpr(d, splits, position, heightLimit, apply_induction, etran);
+ return TrSplitExpr(d, splits, position, heightLimit, inlineProtectedFunctions, apply_induction, etran);
}
} else if (expr is UnaryOpExpr) {
var e = (UnaryOpExpr)expr;
if (e.Op == UnaryOpExpr.Opcode.Not) {
var ss = new List<SplitExprInfo>();
- if (TrSplitExpr(e.E, ss, !position, heightLimit, apply_induction, etran)) {
+ if (TrSplitExpr(e.E, ss, !position, heightLimit, inlineProtectedFunctions, apply_induction, etran)) {
foreach (var s in ss) {
splits.Add(new SplitExprInfo(s.Kind, Bpl.Expr.Unary(s.E.tok, UnaryOperator.Opcode.Not, s.E)));
}
@@ -12355,13 +12994,13 @@ namespace Microsoft.Dafny {
} else if (expr is BinaryExpr) {
var bin = (BinaryExpr)expr;
if (position && bin.ResolvedOp == BinaryExpr.ResolvedOpcode.And) {
- TrSplitExpr(bin.E0, splits, position, heightLimit, apply_induction, etran);
- TrSplitExpr(bin.E1, splits, position, heightLimit, apply_induction, etran);
+ TrSplitExpr(bin.E0, splits, position, heightLimit, inlineProtectedFunctions, apply_induction, etran);
+ TrSplitExpr(bin.E1, splits, position, heightLimit, inlineProtectedFunctions, apply_induction, etran);
return true;
} else if (!position && bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Or) {
- TrSplitExpr(bin.E0, splits, position, heightLimit, apply_induction, etran);
- TrSplitExpr(bin.E1, splits, position, heightLimit, apply_induction, etran);
+ TrSplitExpr(bin.E0, splits, position, heightLimit, inlineProtectedFunctions, apply_induction, etran);
+ TrSplitExpr(bin.E1, splits, position, heightLimit, inlineProtectedFunctions, apply_induction, etran);
return true;
} else if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Imp) {
@@ -12369,14 +13008,14 @@ namespace Microsoft.Dafny {
if (position) {
var lhs = etran.TrExpr(bin.E0);
var ss = new List<SplitExprInfo>();
- TrSplitExpr(bin.E1, ss, position, heightLimit, apply_induction, etran);
+ TrSplitExpr(bin.E1, ss, position, heightLimit, inlineProtectedFunctions, apply_induction, etran);
foreach (var s in ss) {
// as the source location in the following implication, use that of the translated "s"
splits.Add(new SplitExprInfo(s.Kind, Bpl.Expr.Binary(s.E.tok, BinaryOperator.Opcode.Imp, lhs, s.E)));
}
} else {
var ss = new List<SplitExprInfo>();
- TrSplitExpr(bin.E0, ss, !position, heightLimit, apply_induction, etran);
+ TrSplitExpr(bin.E0, ss, !position, heightLimit, inlineProtectedFunctions, apply_induction, etran);
var rhs = etran.TrExpr(bin.E1);
foreach (var s in ss) {
// as the source location in the following implication, use that of the translated "s"
@@ -12401,15 +13040,14 @@ namespace Microsoft.Dafny {
// checked $PrefixEqual#Dt(k, A, B) || (0 < k ==> A.Cons? ==> B.Cons? && A.head == B.head && $PrefixEqual#2#Dt(k - 1, A.tail, B.tail)) // note the #2 in the recursive call, just like for user-defined predicates that are inlined by TrSplitExpr
// free $PrefixEqual#Dt(k, A, B);
var kPos = Bpl.Expr.Lt(Bpl.Expr.Literal(0), k);
- var prefixEqK = CoEqualCall(codecl, e1type.TypeArgs, e2type.TypeArgs, k, etran.LayerN(2), A, B); // FunctionCall(expr.tok, CoPrefixName(codecl, 1), Bpl.Type.Bool, k, A, B);
+ var prefixEqK = CoEqualCall(codecl, e1type.TypeArgs, e2type.TypeArgs, k, etran.layerInterCluster.LayerN((int)FuelSetting.FuelAmount.HIGH), A, B); // FunctionCall(expr.tok, CoPrefixName(codecl, 1), Bpl.Type.Bool, k, A, B);
var kMinusOne = Bpl.Expr.Sub(k, Bpl.Expr.Literal(1));
// for the inlining of the definition of prefix equality, translate the two main equality operands arguments with a higher offset (to obtain #2 functions)
var etran2 = etran.LayerOffset(1);
var A2 = etran2.TrExpr(e.E1);
var B2 = etran2.TrExpr(e.E2);
var needsTokenAdjust = TrSplitNeedsTokenAdjustment(expr);
- // Dan: dafny4/Circ.dfy needs this one to be 2+, rather than 1+
- Bpl.Expr layer = LayerSucc(etran.layerInterCluster, 2);
+ Bpl.Expr layer = etran.layerInterCluster.LayerN((int)FuelSetting.FuelAmount.HIGH);
foreach (var c in CoPrefixEquality(needsTokenAdjust ? new ForceCheckToken(expr.tok) : expr.tok, codecl, e1type.TypeArgs, e2type.TypeArgs, kMinusOne, layer, A2, B2, true)) {
var p = Bpl.Expr.Binary(c.tok, BinaryOperator.Opcode.Or, prefixEqK, Bpl.Expr.Imp(kPos, c));
splits.Add(new SplitExprInfo(SplitExprInfo.K.Checked, p));
@@ -12424,8 +13062,8 @@ namespace Microsoft.Dafny {
var ssThen = new List<SplitExprInfo>();
var ssElse = new List<SplitExprInfo>();
- TrSplitExpr(ite.Thn, ssThen, position, heightLimit, apply_induction, etran);
- TrSplitExpr(ite.Els, ssElse, position, heightLimit, apply_induction, etran);
+ TrSplitExpr(ite.Thn, ssThen, position, heightLimit, inlineProtectedFunctions, apply_induction, etran);
+ TrSplitExpr(ite.Els, ssElse, position, heightLimit, inlineProtectedFunctions, apply_induction, etran);
var op = position ? BinaryOperator.Opcode.Imp : BinaryOperator.Opcode.And;
var test = etran.TrExpr(ite.Test);
@@ -12451,14 +13089,14 @@ namespace Microsoft.Dafny {
if (position) {
var conclusion = etran.TrExpr(e.GetSConclusion());
var ss = new List<SplitExprInfo>();
- TrSplitExpr(e.E, ss, position, heightLimit, apply_induction, etran);
+ TrSplitExpr(e.E, ss, position, heightLimit, inlineProtectedFunctions, apply_induction, etran);
foreach (var s in ss) {
// as the source location in the following implication, use that of the translated "s"
splits.Add(new SplitExprInfo(s.Kind, Bpl.Expr.Binary(s.E.tok, BinaryOperator.Opcode.Imp, conclusion, s.E)));
}
} else {
var ss = new List<SplitExprInfo>();
- TrSplitExpr(e.GetSConclusion(), ss, !position, heightLimit, apply_induction, etran);
+ TrSplitExpr(e.GetSConclusion(), ss, !position, heightLimit, inlineProtectedFunctions, apply_induction, etran);
var rhs = etran.TrExpr(e.E);
foreach (var s in ss) {
// as the source location in the following implication, use that of the translated "s"
@@ -12469,7 +13107,7 @@ namespace Microsoft.Dafny {
} else if (expr is OldExpr) {
var e = (OldExpr)expr;
- return TrSplitExpr(e.E, splits, position, heightLimit, apply_induction, etran.Old);
+ return TrSplitExpr(e.E, splits, position, heightLimit, inlineProtectedFunctions, apply_induction, etran.Old);
} else if (expr is FunctionCallExpr && position) {
var fexp = (FunctionCallExpr)expr;
@@ -12478,20 +13116,17 @@ namespace Microsoft.Dafny {
var module = f.EnclosingClass.Module;
var functionHeight = module.CallGraph.GetSCCRepresentativeId(f);
- if (module == currentModule && functionHeight < heightLimit && f.Body != null && !(f.Body.Resolved is MatchExpr)) {
+ if (functionHeight < heightLimit && f.Body != null && !(f.Body.Resolved is MatchExpr)) {
if (RefinementToken.IsInherited(fexp.tok, currentModule) &&
f is Predicate && ((Predicate)f).BodyOrigin == Predicate.BodyOriginKind.DelayedDefinition &&
(codeContext == null || !codeContext.MustReverify)) {
// The function was inherited as body-less but is now given a body. Don't inline the body (since, apparently, everything
// that needed to be proved about the function was proved already in the previous module, even without the body definition).
- } else if (!FunctionBodyIsAvailable(f, currentModule)) {
+ } else if (!FunctionBodyIsAvailable(f, currentModule, inlineProtectedFunctions)) {
// Don't inline opaque functions or foreign protected functions
+ } else if (Attributes.Contains(f.Attributes, "no_inline")) {
+ // User manually prevented inlining
} else {
- // inline this body
- var body = GetSubstitutedBody(fexp, f, false);
- var typeSpecializedBody = GetSubstitutedBody(fexp, f, true);
- var typeSpecializedResultType = Resolver.SubstType(f.ResultType, fexp.TypeArgumentSubstitutions);
-
// Produce, for a "body" split into b0, b1, b2:
// checked F#canCall(args) ==> F(args) || b0
// checked F#canCall(args) ==> F(args) || b1
@@ -12514,49 +13149,68 @@ namespace Microsoft.Dafny {
// F(args)
fargs = etran.TrExpr(fexp);
- // recurse on body
- var ss = new List<SplitExprInfo>();
- TrSplitExpr(typeSpecializedBody, ss, position, functionHeight, apply_induction, etran);
- var needsTokenAdjust = TrSplitNeedsTokenAdjustment(typeSpecializedBody);
- foreach (var s in ss) {
- if (s.IsChecked) {
- var unboxedConjunct = CondApplyUnbox(s.E.tok, s.E, typeSpecializedResultType, expr.Type);
- var bodyOrConjunct = Bpl.Expr.Or(fargs, unboxedConjunct);
- var tok = needsTokenAdjust ? (IToken)new ForceCheckToken(typeSpecializedBody.tok) : (IToken)new NestedToken(fexp.tok, s.E.tok);
- var p = Bpl.Expr.Binary(tok, BinaryOperator.Opcode.Imp, canCall, bodyOrConjunct);
- splits.Add(new SplitExprInfo(SplitExprInfo.K.Checked, p));
+ if (!CanSafelyInline(fexp, f)) {
+ // Skip inlining, as it would cause arbitrary expressions to pop up in the trigger
+ // TODO this should appear at the outmost call site, not at the innermost. See SnapshotableTrees.dfy
+ reporter.Info(MessageSource.Translator, fexp.tok, "Some instances of this call cannot safely be inlined.");
+ // F#canCall(args) ==> F(args)
+ var p = Bpl.Expr.Binary(fargs.tok, BinaryOperator.Opcode.Imp, canCall, fargs);
+ splits.Add(new SplitExprInfo(SplitExprInfo.K.Checked, p));
+ // F#canCall(args) && F(args)
+ var fr = Bpl.Expr.And(canCall, fargs);
+ splits.Add(new SplitExprInfo(SplitExprInfo.K.Free, fr));
+
+ } else {
+ // inline this body
+ var typeSpecializedBody = GetSubstitutedBody(fexp, f);
+ var typeSpecializedResultType = Resolver.SubstType(f.ResultType, fexp.TypeArgumentSubstitutions);
+
+ // recurse on body
+ var ss = new List<SplitExprInfo>();
+ TrSplitExpr(typeSpecializedBody, ss, position, functionHeight, inlineProtectedFunctions, apply_induction, etran);
+ var needsTokenAdjust = TrSplitNeedsTokenAdjustment(typeSpecializedBody);
+ foreach (var s in ss) {
+ if (s.IsChecked) {
+ var unboxedConjunct = CondApplyUnbox(s.E.tok, s.E, typeSpecializedResultType, expr.Type);
+ var bodyOrConjunct = Bpl.Expr.Or(fargs, unboxedConjunct);
+ var tok = needsTokenAdjust ? (IToken)new ForceCheckToken(typeSpecializedBody.tok) : (IToken)new NestedToken(fexp.tok, s.E.tok);
+ var p = Bpl.Expr.Binary(tok, BinaryOperator.Opcode.Imp, canCall, bodyOrConjunct);
+ splits.Add(new SplitExprInfo(SplitExprInfo.K.Checked, p));
+ }
}
- }
- // allocatedness for arguments to the inlined call in body
- if (typeSpecializedBody is FunctionCallExpr) {
- FunctionCallExpr e = (FunctionCallExpr)typeSpecializedBody;
- for (int i = 0; i < e.Args.Count; i++) {
- Expression ee = e.Args[i];
- Type t = e.Function.Formals[i].Type;
- Expr tr_ee = etran.TrExpr(ee);
- Bpl.Expr wh = GetWhereClause(e.tok, tr_ee, cce.NonNull(ee.Type), etran);
- if (wh != null) { fargs = Bpl.Expr.And(fargs, wh); }
+ // allocatedness for arguments to the inlined call in body
+ if (typeSpecializedBody is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)typeSpecializedBody;
+ for (int i = 0; i < e.Args.Count; i++) {
+ Expression ee = e.Args[i];
+ Type t = e.Function.Formals[i].Type;
+ Expr tr_ee = etran.TrExpr(ee);
+ Bpl.Expr wh = GetWhereClause(e.tok, tr_ee, cce.NonNull(ee.Type), etran);
+ if (wh != null) { fargs = Bpl.Expr.And(fargs, wh); }
+ }
}
- }
- // body
- var trBody = etran.TrExpr(typeSpecializedBody);
- trBody = CondApplyUnbox(trBody.tok, trBody, typeSpecializedResultType, expr.Type);
- // F#canCall(args) && F(args) && (b0 && b1 && b2)
- var fr = Bpl.Expr.And(canCall, BplAnd(fargs, trBody));
- splits.Add(new SplitExprInfo(SplitExprInfo.K.Free, fr));
+ // body
+ var trBody = etran.TrExpr(typeSpecializedBody);
+ trBody = CondApplyUnbox(trBody.tok, trBody, typeSpecializedResultType, expr.Type);
+ // F#canCall(args) && F(args) && (b0 && b1 && b2)
+ var fr = Bpl.Expr.And(canCall, BplAnd(fargs, trBody));
+ splits.Add(new SplitExprInfo(SplitExprInfo.K.Free, fr));
+ }
return true;
}
}
+ } else if (expr is QuantifierExpr && ((QuantifierExpr)expr).SplitQuantifier != null) {
+ return TrSplitExpr(((QuantifierExpr)expr).SplitQuantifierExpression, splits, position, heightLimit, inlineProtectedFunctions, apply_induction, etran);
} else if (((position && expr is ForallExpr) || (!position && expr is ExistsExpr))
- /* NB: only for type arg less quantifiers for now: */
- && ((QuantifierExpr)expr).TypeArgs.Count == 0) {
+ /* NB: only for type arg less quantifiers for now: */
+ && ((QuantifierExpr)expr).TypeArgs.Count == 0) {
var e = (QuantifierExpr)expr;
- var inductionVariables = ApplyInduction(e);
- if (apply_induction && 2 <= DafnyOptions.O.Induction && inductionVariables.Count != 0) {
+ var inductionVariables = ApplyInduction(e.BoundVars, e.Attributes);
+ if (apply_induction && inductionVariables.Count != 0) {
// From the given quantifier (forall n :: P(n)), generate the seemingly weaker proof obligation
// (forall n :: (forall k :: k < n ==> P(k)) ==> P(n))
// For an existential (exists n :: P(n)), it is
@@ -12595,12 +13249,8 @@ namespace Microsoft.Dafny {
List<Variable> bvars = new List<Variable>();
Bpl.Expr typeAntecedent = etran.TrBoundVariables(kvars, bvars);
Bpl.Expr ih;
- if (Attributes.Contains(e.Attributes, "trigger")) {
- Bpl.Trigger tr = TrTrigger(etran, e.Attributes, expr.tok, substMap);
- ih = new Bpl.ForallExpr(expr.tok, bvars, tr, Bpl.Expr.Imp(typeAntecedent, ihBody));
- } else {
- ih = new Bpl.ForallExpr(expr.tok, bvars, Bpl.Expr.Imp(typeAntecedent, ihBody));
- }
+ var tr = TrTrigger(etran, e.Attributes, expr.tok, substMap);
+ ih = new Bpl.ForallExpr(expr.tok, bvars, tr, Bpl.Expr.Imp(typeAntecedent, ihBody));
// More precisely now:
// (forall n :: n-has-expected-type && (forall k :: k < n ==> P(k)) && case0(n) ==> P(n))
@@ -12629,17 +13279,14 @@ namespace Microsoft.Dafny {
typeAntecedent = etran.TrBoundVariables(e.BoundVars, bvars);
foreach (var kase in caseProduct) {
var ante = BplAnd(BplAnd(typeAntecedent, ih), kase);
- var bdy = etran.LayerOffset(1).TrExpr(e.LogicalBody());
+ var etranBody = etran.LayerOffset(1);
+ var bdy = etranBody.TrExpr(e.LogicalBody());
Bpl.Expr q;
+ var trig = TrTrigger(etranBody, e.Attributes, expr.tok);
if (position) {
- if (Attributes.Contains(e.Attributes, "trigger")) {
- Bpl.Trigger tr = TrTrigger(etran, e.Attributes, expr.tok);
- q = new Bpl.ForallExpr(kase.tok, bvars, tr, Bpl.Expr.Imp(ante, bdy));
- } else {
- q = new Bpl.ForallExpr(kase.tok, bvars, Bpl.Expr.Imp(ante, bdy));
- }
+ q = new Bpl.ForallExpr(kase.tok, bvars, trig, Bpl.Expr.Imp(ante, bdy));
} else {
- q = new Bpl.ExistsExpr(kase.tok, bvars, Bpl.Expr.And(ante, bdy));
+ q = new Bpl.ExistsExpr(kase.tok, bvars, trig, Bpl.Expr.And(ante, bdy));
}
splits.Add(new SplitExprInfo(SplitExprInfo.K.Checked, q));
}
@@ -12676,8 +13323,10 @@ namespace Microsoft.Dafny {
translatedExpression = etran.TrExpr(expr);
splitHappened = false;
} else {
+ // for quantifierExpr, we don't want to increase fuel in assert context
+ var existEtran = etran;
etran = etran.LayerOffset(1);
- translatedExpression = etran.TrExpr(expr);
+ translatedExpression = etran.TrExpr(expr, null, etran, existEtran);
splitHappened = etran.Statistics_CustomLayerFunctionCount != 0; // return true if the LayerOffset(1) came into play
}
if (TrSplitNeedsTokenAdjustment(expr)) {
@@ -12688,14 +13337,62 @@ namespace Microsoft.Dafny {
return splitHappened;
}
- private Expression GetSubstitutedBody(FunctionCallExpr fexp, Function f, bool specializeTypeParameters) {
+ private bool CanSafelyInline(FunctionCallExpr fexp, Function f) {
+ var visitor = new TriggersExplorer();
+ visitor.Visit(f);
+ return f.Formals.Zip(fexp.Args).All(formal_concrete => CanSafelySubstitute(visitor.TriggerVariables, formal_concrete.Item1, formal_concrete.Item2));
+ }
+
+ // Using an empty set of old expressions is ok here; the only uses of the triggersCollector will be to check for trigger killers.
+ Triggers.TriggersCollector triggersCollector = new Triggers.TriggersCollector(new HashSet<Expression>());
+
+ private bool CanSafelySubstitute(ISet<IVariable> protectedVariables, IVariable variable, Expression substitution) {
+ return !(protectedVariables.Contains(variable) && triggersCollector.IsTriggerKiller(substitution));
+ }
+
+ private class VariablesCollector: BottomUpVisitor {
+ internal ISet<IVariable> variables;
+
+ internal VariablesCollector() {
+ this.variables = new HashSet<IVariable>();
+ }
+
+ protected override void VisitOneExpr(Expression expr) {
+ if (expr is IdentifierExpr) {
+ variables.Add((expr as IdentifierExpr).Var);
+ }
+ }
+ }
+
+ private class TriggersExplorer : BottomUpVisitor {
+ VariablesCollector collector;
+
+ internal ISet<IVariable> TriggerVariables { get { return collector.variables; } }
+
+ internal TriggersExplorer() {
+ collector = new VariablesCollector();
+ }
+
+ protected override void VisitOneExpr(Expression expr) {
+ if (expr is QuantifierExpr) {
+ var e = (QuantifierExpr)expr;
+ if (e.SplitQuantifier == null) {
+ foreach (var trigger in (expr as QuantifierExpr).Attributes.AsEnumerable().Where(a => a.Name == "trigger").SelectMany(a => a.Args)) {
+ collector.Visit(trigger);
+ }
+ }
+ }
+ }
+ }
+
+ private Expression GetSubstitutedBody(FunctionCallExpr fexp, Function f) {
Contract.Requires(fexp != null);
Contract.Requires(f != null);
var substMap = new Dictionary<IVariable, Expression>();
Contract.Assert(fexp.Args.Count == f.Formals.Count);
for (int i = 0; i < f.Formals.Count; i++) {
Formal p = f.Formals[i];
- var formalType = specializeTypeParameters ? Resolver.SubstType(p.Type, fexp.TypeArgumentSubstitutions) : p.Type;
+ var formalType = Resolver.SubstType(p.Type, fexp.TypeArgumentSubstitutions);
Expression arg = fexp.Args[i];
arg = new BoxingCastExpr(arg, cce.NonNull(arg.Type), formalType);
arg.Type = formalType; // resolve here
@@ -12706,7 +13403,7 @@ namespace Microsoft.Dafny {
var pp = (PrefixPredicate)f;
body = PrefixSubstitution(pp, body);
}
- body = Substitute(body, fexp.Receiver, substMap, specializeTypeParameters ? fexp.TypeArgumentSubstitutions : null);
+ body = Substitute(body, fexp.Receiver, substMap, fexp.TypeArgumentSubstitutions);
return body;
}
@@ -12715,297 +13412,29 @@ namespace Microsoft.Dafny {
return RefinementToken.IsInherited(expr.tok, currentModule) && (codeContext == null || !codeContext.MustReverify) && RefinementTransformer.ContainsChange(expr, currentModule);
}
- List<BoundVar> ApplyInduction(QuantifierExpr e) {
- Contract.Requires(e.TypeArgs.Count == 0);
- return ApplyInduction(e.BoundVars, e.Attributes, new List<Expression>() { e.LogicalBody() },
- delegate(System.IO.TextWriter wr) { new Printer(wr).PrintExpression(e, true); });
- }
-
- delegate void TracePrinter(System.IO.TextWriter wr);
-
/// <summary>
/// Return a subset of "boundVars" (in the order giving in "boundVars") to which to apply induction to,
- /// according to :induction attributes in "attributes" and heuristically interesting subexpressions of
- /// "searchExprs".
+ /// according to :_induction attribute in "attributes".
/// </summary>
- List<VarType> ApplyInduction<VarType>(List<VarType> boundVars, Attributes attributes, List<Expression> searchExprs, TracePrinter tracePrinter) where VarType : class, IVariable
+ List<VarType> ApplyInduction<VarType>(List<VarType> boundVars, Attributes attributes) where VarType : class, IVariable
{
Contract.Requires(boundVars != null);
- Contract.Requires(searchExprs != null);
- Contract.Requires(tracePrinter != null);
Contract.Ensures(Contract.Result<List<VarType>>() != null);
- if (DafnyOptions.O.Induction == 0) {
- return new List<VarType>(); // don't apply induction
- }
-
- for (var a = attributes; a != null; a = a.Prev) {
- if (a.Name == "induction") {
- // Here are the supported forms of the :induction attribute.
- // :induction -- apply induction to all bound variables
- // :induction false -- suppress induction, that is, don't apply it to any bound variable
- // :induction L where L is a list consisting entirely of bound variables:
- // -- apply induction to the specified bound variables
- // :induction X where X is anything else
- // -- treat the same as {:induction}, that is, apply induction to all
- // bound variables
-
- // Handle {:induction false}
- if (a.Args.Count == 1) {
- var arg = a.Args[0] as LiteralExpr;
- if (arg != null && arg.Value is bool && !(bool)arg.Value) {
- if (CommandLineOptions.Clo.Trace) {
- Console.Write("Suppressing automatic induction for: ");
- tracePrinter(Console.Out);
- Console.WriteLine();
- }
- return new List<VarType>();
- }
- }
-
- // Handle {:induction L}
- if (a.Args.Count != 0) {
- // check that all attribute arguments refer to bound variables; otherwise, go to default_form
- var argsAsVars = new List<VarType>();
- foreach (var arg in a.Args) {
- var theArg = arg.Resolved;
- if (theArg is ThisExpr) {
- foreach (var bv in boundVars) {
- if (bv is ThisSurrogate) {
- argsAsVars.Add(bv);
- goto TRY_NEXT_ATTRIBUTE_ARGUMENT;
- }
- }
- } else if (theArg is IdentifierExpr) {
- var id = (IdentifierExpr)theArg;
- var bv = id.Var as VarType;
- if (bv != null && boundVars.Contains(bv)) {
- argsAsVars.Add(bv);
- goto TRY_NEXT_ATTRIBUTE_ARGUMENT;
- }
- }
- // the attribute argument was not one of the possible induction variables
- goto USE_DEFAULT_FORM;
- TRY_NEXT_ATTRIBUTE_ARGUMENT:
- ;
- }
- // so, all attribute arguments are variables; add them to L in the order of the bound variables (not necessarily the order in the attribute)
- var L = new List<VarType>();
- foreach (var bv in boundVars) {
- if (argsAsVars.Contains(bv)) {
- L.Add(bv);
- }
- }
- if (CommandLineOptions.Clo.Trace) {
- string sep = "Applying requested induction on ";
- foreach (var bv in L) {
- Console.Write("{0}{1}", sep, bv.Name);
- sep = ", ";
- }
- Console.Write(" of: ");
- tracePrinter(Console.Out);
- Console.WriteLine();
- }
- return L;
- USE_DEFAULT_FORM: ;
- }
-
- // We have the {:induction} case, or something to be treated in the same way
- if (CommandLineOptions.Clo.Trace) {
- Console.Write("Applying requested induction on all bound variables of: ");
- tracePrinter(Console.Out);
- Console.WriteLine();
- }
- return boundVars;
- }
- }
-
- if (DafnyOptions.O.Induction < 2) {
+ var args = Attributes.FindExpressions(attributes, "_induction");
+ if (args == null) {
return new List<VarType>(); // don't apply induction
}
- // consider automatically applying induction
- var inductionVariables = new List<VarType>();
- foreach (var n in boundVars) {
- if (!n.Type.IsTypeParameter && searchExprs.Exists(expr => VarOccursInArgumentToRecursiveFunction(expr, n))) {
- if (CommandLineOptions.Clo.Trace) {
- Console.Write("Applying automatic induction on variable '{0}' of: ", n.Name);
- tracePrinter(Console.Out);
- Console.WriteLine();
- }
- inductionVariables.Add(n);
- }
- }
-
- return inductionVariables;
- }
-
- /// <summary>
- /// Returns 'true' iff by looking at 'expr' the Induction Heuristic determines that induction should be applied to 'n'.
- /// More precisely:
- /// DafnyInductionHeuristic Return 'true'
- /// ----------------------- -------------
- /// 0 always
- /// 1 if 'n' occurs as any subexpression (of 'expr')
- /// 2 if 'n' occurs as any subexpression of any index argument of an array/sequence select expression or any argument to a recursive function
- /// 3 if 'n' occurs as a prominent subexpression of any index argument of an array/sequence select expression or any argument to a recursive function
- /// 4 if 'n' occurs as any subexpression of any argument to a recursive function
- /// 5 if 'n' occurs as a prominent subexpression of any argument to a recursive function
- /// 6 if 'n' occurs as a prominent subexpression of any decreases-influencing argument to a recursive function
- /// Parameter 'n' is allowed to be a ThisSurrogate.
- /// </summary>
- bool VarOccursInArgumentToRecursiveFunction(Expression expr, IVariable n) {
- switch (DafnyOptions.O.InductionHeuristic) {
- case 0: return true;
- case 1: return ContainsFreeVariable(expr, false, n);
- default: return VarOccursInArgumentToRecursiveFunction(expr, n, false);
- }
- }
-
- /// <summary>
- /// Worker routine for VarOccursInArgumentToRecursiveFunction(expr,n), where the additional parameter 'exprIsProminent' says whether or
- /// not 'expr' has prominent status in its context.
- /// DafnyInductionHeuristic cases 0 and 1 are assumed to be handled elsewhere (i.e., a precondition of this method is DafnyInductionHeuristic is at least 2).
- /// Parameter 'n' is allowed to be a ThisSurrogate.
- /// </summary>
- bool VarOccursInArgumentToRecursiveFunction(Expression expr, IVariable n, bool exprIsProminent) {
- Contract.Requires(expr != null);
- Contract.Requires(n != null);
-
- // The following variable is what gets passed down to recursive calls if the subexpression does not itself acquire prominent status.
- var subExprIsProminent = DafnyOptions.O.InductionHeuristic == 2 || DafnyOptions.O.InductionHeuristic == 4 ? /*once prominent, always prominent*/exprIsProminent : /*reset the prominent status*/false;
-
- if (expr is ThisExpr) {
- return exprIsProminent && n is ThisSurrogate;
- } else if (expr is IdentifierExpr) {
- var e = (IdentifierExpr)expr;
- return exprIsProminent && e.Var == n;
- } else if (expr is SeqSelectExpr) {
- var e = (SeqSelectExpr)expr;
- var q = DafnyOptions.O.InductionHeuristic < 4 || subExprIsProminent;
- return VarOccursInArgumentToRecursiveFunction(e.Seq, n, subExprIsProminent) || // this subexpression does not acquire "prominent" status
- (e.E0 != null && VarOccursInArgumentToRecursiveFunction(e.E0, n, q)) || // this one does (unless arrays/sequences are excluded)
- (e.E1 != null && VarOccursInArgumentToRecursiveFunction(e.E1, n, q)); // ditto
- } else if (expr is MultiSelectExpr) {
- var e = (MultiSelectExpr)expr;
- var q = DafnyOptions.O.InductionHeuristic < 4 || subExprIsProminent;
- return VarOccursInArgumentToRecursiveFunction(e.Array, n, subExprIsProminent) ||
- e.Indices.Exists(exp => VarOccursInArgumentToRecursiveFunction(exp, n, q));
- } else if (expr is FunctionCallExpr) {
- var e = (FunctionCallExpr)expr;
- // For recursive functions: arguments are "prominent"
- // For non-recursive function: arguments are "prominent" if the call is
- var rec = e.Function.IsRecursive && e.CoCall != FunctionCallExpr.CoCallResolution.Yes;
- var decr = e.Function.Decreases.Expressions;
- bool variantArgument;
- if (DafnyOptions.O.InductionHeuristic < 6) {
- variantArgument = rec;
- } else {
- // The receiver is considered to be "variant" if the function is recursive and the receiver participates
- // in the effective decreases clause of the function. The receiver participates if it's a free variable
- // of a term in the explicit decreases clause.
- variantArgument = rec && decr.Exists(ee => ContainsFreeVariable(ee, true, null));
- }
- if (VarOccursInArgumentToRecursiveFunction(e.Receiver, n, variantArgument || subExprIsProminent)) {
- return true;
- }
- Contract.Assert(e.Function.Formals.Count == e.Args.Count);
- for (int i = 0; i < e.Function.Formals.Count; i++) {
- var f = e.Function.Formals[i];
- var exp = e.Args[i];
- if (DafnyOptions.O.InductionHeuristic < 6) {
- variantArgument = rec;
- } else if (rec) {
- // The argument position is considered to be "variant" if the function is recursive and...
- // ... it has something to do with why the callee is well-founded, which happens when...
- if (f is ImplicitFormal) {
- // ... it is the argument is the implicit _k parameter, which is always first in the effective decreases clause of a prefix lemma, or
- variantArgument = true;
- } else if (decr.Exists(ee => ContainsFreeVariable(ee, false, f))) {
- // ... it participates in the effective decreases clause of the function, which happens when it is
- // a free variable of a term in the explicit decreases clause, or
- variantArgument = true;
- } else {
- // ... the callee is a prefix predicate.
- variantArgument = true;
- }
- }
- if (VarOccursInArgumentToRecursiveFunction(exp, n, variantArgument || subExprIsProminent)) {
- return true;
- }
- }
- return false;
- } else if (expr is TernaryExpr) {
- var e = (TernaryExpr)expr;
- switch (e.Op) {
- case TernaryExpr.Opcode.PrefixEqOp:
- case TernaryExpr.Opcode.PrefixNeqOp:
- return VarOccursInArgumentToRecursiveFunction(e.E0, n, true) ||
- VarOccursInArgumentToRecursiveFunction(e.E1, n, subExprIsProminent) ||
- VarOccursInArgumentToRecursiveFunction(e.E2, n, subExprIsProminent);
- default:
- Contract.Assert(false); throw new cce.UnreachableException(); // unexpected ternary expression
- }
- } else if (expr is DatatypeValue) {
- var e = (DatatypeValue)expr;
- var q = n.Type.IsDatatype ? exprIsProminent : subExprIsProminent; // prominent status continues, if we're looking for a variable whose type is a datatype
- return e.Arguments.Exists(exp => VarOccursInArgumentToRecursiveFunction(exp, n, q));
- } else if (expr is UnaryExpr) {
- var e = (UnaryExpr)expr;
- // both Not and SeqLength preserve prominence
- return VarOccursInArgumentToRecursiveFunction(e.E, n, exprIsProminent);
- } else if (expr is BinaryExpr) {
- var e = (BinaryExpr)expr;
- bool q;
- switch (e.ResolvedOp) {
- case BinaryExpr.ResolvedOpcode.Add:
- case BinaryExpr.ResolvedOpcode.Sub:
- case BinaryExpr.ResolvedOpcode.Mul:
- case BinaryExpr.ResolvedOpcode.Div:
- case BinaryExpr.ResolvedOpcode.Mod:
- case BinaryExpr.ResolvedOpcode.Union:
- case BinaryExpr.ResolvedOpcode.Intersection:
- case BinaryExpr.ResolvedOpcode.SetDifference:
- case BinaryExpr.ResolvedOpcode.Concat:
- // these operators preserve prominence
- q = exprIsProminent;
- break;
- default:
- // whereas all other binary operators do not
- q = subExprIsProminent;
- break;
- }
- return VarOccursInArgumentToRecursiveFunction(e.E0, n, q) ||
- VarOccursInArgumentToRecursiveFunction(e.E1, n, q);
- } else if (expr is StmtExpr) {
- var e = (StmtExpr)expr;
- // ignore the statement
- return VarOccursInArgumentToRecursiveFunction(e.E, n);
-
- } else if (expr is ITEExpr) {
- var e = (ITEExpr)expr;
- return VarOccursInArgumentToRecursiveFunction(e.Test, n, subExprIsProminent) || // test is not "prominent"
- VarOccursInArgumentToRecursiveFunction(e.Thn, n, exprIsProminent) || // but the two branches are
- VarOccursInArgumentToRecursiveFunction(e.Els, n, exprIsProminent);
- } else if (expr is OldExpr ||
- expr is ConcreteSyntaxExpression ||
- expr is BoxingCastExpr ||
- expr is UnboxingCastExpr) {
- foreach (var exp in expr.SubExpressions) {
- if (VarOccursInArgumentToRecursiveFunction(exp, n, exprIsProminent)) { // maintain prominence
- return true;
- }
- }
- return false;
- } else {
- // in all other cases, reset the prominence status and recurse on the subexpressions
- foreach (var exp in expr.SubExpressions) {
- if (VarOccursInArgumentToRecursiveFunction(exp, n, subExprIsProminent)) {
- return true;
- }
- }
- return false;
+ var argsAsVars = new List<VarType>();
+ foreach (var arg in args) {
+ // We expect each "arg" to be an IdentifierExpr among "boundVars"
+ var id = (IdentifierExpr)arg;
+ var bv = (VarType)id.Var;
+ Contract.Assume(boundVars.Contains(bv));
+ argsAsVars.Add(bv);
}
+ return argsAsVars;
}
IEnumerable<Bpl.Expr> InductionCases(Type ty, Bpl.Expr expr, ExpressionTranslator etran) {
@@ -13039,7 +13468,8 @@ namespace Microsoft.Dafny {
}
i++;
}
- q = new Bpl.ExistsExpr(ctor.tok, bvs, BplAnd(typeAntecedent, q));
+ var trigger = BplTrigger(ct); // this is probably never used, because this quantifier is not expected ever to appear in a context where it needs to be instantiated
+ q = new Bpl.ExistsExpr(ctor.tok, bvs, trigger, BplAnd(typeAntecedent, q));
}
yield return q;
}
@@ -13051,12 +13481,12 @@ namespace Microsoft.Dafny {
/// Parameter 'v' is allowed to be a ThisSurrogate, in which case the method return true iff 'this'
/// occurs in 'expr'.
/// </summary>
- static bool ContainsFreeVariable(Expression expr, bool lookForReceiver, IVariable v) {
+ public static bool ContainsFreeVariable(Expression expr, bool lookForReceiver, IVariable v) {
Contract.Requires(expr != null);
Contract.Requires(lookForReceiver || v != null);
if (expr is ThisExpr) {
- return lookForReceiver || v is ThisSurrogate;
+ return lookForReceiver;
} else if (expr is IdentifierExpr) {
IdentifierExpr e = (IdentifierExpr)expr;
return e.Var == v;
@@ -13083,7 +13513,20 @@ namespace Microsoft.Dafny {
ty.NormalizeExpand().TypeArgs.Iter(tt => ComputeFreeTypeVariables(tt, fvs));
}
- public static void ComputeFreeVariables(Expression expr, ISet<IVariable> fvs, ref bool usesHeap, ref bool usesOldHeap, ref Type usesThis, bool inOldContext) {
+ public static ISet<IVariable> ComputeFreeVariables(Expression expr) {
+ Contract.Requires(expr != null);
+ ISet<IVariable> fvs = new HashSet<IVariable>();
+ ComputeFreeVariables(expr, fvs);
+ return fvs;
+ }
+ public static void ComputeFreeVariables(Expression expr, ISet<IVariable> fvs) {
+ Contract.Requires(expr != null);
+ Contract.Requires(fvs != null);
+ bool dontCare0 = false, dontCare1 = false;
+ Type dontCareT = null;
+ ComputeFreeVariables(expr, fvs, ref dontCare0, ref dontCare1, ref dontCareT);
+ }
+ public static void ComputeFreeVariables(Expression expr, ISet<IVariable> fvs, ref bool usesHeap, ref bool usesOldHeap, ref Type usesThis) {
Contract.Requires(expr != null);
if (expr is ThisExpr) {
@@ -13094,13 +13537,6 @@ namespace Microsoft.Dafny {
var e = (IdentifierExpr)expr;
fvs.Add(e.Var);
return;
- } else if (expr is OldExpr) {
- var e = (OldExpr)expr;
- bool subUsesHeap = false;
- ComputeFreeVariables(e.E, fvs, ref subUsesHeap, ref usesOldHeap, ref usesThis, true);
- if (subUsesHeap) {
- usesOldHeap = true;
- }
} else if (expr is MemberSelectExpr) {
usesHeap = true;
} else if (expr is SeqSelectExpr) {
@@ -13124,11 +13560,11 @@ namespace Microsoft.Dafny {
// visit subexpressions
bool uHeap = false, uOldHeap = false;
Type uThis = null;
- expr.SubExpressions.Iter(ee => ComputeFreeVariables(ee, fvs, ref uHeap, ref uOldHeap, ref uThis, inOldContext));
+ expr.SubExpressions.Iter(ee => ComputeFreeVariables(ee, fvs, ref uHeap, ref uOldHeap, ref uThis));
Contract.Assert(usesThis == null || uThis == null || usesThis.Equals(uThis));
usesThis = usesThis ?? uThis;
usesHeap |= uHeap;
- usesOldHeap |= uOldHeap;
+ usesOldHeap |= expr is OldExpr ? uHeap | uOldHeap : uOldHeap;
if (expr is LetExpr) {
var e = (LetExpr)expr;
@@ -13256,7 +13692,7 @@ namespace Microsoft.Dafny {
List<Expression> newElements = SubstituteExprList(e.Elements);
if (newElements != e.Elements) {
if (expr is SetDisplayExpr) {
- newExpr = new SetDisplayExpr(expr.tok, newElements);
+ newExpr = new SetDisplayExpr(expr.tok, ((SetDisplayExpr)expr).Finite, newElements);
} else if (expr is MultiSetDisplayExpr) {
newExpr = new MultiSetDisplayExpr(expr.tok, newElements);
} else {
@@ -13421,6 +13857,7 @@ namespace Microsoft.Dafny {
if (newCasePatterns != e.LHSs) {
anythingChanged = true;
}
+
var body = Substitute(e.Body);
// undo any changes to substMap (could be optimized to do this only if newBoundVars != e.Vars)
foreach (var bv in e.BoundVars) {
@@ -13482,13 +13919,19 @@ namespace Microsoft.Dafny {
var e = (ComprehensionExpr)expr;
// For quantifiers and setComprehesion we want to make sure that we don't introduce name clashes with
// the enclosing scopes.
+
+ var q = e as QuantifierExpr;
+ if (q != null && q.SplitQuantifier != null) {
+ return Substitute(q.SplitQuantifierExpression);
+ }
+
var newBoundVars = CreateBoundVarSubstitutions(e.BoundVars, expr is ForallExpr || expr is ExistsExpr || expr is SetComprehension);
- Expression newRange = e.Range == null ? null : Substitute(e.Range);
- Expression newTerm = Substitute(e.Term);
- Attributes newAttrs = SubstAttributes(e.Attributes);
+ var newRange = e.Range == null ? null : Substitute(e.Range);
+ var newTerm = Substitute(e.Term);
+ var newAttrs = SubstAttributes(e.Attributes);
if (newBoundVars != e.BoundVars || newRange != e.Range || newTerm != e.Term || newAttrs != e.Attributes) {
if (e is SetComprehension) {
- newExpr = new SetComprehension(expr.tok, newBoundVars, newRange, newTerm, newAttrs);
+ newExpr = new SetComprehension(expr.tok, ((SetComprehension)e).Finite, newBoundVars, newRange, newTerm, newAttrs);
} else if (e is MapComprehension) {
newExpr = new MapComprehension(expr.tok, ((MapComprehension)e).Finite, newBoundVars, newRange, newTerm, newAttrs);
} else if (expr is ForallExpr) {
@@ -13501,6 +13944,9 @@ namespace Microsoft.Dafny {
} else {
Contract.Assert(false); // unexpected ComprehensionExpr
}
+ if (e.Bounds != null) {
+ ((ComprehensionExpr)newExpr).Bounds = e.Bounds.ConvertAll(bound => SubstituteBoundedPool(bound));
+ }
}
// undo any changes to substMap (could be optimized to do this only if newBoundVars != e.BoundVars)
foreach (var bv in e.BoundVars) {
@@ -13558,6 +14004,44 @@ namespace Microsoft.Dafny {
}
}
+ public ComprehensionExpr.BoundedPool SubstituteBoundedPool(ComprehensionExpr.BoundedPool bound) {
+ if (bound == null) {
+ return null;
+ } else if (bound is ComprehensionExpr.ExactBoundedPool) {
+ var b = (ComprehensionExpr.ExactBoundedPool)bound;
+ return new ComprehensionExpr.ExactBoundedPool(Substitute(b.E));
+ } else if (bound is ComprehensionExpr.BoolBoundedPool) {
+ return bound; // nothing to substitute
+ } else if (bound is ComprehensionExpr.CharBoundedPool) {
+ return bound; // nothing to substitute
+ } else if (bound is ComprehensionExpr.RefBoundedPool) {
+ return bound; // nothing to substitute
+ } else if (bound is ComprehensionExpr.IntBoundedPool) {
+ var b = (ComprehensionExpr.IntBoundedPool)bound;
+ return new ComprehensionExpr.IntBoundedPool(b.LowerBound == null ? null : Substitute(b.LowerBound), b.UpperBound == null ? null : Substitute(b.UpperBound));
+ } else if (bound is ComprehensionExpr.SetBoundedPool) {
+ var b = (ComprehensionExpr.SetBoundedPool)bound;
+ return new ComprehensionExpr.SetBoundedPool(Substitute(b.Set));
+ } else if (bound is ComprehensionExpr.SubSetBoundedPool) {
+ var b = (ComprehensionExpr.SubSetBoundedPool)bound;
+ return new ComprehensionExpr.SubSetBoundedPool(Substitute(b.UpperBound));
+ } else if (bound is ComprehensionExpr.SuperSetBoundedPool) {
+ var b = (ComprehensionExpr.SuperSetBoundedPool)bound;
+ return new ComprehensionExpr.SuperSetBoundedPool(Substitute(b.LowerBound));
+ } else if (bound is ComprehensionExpr.MapBoundedPool) {
+ var b = (ComprehensionExpr.MapBoundedPool)bound;
+ return new ComprehensionExpr.MapBoundedPool(Substitute(b.Map));
+ } else if (bound is ComprehensionExpr.SeqBoundedPool) {
+ var b = (ComprehensionExpr.SeqBoundedPool)bound;
+ return new ComprehensionExpr.SeqBoundedPool(Substitute(b.Seq));
+ } else if (bound is ComprehensionExpr.DatatypeBoundedPool) {
+ return bound; // nothing to substitute
+ } else {
+ Contract.Assume(false); // unexpected ComprehensionExpr.BoundedPool
+ throw new cce.UnreachableException(); // to please compiler
+ }
+ }
+
/// <summary>
/// Return a list of bound variables, of the same length as 'vars' but with possible substitutions.
/// For any change necessary, update 'substMap' to reflect the new substitution; the caller is responsible for
@@ -13733,7 +14217,7 @@ namespace Microsoft.Dafny {
r = SubstBlockStmt((BlockStmt)stmt);
} else if (stmt is IfStmt) {
var s = (IfStmt)stmt;
- r = new IfStmt(s.Tok, s.EndTok, Substitute(s.Guard), SubstBlockStmt(s.Thn), SubstStmt(s.Els));
+ r = new IfStmt(s.Tok, s.EndTok, s.IsExistentialGuard, Substitute(s.Guard), SubstBlockStmt(s.Thn), SubstStmt(s.Els));
} else if (stmt is AlternativeStmt) {
var s = (AlternativeStmt)stmt;
r = new AlternativeStmt(s.Tok, s.EndTok, s.Alternatives.ConvertAll(SubstGuardedAlternative));
@@ -13757,7 +14241,7 @@ namespace Microsoft.Dafny {
r = rr;
} else if (stmt is CalcStmt) {
var s = (CalcStmt)stmt;
- var rr = new CalcStmt(s.Tok, s.EndTok, SubstCalcOp(s.Op), s.Lines.ConvertAll(Substitute), s.Hints.ConvertAll(SubstBlockStmt), s.StepOps.ConvertAll(SubstCalcOp), SubstCalcOp(s.ResultOp));
+ var rr = new CalcStmt(s.Tok, s.EndTok, SubstCalcOp(s.Op), s.Lines.ConvertAll(Substitute), s.Hints.ConvertAll(SubstBlockStmt), s.StepOps.ConvertAll(SubstCalcOp), SubstCalcOp(s.ResultOp), SubstAttributes(s.Attributes));
rr.Steps.AddRange(s.Steps.ConvertAll(Substitute));
rr.Result = Substitute(s.Result);
r = rr;
@@ -13822,7 +14306,7 @@ namespace Microsoft.Dafny {
protected GuardedAlternative SubstGuardedAlternative(GuardedAlternative alt) {
Contract.Requires(alt != null);
- return new GuardedAlternative(alt.Tok, Substitute(alt.Guard), alt.Body.ConvertAll(SubstStmt));
+ return new GuardedAlternative(alt.Tok, alt.IsExistentialGuard, Substitute(alt.Guard), alt.Body.ConvertAll(SubstStmt));
}
protected MaybeFreeExpression SubstMayBeFreeExpr(MaybeFreeExpression expr) {
@@ -13950,22 +14434,91 @@ namespace Microsoft.Dafny {
Bpl.Expr.Eq(oldHeap, newHeap),
FunctionCall(newHeap.tok, BuiltinFunction.HeapSucc, null, oldHeap, newHeap));
}
+ Bpl.Expr HeapSucc(Bpl.Expr oldHeap, Bpl.Expr newHeap, bool useGhostHeapSucc = false) {
+ return FunctionCall(newHeap.tok, useGhostHeapSucc ? BuiltinFunction.HeapSuccGhost : BuiltinFunction.HeapSucc, null, oldHeap, newHeap);
+ }
// Bpl-making-utilities
+ /// <summary>
+ /// Create a Boogie quantifier with body "A ==> body" and triggers "trg", but use only the subset of bound
+ /// variables from "varsAndAntecedents" that actually occur free in "body" or "trg", and "A" is the conjunction of
+ /// antecedents for those corresponding bound variables. If none of the bound variables is used, "body"
+ /// is returned.
+ /// The order of the contents of "varsAndAntecedents" matters: For any index "i" into "varsAndAntecedents", the
+ /// antecedent varsAndAntecedents[i].Item2 may depend on a variable varsAndAntecedents[j].Item1 if "j GREATER-OR-EQUAL i"
+ /// but not if "j LESS i".
+ /// Caution: if "trg" is null, makes a forall without any triggers.
+ /// </summary>
+ static Bpl.Expr BplForallTrim(IEnumerable<Tuple<Bpl.Variable, Bpl.Expr/*?*/>> varsAndAntecedents, Bpl.Trigger trg, Bpl.Expr body) {
+ Contract.Requires(varsAndAntecedents != null);
+ Contract.Requires(body != null);
+
+ // We'd like to compute the free variables if "body" and "trg". It would be nice to use the Boogie
+ // routine Bpl.Expr.ComputeFreeVariables for this purpose. However, calling it requires the Boogie
+ // expression to be resolved. Instead, we do the cheesy thing of computing the set of names of
+ // free variables in "body" and "trg".
+ var vis = new VariableNameVisitor();
+ vis.Visit(body);
+ for (var tt = trg; tt != null; tt = tt.Next) {
+ tt.Tr.Iter(ee => vis.Visit(ee));
+ }
+
+ var args = new List<Bpl.Variable>();
+ Bpl.Expr typeAntecedent = Bpl.Expr.True;
+ foreach (var pair in varsAndAntecedents) {
+ var bv = pair.Item1;
+ var wh = pair.Item2;
+ if (vis.Names.Contains(bv.Name)) {
+ args.Add(bv);
+ if (wh != null) {
+ typeAntecedent = BplAnd(typeAntecedent, wh);
+ vis.Visit(wh); // this adds to "vis.Names" the free variables of "wh"
+ }
+ }
+ }
+ if (args.Count == 0) {
+ return body;
+ } else {
+ return new Bpl.ForallExpr(body.tok, args, trg, BplImp(typeAntecedent, body));
+ }
+ }
+ class VariableNameVisitor : Boogie.StandardVisitor
+ {
+ public readonly HashSet<string> Names = new HashSet<string>();
+ public override Expr VisitIdentifierExpr(Bpl.IdentifierExpr node) {
+ Names.Add(node.Name);
+ return base.VisitIdentifierExpr(node);
+ }
+ public override BinderExpr VisitBinderExpr(BinderExpr node) {
+ var vis = new VariableNameVisitor();
+ vis.Visit(node.Body);
+ var dummyNames = new HashSet<string>(node.Dummies.Select(v => v.Name));
+ foreach (var nm in vis.Names) {
+ if (!dummyNames.Contains(nm)) {
+ Names.Add(nm);
+ }
+ }
+ return base.VisitBinderExpr(node);
+ }
+ }
+
static Bpl.Expr BplForall(IEnumerable<Bpl.Variable> args_in, Bpl.Expr body) {
+ Contract.Requires(args_in != null);
+ Contract.Requires(body != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
var args = new List<Bpl.Variable>(args_in);
if (args.Count == 0) {
return body;
} else {
- return new Bpl.ForallExpr(body.tok, args, body);
+ return new Bpl.ForallExpr(body.tok, args, body); // NO_TRIGGER
}
}
// Note: if the trigger is null, makes a forall without any triggers
static Bpl.Expr BplForall(IEnumerable<Bpl.Variable> args_in, Bpl.Trigger trg, Bpl.Expr body) {
if (trg == null) {
- return BplForall(args_in, body);
+ return BplForall(args_in, body); // NO_TRIGGER
} else {
var args = new List<Bpl.Variable>(args_in);
if (args.Count == 0) {
@@ -14045,7 +14598,7 @@ namespace Microsoft.Dafny {
Contract.Requires(b != null);
Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
- if (a == Bpl.Expr.True || b == Bpl.Expr.True || b == Bpl.Expr.False) {
+ if (a == Bpl.Expr.True || b == Bpl.Expr.True) {
return b;
} else if (a == Bpl.Expr.False) {
return Bpl.Expr.True;
@@ -14077,7 +14630,7 @@ namespace Microsoft.Dafny {
/// Makes a simple trigger
static Bpl.Trigger BplTrigger(Bpl.Expr e) {
- return new Trigger(e.tok, true, new List<Bpl.Expr> { e });
+ return new Bpl.Trigger(e.tok, true, new List<Bpl.Expr> { e });
}
static Bpl.Axiom BplAxiom(Bpl.Expr e) {
diff --git a/Source/Dafny/Triggers/QuantifierSplitter.cs b/Source/Dafny/Triggers/QuantifierSplitter.cs
new file mode 100644
index 00000000..b039a402
--- /dev/null
+++ b/Source/Dafny/Triggers/QuantifierSplitter.cs
@@ -0,0 +1,138 @@
+using Microsoft.Boogie;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Dafny.Triggers {
+ class QuantifierSplitter : BottomUpVisitor {
+ /// This cache was introduced because some statements (notably calc) return the same SubExpression multiple times.
+ /// This ended up causing an inconsistent situation when the calc statement's subexpressions contained the same quantifier
+ /// twice: on the first pass that quantifier got its SplitQuantifiers generated, and on the the second pass these
+ /// split quantifiers got re-split, creating a situation where the direct children of a split quantifier were
+ /// also split quantifiers.
+ private Dictionary<QuantifierExpr, List<Expression>> splits = new Dictionary<QuantifierExpr, List<Expression>>();
+
+ private static BinaryExpr.Opcode FlipOpcode(BinaryExpr.Opcode opCode) {
+ if (opCode == BinaryExpr.Opcode.And) {
+ return BinaryExpr.Opcode.Or;
+ } else if (opCode == BinaryExpr.Opcode.Or) {
+ return BinaryExpr.Opcode.And;
+ } else {
+ throw new ArgumentException();
+ }
+ }
+
+ // NOTE: If we wanted to split quantifiers as far as possible, it would be
+ // enough to put the formulas in DNF (for foralls) or CNF (for exists).
+ // Unfortunately, this would cause ill-behaved quantifiers to produce
+ // exponentially many smaller quantifiers. Thus we only do one step of
+ // distributing, which takes care of the usual precondition case:
+ // forall x :: P(x) ==> (Q(x) && R(x))
+
+ private static UnaryOpExpr Not(Expression expr) {
+ return new UnaryOpExpr(expr.tok, UnaryOpExpr.Opcode.Not, expr) { Type = expr.Type };
+ }
+
+ private static Attributes CopyAttributes(Attributes source) {
+ if (source == null) {
+ return null;
+ } else {
+ return new Attributes(source.Name, source.Args, CopyAttributes(source.Prev));
+ }
+ }
+
+ internal static IEnumerable<Expression> SplitExpr(Expression expr, BinaryExpr.Opcode separator) {
+ expr = expr.Resolved;
+ var unary = expr as UnaryOpExpr;
+ var binary = expr as BinaryExpr;
+
+ if (unary != null && unary.Op == UnaryOpExpr.Opcode.Not) {
+ foreach (var e in SplitExpr(unary.E, FlipOpcode(separator))) { yield return Not(e); }
+ } else if (binary != null && binary.Op == separator) {
+ foreach (var e in SplitExpr(binary.E0, separator)) { yield return e; }
+ foreach (var e in SplitExpr(binary.E1, separator)) { yield return e; }
+ } else if (binary != null && binary.Op == BinaryExpr.Opcode.Imp && separator == BinaryExpr.Opcode.Or) {
+ foreach (var e in SplitExpr(Not(binary.E0), separator)) { yield return e; }
+ foreach (var e in SplitExpr(binary.E1, separator)) { yield return e; }
+ } else {
+ yield return expr;
+ }
+ }
+
+ internal static IEnumerable<Expression> SplitAndStich(BinaryExpr pair, BinaryExpr.Opcode separator) {
+ foreach (var e1 in SplitExpr(pair.E1, separator)) {
+ // Notice the token. This makes triggers/splitting-picks-the-right-tokens.dfy possible
+ yield return new BinaryExpr(e1.tok, pair.Op, pair.E0, e1) { ResolvedOp = pair.ResolvedOp, Type = pair.Type };
+ }
+ }
+
+ internal static IEnumerable<Expression> SplitQuantifier(QuantifierExpr quantifier) {
+ var body = quantifier.Term;
+ var binary = body as BinaryExpr;
+
+ if (quantifier is ForallExpr) {
+ IEnumerable<Expression> stream;
+ if (binary != null && (binary.Op == BinaryExpr.Opcode.Imp || binary.Op == BinaryExpr.Opcode.Or)) {
+ stream = SplitAndStich(binary, BinaryExpr.Opcode.And);
+ } else {
+ stream = SplitExpr(body, BinaryExpr.Opcode.And);
+ }
+ foreach (var e in stream) {
+ var tok = new NestedToken(quantifier.tok, e.tok);
+ yield return new ForallExpr(tok, quantifier.TypeArgs, quantifier.BoundVars, quantifier.Range, e, CopyAttributes(quantifier.Attributes)) { Type = quantifier.Type };
+ }
+ } else if (quantifier is ExistsExpr) {
+ IEnumerable<Expression> stream;
+ if (binary != null && binary.Op == BinaryExpr.Opcode.And) {
+ stream = SplitAndStich(binary, BinaryExpr.Opcode.Or);
+ } else {
+ stream = SplitExpr(body, BinaryExpr.Opcode.Or);
+ }
+ foreach (var e in stream) {
+ var tok = new NestedToken(quantifier.tok, e.tok);
+ yield return new ExistsExpr(tok, quantifier.TypeArgs, quantifier.BoundVars, quantifier.Range, e, CopyAttributes(quantifier.Attributes)) { Type = quantifier.Type };
+ }
+ } else {
+ yield return quantifier;
+ }
+ }
+
+ private static bool AllowsSplitting(QuantifierExpr quantifier) {
+ // allow split if attributes doesn't contains "split" or it is "split: true" and it is not an empty QuantifierExpr (boundvar.count>0)
+ bool splitAttr = true;
+ return (!Attributes.ContainsBool(quantifier.Attributes, "split", ref splitAttr) || splitAttr) && (quantifier.BoundVars.Count > 0);
+ }
+
+ protected override void VisitOneExpr(Expression expr) {
+ var quantifier = expr as QuantifierExpr;
+ if (quantifier != null) {
+ Contract.Assert(quantifier.SplitQuantifier == null);
+ if (!splits.ContainsKey(quantifier) && AllowsSplitting(quantifier)) {
+ splits[quantifier] = SplitQuantifier(quantifier).ToList();
+ }
+ }
+ }
+
+ protected override void VisitOneStmt(Statement stmt) {
+ if (stmt is ForallStmt) {
+ ForallStmt s = (ForallStmt)stmt;
+ if (s.ForallExpressions != null) {
+ foreach (Expression expr in s.ForallExpressions) {
+ VisitOneExpr(expr);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// See comments above definition of splits for reason why this method exists
+ /// </summary>
+ internal void Commit() {
+ foreach (var quantifier in splits.Keys) {
+ quantifier.SplitQuantifier = splits[quantifier];
+ }
+ }
+ }
+}
diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs
new file mode 100644
index 00000000..f72dab7f
--- /dev/null
+++ b/Source/Dafny/Triggers/QuantifiersCollection.cs
@@ -0,0 +1,211 @@
+#define QUANTIFIER_WARNINGS
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Boogie;
+using System.Diagnostics.Contracts;
+
+namespace Microsoft.Dafny.Triggers {
+ class QuantifierWithTriggers {
+ internal QuantifierExpr quantifier;
+ internal List<TriggerTerm> CandidateTerms;
+ internal List<TriggerCandidate> Candidates;
+ internal List<TriggerCandidate> RejectedCandidates;
+
+ internal bool AllowsLoops { get { return TriggerUtils.AllowsMatchingLoops(quantifier); } }
+ internal bool CouldSuppressLoops { get; set; }
+
+ internal QuantifierWithTriggers(QuantifierExpr quantifier) {
+ this.quantifier = quantifier;
+ this.RejectedCandidates = new List<TriggerCandidate>();
+ }
+
+ internal void TrimInvalidTriggers() {
+ Contract.Requires(CandidateTerms != null);
+ Contract.Requires(Candidates != null);
+ Candidates = TriggerUtils.Filter(Candidates, tr => tr, (tr, _) => tr.MentionsAll(quantifier.BoundVars), (tr, _) => { }).ToList();
+ }
+ }
+
+ class QuantifiersCollection {
+ readonly ErrorReporter reporter;
+ readonly List<QuantifierWithTriggers> quantifiers;
+
+ internal QuantifiersCollection(IEnumerable<QuantifierExpr> quantifiers, ErrorReporter reporter) {
+ Contract.Requires(quantifiers.All(q => q.SplitQuantifier == null));
+ this.reporter = reporter;
+ this.quantifiers = quantifiers.Select(q => new QuantifierWithTriggers(q)).ToList();
+ }
+
+ internal void ComputeTriggers(TriggersCollector triggersCollector) {
+ CollectAndShareTriggers(triggersCollector);
+ TrimInvalidTriggers();
+ BuildDependenciesGraph();
+ SuppressMatchingLoops();
+ SelectTriggers();
+ }
+
+ private bool SubsetGenerationPredicate(TriggerUtils.SetOfTerms terms, TriggerTerm additionalTerm) {
+ return true; // FIXME Remove this
+ //return additionalTerm.Variables.Where(v => v is BoundVar && !terms.Any(t => t.Variables.Contains(v))).Any();
+ }
+
+ /// <summary>
+ /// Collect triggers from the body of each quantifier, and share them
+ /// between all quantifiers. This method assumes that all quantifiers
+ /// actually come from the same context, and were the result of a split that
+ /// gave them all the same variables.
+ /// </summary>
+ /// <param name="triggersCollector"></param>
+ void CollectAndShareTriggers(TriggersCollector triggersCollector) {
+ var pool = quantifiers.SelectMany(q => triggersCollector.CollectTriggers(q.quantifier));
+ var distinctPool = pool.Deduplicate(TriggerTerm.Eq);
+
+ foreach (var q in quantifiers) {
+ q.CandidateTerms = distinctPool; // The list of candidate terms is immutable
+ q.Candidates = TriggerUtils.AllNonEmptySubsets(distinctPool, SubsetGenerationPredicate, q.quantifier.BoundVars).Select(set => set.ToTriggerCandidate()).ToList();
+ }
+ }
+
+ private void TrimInvalidTriggers() {
+ foreach (var q in quantifiers) {
+ q.TrimInvalidTriggers();
+ }
+ }
+
+ void BuildDependenciesGraph() {
+ // The problem of finding matching loops between multiple-triggers is hard; it
+ // seems to require one to track exponentially-sized dependencies between parts
+ // of triggers and quantifiers. For now, we only do single-quantifier loop
+ // detection
+ }
+
+ void SuppressMatchingLoops() {
+ // NOTE: This only looks for self-loops; that is, loops involving a single
+ // quantifier.
+
+ // For a given quantifier q, we introduce a triggering relation between trigger
+ // candidates by writing t1 → t2 if instantiating q from t1 introduces a ground
+ // term that matches t2. Then, we notice that this relation is transitive, since
+ // all triggers yield the same set of terms. This means that any matching loop
+ // t1 → ... → t1 can be reduced to a self-loop t1 → t1. Detecting such
+ // self-loops is then only a matter of finding terms in the body of the
+ // quantifier that match a given trigger.
+
+ // Of course, each trigger that actually appears in the body of the quantifier
+ // yields a trivial self-loop (e.g. P(i) in [∀ i {P(i)} ⋅ P(i)]), so we
+ // ignore this type of loops. In fact, we ignore any term in the body of the
+ // quantifier that matches one of the terms of the trigger (this ensures that
+ // [∀ x {f(x), f(f(x))} ⋅ f(x) = f(f(x))] is not a loop). And we even
+ // ignore terms that almost match a trigger term, modulo a single variable
+ // (this ensures that [∀ x y {a(x, y)} ⋅ a(x, y) == a(y, x)] is not a loop).
+ // In addition, we ignore cases where the only differences between a trigger
+ // and a trigger match are places where a variable is replaced with an
+ // expression whose free variables do not intersect that of the quantifier
+ // in which that expression is found. For examples of this behavious, see
+ // triggers/literals-do-not-cause-loops.
+ // This ignoring logic is implemented by the CouldCauseLoops method.
+
+ foreach (var q in quantifiers) {
+ var looping = new List<TriggerCandidate>();
+
+ var safe = TriggerUtils.Filter(
+ q.Candidates,
+ candidate => candidate.LoopingSubterms(q.quantifier).ToList(),
+ (candidate, loopingSubterms) => !loopingSubterms.Any(),
+ (candidate, loopingSubterms) => {
+ looping.Add(candidate);
+ candidate.Annotation = "may loop with " + loopingSubterms.MapConcat(t => "\"" + Printer.ExprToString(t.OriginalExpr) + "\"", ", ");
+ }).ToList();
+
+ q.CouldSuppressLoops = safe.Count > 0;
+
+ if (!q.AllowsLoops && q.CouldSuppressLoops) {
+ q.Candidates = safe;
+ q.RejectedCandidates.AddRange(looping);
+ }
+ }
+ }
+
+ void SelectTriggers() {
+ foreach (var q in quantifiers) { //FIXME Check whether this makes verification faster
+ q.Candidates = TriggerUtils.Filter(q.Candidates,
+ candidate => q.Candidates.Where(candidate.IsStrongerThan).ToList(),
+ (candidate, weakerCandidates) => !weakerCandidates.Any(),
+ (candidate, weakerCandidates) => {
+ q.RejectedCandidates.Add(candidate);
+ candidate.Annotation = "more specific than " + String.Join(", ", weakerCandidates);
+ }).ToList();
+ }
+ }
+
+ private void CommitOne(QuantifierWithTriggers q, bool addHeader) {
+ var errorLevel = ErrorLevel.Info;
+ var msg = new StringBuilder();
+ var indent = addHeader ? " " : "";
+ bool suppressWarnings = Attributes.Contains(q.quantifier.Attributes, "nowarn");
+
+ if (!TriggerUtils.NeedsAutoTriggers(q.quantifier)) { // NOTE: split and autotriggers attributes are passed down to Boogie
+ var extraMsg = TriggerUtils.WantsAutoTriggers(q.quantifier) ? "" : " Note that {:autotriggers false} can cause instabilities. Consider using {:nowarn}, {:matchingloop} (not great either), or a manual trigger instead.";
+ msg.AppendFormat("Not generating triggers for \"{0}\".{1}", Printer.ExprToString(q.quantifier.Term), extraMsg).AppendLine();
+ } else {
+ if (addHeader) {
+ msg.AppendFormat("For expression \"{0}\":", Printer.ExprToString(q.quantifier.Term)).AppendLine();
+ }
+
+ foreach (var candidate in q.Candidates) {
+ q.quantifier.Attributes = new Attributes("trigger", candidate.Terms.Select(t => t.Expr).ToList(), q.quantifier.Attributes);
+ }
+
+ AddTriggersToMessage("Selected triggers:", q.Candidates, msg, indent);
+ AddTriggersToMessage("Rejected triggers:", q.RejectedCandidates, msg, indent, true);
+
+#if QUANTIFIER_WARNINGS
+ var WARN_TAG = DafnyOptions.O.UnicodeOutput ? "âš  " : "/!\\ ";
+ var WARN_TAG_OVERRIDE = suppressWarnings ? "(Suppressed warning) " : WARN_TAG;
+ var WARN_LEVEL = suppressWarnings ? ErrorLevel.Info : ErrorLevel.Warning;
+ var WARN = indent + WARN_TAG_OVERRIDE;
+ if (!q.CandidateTerms.Any()) {
+ errorLevel = WARN_LEVEL;
+ msg.Append(WARN).AppendLine("No terms found to trigger on.");
+ } else if (!q.Candidates.Any()) {
+ errorLevel = WARN_LEVEL;
+ msg.Append(WARN).AppendLine("No trigger covering all quantified variables found.");
+ } else if (!q.CouldSuppressLoops && !q.AllowsLoops) {
+ errorLevel = WARN_LEVEL;
+ msg.Append(WARN).AppendLine("Suppressing loops would leave this expression without triggers.");
+ } else if (suppressWarnings) {
+ errorLevel = ErrorLevel.Warning;
+ msg.Append(indent).Append(WARN_TAG).AppendLine("There is no warning here to suppress.");
+ }
+#endif
+ }
+
+ if (msg.Length > 0) {
+ var msgStr = msg.ToString().TrimEnd("\r\n ".ToCharArray());
+ reporter.Message(MessageSource.Rewriter, errorLevel, q.quantifier.tok, msgStr);
+ }
+ }
+
+ private static void AddTriggersToMessage<T>(string header, List<T> triggers, StringBuilder msg, string indent, bool newlines = false) {
+ if (triggers.Any()) {
+ msg.Append(indent).Append(header);
+ if (triggers.Count == 1) {
+ msg.Append(" ");
+ } else if (triggers.Count > 1) {
+ msg.AppendLine().Append(indent).Append(" ");
+ }
+ var separator = newlines && triggers.Count > 1 ? Environment.NewLine + indent + " " : ", ";
+ msg.AppendLine(String.Join(separator, triggers));
+ }
+ }
+
+ internal void CommitTriggers() {
+ foreach (var q in quantifiers) {
+ CommitOne(q, quantifiers.Count > 1);
+ }
+ }
+ }
+}
diff --git a/Source/Dafny/Triggers/QuantifiersCollector.cs b/Source/Dafny/Triggers/QuantifiersCollector.cs
new file mode 100644
index 00000000..cb2d5c6d
--- /dev/null
+++ b/Source/Dafny/Triggers/QuantifiersCollector.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Boogie;
+using System.Collections.ObjectModel;
+using System.Diagnostics.Contracts;
+
+namespace Microsoft.Dafny.Triggers {
+ internal class QuantifierCollector : TopDownVisitor<bool> {
+ readonly ErrorReporter reporter;
+ private readonly HashSet<Expression> quantifiers = new HashSet<Expression>();
+ internal readonly HashSet<Expression> exprsInOldContext = new HashSet<Expression>();
+ internal readonly List<QuantifiersCollection> quantifierCollections = new List<QuantifiersCollection>();
+
+ public QuantifierCollector(ErrorReporter reporter) {
+ Contract.Requires(reporter != null);
+ this.reporter = reporter;
+ }
+
+ protected override bool VisitOneExpr(Expression expr, ref bool inOldContext) {
+ var quantifier = expr as QuantifierExpr;
+
+ // only consider quantifiers that are not empty (Bound.Vars.Count > 0)
+ if (quantifier != null && (quantifier.BoundVars.Count > 0) && !quantifiers.Contains(quantifier)) {
+ quantifiers.Add(quantifier);
+ if (quantifier.SplitQuantifier != null) {
+ var collection = quantifier.SplitQuantifier.Select(q => q as QuantifierExpr).Where(q => q != null);
+ quantifierCollections.Add(new QuantifiersCollection(collection, reporter));
+ quantifiers.UnionWith(quantifier.SplitQuantifier);
+ } else {
+ quantifierCollections.Add(new QuantifiersCollection(Enumerable.Repeat(quantifier, 1), reporter));
+ }
+ }
+
+ if (expr is OldExpr) {
+ inOldContext = true;
+ } else if (inOldContext) { // FIXME be more restrctive on the type of stuff that we annotate
+ exprsInOldContext.Add(expr);
+ }
+
+ return true;
+ }
+
+ protected override bool VisitOneStmt(Statement stmt, ref bool st) {
+ if (stmt is ForallStmt) {
+ ForallStmt s = (ForallStmt)stmt;
+ if (s.ForallExpressions != null) {
+ foreach (Expression expr in s.ForallExpressions) {
+ VisitOneExpr(expr, ref st);
+ }
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/Source/Dafny/Triggers/TriggerExtensions.cs b/Source/Dafny/Triggers/TriggerExtensions.cs
new file mode 100644
index 00000000..d4a822a8
--- /dev/null
+++ b/Source/Dafny/Triggers/TriggerExtensions.cs
@@ -0,0 +1,509 @@
+#define THROW_UNSUPPORTED_COMPARISONS
+
+using Microsoft.Dafny;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Dafny.Triggers {
+ internal static class DeduplicateExtension {
+ public static List<T> Deduplicate<T>(this IEnumerable<T> seq, Func<T, T, bool> eq) {
+ List<T> deduplicated = new List<T>();
+
+ foreach (var elem in seq) {
+ if (!deduplicated.Any(other => eq(elem, other))) {
+ deduplicated.Add(elem);
+ }
+ }
+
+ return deduplicated;
+ }
+ }
+
+ internal struct TriggerMatch {
+ internal Expression Expr;
+ internal Expression OriginalExpr;
+ internal Dictionary<IVariable, Expression> Bindings;
+
+ internal static bool Eq(TriggerMatch t1, TriggerMatch t2) {
+ return ExprExtensions.ExpressionEq(t1.Expr, t2.Expr);
+ }
+
+ /// <summary>
+ /// This method checks whether this match could actually cause a loop, given a set of terms participating in a trigger;
+ /// to compute an answer, we match the Expr of this match against the Exprs of each of these term, allowing for harmless
+ /// variations. If any of these tests does match, this term likely won't cause a loop.
+ /// The boundVars list is useful to determine that forall x :: P(x) == P(y+z) does not loop.
+ /// </summary>
+ internal bool CouldCauseLoops(List<TriggerTerm> terms, ISet<BoundVar> boundVars) {
+ var expr = Expr;
+ return !terms.Any(term => term.Expr.ExpressionEqModuloExpressionsNotInvolvingBoundVariables(expr, boundVars));
+ }
+ }
+
+ internal static class ExprExtensions {
+ internal static IEnumerable<Expression> AllSubExpressions(this Expression expr, bool wrapOld, bool strict) {
+ bool isOld = expr is OldExpr;
+
+ foreach (var subexpr in expr.SubExpressions) {
+ foreach (var r_subexpr in AllSubExpressions(subexpr, wrapOld, false)) {
+ yield return TriggerUtils.MaybeWrapInOld(r_subexpr, isOld);
+ }
+ }
+
+ if (expr is StmtExpr) {
+ foreach (var r_subexpr in AllSubExpressions(((StmtExpr)expr).S, wrapOld, false)) {
+ yield return TriggerUtils.MaybeWrapInOld(r_subexpr, isOld);
+ }
+ }
+
+ if (!strict) {
+ yield return expr;
+ }
+ }
+
+ internal static IEnumerable<Expression> AllSubExpressions(this Statement stmt, bool wrapOld, bool strict) {
+ foreach (var subexpr in stmt.SubExpressions) {
+ foreach (var r_subexpr in AllSubExpressions(subexpr, wrapOld, false)) {
+ yield return r_subexpr;
+ }
+ }
+
+ foreach (var substmt in stmt.SubStatements) {
+ foreach (var r_subexpr in AllSubExpressions(substmt, wrapOld, false)) {
+ yield return r_subexpr;
+ }
+ }
+ }
+
+ internal static bool ExpressionEq(this Expression expr1, Expression expr2) {
+ expr1 = expr1.Resolved;
+ expr2 = expr2.Resolved;
+
+ return ShallowEq_Top(expr1, expr2) && TriggerUtils.SameLists(expr1.SubExpressions, expr2.SubExpressions, (e1, e2) => ExpressionEq(e1, e2));
+ }
+
+ internal static bool ExpressionEqModuloExpressionsNotInvolvingBoundVariables(this Expression expr1, Expression expr2, ISet<BoundVar> boundVars) {
+ expr1 = expr1.Resolved;
+ expr2 = expr2.Resolved;
+
+ if (expr1 is IdentifierExpr) {
+ if (expr2 is IdentifierExpr) {
+ return true;
+ } else {
+ var freeInE2 = Translator.ComputeFreeVariables(expr2);
+ freeInE2.IntersectWith(boundVars);
+ return !freeInE2.Any();
+ }
+ }
+
+ return ShallowEq_Top(expr1, expr2) && TriggerUtils.SameLists(expr1.SubExpressions,
+ expr2.SubExpressions, (e1, e2) => ExpressionEqModuloExpressionsNotInvolvingBoundVariables(e1, e2, boundVars));
+ }
+
+ internal static bool MatchesTrigger(this Expression expr, Expression trigger, ISet<BoundVar> holes, Dictionary<IVariable, Expression> bindings) {
+ expr = expr.Resolved;
+ trigger = trigger.Resolved;
+
+ if (trigger is IdentifierExpr) {
+ var var = ((IdentifierExpr)trigger).Var;
+
+ if (holes.Contains(var)) {
+ Expression existing_binding = null;
+ if (bindings.TryGetValue(var, out existing_binding)) {
+ return ExpressionEq(expr, existing_binding);
+ } else {
+ bindings[var] = expr;
+ return true;
+ }
+ }
+ }
+
+ return ShallowEq_Top(expr, trigger) && TriggerUtils.SameLists(expr.SubExpressions, trigger.SubExpressions, (e1, e2) => MatchesTrigger(e1, e2, holes, bindings));
+ }
+
+ private static TriggerMatch? MatchAgainst(this Expression expr, Expression trigger, IEnumerable<BoundVar> holes, Expression originalExpr) {
+ var bindings = new Dictionary<IVariable, Expression>();
+ if (expr.MatchesTrigger(trigger, new HashSet<BoundVar>(holes), bindings)) {
+ return new TriggerMatch { Expr = expr, OriginalExpr = originalExpr ?? expr, Bindings = bindings };
+ } else {
+ return null;
+ }
+ }
+
+ internal static IEnumerable<TriggerMatch> SubexpressionsMatchingTrigger(this QuantifierExpr quantifier, Expression trigger) {
+ return quantifier.AllSubExpressions(true, true)
+ .Select(e => TriggerUtils.PrepareExprForInclusionInTrigger(e).MatchAgainst(trigger, quantifier.BoundVars, e))
+ .Where(e => e.HasValue).Select(e => e.Value);
+ }
+
+ private static bool ShallowSameAttributes(Attributes attributes1, Attributes attributes2) {
+ return TriggerUtils.SameLists(attributes1.AsEnumerable(), attributes2.AsEnumerable(), ShallowSameSingleAttribute);
+ }
+
+ private static bool ShallowSameSingleAttribute(Attributes arg1, Attributes arg2) {
+ return arg1.Name == arg2.Name;
+ }
+
+ private static bool SameBoundVar(IVariable arg1, IVariable arg2) {
+ return arg1 == arg2 ||
+ (arg1.Name == arg2.Name &&
+ arg1.CompileName == arg2.CompileName &&
+ arg1.DisplayName == arg2.DisplayName &&
+ arg1.UniqueName == arg2.UniqueName &&
+ arg1.IsGhost == arg2.IsGhost &&
+ arg1.IsMutable == arg2.IsMutable &&
+ ((arg1.Type == null && arg2.Type == null) || arg1.Type.Equals(arg2.Type)));
+ }
+
+ /// <summary>
+ /// Compares two abstract syntax expressions, looking only at their direct members. Subexpressions and substatements are not compared.
+ /// </summary>
+ /// <returns></returns>
+ internal static bool ShallowEq_Top(Expression expr1, Expression expr2) {
+ Contract.Requires(expr1 != null);
+ Contract.Requires(expr2 != null);
+
+ // We never compare concrete expressions
+ Contract.Requires(!(expr1 is ConcreteSyntaxExpression));
+ Contract.Requires(!(expr2 is ConcreteSyntaxExpression));
+
+ // CPC: Hey future editor: the following block of code is auto-generated. Just add your own cases at the end.
+ // This could be a visitor pattern, except I need to visit a pair of nodes.
+ // It could also be implemented in each individual class. I'd have a slight preference for that.
+ // This really just wants to use double dispatch.
+ if (expr1 is UnboxingCastExpr && expr2 is UnboxingCastExpr) {
+ return ShallowEq((UnboxingCastExpr)expr1, (UnboxingCastExpr)expr2);
+ } else if (expr1 is BoxingCastExpr && expr2 is BoxingCastExpr) {
+ return ShallowEq((BoxingCastExpr)expr1, (BoxingCastExpr)expr2);
+ } else if (expr1 is MatchExpr && expr2 is MatchExpr) {
+ return ShallowEq((MatchExpr)expr1, (MatchExpr)expr2);
+ } else if (expr1 is ITEExpr && expr2 is ITEExpr) {
+ return ShallowEq((ITEExpr)expr1, (ITEExpr)expr2);
+ } else if (expr1 is StmtExpr && expr2 is StmtExpr) {
+ return ShallowEq((StmtExpr)expr1, (StmtExpr)expr2);
+ } else if (expr1 is WildcardExpr && expr2 is WildcardExpr) {
+ return ShallowEq((WildcardExpr)expr1, (WildcardExpr)expr2);
+ } else if (expr1 is ComprehensionExpr && expr2 is ComprehensionExpr) {
+ return ShallowEq((ComprehensionExpr)expr1, (ComprehensionExpr)expr2);
+ } else if (expr1 is NamedExpr && expr2 is NamedExpr) {
+ return ShallowEq((NamedExpr)expr1, (NamedExpr)expr2);
+ } else if (expr1 is LetExpr && expr2 is LetExpr) {
+ return ShallowEq((LetExpr)expr1, (LetExpr)expr2);
+ } else if (expr1 is TernaryExpr && expr2 is TernaryExpr) {
+ return ShallowEq((TernaryExpr)expr1, (TernaryExpr)expr2);
+ } else if (expr1 is BinaryExpr && expr2 is BinaryExpr) {
+ return ShallowEq((BinaryExpr)expr1, (BinaryExpr)expr2);
+ } else if (expr1 is UnaryExpr && expr2 is UnaryExpr) {
+ return ShallowEq((UnaryExpr)expr1, (UnaryExpr)expr2);
+ } else if (expr1 is MultiSetFormingExpr && expr2 is MultiSetFormingExpr) {
+ return ShallowEq((MultiSetFormingExpr)expr1, (MultiSetFormingExpr)expr2);
+ } else if (expr1 is OldExpr && expr2 is OldExpr) {
+ return ShallowEq((OldExpr)expr1, (OldExpr)expr2);
+ } else if (expr1 is FunctionCallExpr && expr2 is FunctionCallExpr) {
+ return ShallowEq((FunctionCallExpr)expr1, (FunctionCallExpr)expr2);
+ } else if (expr1 is ApplyExpr && expr2 is ApplyExpr) {
+ return ShallowEq((ApplyExpr)expr1, (ApplyExpr)expr2);
+ } else if (expr1 is SeqUpdateExpr && expr2 is SeqUpdateExpr) {
+ return ShallowEq((SeqUpdateExpr)expr1, (SeqUpdateExpr)expr2);
+ } else if (expr1 is MultiSelectExpr && expr2 is MultiSelectExpr) {
+ return ShallowEq((MultiSelectExpr)expr1, (MultiSelectExpr)expr2);
+ } else if (expr1 is SeqSelectExpr && expr2 is SeqSelectExpr) {
+ return ShallowEq((SeqSelectExpr)expr1, (SeqSelectExpr)expr2);
+ } else if (expr1 is MemberSelectExpr && expr2 is MemberSelectExpr) {
+ return ShallowEq((MemberSelectExpr)expr1, (MemberSelectExpr)expr2);
+ } else if (expr1 is MapDisplayExpr && expr2 is MapDisplayExpr) { //Note: MapDisplayExpr is not a DisplayExpression
+ return ShallowEq((MapDisplayExpr)expr1, (MapDisplayExpr)expr2);
+ } else if (expr1 is DisplayExpression && expr2 is DisplayExpression) {
+ return ShallowEq((DisplayExpression)expr1, (DisplayExpression)expr2);
+ } else if (expr1 is IdentifierExpr && expr2 is IdentifierExpr) {
+ return ShallowEq((IdentifierExpr)expr1, (IdentifierExpr)expr2);
+ } else if (expr1 is ThisExpr && expr2 is ThisExpr) {
+ return ShallowEq((ThisExpr)expr1, (ThisExpr)expr2);
+ } else if (expr1 is DatatypeValue && expr2 is DatatypeValue) {
+ return ShallowEq((DatatypeValue)expr1, (DatatypeValue)expr2);
+ } else if (expr1 is LiteralExpr && expr2 is LiteralExpr) {
+ return ShallowEq((LiteralExpr)expr1, (LiteralExpr)expr2);
+ } else {
+ // If this assertion fail, then a new abstract AST node was probably introduced but not registered here.
+ Contract.Assert(expr1.GetType() != expr2.GetType());
+ return false;
+ }
+ }
+
+ private static bool ShallowEq(UnboxingCastExpr expr1, UnboxingCastExpr expr2) {
+ Contract.Requires(false);
+ throw new InvalidOperationException();
+ }
+
+ private static bool ShallowEq(BoxingCastExpr expr1, BoxingCastExpr expr2) {
+ return expr1.FromType == expr2.FromType &&
+ expr1.ToType == expr2.ToType;
+ }
+
+ private static bool ShallowEq(MatchExpr expr1, MatchExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(ITEExpr expr1, ITEExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(StmtExpr expr1, StmtExpr expr2) {
+#if THROW_UNSUPPORTED_COMPARISONS
+ Contract.Assume(false); // This kind of expression never appears in a trigger
+ throw new NotImplementedException();
+#else
+ return expr1.S == expr2.S;
+#endif
+ }
+
+ private static bool ShallowEq(WildcardExpr expr1, WildcardExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(LambdaExpr expr1, LambdaExpr expr2) {
+#if THROW_UNSUPPORTED_COMPARISONS
+ Contract.Assume(false); // This kind of expression never appears in a trigger
+ throw new NotImplementedException();
+#else
+ return false;
+#endif
+ }
+
+ private static bool ShallowEq(MapComprehension expr1, MapComprehension expr2) {
+ return expr1.Finite == expr2.Finite;
+ }
+
+ private static bool ShallowEq(SetComprehension expr1, SetComprehension expr2) {
+ return expr1.TermIsImplicit == expr2.TermIsImplicit && //TODO
+ expr1.Finite == expr2.Finite;
+ }
+
+ private static bool ShallowEq(ExistsExpr expr1, ExistsExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(ForallExpr expr1, ForallExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(QuantifierExpr expr1, QuantifierExpr expr2) {
+ if (!TriggerUtils.SameNullity(expr1.SplitQuantifier, expr2.SplitQuantifier)) {
+ return false;
+ }
+
+ if (expr1.SplitQuantifier != null && expr2.SplitQuantifier != null) {
+ return ShallowEq_Top(expr1.SplitQuantifierExpression, expr2.SplitQuantifierExpression);
+ }
+
+ if (expr1.TypeArgs.Count != expr2.TypeArgs.Count ||
+ !TriggerUtils.SameNullity(expr1.Range, expr2.Range)) {
+ return false;
+ }
+
+ if (expr1 is ExistsExpr && expr2 is ExistsExpr) {
+ return ShallowEq((ExistsExpr)expr1, (ExistsExpr)expr2);
+ } else if (expr1 is ForallExpr && expr2 is ForallExpr) {
+ return ShallowEq((ForallExpr)expr1, (ForallExpr)expr2);
+ } else {
+ return false;
+ }
+ }
+
+ private static bool ShallowEq(ComprehensionExpr expr1, ComprehensionExpr expr2) {
+ if (!TriggerUtils.SameLists(expr1.BoundVars, expr2.BoundVars, SameBoundVar) ||
+ !ShallowSameAttributes(expr1.Attributes, expr2.Attributes) ||
+ // Filled in during resolution: !SameLists(expr1.Bounds, expr2.Bounds, ReferenceCompare) ||
+ // !SameLists(expr1.MissingBounds, expr2.MissingBounds, SameBoundVar) ||
+ !TriggerUtils.SameNullity(expr1.Range, expr2.Range)) { //TODO Check
+ return false;
+ }
+
+ if (expr1 is LambdaExpr && expr2 is LambdaExpr) {
+ return ShallowEq((LambdaExpr)expr1, (LambdaExpr)expr2);
+ } else if (expr1 is MapComprehension && expr2 is MapComprehension) {
+ return ShallowEq((MapComprehension)expr1, (MapComprehension)expr2);
+ } else if (expr1 is SetComprehension && expr2 is SetComprehension) {
+ return ShallowEq((SetComprehension)expr1, (SetComprehension)expr2);
+ } else if (expr1 is QuantifierExpr && expr2 is QuantifierExpr) {
+ return ShallowEq((QuantifierExpr)expr1, (QuantifierExpr)expr2);
+ } else {
+ return false; // ComprehensionExpr is abstract
+ }
+ }
+
+ private static bool ShallowEq(NamedExpr expr1, NamedExpr expr2) {
+ return expr1.Name == expr2.Name &&
+ TriggerUtils.SameNullity(expr1.Contract, expr2.Contract);
+ }
+
+ private static bool ShallowEq(LetExpr expr1, LetExpr expr2) {
+ return expr1.Exact == expr2.Exact &&
+ ShallowSameAttributes(expr1.Attributes, expr2.Attributes);
+ }
+
+ private static bool ShallowEq(TernaryExpr expr1, TernaryExpr expr2) {
+ return expr1.Op == expr2.Op;
+ }
+
+ private static bool ShallowEq(BinaryExpr expr1, BinaryExpr expr2) {
+ Contract.Requires(expr1.ResolvedOp != BinaryExpr.ResolvedOpcode.YetUndetermined);
+ Contract.Requires(expr2.ResolvedOp != BinaryExpr.ResolvedOpcode.YetUndetermined);
+ return expr1.ResolvedOp == expr2.ResolvedOp;
+ }
+
+ private static bool ShallowEq(ConversionExpr expr1, ConversionExpr expr2) {
+ return expr1.Type == expr2.Type; //TODO equality on types?
+ }
+
+ private static bool ShallowEq(UnaryOpExpr expr1, UnaryOpExpr expr2) {
+ return expr1.Op == expr2.Op;
+ }
+
+ private static bool ShallowEq(UnaryExpr expr1, UnaryExpr expr2) {
+ if (expr1 is ConversionExpr && expr2 is ConversionExpr) {
+ return ShallowEq((ConversionExpr)expr1, (ConversionExpr)expr2);
+ } else if (expr1 is UnaryOpExpr && expr2 is UnaryOpExpr) {
+ return ShallowEq((UnaryOpExpr)expr1, (UnaryOpExpr)expr2);
+ } else {
+ return false; // UnaryExpr is abstract
+ }
+ }
+
+ private static bool ShallowEq(MultiSetFormingExpr expr1, MultiSetFormingExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(OldExpr expr1, OldExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(FunctionCallExpr expr1, FunctionCallExpr expr2) {
+ return expr1.Name == expr2.Name &&
+ expr1.CoCall == expr2.CoCall && //TODO
+ expr1.Function == expr2.Function; // TODO TypeArgumentSubstitutions?
+ }
+
+ private static bool ShallowEq(ApplyExpr expr1, ApplyExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(SeqUpdateExpr expr1, SeqUpdateExpr expr2) {
+ Contract.Requires(expr1.ResolvedUpdateExpr != null && expr2.ResolvedUpdateExpr != null);
+ return true;
+ }
+
+ private static bool ShallowEq(MultiSelectExpr expr1, MultiSelectExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(SeqSelectExpr expr1, SeqSelectExpr expr2) {
+ return expr1.SelectOne == expr2.SelectOne &&
+ TriggerUtils.SameNullity(expr1.Seq, expr2.Seq) &&
+ TriggerUtils.SameNullity(expr1.E0, expr2.E0) &&
+ TriggerUtils.SameNullity(expr1.E1, expr2.E1);
+ }
+
+ private static bool ShallowEq(MemberSelectExpr expr1, MemberSelectExpr expr2) {
+ return expr1.MemberName == expr2.MemberName &&
+ expr1.Member == expr2.Member &&
+ TriggerUtils.SameLists(expr1.TypeApplication, expr2.TypeApplication, Microsoft.Dafny.Type.Equals);
+ }
+
+ private static bool ShallowEq(SeqDisplayExpr expr1, SeqDisplayExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(MapDisplayExpr expr1, MapDisplayExpr expr2) {
+ return expr1.Finite == expr2.Finite;
+ }
+
+ private static bool ShallowEq(MultiSetDisplayExpr expr1, MultiSetDisplayExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(SetDisplayExpr expr1, SetDisplayExpr expr2) {
+ return expr1.Finite == expr2.Finite;
+ }
+
+ private static bool ShallowEq(DisplayExpression expr1, DisplayExpression expr2) {
+ if (expr1 is SeqDisplayExpr && expr2 is SeqDisplayExpr) {
+ return ShallowEq((SeqDisplayExpr)expr1, (SeqDisplayExpr)expr2);
+ } else if (expr1 is MultiSetDisplayExpr && expr2 is MultiSetDisplayExpr) {
+ return ShallowEq((MultiSetDisplayExpr)expr1, (MultiSetDisplayExpr)expr2);
+ } else if (expr1 is SetDisplayExpr && expr2 is SetDisplayExpr) {
+ return ShallowEq((SetDisplayExpr)expr1, (SetDisplayExpr)expr2);
+ } else {
+ return false;
+ }
+ }
+
+ private static bool ShallowEq(AutoGhostIdentifierExpr expr1, AutoGhostIdentifierExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(IdentifierExpr expr1, IdentifierExpr expr2) {
+ if (expr1.Name != expr2.Name ||
+ expr1.Var != expr2.Var) {
+ return false;
+ }
+
+ if (expr1 is AutoGhostIdentifierExpr && expr2 is AutoGhostIdentifierExpr) {
+ return ShallowEq((AutoGhostIdentifierExpr)expr1, (AutoGhostIdentifierExpr)expr2);
+ } else {
+ return true;
+ }
+ }
+
+ private static bool ShallowEq(ImplicitThisExpr expr1, ImplicitThisExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(ThisExpr expr1, ThisExpr expr2) {
+ if (expr1 is ImplicitThisExpr && expr2 is ImplicitThisExpr) {
+ return ShallowEq((ImplicitThisExpr)expr1, (ImplicitThisExpr)expr2);
+ } else {
+ return (expr1.GetType() == expr2.GetType()); // LiteralExpr is not abstract
+ }
+ }
+
+ private static bool ShallowEq(DatatypeValue expr1, DatatypeValue expr2) {
+ return // Implied by Ctor equality: expr1.DatatypeName == expr2.DatatypeName &&
+ // Implied by Ctor equality: expr1.MemberName == expr2.MemberName &&
+ expr1.Ctor == expr2.Ctor &&
+ // Contextual information: expr1.IsCoCall == expr2.IsCoCall &&
+ TriggerUtils.SameLists(expr1.InferredTypeArgs, expr2.InferredTypeArgs, Microsoft.Dafny.Type.Equals);
+ }
+
+ private static bool ShallowEq(StringLiteralExpr expr1, StringLiteralExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(CharLiteralExpr expr1, CharLiteralExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(StaticReceiverExpr expr1, StaticReceiverExpr expr2) {
+ return true;
+ }
+
+ private static bool ShallowEq(LiteralExpr expr1, LiteralExpr expr2) {
+ if (!TriggerUtils.SameNullity(expr1.Value, expr2.Value) || (expr1.Value != null && !expr1.Value.Equals(expr2.Value))) {
+ return false;
+ }
+
+ if (expr1 is StringLiteralExpr && expr2 is StringLiteralExpr) {
+ return ShallowEq((StringLiteralExpr)expr1, (StringLiteralExpr)expr2);
+ } else if (expr1 is CharLiteralExpr && expr2 is CharLiteralExpr) {
+ return ShallowEq((CharLiteralExpr)expr1, (CharLiteralExpr)expr2);
+ } else if (expr1 is StaticReceiverExpr && expr2 is StaticReceiverExpr) {
+ return ShallowEq((StaticReceiverExpr)expr1, (StaticReceiverExpr)expr2);
+ } else {
+ return (expr1.GetType() == expr2.GetType()); // LiteralExpr is not abstract
+ }
+ }
+ }
+}
diff --git a/Source/Dafny/Triggers/TriggerUtils.cs b/Source/Dafny/Triggers/TriggerUtils.cs
new file mode 100644
index 00000000..c16a3e44
--- /dev/null
+++ b/Source/Dafny/Triggers/TriggerUtils.cs
@@ -0,0 +1,265 @@
+#define DEBUG_AUTO_TRIGGERS
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Dafny.Triggers {
+ class TriggerUtils {
+ internal static List<T> CopyAndAdd<T>(List<T> seq, T elem) {
+ var copy = new List<T>(seq);
+ copy.Add(elem);
+ return copy;
+ }
+
+ internal class SetOfTerms {
+ internal bool IsRedundant { get; private set; }
+ internal List<TriggerTerm> Terms { get; set; }
+
+ private ISet<BoundVar> variables;
+ private Dictionary<BoundVar, TriggerTerm> termOwningAUniqueVar;
+ private Dictionary<TriggerTerm, ISet<BoundVar>> uniqueVarsOwnedByATerm;
+
+ public int Count { get { return Terms.Count; } }
+
+ private SetOfTerms() { }
+
+ internal TriggerCandidate ToTriggerCandidate() {
+ return new TriggerCandidate(Terms);
+ }
+
+ internal static SetOfTerms Empty() {
+ var newSet = new SetOfTerms();
+ newSet.IsRedundant = false;
+ newSet.Terms = new List<TriggerTerm>();
+ newSet.variables = new HashSet<BoundVar>();
+ newSet.termOwningAUniqueVar = new Dictionary<BoundVar, TriggerTerm>();
+ newSet.uniqueVarsOwnedByATerm = new Dictionary<TriggerTerm, ISet<BoundVar>>();
+ return newSet;
+ }
+
+ /// <summary>
+ /// Simple formulas like [P0(i) && P1(i) && P2(i) && P3(i) && P4(i)] yield very
+ /// large numbers of multi-triggers, most of which are useless. As it copies its
+ /// argument, this method updates various datastructures that allow it to
+ /// efficiently track ownership relations between formulae and bound variables,
+ /// and sets the IsRedundant flag of the returned set, allowing the caller to
+ /// discard that set of terms, and the ones that would have been built on top of
+ /// it, thus ensuring that the only sets of terms produced in the end are sets
+ /// of terms in which each element contributes (owns) at least one variable.
+ ///
+ /// Note that this is trickier than just checking every term for new variables;
+ /// indeed, a new term that does bring new variables in can make an existing
+ /// term redundant (see redundancy-detection-does-its-job-properly.dfy).
+ /// </summary>
+ internal SetOfTerms CopyWithAdd(TriggerTerm term, ISet<BoundVar> relevantVariables) {
+ var copy = new SetOfTerms();
+ copy.Terms = new List<TriggerTerm>(Terms);
+ copy.variables = new HashSet<BoundVar>(variables);
+ copy.termOwningAUniqueVar = new Dictionary<BoundVar, TriggerTerm>(termOwningAUniqueVar);
+ copy.uniqueVarsOwnedByATerm = new Dictionary<TriggerTerm, ISet<BoundVar>>(uniqueVarsOwnedByATerm);
+
+ copy.Terms.Add(term);
+
+ var varsInNewTerm = new HashSet<BoundVar>(term.BoundVars);
+ varsInNewTerm.IntersectWith(relevantVariables);
+
+ var ownedByNewTerm = new HashSet<BoundVar>();
+
+ // Check #0: does this term bring anything new?
+ copy.IsRedundant = IsRedundant || varsInNewTerm.All(bv => copy.variables.Contains(bv));
+ copy.variables.UnionWith(varsInNewTerm);
+
+ // Check #1: does this term claiming ownership of all its variables cause another term to become useless?
+ foreach (var v in varsInNewTerm) {
+ TriggerTerm originalOwner;
+ if (copy.termOwningAUniqueVar.TryGetValue(v, out originalOwner)) {
+ var alsoOwnedByOriginalOwner = copy.uniqueVarsOwnedByATerm[originalOwner];
+ alsoOwnedByOriginalOwner.Remove(v);
+ if (!alsoOwnedByOriginalOwner.Any()) {
+ copy.IsRedundant = true;
+ }
+ } else {
+ ownedByNewTerm.Add(v);
+ copy.termOwningAUniqueVar[v] = term;
+ }
+ }
+
+ // Actually claim ownership
+ copy.uniqueVarsOwnedByATerm[term] = ownedByNewTerm;
+
+ return copy;
+ }
+ }
+
+ internal static IEnumerable<SetOfTerms> AllSubsets(IList<TriggerTerm> source, Func<SetOfTerms, TriggerTerm, bool> predicate, int offset, ISet<BoundVar> relevantVariables) {
+ if (offset >= source.Count) {
+ yield return SetOfTerms.Empty();
+ yield break;
+ }
+
+ foreach (var subset in AllSubsets(source, predicate, offset + 1, relevantVariables)) {
+ var newSet = subset.CopyWithAdd(source[offset], relevantVariables);
+ if (!newSet.IsRedundant && predicate(subset, source[offset])) { // Fixme remove the predicate?
+ yield return newSet;
+ }
+ yield return subset;
+ }
+ }
+
+ internal static IEnumerable<SetOfTerms> AllNonEmptySubsets(IList<TriggerTerm> source, Func<SetOfTerms, TriggerTerm, bool> predicate, IEnumerable<BoundVar> relevantVariables) {
+ foreach (var subset in AllSubsets(source, predicate, 0, new HashSet<BoundVar>(relevantVariables))) {
+ if (subset.Count > 0) {
+ yield return subset;
+ }
+ }
+ }
+
+ internal static List<T> MergeAlterFirst<T>(List<T> a, List<T> b) {
+ Contract.Requires(a != null);
+ Contract.Requires(b != null);
+ a.AddRange(b);
+ return a;
+ }
+
+ internal static ISet<T> MergeAlterFirst<T>(ISet<T> a, ISet<T> b) {
+ Contract.Requires(a != null);
+ Contract.Requires(b != null);
+ a.UnionWith(b);
+ return a;
+ }
+
+ internal static bool SameLists<T>(IEnumerable<T> list1, IEnumerable<T> list2, Func<T, T, bool> comparer) {
+ if (ReferenceEquals(list1, list2)) {
+ return true;
+ } else if ((list1 == null) != (list2 == null)) {
+ return false;
+ }
+
+ var it1 = list1.GetEnumerator();
+ var it2 = list2.GetEnumerator();
+ bool it1_has, it2_has;
+ bool acc = true;
+
+ do {
+ it1_has = it1.MoveNext();
+ it2_has = it2.MoveNext();
+
+ if (it1_has == true && it2_has == true) {
+ acc = acc && comparer(it1.Current, it2.Current);
+ }
+ } while (it1_has && it2_has);
+
+ return it1_has == it2_has && acc;
+ }
+
+ internal static IEnumerable<T> Filter<T, U>(IEnumerable<T> elements, Func<T, U> Converter, Func<T, U, bool> predicate, Action<T, U> reject) {
+ var positive = new List<T>();
+ foreach (var elem in elements) {
+ var conv = Converter(elem);
+ if (predicate(elem, conv)) {
+ yield return elem;
+ } else {
+ reject(elem, conv);
+ }
+ }
+ }
+
+ internal static bool SameNullity<T>(T x1, T x2) where T : class {
+ return (x1 == null) == (x2 == null);
+ }
+
+ internal string JoinStringsWithHeader(string header, IEnumerable<string> lines) {
+ return header + String.Join(Environment.NewLine + new String(' ', header.Length), lines);
+ }
+
+ [Conditional("DEBUG_AUTO_TRIGGERS")]
+ internal static void DebugTriggers(string format, params object[] more) {
+ Console.Error.WriteLine(format, more);
+ }
+
+ internal static bool AllowsMatchingLoops(QuantifierExpr quantifier) {
+ Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier
+ // This is different from nowarn: it won't remove loops at all, even if another trigger is available.
+ return Attributes.Contains(quantifier.Attributes, "matchingloop");
+ }
+
+ internal static bool WantsAutoTriggers(QuantifierExpr quantifier) {
+ Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier
+ bool wantsAutoTriggers = true;
+ return !Attributes.ContainsBool(quantifier.Attributes, "autotriggers", ref wantsAutoTriggers) || wantsAutoTriggers;
+ }
+
+ internal static bool NeedsAutoTriggers(QuantifierExpr quantifier) {
+ Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier
+ return !Attributes.Contains(quantifier.Attributes, "trigger") && WantsAutoTriggers(quantifier);
+ }
+
+ internal static BinaryExpr.ResolvedOpcode RemoveNotInBinaryExprIn(BinaryExpr.ResolvedOpcode opcode) {
+ switch (opcode) {
+ case BinaryExpr.ResolvedOpcode.NotInMap:
+ return BinaryExpr.ResolvedOpcode.InMap;
+ case BinaryExpr.ResolvedOpcode.NotInSet:
+ return BinaryExpr.ResolvedOpcode.InSet;
+ case BinaryExpr.ResolvedOpcode.NotInSeq:
+ return BinaryExpr.ResolvedOpcode.InSeq;
+ case BinaryExpr.ResolvedOpcode.NotInMultiSet:
+ return BinaryExpr.ResolvedOpcode.InMultiSet;
+ }
+
+ Contract.Assert(false);
+ throw new ArgumentException();
+ }
+
+ internal static Expression PrepareExprForInclusionInTrigger(Expression expr, out bool isKiller) {
+ isKiller = false;
+
+ var ret = expr;
+ if (ret is BinaryExpr) {
+ ret = PrepareInMultisetForInclusionInTrigger(PrepareNotInForInclusionInTrigger((BinaryExpr)ret), ref isKiller);
+ }
+
+ return ret;
+ }
+
+ private static BinaryExpr PrepareNotInForInclusionInTrigger(BinaryExpr bexpr) {
+ if (bexpr.Op == BinaryExpr.Opcode.NotIn) {
+ var new_expr = new BinaryExpr(bexpr.tok, BinaryExpr.Opcode.In, bexpr.E0, bexpr.E1);
+ new_expr.ResolvedOp = RemoveNotInBinaryExprIn(bexpr.ResolvedOp);
+ new_expr.Type = bexpr.Type;
+ return new_expr;
+ } else {
+ return bexpr;
+ }
+ }
+
+ private static Expression PrepareInMultisetForInclusionInTrigger(BinaryExpr bexpr, ref bool isKiller) {
+ if (bexpr.ResolvedOp == BinaryExpr.ResolvedOpcode.InMultiSet) {
+ var new_expr = new SeqSelectExpr(bexpr.tok, true, bexpr.E1, bexpr.E0, null);
+ new_expr.Type = bexpr.Type;
+ isKiller = true; // [a in s] becomes [s[a] > 0], which is a trigger killer
+ return new_expr;
+ } else {
+ return bexpr;
+ }
+ }
+
+ internal static Expression PrepareExprForInclusionInTrigger(Expression expr) {
+ bool _;
+ return PrepareExprForInclusionInTrigger(expr, out _);
+ }
+
+ internal static Expression MaybeWrapInOld(Expression expr, bool wrap) {
+ if (wrap && !(expr is NameSegment) && !(expr is IdentifierExpr)) {
+ var newExpr = new OldExpr(expr.tok, expr);
+ newExpr.Type = expr.Type;
+ return newExpr;
+ } else {
+ return expr;
+ }
+ }
+ }
+}
diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs
new file mode 100644
index 00000000..4204cc29
--- /dev/null
+++ b/Source/Dafny/Triggers/TriggersCollector.cs
@@ -0,0 +1,325 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Boogie;
+using System.Diagnostics.Contracts;
+using System.Diagnostics;
+
+namespace Microsoft.Dafny.Triggers {
+ class TriggerTerm {
+ internal Expression Expr { get; set; }
+ internal Expression OriginalExpr { get; set; }
+ internal ISet<IVariable> Variables { get; set; }
+ internal IEnumerable<BoundVar> BoundVars {
+ get {
+ return Variables.Select(v => v as BoundVar).Where(v => v != null);
+ }
+ }
+
+ public override string ToString() {
+ return Printer.ExprToString(Expr);
+ // NOTE: Using OriginalExpr here could cause some confusion:
+ // for example, {a !in b} is a binary expression, yielding
+ // trigger {a in b}. Saying the trigger is a !in b would be
+ // rather misleading.
+ }
+
+ internal enum TermComparison {
+ SameStrength = 0, Stronger = 1, NotStronger = -1
+ }
+
+ internal TermComparison CompareTo(TriggerTerm other) {
+ if (this == other) {
+ return TermComparison.SameStrength;
+ } else if (Expr.AllSubExpressions(true, true).Any(other.Expr.ExpressionEq)) {
+ return TermComparison.Stronger;
+ } else {
+ return TermComparison.NotStronger;
+ }
+ }
+
+ internal static bool Eq(TriggerTerm t1, TriggerTerm t2) {
+ return ExprExtensions.ExpressionEq(t1.Expr, t2.Expr);
+ }
+ }
+
+ class TriggerCandidate {
+ internal List<TriggerTerm> Terms { get; set; }
+ internal string Annotation { get; set; }
+
+ internal TriggerCandidate(List<TriggerTerm> terms) {
+ this.Terms = terms;
+ }
+
+ public TriggerCandidate(TriggerCandidate candidate) {
+ this.Terms = candidate.Terms;
+ }
+
+ internal bool MentionsAll(List<BoundVar> vars) {
+ return vars.All(x => Terms.Any(term => term.Variables.Contains(x)));
+ }
+
+ internal string Repr { get { return String.Join(", ", Terms); } }
+
+ public override string ToString() {
+ return "{" + Repr + "}" + (String.IsNullOrWhiteSpace(Annotation) ? "" : " (" + Annotation + ")");
+ }
+
+ internal IEnumerable<TriggerMatch> LoopingSubterms(QuantifierExpr quantifier) {
+ Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier
+ var matchingSubterms = this.MatchingSubterms(quantifier);
+ var boundVars = new HashSet<BoundVar>(quantifier.BoundVars);
+ return matchingSubterms.Where(tm => tm.CouldCauseLoops(Terms, boundVars));
+ }
+
+ internal List<TriggerMatch> MatchingSubterms(QuantifierExpr quantifier) {
+ Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier
+ return Terms.SelectMany(term => quantifier.SubexpressionsMatchingTrigger(term.Expr)).Deduplicate(TriggerMatch.Eq);
+ }
+
+ internal bool IsStrongerThan(TriggerCandidate that) {
+ if (this == that) {
+ return false;
+ }
+
+ var hasStrictlyStrongerTerm = false;
+ foreach (var t in Terms) {
+ var comparison = that.Terms.Select(t.CompareTo).Max();
+
+ // All terms of `this` must be at least as strong as a term of `that`
+ if (comparison == TriggerTerm.TermComparison.NotStronger) { return false; }
+
+ // Did we find a strictly stronger term?
+ hasStrictlyStrongerTerm = hasStrictlyStrongerTerm || comparison == TriggerTerm.TermComparison.Stronger;
+ }
+
+ return hasStrictlyStrongerTerm;
+ }
+ }
+
+ internal class TriggerAnnotation {
+ internal bool IsTriggerKiller;
+ internal ISet<IVariable> Variables;
+ internal readonly List<TriggerTerm> PrivateTerms;
+ internal readonly List<TriggerTerm> ExportedTerms;
+
+ internal TriggerAnnotation(bool IsTriggerKiller, IEnumerable<IVariable> Variables, IEnumerable<TriggerTerm> AllTerms, IEnumerable<TriggerTerm> PrivateTerms = null) {
+ this.IsTriggerKiller = IsTriggerKiller;
+ this.Variables = new HashSet<IVariable>(Variables);
+ this.PrivateTerms = new List<TriggerTerm>(PrivateTerms == null ? Enumerable.Empty<TriggerTerm>() : PrivateTerms);
+ this.ExportedTerms = new List<TriggerTerm>(AllTerms == null ? Enumerable.Empty<TriggerTerm>() : AllTerms.Except(this.PrivateTerms));
+ }
+
+ public override string ToString() {
+ StringBuilder sb = new StringBuilder();
+ string indent = " {0}", nindent = "\n - {0}", subindent = "\n * {0}";
+
+ sb.AppendFormat(indent, IsTriggerKiller);
+
+ sb.AppendFormat(nindent, "Variables:");
+ foreach (var bv in Variables) {
+ sb.AppendFormat(subindent, bv.Name);
+ }
+
+ sb.AppendFormat(nindent, "Exported terms:");
+ foreach (var term in ExportedTerms) {
+ sb.AppendFormat(subindent, term);
+ }
+
+ if (PrivateTerms.Any()) {
+ sb.AppendFormat(nindent, "Private terms:");
+ foreach (var term in PrivateTerms) {
+ sb.AppendFormat(subindent, term);
+ }
+ }
+
+ return sb.ToString();
+ }
+ }
+
+ internal class TriggerAnnotationsCache {
+ public readonly HashSet<Expression> exprsInOldContext;
+ public readonly Dictionary<Expression, TriggerAnnotation> annotations;
+
+ /// <summary>
+ /// For certain operations, the TriggersCollector class needs to know whether
+ /// an particular expression is under an old(...) wrapper. This is in particular
+ /// true for generating trigger terms (but it is not for checking wehter something
+ /// is a trigger killer, so passing an empty set here for that case would be fine.
+ /// </summary>
+ public TriggerAnnotationsCache(HashSet<Expression> exprsInOldContext) {
+ this.exprsInOldContext = exprsInOldContext;
+ annotations = new Dictionary<Expression, TriggerAnnotation>();
+ }
+ }
+
+ internal class TriggersCollector {
+ TriggerAnnotationsCache cache;
+
+ internal TriggersCollector(HashSet<Expression> exprsInOldContext) {
+ this.cache = new TriggerAnnotationsCache(exprsInOldContext);
+ }
+
+ private T ReduceAnnotatedSubExpressions<T>(Expression expr, T seed, Func<TriggerAnnotation, T> map, Func<T, T, T> reduce) {
+ return expr.SubExpressions.Select(e => map(Annotate(e)))
+ .Aggregate(seed, (acc, e) => reduce(acc, e));
+ }
+
+ private List<TriggerTerm> CollectExportedCandidates(Expression expr) {
+ return ReduceAnnotatedSubExpressions<List<TriggerTerm>>(expr, new List<TriggerTerm>(), a => a.ExportedTerms, TriggerUtils.MergeAlterFirst);
+ }
+
+ private ISet<IVariable> CollectVariables(Expression expr) {
+ return ReduceAnnotatedSubExpressions(expr, new HashSet<IVariable>(), a => a.Variables, TriggerUtils.MergeAlterFirst);
+ }
+
+ private bool CollectIsKiller(Expression expr) {
+ return ReduceAnnotatedSubExpressions(expr, false, a => a.IsTriggerKiller, (a, b) => a || b);
+ }
+
+ private IEnumerable<TriggerTerm> OnlyPrivateCandidates(List<TriggerTerm> terms, IEnumerable<IVariable> privateVars) {
+ return terms.Where(c => privateVars.Intersect(c.Variables).Any()); //TODO Check perf
+ }
+
+ private TriggerAnnotation Annotate(Expression expr) {
+ TriggerAnnotation cached;
+ if (cache.annotations.TryGetValue(expr, out cached)) {
+ return cached;
+ }
+
+ expr.SubExpressions.Iter(e => Annotate(e));
+
+ TriggerAnnotation annotation; // TODO: Using ApplySuffix fixes the unresolved members problem in GenericSort
+ if (expr is FunctionCallExpr ||
+ expr is SeqSelectExpr ||
+ expr is MultiSelectExpr ||
+ expr is MemberSelectExpr ||
+ expr is OldExpr ||
+ expr is ApplyExpr ||
+ expr is DisplayExpression ||
+ TranslateToFunctionCall(expr) ||
+ (expr is UnaryOpExpr && (((UnaryOpExpr)expr).Op == UnaryOpExpr.Opcode.Cardinality)) || // FIXME || ((UnaryOpExpr)expr).Op == UnaryOpExpr.Opcode.Fresh doesn't work, as fresh is a pretty tricky predicate when it's not about datatypes. See translator.cs:10944
+ (expr is BinaryExpr && (((BinaryExpr)expr).Op == BinaryExpr.Opcode.NotIn || ((BinaryExpr)expr).Op == BinaryExpr.Opcode.In))) {
+ annotation = AnnotatePotentialCandidate(expr);
+ } else if (expr is QuantifierExpr) {
+ annotation = AnnotateQuantifier((QuantifierExpr)expr);
+ } else if (expr is LetExpr) {
+ annotation = AnnotateLetExpr((LetExpr)expr);
+ } else if (expr is IdentifierExpr) {
+ annotation = AnnotateIdentifier((IdentifierExpr)expr);
+ } else if (expr is ApplySuffix) {
+ annotation = AnnotateApplySuffix((ApplySuffix)expr);
+ } else if (expr is ComprehensionExpr) {
+ annotation = AnnotateComprehensionExpr((ComprehensionExpr)expr);
+ } else if (expr is ConcreteSyntaxExpression ||
+ expr is LiteralExpr ||
+ expr is OldExpr ||
+ expr is ThisExpr ||
+ expr is BoxingCastExpr ||
+ expr is DatatypeValue) {
+ annotation = AnnotateOther(expr, false);
+ } else {
+ annotation = AnnotateOther(expr, true);
+ }
+
+ TriggerUtils.DebugTriggers("{0} ({1})\n{2}", Printer.ExprToString(expr), expr.GetType(), annotation);
+ cache.annotations[expr] = annotation;
+ return annotation;
+ }
+
+ // math operations can be turned into a Boogie-level function as in the
+ // case with /noNLarith.
+ public bool TranslateToFunctionCall(Expression expr) {
+ if (!(expr is BinaryExpr)) {
+ return false;
+ }
+ BinaryExpr e = (BinaryExpr) expr;
+ bool isReal = e.E0.Type.IsNumericBased(Type.NumericPersuation.Real);
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.Lt:
+ case BinaryExpr.ResolvedOpcode.Le:
+ case BinaryExpr.ResolvedOpcode.Ge:
+ case BinaryExpr.ResolvedOpcode.Gt:
+ case BinaryExpr.ResolvedOpcode.Add:
+ case BinaryExpr.ResolvedOpcode.Sub:
+ case BinaryExpr.ResolvedOpcode.Mul:
+ case BinaryExpr.ResolvedOpcode.Div:
+ case BinaryExpr.ResolvedOpcode.Mod:
+ if (!isReal && DafnyOptions.O.DisableNLarith) {
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+ private TriggerAnnotation AnnotatePotentialCandidate(Expression expr) {
+ bool expr_is_killer = false;
+ var new_expr = TriggerUtils.MaybeWrapInOld(TriggerUtils.PrepareExprForInclusionInTrigger(expr, out expr_is_killer), cache.exprsInOldContext.Contains(expr));
+ var new_term = new TriggerTerm { Expr = new_expr, OriginalExpr = expr, Variables = CollectVariables(expr) };
+
+ List<TriggerTerm> collected_terms = CollectExportedCandidates(expr);
+ var children_contain_killers = CollectIsKiller(expr);
+
+ if (!children_contain_killers) {
+ // Add only if the children are not killers; the head has been cleaned up into non-killer form
+ collected_terms.Add(new_term);
+ }
+
+ // This new node is a killer if its children were killers, or if it's non-cleaned-up head is a killer
+ return new TriggerAnnotation(children_contain_killers || expr_is_killer, new_term.Variables, collected_terms);
+ }
+
+ private TriggerAnnotation AnnotateApplySuffix(ApplySuffix expr) {
+ // This is a bit tricky. A funcall node is generally meaningful as a trigger candidate,
+ // but when it's part of an ApplySuffix the function call itself may not resolve properly
+ // when the second round of resolving is done after modules are duplicated.
+ // Thus first we annotate expr and create a trigger candidate, and then we remove the
+ // candidate matching its direct subexpression if needed. Note that function calls are not the
+ // only possible child here; there can be DatatypeValue nodes, for example (see vstte2012/Combinators.dfy).
+ var annotation = AnnotatePotentialCandidate(expr);
+ // Comparing by reference is fine here. Using sets could yield a small speedup
+ annotation.ExportedTerms.RemoveAll(term => expr.SubExpressions.Contains(term.Expr));
+ return annotation;
+ }
+
+ private TriggerAnnotation AnnotateQuantifierOrLetExpr(Expression expr, IEnumerable<BoundVar> boundVars) {
+ var terms = CollectExportedCandidates(expr);
+ return new TriggerAnnotation(true, CollectVariables(expr), terms, OnlyPrivateCandidates(terms, boundVars));
+ }
+
+ private TriggerAnnotation AnnotateQuantifier(QuantifierExpr expr) {
+ return AnnotateQuantifierOrLetExpr(expr, expr.BoundVars);
+ }
+
+ private TriggerAnnotation AnnotateLetExpr(LetExpr expr) {
+ return AnnotateQuantifierOrLetExpr(expr, expr.BoundVars);
+ }
+
+ private TriggerAnnotation AnnotateIdentifier(IdentifierExpr expr) {
+ return new TriggerAnnotation(false, Enumerable.Repeat(expr.Var, 1), null);
+ }
+
+ private TriggerAnnotation AnnotateComprehensionExpr(ComprehensionExpr expr) {
+ var terms = CollectExportedCandidates(expr);
+ return new TriggerAnnotation(true, CollectVariables(expr), terms, OnlyPrivateCandidates(terms, expr.BoundVars));
+ }
+
+ private TriggerAnnotation AnnotateOther(Expression expr, bool isTriggerKiller) {
+ return new TriggerAnnotation(isTriggerKiller || CollectIsKiller(expr), CollectVariables(expr), CollectExportedCandidates(expr));
+ }
+
+ /// <summary>
+ /// Collect terms in the body of the subexpressions of the argument that look like quantifiers. The results of this function can contain duplicate terms.
+ /// </summary>
+ internal List<TriggerTerm> CollectTriggers(QuantifierExpr quantifier) {
+ Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier
+ // NOTE: We could check for existing trigger attributes and return that instead
+ return Annotate(quantifier).PrivateTerms;
+ }
+
+ internal bool IsTriggerKiller(Expression expr) {
+ return Annotate(expr).IsTriggerKiller;
+ }
+ }
+}
diff --git a/Source/Dafny/Util.cs b/Source/Dafny/Util.cs
index f9421659..eaf599e3 100644
--- a/Source/Dafny/Util.cs
+++ b/Source/Dafny/Util.cs
@@ -8,7 +8,7 @@ using Microsoft.Boogie;
namespace Microsoft.Dafny {
- class Util
+ public class Util
{
public static string Comma<T>(IEnumerable<T> l, Func<T, string> f) {
return Comma(",", l, f);
@@ -72,13 +72,7 @@ namespace Microsoft.Dafny {
/// </summary>
public static string RemoveUnderscores(string s) {
Contract.Requires(s != null);
- while (true) {
- var j = s.IndexOf('_');
- if (j == -1) {
- return s;
- }
- s = s.Substring(0, j) + s.Substring(j + 1);
- }
+ return s.Replace("_", "");
}
/// <summary>
@@ -175,5 +169,152 @@ namespace Microsoft.Dafny {
}
}
+ /// <summary>
+ /// Class dedicated to traversing the function call graph
+ /// </summary>
+ class FunctionCallFinder : TopDownVisitor<List<Function>> {
+ protected override bool VisitOneExpr(Expression expr, ref List<Function> calls) {
+ if (expr is FunctionCallExpr) {
+ calls.Add(((FunctionCallExpr)expr).Function);
+ }
+ return true;
+ }
+ }
+
+ static Graph<Function> BuildFunctionCallGraph(Dafny.Program program) {
+ Graph<Function> functionCallGraph = new Graph<Function>();
+ FunctionCallFinder callFinder = new FunctionCallFinder();
+
+ foreach (var module in program.Modules) {
+ foreach (var decl in module.TopLevelDecls) {
+ if (decl is ClassDecl) {
+ var c = (ClassDecl)decl;
+ foreach (var member in c.Members) {
+ if (member is Function) {
+ var f = (Function)member;
+
+ List<Function> calls = new List<Function>();
+ foreach (var e in f.Reads) { if (e != null && e.E != null) { callFinder.Visit(e.E, calls); } }
+ foreach (var e in f.Req) { if (e != null) { callFinder.Visit(e, calls); } }
+ foreach (var e in f.Ens) { if (e != null) { callFinder.Visit(e, calls); } }
+ if (f.Body != null) {
+ callFinder.Visit(f.Body, calls);
+ }
+
+ foreach (var callee in calls) {
+ functionCallGraph.AddEdge(f, callee);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return functionCallGraph;
+ }
+
+ /// <summary>
+ /// Prints the program's function call graph in a format suitable for consumption in other tools
+ /// </summary>
+ public static void PrintFunctionCallGraph(Dafny.Program program) {
+ var functionCallGraph = BuildFunctionCallGraph(program);
+
+ foreach (var vertex in functionCallGraph.GetVertices()) {
+ var func = vertex.N;
+ Console.Write("{0},{1}=", func.CompileName, func.EnclosingClass.Module.CompileName);
+ foreach (var callee in vertex.Successors) {
+ Console.Write("{0} ", callee.N.CompileName);
+ }
+ Console.Write("\n");
+ }
+ }
+
+ /// <summary>
+ /// Generic statistic counter
+ /// </summary>
+ static void IncrementStat(IDictionary<string, ulong> stats, string stat) {
+ ulong currentValue;
+ if (stats.TryGetValue(stat, out currentValue)) {
+ stats[stat] += 1;
+ } else {
+ stats.Add(stat, 1);
+ }
+ }
+
+ /// <summary>
+ /// Track the maximum value of some statistic
+ /// </summary>
+ static void UpdateMax(IDictionary<string, ulong> stats, string stat, ulong val) {
+ ulong currentValue;
+ if (stats.TryGetValue(stat, out currentValue)) {
+ if (val > currentValue) {
+ stats[stat] = val;
+ }
+ } else {
+ stats.Add(stat, val);
+ }
+ }
+
+ /// <summary>
+ /// Compute various interesting statistics about the Dafny program
+ /// </summary>
+ public static void PrintStats(Dafny.Program program) {
+ SortedDictionary<string, ulong> stats = new SortedDictionary<string, ulong>();
+
+ foreach (var module in program.Modules) {
+ IncrementStat(stats, "Modules");
+ UpdateMax(stats, "Module height (max)", (ulong)module.Height);
+
+ ulong num_scc = (ulong)module.CallGraph.TopologicallySortedComponents().Count;
+ UpdateMax(stats, "Call graph width (max)", num_scc);
+
+ foreach (var decl in module.TopLevelDecls) {
+ if (decl is DatatypeDecl) {
+ IncrementStat(stats, "Datatypes");
+ } else if (decl is ClassDecl) {
+ var c = (ClassDecl)decl;
+ if (c.Name != "_default") {
+ IncrementStat(stats, "Classes");
+ }
+
+ foreach (var member in c.Members) {
+ if (member is Function) {
+ IncrementStat(stats, "Functions (total)");
+ var f = (Function)member;
+ if (f.IsRecursive) {
+ IncrementStat(stats, "Functions recursive");
+ }
+ } else if (member is Method) {
+ IncrementStat(stats, "Methods (total)");
+ var method = (Method)member;
+ if (method.IsRecursive) {
+ IncrementStat(stats, "Methods recursive");
+ }
+ if (method.IsGhost) {
+ IncrementStat(stats, "Methods ghost");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Print out the results, with some nice formatting
+ Console.WriteLine("");
+ Console.WriteLine("Statistics");
+ Console.WriteLine("----------");
+
+ int max_key_length = 0;
+ foreach (var key in stats.Keys) {
+ if (key.Length > max_key_length) {
+ max_key_length = key.Length;
+ }
+ }
+
+ foreach (var keypair in stats) {
+ string keyString = keypair.Key.PadRight(max_key_length + 2);
+ Console.WriteLine("{0} {1,4}", keyString, keypair.Value);
+ }
+ }
}
}
diff --git a/Source/DafnyDriver/DafnyDriver.cs b/Source/DafnyDriver/DafnyDriver.cs
index 854269c1..464aff79 100644
--- a/Source/DafnyDriver/DafnyDriver.cs
+++ b/Source/DafnyDriver/DafnyDriver.cs
@@ -13,14 +13,16 @@ namespace Microsoft.Dafny
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
+ using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.IO;
+ using System.Reflection;
+
using Microsoft.Boogie;
using Bpl = Microsoft.Boogie;
public class DafnyDriver
{
-
enum ExitValue { VERIFIED = 0, PREPROCESSING_ERROR, DAFNY_ERROR, NOT_VERIFIED }
@@ -39,11 +41,11 @@ namespace Microsoft.Dafny
public static int ThreadMain(string[] args)
{
Contract.Requires(cce.NonNullElements(args));
-
- printer = new DafnyConsolePrinter();
- ExecutionEngine.printer = printer;
- DafnyOptions.Install(new DafnyOptions());
+ ErrorReporter reporter = new ConsoleErrorReporter();
+ ExecutionEngine.printer = new DafnyConsolePrinter(); // For boogie errors
+
+ DafnyOptions.Install(new DafnyOptions(reporter));
ExitValue exitValue = ExitValue.VERIFIED;
CommandLineOptions.Clo.RunningBoogieFromCommandLine = true;
@@ -55,14 +57,14 @@ namespace Microsoft.Dafny
if (CommandLineOptions.Clo.Files.Count == 0)
{
- printer.ErrorWriteLine(Console.Out, "*** Error: No input files were specified.");
+ ExecutionEngine.printer.ErrorWriteLine(Console.Out, "*** Error: No input files were specified.");
exitValue = ExitValue.PREPROCESSING_ERROR;
goto END;
}
if (CommandLineOptions.Clo.XmlSink != null) {
string errMsg = CommandLineOptions.Clo.XmlSink.Open();
if (errMsg != null) {
- printer.ErrorWriteLine(Console.Out, "*** Error: " + errMsg);
+ ExecutionEngine.printer.ErrorWriteLine(Console.Out, "*** Error: " + errMsg);
exitValue = ExitValue.PREPROCESSING_ERROR;
goto END;
}
@@ -81,19 +83,27 @@ namespace Microsoft.Dafny
Console.WriteLine("--------------------");
}
+ var dafnyFiles = new List<string>();
+ var otherFiles = new List<string>();
+
foreach (string file in CommandLineOptions.Clo.Files)
- {Contract.Assert(file != null);
+ { Contract.Assert(file != null);
string extension = Path.GetExtension(file);
if (extension != null) { extension = extension.ToLower(); }
- if (extension != ".dfy")
- {
- printer.ErrorWriteLine(Console.Out, "*** Error: '{0}': Filename extension '{1}' is not supported. Input files must be Dafny programs (.dfy).", file,
- extension == null ? "" : extension);
- exitValue = ExitValue.PREPROCESSING_ERROR;
- goto END;
+ if (extension == ".dfy") {
+ dafnyFiles.Add(file);
+ }
+ else if ((extension == ".cs") || (extension == ".dll")) {
+ otherFiles.Add(file);
+ }
+ else {
+ ExecutionEngine.printer.ErrorWriteLine(Console.Out, "*** Error: '{0}': Filename extension '{1}' is not supported. Input files must be Dafny programs (.dfy) or C# files (.cs) or managed DLLS (.dll)", file,
+ extension == null ? "" : extension);
+ exitValue = ExitValue.PREPROCESSING_ERROR;
+ goto END;
}
}
- exitValue = ProcessFiles(CommandLineOptions.Clo.Files);
+ exitValue = ProcessFiles(dafnyFiles, otherFiles.AsReadOnly(), reporter);
END:
if (CommandLineOptions.Clo.XmlSink != null) {
@@ -104,9 +114,8 @@ namespace Microsoft.Dafny
Console.WriteLine("Press Enter to exit.");
Console.ReadLine();
}
- if (CommandLineOptions.Clo.UseBaseNameForFileName && exitValue != ExitValue.PREPROCESSING_ERROR)
+ if (!DafnyOptions.O.CountVerificationErrors && exitValue != ExitValue.PREPROCESSING_ERROR)
{
- // TODO(wuestholz): We should probably add a separate flag for this. This is currently only used by the new testing infrastructure.
return 0;
}
//Console.ReadKey();
@@ -114,9 +123,10 @@ namespace Microsoft.Dafny
}
- static ExitValue ProcessFiles(IList<string/*!*/>/*!*/ fileNames, bool lookForSnapshots = true, string programId = null)
- {
- Contract.Requires(cce.NonNullElements(fileNames));
+ static ExitValue ProcessFiles(IList<string/*!*/>/*!*/ dafnyFileNames, ReadOnlyCollection<string> otherFileNames,
+ ErrorReporter reporter, bool lookForSnapshots = true, string programId = null)
+ {
+ Contract.Requires(cce.NonNullElements(dafnyFileNames));
if (programId == null)
{
@@ -124,13 +134,18 @@ namespace Microsoft.Dafny
}
ExitValue exitValue = ExitValue.VERIFIED;
- if (CommandLineOptions.Clo.VerifySeparately && 1 < fileNames.Count)
+ if (CommandLineOptions.Clo.VerifySeparately && 1 < dafnyFileNames.Count)
{
- foreach (var f in fileNames)
+ foreach (var f in dafnyFileNames)
{
+ string extension = Path.GetExtension(f);
+ if (extension != null) { extension = extension.ToLower(); }
+ if (extension != ".dfy"){
+ continue;
+ }
Console.WriteLine();
Console.WriteLine("-------------------- {0} --------------------", f);
- var ev = ProcessFiles(new List<string> { f }, lookForSnapshots, f);
+ var ev = ProcessFiles(new List<string> { f }, new List<string>().AsReadOnly(), reporter, lookForSnapshots, f);
if (exitValue != ev && ev != ExitValue.VERIFIED)
{
exitValue = ev;
@@ -141,10 +156,10 @@ namespace Microsoft.Dafny
if (0 <= CommandLineOptions.Clo.VerifySnapshots && lookForSnapshots)
{
- var snapshotsByVersion = ExecutionEngine.LookForSnapshots(fileNames);
+ var snapshotsByVersion = ExecutionEngine.LookForSnapshots(dafnyFileNames);
foreach (var s in snapshotsByVersion)
{
- var ev = ProcessFiles(new List<string>(s), false, programId);
+ var ev = ProcessFiles(new List<string>(s), new List<string>().AsReadOnly(), reporter, false, programId);
if (exitValue != ev && ev != ExitValue.VERIFIED)
{
exitValue = ev;
@@ -153,16 +168,16 @@ namespace Microsoft.Dafny
return exitValue;
}
- using (XmlFileScope xf = new XmlFileScope(CommandLineOptions.Clo.XmlSink, fileNames[fileNames.Count-1])) {
+ using (XmlFileScope xf = new XmlFileScope(CommandLineOptions.Clo.XmlSink, dafnyFileNames[dafnyFileNames.Count-1])) {
Dafny.Program dafnyProgram;
- string programName = fileNames.Count == 1 ? fileNames[0] : "the program";
- string err = Dafny.Main.ParseCheck(fileNames, programName, out dafnyProgram);
+ string programName = dafnyFileNames.Count == 1 ? dafnyFileNames[0] : "the program";
+ string err = Dafny.Main.ParseCheck(dafnyFileNames, programName, reporter, out dafnyProgram);
if (err != null) {
exitValue = ExitValue.DAFNY_ERROR;
- printer.ErrorWriteLine(Console.Out, err);
+ ExecutionEngine.printer.ErrorWriteLine(Console.Out, err);
} else if (dafnyProgram != null && !CommandLineOptions.Clo.NoResolve && !CommandLineOptions.Clo.NoTypecheck
&& DafnyOptions.O.DafnyVerify) {
- Dafny.Translator translator = new Dafny.Translator();
+ Dafny.Translator translator = new Dafny.Translator(dafnyProgram.reporter);
Bpl.Program boogieProgram = translator.Translate(dafnyProgram);
if (CommandLineOptions.Clo.PrintFile != null)
{
@@ -173,7 +188,7 @@ namespace Microsoft.Dafny
if (CommandLineOptions.Clo.PrintFile != null) {
bplFilename = CommandLineOptions.Clo.PrintFile;
} else {
- string baseName = cce.NonNull(Path.GetFileName(fileNames[fileNames.Count-1]));
+ string baseName = cce.NonNull(Path.GetFileName(dafnyFileNames[dafnyFileNames.Count-1]));
baseName = cce.NonNull(Path.ChangeExtension(baseName, "bpl"));
bplFilename = Path.Combine(Path.GetTempPath(), baseName);
}
@@ -183,14 +198,14 @@ namespace Microsoft.Dafny
var allOk = stats.ErrorCount == 0 && stats.InconclusiveCount == 0 && stats.TimeoutCount == 0 && stats.OutOfMemoryCount == 0;
switch (oc) {
case PipelineOutcome.VerificationCompleted:
- printer.WriteTrailer(stats);
+ ExecutionEngine.printer.WriteTrailer(stats);
if ((DafnyOptions.O.Compile && allOk && CommandLineOptions.Clo.ProcsToCheck == null) || DafnyOptions.O.ForceCompile)
- CompileDafnyProgram(dafnyProgram, fileNames[0]);
+ CompileDafnyProgram(dafnyProgram, dafnyFileNames[0], otherFileNames);
break;
case PipelineOutcome.Done:
- printer.WriteTrailer(stats);
+ ExecutionEngine.printer.WriteTrailer(stats);
if (DafnyOptions.O.ForceCompile)
- CompileDafnyProgram(dafnyProgram, fileNames[0]);
+ CompileDafnyProgram(dafnyProgram, dafnyFileNames[0], otherFileNames);
break;
default:
// error has already been reported to user
@@ -198,6 +213,13 @@ namespace Microsoft.Dafny
}
exitValue = allOk ? ExitValue.VERIFIED : ExitValue.NOT_VERIFIED;
}
+
+ if (err == null && dafnyProgram != null && DafnyOptions.O.PrintStats) {
+ Util.PrintStats(dafnyProgram);
+ }
+ if (err == null && dafnyProgram != null && DafnyOptions.O.PrintFunctionCallGraph) {
+ Util.PrintFunctionCallGraph(dafnyProgram);
+ }
}
return exitValue;
}
@@ -220,8 +242,8 @@ namespace Microsoft.Dafny
stats = new PipelineStatistics();
LinearTypeChecker ltc;
- MoverTypeChecker mtc;
- PipelineOutcome oc = ExecutionEngine.ResolveAndTypecheck(program, bplFileName, out ltc, out mtc);
+ CivlTypeChecker ctc;
+ PipelineOutcome oc = ExecutionEngine.ResolveAndTypecheck(program, bplFileName, out ltc, out ctc);
switch (oc) {
case PipelineOutcome.Done:
return oc;
@@ -238,7 +260,7 @@ namespace Microsoft.Dafny
fileNames.Add(bplFileName);
Bpl.Program reparsedProgram = ExecutionEngine.ParseBoogieProgram(fileNames, true);
if (reparsedProgram != null) {
- ExecutionEngine.ResolveAndTypecheck(reparsedProgram, bplFileName, out ltc, out mtc);
+ ExecutionEngine.ResolveAndTypecheck(reparsedProgram, bplFileName, out ltc, out ctc);
}
}
return oc;
@@ -257,15 +279,18 @@ namespace Microsoft.Dafny
#region Output
-
- static OutputPrinter printer;
-
-
+
class DafnyConsolePrinter : ConsolePrinter
{
public override void ReportBplError(IToken tok, string message, bool error, TextWriter tw, string category = null)
{
- base.ReportBplError(tok, message, error, tw, category);
+ // Dafny has 0-indexed columns, but Boogie counts from 1
+ var realigned_tok = new Token(tok.line, tok.col - 1);
+ realigned_tok.kind = tok.kind;
+ realigned_tok.pos = tok.pos;
+ realigned_tok.val = tok.val;
+ realigned_tok.filename = tok.filename;
+ base.ReportBplError(realigned_tok, message, error, tw, category);
if (tok is Dafny.NestedToken)
{
@@ -280,7 +305,24 @@ namespace Microsoft.Dafny
#region Compilation
- public static void CompileDafnyProgram(Dafny.Program dafnyProgram, string dafnyProgramName, TextWriter outputWriter = null)
+ static string WriteDafnyProgramToFile(string dafnyProgramName, string csharpProgram, bool completeProgram, TextWriter outputWriter)
+ {
+ string targetFilename = Path.ChangeExtension(dafnyProgramName, "cs");
+ using (TextWriter target = new StreamWriter(new FileStream(targetFilename, System.IO.FileMode.Create))) {
+ target.Write(csharpProgram);
+ string relativeTarget = Path.GetFileName(targetFilename);
+ if (completeProgram) {
+ outputWriter.WriteLine("Compiled program written to {0}", relativeTarget);
+ }
+ else {
+ outputWriter.WriteLine("File {0} contains the partially compiled program", relativeTarget);
+ }
+ }
+ return targetFilename;
+ }
+
+ public static void CompileDafnyProgram(Dafny.Program dafnyProgram, string dafnyProgramName,
+ ReadOnlyCollection<string> otherFileNames, TextWriter outputWriter = null)
{
Contract.Requires(dafnyProgram != null);
@@ -291,33 +333,22 @@ namespace Microsoft.Dafny
// Compile the Dafny program into a string that contains the C# program
StringWriter sw = new StringWriter();
- Dafny.Compiler compiler = new Dafny.Compiler(sw);
+ Dafny.Compiler compiler = new Dafny.Compiler();
compiler.ErrorWriter = outputWriter;
var hasMain = compiler.HasMain(dafnyProgram);
if (DafnyOptions.O.RunAfterCompile && !hasMain) {
// do no more
return;
}
- compiler.Compile(dafnyProgram);
+ compiler.Compile(dafnyProgram, sw);
var csharpProgram = sw.ToString();
bool completeProgram = compiler.ErrorCount == 0;
- // blurt out the code to a file
- if (DafnyOptions.O.SpillTargetCode)
+ // blurt out the code to a file, if requested, or if other files were specified for the C# command line.
+ string targetFilename = null;
+ if (DafnyOptions.O.SpillTargetCode || (otherFileNames.Count > 0))
{
- string targetFilename = Path.ChangeExtension(dafnyProgramName, "cs");
- using (TextWriter target = new StreamWriter(new FileStream(targetFilename, System.IO.FileMode.Create)))
- {
- target.Write(csharpProgram);
- if (completeProgram)
- {
- outputWriter.WriteLine("Compiled program written to {0}", targetFilename);
- }
- else
- {
- outputWriter.WriteLine("File {0} contains the partially compiled program", targetFilename);
- }
- }
+ targetFilename = WriteDafnyProgramToFile(dafnyProgramName, csharpProgram, completeProgram, outputWriter);
}
// compile the program into an assembly
@@ -331,7 +362,7 @@ namespace Microsoft.Dafny
}
else
{
- var provider = CodeDomProvider.CreateProvider("CSharp");
+ var provider = CodeDomProvider.CreateProvider("CSharp", new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });
var cp = new System.CodeDom.Compiler.CompilerParameters();
cp.GenerateExecutable = hasMain;
if (DafnyOptions.O.RunAfterCompile) {
@@ -345,8 +376,50 @@ namespace Microsoft.Dafny
}
cp.CompilerOptions = "/debug /nowarn:0164 /nowarn:0219"; // warning CS0164 complains about unreferenced labels, CS0219 is about unused variables
cp.ReferencedAssemblies.Add("System.Numerics.dll");
+ cp.ReferencedAssemblies.Add("System.Core.dll");
+ cp.ReferencedAssemblies.Add("System.dll");
+
+ var libPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar;
+ var immutableDllFileName = "System.Collections.Immutable.dll";
+ var immutableDllPath = libPath + immutableDllFileName;
- var cr = provider.CompileAssemblyFromSource(cp, csharpProgram);
+ if (DafnyOptions.O.Optimize) {
+ cp.CompilerOptions += " /optimize /define:DAFNY_USE_SYSTEM_COLLECTIONS_IMMUTABLE";
+ cp.ReferencedAssemblies.Add(immutableDllPath);
+ cp.ReferencedAssemblies.Add("System.Runtime.dll");
+ }
+
+ int numOtherSourceFiles = 0;
+ if (otherFileNames.Count > 0) {
+ foreach (var file in otherFileNames) {
+ string extension = Path.GetExtension(file);
+ if (extension != null) { extension = extension.ToLower(); }
+ if (extension == ".cs") {
+ numOtherSourceFiles++;
+ }
+ else if (extension == ".dll") {
+ cp.ReferencedAssemblies.Add(file);
+ }
+ }
+ }
+
+ CompilerResults cr;
+ if (numOtherSourceFiles > 0) {
+ string[] sourceFiles = new string[numOtherSourceFiles + 1];
+ sourceFiles[0] = targetFilename;
+ int index = 1;
+ foreach (var file in otherFileNames) {
+ string extension = Path.GetExtension(file);
+ if (extension != null) { extension = extension.ToLower(); }
+ if (extension == ".cs") {
+ sourceFiles[index++] = file;
+ }
+ }
+ cr = provider.CompileAssemblyFromFile(cp, sourceFiles);
+ }
+ else {
+ cr = provider.CompileAssemblyFromSource(cp, csharpProgram);
+ }
var assemblyName = Path.GetFileName(cr.PathToAssembly);
if (DafnyOptions.O.RunAfterCompile && cr.Errors.Count == 0) {
outputWriter.WriteLine("Program compiled successfully");
@@ -365,6 +438,15 @@ namespace Microsoft.Dafny
}
} else if (cr.Errors.Count == 0) {
outputWriter.WriteLine("Compiled assembly into {0}", assemblyName);
+ if (DafnyOptions.O.Optimize) {
+ var outputDir = Path.GetDirectoryName(dafnyProgramName);
+ if (string.IsNullOrWhiteSpace(outputDir)) {
+ outputDir = ".";
+ }
+ var destPath = outputDir + Path.DirectorySeparatorChar + immutableDllFileName;
+ File.Copy(immutableDllPath, destPath, true);
+ outputWriter.WriteLine("Copied /optimize dependency {0} to {1}", immutableDllFileName, outputDir);
+ }
} else {
outputWriter.WriteLine("Errors compiling program into {0}", assemblyName);
foreach (var ce in cr.Errors) {
diff --git a/Source/DafnyExtension/BufferIdleEventUtil.cs b/Source/DafnyExtension/BufferIdleEventUtil.cs
index 5ab9df09..8a1ad0ed 100644
--- a/Source/DafnyExtension/BufferIdleEventUtil.cs
+++ b/Source/DafnyExtension/BufferIdleEventUtil.cs
@@ -120,7 +120,7 @@ namespace DafnyLanguage
{
timer = new DispatcherTimer(DispatcherPriority.ApplicationIdle)
{
- Interval = TimeSpan.FromMilliseconds(50)
+ Interval = TimeSpan.FromMilliseconds(500)
};
timer.Tick += (s, e) =>
diff --git a/Source/DafnyExtension/DafnyDriver.cs b/Source/DafnyExtension/DafnyDriver.cs
index 5b8cc943..13b53a1b 100644
--- a/Source/DafnyExtension/DafnyDriver.cs
+++ b/Source/DafnyExtension/DafnyDriver.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
@@ -18,13 +19,16 @@ namespace DafnyLanguage
{
readonly string _filename;
readonly ITextSnapshot _snapshot;
+ readonly ITextBuffer _buffer;
Dafny.Program _program;
+ static object bufferDafnyKey = new object();
List<DafnyError> _errors = new List<DafnyError>();
public List<DafnyError> Errors { get { return _errors; } }
- public DafnyDriver(ITextSnapshot snapshot, string filename) {
- _snapshot = snapshot;
+ public DafnyDriver(ITextBuffer buffer, string filename) {
+ _buffer = buffer;
+ _snapshot = buffer.CurrentSnapshot;
_filename = filename;
}
@@ -37,9 +41,11 @@ namespace DafnyLanguage
if (Dafny.DafnyOptions.O == null) {
var options = new Dafny.DafnyOptions();
options.ProverKillTime = 10;
+ options.AutoTriggers = true;
options.ErrorTrace = 0;
options.VcsCores = Math.Max(1, System.Environment.ProcessorCount - 1);
options.ModelViewFile = "-";
+ options.UnicodeOutput = true;
Dafny.DafnyOptions.Install(options);
// Read additional options from DafnyOptions.txt
@@ -105,82 +111,84 @@ namespace DafnyLanguage
#region Parsing and type checking
- internal Dafny.Program ProcessResolution() {
- if (!ParseAndTypeCheck()) {
+ internal Dafny.Program ProcessResolution(bool runResolver) {
+ if (!ParseAndTypeCheck(runResolver)) {
return null;
}
return _program;
}
- bool ParseAndTypeCheck() {
- Dafny.ModuleDecl module = new Dafny.LiteralModuleDecl(new Dafny.DefaultModuleDecl(), null);
- Dafny.BuiltIns builtIns = new Dafny.BuiltIns();
- Dafny.Errors parseErrors = new VSErrors(this);
- int errorCount = Dafny.Parser.Parse(_snapshot.GetText(), _filename, module, builtIns, parseErrors);
- string errString = Dafny.Main.ParseIncludes(module, builtIns, new List<string>(), parseErrors);
- if (errorCount != 0 || errString != null)
+ bool ParseAndTypeCheck(bool runResolver) {
+ Tuple<ITextSnapshot, Dafny.Program, List<DafnyError>> parseResult;
+ Dafny.Program program;
+ var errorReporter = new VSErrorReporter(this);
+ if (_buffer.Properties.TryGetProperty(bufferDafnyKey, out parseResult) &&
+ (parseResult.Item1 == _snapshot)) {
+ // already parsed;
+ program = parseResult.Item2;
+ _errors = parseResult.Item3;
+ if (program == null)
+ runResolver = false;
+ } else {
+ Dafny.ModuleDecl module = new Dafny.LiteralModuleDecl(new Dafny.DefaultModuleDecl(), null);
+ Dafny.BuiltIns builtIns = new Dafny.BuiltIns();
+ var parseErrors = new Dafny.Errors(errorReporter);
+ int errorCount = Dafny.Parser.Parse(_snapshot.GetText(), _filename, _filename, module, builtIns, parseErrors);
+ string errString = Dafny.Main.ParseIncludes(module, builtIns, new List<string>(), parseErrors);
+
+ if (errorCount != 0 || errString != null) {
+ runResolver = false;
+ program = null;
+ } else {
+ program = new Dafny.Program(_filename, module, builtIns, errorReporter);
+ }
+ _buffer.Properties[bufferDafnyKey] = new Tuple<ITextSnapshot, Dafny.Program, List<DafnyError>>(_snapshot, program, _errors);
+ }
+ if (!runResolver) {
return false;
- Dafny.Program program = new Dafny.Program(_filename, module, builtIns);
+ }
- var r = new VSResolver(program, this);
+ var r = new Resolver(program);
r.ResolveProgram(program);
- if (r.ErrorCount != 0)
+ if (errorReporter.Count(ErrorLevel.Error) != 0)
return false;
- program.AdditionalInformation.AddRange(r.AdditionalInformation);
_program = program;
return true; // success
}
+
void RecordError(string filename, int line, int col, ErrorCategory cat, string msg, bool isRecycled = false)
{
- _errors.Add(new DafnyError(filename, line, col, cat, msg, _snapshot, isRecycled, null, System.IO.Path.GetFullPath(this._filename) == filename));
+ _errors.Add(new DafnyError(filename, line - 1, col - 1, cat, msg, _snapshot, isRecycled, null, System.IO.Path.GetFullPath(this._filename) == filename));
}
- class VSErrors : Dafny.Errors
+ class VSErrorReporter : Dafny.ErrorReporter
{
DafnyDriver dd;
- public VSErrors(DafnyDriver dd) {
- this.dd = dd;
- }
- public override void SynErr(string filename, int line, int col, string msg) {
- dd.RecordError(filename, line - 1, col - 1, ErrorCategory.ParseError, msg);
- count++;
- }
- public override void SemErr(string filename, int line, int col, string msg) {
- dd.RecordError(filename, line - 1, col - 1, ErrorCategory.ResolveError, msg);
- count++;
- }
- public override void Warning(string filename, int line, int col, string msg) {
- dd.RecordError(filename, line - 1, col - 1, ErrorCategory.ParseWarning, msg);
- }
- }
- class VSResolver : Dafny.Resolver
- {
- DafnyDriver dd;
- Dictionary<IToken, HashSet<AdditionalInformation>> _additionalInformation = new Dictionary<IToken, HashSet<AdditionalInformation>>();
- public List<AdditionalInformation> AdditionalInformation { get { return _additionalInformation.Values.SelectMany(i => i).ToList(); } }
-
- public VSResolver(Dafny.Program program, DafnyDriver dd)
- : base(program) {
+ public VSErrorReporter(DafnyDriver dd) {
this.dd = dd;
-
- AdditionalInformationReporter =
- (addinfo)
- =>
- {
- if (!_additionalInformation.ContainsKey(addinfo.Token)) {
- _additionalInformation.Add(addinfo.Token, new HashSet<AdditionalInformation>());
- }
- _additionalInformation[addinfo.Token].Add(addinfo);
- };
}
- public override void Error(Bpl.IToken tok, string msg, params object[] args) {
- string s = string.Format(msg, args);
- dd.RecordError(tok.filename, tok.line - 1, tok.col - 1, ErrorCategory.ResolveError, s);
- ErrorCount++;
+ // TODO: The error tracking could be made better to track the full information returned by Dafny
+ public override bool Message(MessageSource source, ErrorLevel level, IToken tok, string msg) {
+ if (base.Message(source, level, tok, msg)) {
+ switch (level) {
+ case ErrorLevel.Error:
+ dd.RecordError(tok.filename, tok.line, tok.col, source == MessageSource.Parser ? ErrorCategory.ParseError : ErrorCategory.ResolveError, msg);
+ break;
+ case ErrorLevel.Warning:
+ dd.RecordError(tok.filename, tok.line, tok.col, source == MessageSource.Parser ? ErrorCategory.ParseWarning : ErrorCategory.ResolveWarning, msg);
+ break;
+ case ErrorLevel.Info:
+ // The AllMessages variable already keeps track of this
+ break;
+ }
+ return true;
+ } else {
+ return false;
+ }
}
}
@@ -191,7 +199,10 @@ namespace DafnyLanguage
public static void Compile(Dafny.Program dafnyProgram, TextWriter outputWriter)
{
Microsoft.Dafny.DafnyOptions.O.SpillTargetCode = true;
- Microsoft.Dafny.DafnyDriver.CompileDafnyProgram(dafnyProgram, dafnyProgram.FullName, outputWriter);
+ // Currently there are no provisions for specifying other files to compile with from the
+ // VS interface, so just send an empty list.
+ ReadOnlyCollection<string> otherFileNames = new List<string>().AsReadOnly();
+ Microsoft.Dafny.DafnyDriver.CompileDafnyProgram(dafnyProgram, dafnyProgram.FullName, otherFileNames, outputWriter);
}
#endregion
@@ -239,6 +250,11 @@ namespace DafnyLanguage
return Dafny.DafnyOptions.Clo.VerifySnapshots;
}
+ public static void SetDiagnoseTimeouts(bool v)
+ {
+ Dafny.DafnyOptions.Clo.RunDiagnosticsOnTimeout = v;
+ }
+
public static int ChangeIncrementalVerification(int mode)
{
var old = Dafny.DafnyOptions.Clo.VerifySnapshots;
@@ -260,8 +276,15 @@ namespace DafnyLanguage
return Dafny.DafnyOptions.Clo.VerifySnapshots;
}
+ public static bool ChangeAutomaticInduction() {
+ var old = Dafny.DafnyOptions.O.Induction;
+ // toggle between modes 1 and 3
+ Dafny.DafnyOptions.O.Induction = old == 1 ? 3 : 1;
+ return Dafny.DafnyOptions.O.Induction == 3;
+ }
+
public static bool Verify(Dafny.Program dafnyProgram, ResolverTagger resolver, string uniqueIdPrefix, string requestId, ErrorReporterDelegate er) {
- Dafny.Translator translator = new Dafny.Translator();
+ Dafny.Translator translator = new Dafny.Translator(dafnyProgram.reporter);
translator.InsertChecksums = true;
translator.UniqueIdPrefix = uniqueIdPrefix;
Bpl.Program boogieProgram = translator.Translate(dafnyProgram);
diff --git a/Source/DafnyExtension/DafnyExtension.csproj b/Source/DafnyExtension/DafnyExtension.csproj
index 3dc67098..08040853 100644
--- a/Source/DafnyExtension/DafnyExtension.csproj
+++ b/Source/DafnyExtension/DafnyExtension.csproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
@@ -106,7 +106,9 @@
<Reference Include="Houdini">
<HintPath>..\..\..\boogie\Binaries\Houdini.dll</HintPath>
</Reference>
- <Reference Include="Microsoft.VisualStudio.Language.Intellisense, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Language.Intellisense, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Language.11.0.4\lib\net45\Microsoft.VisualStudio.Language.Intellisense.dll</HintPath>
+ </Reference>
<Reference Include="Model">
<HintPath>..\..\..\boogie\Binaries\Model.dll</HintPath>
</Reference>
@@ -124,23 +126,57 @@
</Reference>
</ItemGroup>
<ItemGroup>
- <Reference Include="Microsoft.VisualStudio.Shell.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <Reference Include="Microsoft.VisualStudio.Editor, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.ComponentModelHost, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.Shell.11.0, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.Shell.Immutable.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.OLE.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <Reference Include="Microsoft.VisualStudio.Shell.Interop.11.0, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <Reference Include="Microsoft.VisualStudio.Shell.Interop.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <Reference Include="Microsoft.VisualStudio.Shell.Interop.8.0, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <Reference Include="Microsoft.VisualStudio.Shell.Interop.9.0, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <Reference Include="Microsoft.VisualStudio.CoreUtility, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.TextManager.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <Reference Include="Microsoft.VisualStudio.Text.Data, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.Text.Logic, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.Text.UI, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.Text.UI.Wpf, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.Language.StandardClassification, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\packages\VSSDK.Shell.Interop.7.0.4\lib\net20\Microsoft.VisualStudio.Shell.Interop.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Editor, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Editor.11.0.4\lib\net45\Microsoft.VisualStudio.Editor.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.ComponentModelHost, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.ComponentModelHost.11.0.4\lib\net45\Microsoft.VisualStudio.ComponentModelHost.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.11.0, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Shell.11.11.0.4\lib\net45\Microsoft.VisualStudio.Shell.11.0.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Immutable.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Shell.Immutable.10.10.0.4\lib\net40\Microsoft.VisualStudio.Shell.Immutable.10.0.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.OLE.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\packages\VSSDK.OLE.Interop.7.0.4\lib\net20\Microsoft.VisualStudio.OLE.Interop.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.11.0, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Shell.Interop.11.11.0.4\lib\net20\Microsoft.VisualStudio.Shell.Interop.11.0.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Shell.Interop.10.10.0.4\lib\net20\Microsoft.VisualStudio.Shell.Interop.10.0.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.8.0, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\packages\VSSDK.Shell.Interop.8.8.0.4\lib\net20\Microsoft.VisualStudio.Shell.Interop.8.0.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.9.0, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\packages\VSSDK.Shell.Interop.9.9.0.4\lib\net20\Microsoft.VisualStudio.Shell.Interop.9.0.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.CoreUtility, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.CoreUtility.11.0.4\lib\net45\Microsoft.VisualStudio.CoreUtility.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.TextManager.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\packages\VSSDK.TextManager.Interop.7.0.4\lib\net20\Microsoft.VisualStudio.TextManager.Interop.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.Data, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Text.11.0.4\lib\net45\Microsoft.VisualStudio.Text.Data.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.Logic, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Text.11.0.4\lib\net45\Microsoft.VisualStudio.Text.Logic.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.UI, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Text.11.0.4\lib\net45\Microsoft.VisualStudio.Text.UI.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.UI.Wpf, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Text.11.0.4\lib\net45\Microsoft.VisualStudio.Text.UI.Wpf.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Language.StandardClassification, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Language.11.0.4\lib\net45\Microsoft.VisualStudio.Language.StandardClassification.dll</HintPath>
+ </Reference>
</ItemGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
@@ -200,6 +236,9 @@
</Content>
</ItemGroup>
<ItemGroup>
+ <None Include="packages.config">
+ <SubType>Designer</SubType>
+ </None>
<None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
</None>
@@ -250,4 +289,4 @@ copy /y "..\Binaries\z3.exe" "$(ProjectDir)"</PreBuildEvent>
<Target Name="AfterBuild">
</Target>
-->
-</Project> \ No newline at end of file
+</Project>
diff --git a/Source/DafnyExtension/IdentifierTagger.cs b/Source/DafnyExtension/IdentifierTagger.cs
index 3bb8f01c..998a2d7f 100644
--- a/Source/DafnyExtension/IdentifierTagger.cs
+++ b/Source/DafnyExtension/IdentifierTagger.cs
@@ -15,7 +15,6 @@ using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.Utilities;
using Bpl = Microsoft.Boogie;
-
namespace DafnyLanguage
{
@@ -136,9 +135,8 @@ namespace DafnyLanguage
List<IdRegion> newRegions = new List<IdRegion>();
- foreach (var addInfo in program.AdditionalInformation)
- {
- IdRegion.Add(newRegions, addInfo.Token, addInfo.Text, addInfo.Length);
+ foreach (var info in program.reporter.AllMessages[ErrorLevel.Info]) {
+ IdRegion.Add(newRegions, program, info.token, info.message, info.token.val.Length);
}
foreach (var module in program.Modules) {
@@ -151,73 +149,75 @@ namespace DafnyLanguage
foreach (var ctor in dt.Ctors) {
foreach (var dtor in ctor.Destructors) {
if (dtor.CorrespondingFormal.HasName) {
- IdRegion.Add(newRegions, dtor.tok, dtor, null, "destructor", true, module);
+ IdRegion.Add(newRegions, program, dtor.tok, dtor, null, "destructor", true, module);
}
}
}
} else if (d is IteratorDecl) {
var iter = (IteratorDecl)d;
foreach (var p in iter.Ins) {
- IdRegion.Add(newRegions, p.tok, p, true, module);
+ IdRegion.Add(newRegions, program, p.tok, p, true, module);
}
foreach (var p in iter.Outs) {
- IdRegion.Add(newRegions, p.tok, p, true, "yield-parameter", module);
+ IdRegion.Add(newRegions, program, p.tok, p, true, "yield-parameter", module);
}
- iter.Reads.Expressions.ForEach(fe => FrameExprRegions(fe, newRegions, true, module));
- iter.Modifies.Expressions.ForEach(fe => FrameExprRegions(fe, newRegions, true, module));
- iter.Requires.ForEach(e => ExprRegions(e.E, newRegions, module));
- iter.YieldRequires.ForEach(e => ExprRegions(e.E, newRegions, module));
- iter.YieldEnsures.ForEach(e => ExprRegions(e.E, newRegions, module));
- iter.Ensures.ForEach(e => ExprRegions(e.E, newRegions, module));
+ iter.Reads.Expressions.ForEach(fe => FrameExprRegions(fe, newRegions, true, program, module));
+ iter.Modifies.Expressions.ForEach(fe => FrameExprRegions(fe, newRegions, true, program, module));
+ iter.Requires.ForEach(e => ExprRegions(e.E, newRegions, program, module));
+ iter.YieldRequires.ForEach(e => ExprRegions(e.E, newRegions, program, module));
+ iter.YieldEnsures.ForEach(e => ExprRegions(e.E, newRegions, program, module));
+ iter.Ensures.ForEach(e => ExprRegions(e.E, newRegions, program, module));
if (!((ICallable)iter).InferredDecreases) {
- iter.Decreases.Expressions.ForEach(e => ExprRegions(e, newRegions, module));
+ iter.Decreases.Expressions.ForEach(e => ExprRegions(e, newRegions, program, module));
}
if (iter.Body != null) {
- StatementRegions(iter.Body, newRegions, module);
+ StatementRegions(iter.Body, newRegions, program, module);
}
} else if (d is ClassDecl) {
var cl = (ClassDecl)d;
foreach (var member in cl.Members) {
- if (member is Function) {
+ if (Attributes.Contains(member.Attributes, "auto_generated")) {
+ // do nothing
+ } else if (member is Function) {
var f = (Function)member;
foreach (var p in f.Formals) {
- IdRegion.Add(newRegions, p.tok, p, true, module);
+ IdRegion.Add(newRegions, program, p.tok, p, true, module);
}
- f.Req.ForEach(e => ExprRegions(e, newRegions, module));
- f.Reads.ForEach(fe => FrameExprRegions(fe, newRegions, true, module));
- f.Ens.ForEach(e => ExprRegions(e, newRegions, module));
- f.Decreases.Expressions.ForEach(e => ExprRegions(e, newRegions, module));
+ f.Req.ForEach(e => ExprRegions(e, newRegions, program, module));
+ f.Reads.ForEach(fe => FrameExprRegions(fe, newRegions, true, program, module));
+ f.Ens.ForEach(e => ExprRegions(e, newRegions, program, module));
+ f.Decreases.Expressions.ForEach(e => ExprRegions(e, newRegions, program, module));
if (f.Body != null) {
- ExprRegions(f.Body, newRegions, module);
+ ExprRegions(f.Body, newRegions, program, module);
}
} else if (member is Method) {
var m = (Method)member;
foreach (var p in m.Ins) {
- IdRegion.Add(newRegions, p.tok, p, true, module);
+ IdRegion.Add(newRegions, program, p.tok, p, true, module);
}
foreach (var p in m.Outs) {
- IdRegion.Add(newRegions, p.tok, p, true, module);
+ IdRegion.Add(newRegions, program, p.tok, p, true, module);
}
- m.Req.ForEach(e => ExprRegions(e.E, newRegions, module));
- m.Mod.Expressions.ForEach(fe => FrameExprRegions(fe, newRegions, true, module));
- m.Ens.ForEach(e => ExprRegions(e.E, newRegions, module));
- m.Decreases.Expressions.ForEach(e => ExprRegions(e, newRegions, module));
+ m.Req.ForEach(e => ExprRegions(e.E, newRegions, program, module));
+ m.Mod.Expressions.ForEach(fe => FrameExprRegions(fe, newRegions, true, program, module));
+ m.Ens.ForEach(e => ExprRegions(e.E, newRegions, program, module));
+ m.Decreases.Expressions.ForEach(e => ExprRegions(e, newRegions, program, module));
if (m.Body != null) {
- StatementRegions(m.Body, newRegions, module);
+ StatementRegions(m.Body, newRegions, program, module);
}
} else if (member is SpecialField) {
// do nothing
} else if (member is Field) {
var fld = (Field)member;
- IdRegion.Add(newRegions, fld.tok, fld, null, "field", true, module);
+ IdRegion.Add(newRegions, program, fld.tok, fld, null, "field", true, module);
}
}
} else if (d is NewtypeDecl) {
var dd = (NewtypeDecl)d;
if (dd.Var != null) {
- IdRegion.Add(newRegions, dd.Var.tok, dd.Var, true, module);
- ExprRegions(dd.Constraint, newRegions, module);
+ IdRegion.Add(newRegions, program, dd.Var.tok, dd.Var, true, module);
+ ExprRegions(dd.Constraint, newRegions, program, module);
}
}
}
@@ -228,67 +228,80 @@ namespace DafnyLanguage
return true;
}
- static void FrameExprRegions(FrameExpression fe, List<IdRegion> regions, bool descendIntoExpressions, ModuleDefinition module) {
+ static void FrameExprRegions(FrameExpression fe, List<IdRegion> regions, bool descendIntoExpressions, Microsoft.Dafny.Program prog, ModuleDefinition module) {
Contract.Requires(fe != null);
Contract.Requires(regions != null);
+ Contract.Requires(prog != null);
if (descendIntoExpressions) {
- ExprRegions(fe.E, regions, module);
+ ExprRegions(fe.E, regions, prog, module);
}
if (fe.Field != null) {
Microsoft.Dafny.Type showType = null; // TODO: if we had the instantiated type of this field, that would have been nice to use here (but the Resolver currently does not compute or store the instantiated type for a FrameExpression)
- IdRegion.Add(regions, fe.tok, fe.Field, showType, "field", false, module);
+ IdRegion.Add(regions, prog, fe.tok, fe.Field, showType, "field", false, module);
}
}
- static void ExprRegions(Microsoft.Dafny.Expression expr, List<IdRegion> regions, ModuleDefinition module) {
+ static void ExprRegions(Microsoft.Dafny.Expression expr, List<IdRegion> regions, Microsoft.Dafny.Program prog, ModuleDefinition module) {
Contract.Requires(expr != null);
Contract.Requires(regions != null);
+ Contract.Requires(prog != null);
if (expr is AutoGeneratedExpression) {
// do nothing
return;
} else if (expr is IdentifierExpr) {
var e = (IdentifierExpr)expr;
- IdRegion.Add(regions, e.tok, e.Var, false, module);
+ IdRegion.Add(regions, prog, e.tok, e.Var, false, module);
} else if (expr is MemberSelectExpr) {
var e = (MemberSelectExpr)expr;
var field = e.Member as Field;
if (field != null) {
- IdRegion.Add(regions, e.tok, field, e.Type, "field", false, module);
+ IdRegion.Add(regions, prog, e.tok, field, e.Type, "field", false, module);
}
} else if (expr is LetExpr) {
var e = (LetExpr)expr;
foreach (var bv in e.BoundVars) {
- IdRegion.Add(regions, bv.tok, bv, true, module);
+ IdRegion.Add(regions, prog, bv.tok, bv, true, module);
}
} else if (expr is ComprehensionExpr) {
var e = (ComprehensionExpr)expr;
foreach (var bv in e.BoundVars) {
- IdRegion.Add(regions, bv.tok, bv, true, module);
+ IdRegion.Add(regions, prog, bv.tok, bv, true, module);
}
} else if (expr is MatchExpr) {
var e = (MatchExpr)expr;
foreach (var kase in e.Cases) {
- kase.Arguments.ForEach(bv => IdRegion.Add(regions, bv.tok, bv, true, module));
+ kase.Arguments.ForEach(bv => {
+ IdRegion.Add(regions, prog, bv.tok, bv, true, module);
+ // if the arguments is an encapsulation of different boundvars from nested match cases,
+ // add the boundvars so that they can show up in the IDE correctly
+ if (bv.tok is MatchCaseToken) {
+ MatchCaseToken mt = (MatchCaseToken)bv.tok;
+ foreach(Tuple<Bpl.IToken, BoundVar, bool> entry in mt.varList) {
+ IdRegion.Add(regions, prog, entry.Item1, entry.Item2, entry.Item3, module);
+ }
+ }
+ });
}
} else if (expr is ChainingExpression) {
var e = (ChainingExpression)expr;
// Do the subexpressions only once (that is, avoid the duplication that occurs in the desugared form of the ChainingExpression)
- e.Operands.ForEach(ee => ExprRegions(ee, regions, module));
+ e.Operands.ForEach(ee => ExprRegions(ee, regions, prog, module));
return; // return here, so as to avoid doing the subexpressions below
}
foreach (var ee in expr.SubExpressions) {
- ExprRegions(ee, regions, module);
+ ExprRegions(ee, regions, prog, module);
}
}
- static void StatementRegions(Statement stmt, List<IdRegion> regions, ModuleDefinition module) {
+ static void StatementRegions(Statement stmt, List<IdRegion> regions, Microsoft.Dafny.Program prog, ModuleDefinition module) {
Contract.Requires(stmt != null);
Contract.Requires(regions != null);
+ Contract.Requires(prog != null);
if (stmt is VarDeclStmt) {
var s = (VarDeclStmt)stmt;
// Add the variables here, once, and then go directly to the RHS's (without letting the sub-statements re-do the LHS's)
foreach (var local in s.Locals) {
- IdRegion.Add(regions, local.Tok, local, true, module);
+ IdRegion.Add(regions, prog, local.Tok, local, true, module);
}
if (s.Update == null) {
// the VarDeclStmt has no associated assignment
@@ -296,48 +309,58 @@ namespace DafnyLanguage
var upd = (UpdateStmt)s.Update;
foreach (var rhs in upd.Rhss) {
foreach (var ee in rhs.SubExpressions) {
- ExprRegions(ee, regions, module);
+ ExprRegions(ee, regions, prog, module);
}
}
} else {
var upd = (AssignSuchThatStmt)s.Update;
- ExprRegions(upd.Expr, regions, module);
+ ExprRegions(upd.Expr, regions, prog, module);
}
// we're done, so don't do the sub-statements/expressions again
return;
} else if (stmt is ForallStmt) {
var s = (ForallStmt)stmt;
- s.BoundVars.ForEach(bv => IdRegion.Add(regions, bv.tok, bv, true, module));
+ s.BoundVars.ForEach(bv => IdRegion.Add(regions, prog, bv.tok, bv, true, module));
} else if (stmt is MatchStmt) {
var s = (MatchStmt)stmt;
foreach (var kase in s.Cases) {
- kase.Arguments.ForEach(bv => IdRegion.Add(regions, bv.tok, bv, true, module));
+ kase.Arguments.ForEach(bv => {
+ IdRegion.Add(regions, prog, bv.tok, bv, true, module);
+ // if the arguments is an encapsulation of different boundvars from nested match cases,
+ // add the boundvars so that they can show up in the IDE correctly
+ if (bv.tok is MatchCaseToken) {
+ MatchCaseToken mt = (MatchCaseToken)bv.tok;
+ foreach (Tuple<Bpl.IToken, BoundVar, bool> entry in mt.varList) {
+ IdRegion.Add(regions, prog, entry.Item1, entry.Item2, entry.Item3, module);
+ }
+ }
+ });
}
} else if (stmt is LoopStmt) {
var s = (LoopStmt)stmt;
if (s.Mod.Expressions != null) {
- s.Mod.Expressions.ForEach(fe => FrameExprRegions(fe, regions, false, module));
+ s.Mod.Expressions.ForEach(fe => FrameExprRegions(fe, regions, false, prog, module));
}
} else if (stmt is CalcStmt) {
var s = (CalcStmt)stmt;
// skip the last line, which is just a duplicate anyway
for (int i = 0; i < s.Lines.Count - 1; i++) {
- ExprRegions(s.Lines[i], regions, module);
+ ExprRegions(s.Lines[i], regions, prog, module);
}
foreach (var ss in stmt.SubStatements) {
- StatementRegions(ss, regions, module);
+ StatementRegions(ss, regions, prog, module);
}
return;
}
foreach (var ee in stmt.SubExpressions) {
- ExprRegions(ee, regions, module);
+ ExprRegions(ee, regions, prog, module);
}
foreach (var ss in stmt.SubStatements) {
- StatementRegions(ss, regions, module);
+ StatementRegions(ss, regions, prog, module);
}
}
- class IdRegion
+ class IdRegion : DafnyRegion
{
public readonly int Start;
public readonly int Length;
@@ -346,40 +369,39 @@ namespace DafnyLanguage
public readonly OccurrenceKind Kind;
public readonly IVariable Variable;
- static bool SurfaceSyntaxToken(Bpl.IToken tok) {
- Contract.Requires(tok != null);
- return !(tok is TokenWrapper);
- }
-
- public static void Add(List<IdRegion> regions, Bpl.IToken tok, IVariable v, bool isDefinition, ModuleDefinition context) {
+ public static void Add(List<IdRegion> regions, Microsoft.Dafny.Program prog, Bpl.IToken tok, IVariable v, bool isDefinition, ModuleDefinition context) {
Contract.Requires(regions != null);
+ Contract.Requires(prog != null);
Contract.Requires(tok != null);
Contract.Requires(v != null);
- Add(regions, tok, v, isDefinition, null, context);
+ Add(regions, prog, tok, v, isDefinition, null, context);
}
- public static void Add(List<IdRegion> regions, Bpl.IToken tok, IVariable v, bool isDefinition, string kind, ModuleDefinition context) {
+ public static void Add(List<IdRegion> regions, Microsoft.Dafny.Program prog, Bpl.IToken tok, IVariable v, bool isDefinition, string kind, ModuleDefinition context) {
Contract.Requires(regions != null);
+ Contract.Requires(prog != null);
Contract.Requires(tok != null);
Contract.Requires(v != null);
- if (SurfaceSyntaxToken(tok)) {
+ if (InMainFileAndUserDefined(prog, tok)) {
regions.Add(new IdRegion(tok, v, isDefinition, kind, context));
}
}
- public static void Add(List<IdRegion> regions, Bpl.IToken tok, Field decl, Microsoft.Dafny.Type showType, string kind, bool isDefinition, ModuleDefinition context) {
+ public static void Add(List<IdRegion> regions, Microsoft.Dafny.Program prog, Bpl.IToken tok, Field decl, Microsoft.Dafny.Type showType, string kind, bool isDefinition, ModuleDefinition context) {
Contract.Requires(regions != null);
+ Contract.Requires(prog != null);
Contract.Requires(tok != null);
Contract.Requires(decl != null);
Contract.Requires(kind != null);
- if (SurfaceSyntaxToken(tok)) {
+ if (InMainFileAndUserDefined(prog, tok)) {
regions.Add(new IdRegion(tok, decl, showType, kind, isDefinition, context));
}
}
- public static void Add(List<IdRegion> regions, Bpl.IToken tok, string text, int length) {
+ public static void Add(List<IdRegion> regions, Microsoft.Dafny.Program prog, Bpl.IToken tok, string text, int length) {
Contract.Requires(regions != null);
+ Contract.Requires(prog != null);
Contract.Requires(tok != null);
Contract.Requires(text != null);
- if (SurfaceSyntaxToken(tok)) {
+ if (InMainFileAndUserDefined(prog, tok)) {
regions.Add(new IdRegion(tok, OccurrenceKind.AdditionalInformation, text, length));
}
}
@@ -433,6 +455,15 @@ namespace DafnyLanguage
}
}
+ public abstract class DafnyRegion
+ {
+ public static bool InMainFileAndUserDefined(Microsoft.Dafny.Program prog, Bpl.IToken tok) {
+ Contract.Requires(prog != null);
+ Contract.Requires(tok != null);
+ return object.Equals(prog.FullName, tok.filename) && !(tok is AutoGeneratedToken);
+ }
+ }
+
#endregion
}
diff --git a/Source/DafnyExtension/MenuProxy.cs b/Source/DafnyExtension/MenuProxy.cs
index 11e1287f..0e061cd3 100644
--- a/Source/DafnyExtension/MenuProxy.cs
+++ b/Source/DafnyExtension/MenuProxy.cs
@@ -33,6 +33,14 @@ namespace DafnyLanguage
&& 0 < DafnyDriver.IncrementalVerificationMode();
}
+ public bool ToggleAutomaticInduction(IWpfTextView activeTextView) {
+ return DafnyDriver.ChangeAutomaticInduction();
+ }
+
+ public bool AutomaticInductionCommandEnabled(IWpfTextView activeTextView) {
+ return activeTextView != null;
+ }
+
public bool StopVerifierCommandEnabled(IWpfTextView activeTextView)
{
DafnyLanguage.ProgressTagger tagger;
@@ -67,6 +75,15 @@ namespace DafnyLanguage
}
}
+ public void DiagnoseTimeouts(IWpfTextView activeTextView)
+ {
+ DafnyLanguage.ProgressTagger tagger;
+ if (activeTextView != null && DafnyLanguage.ProgressTagger.ProgressTaggers.TryGetValue(activeTextView.TextBuffer, out tagger))
+ {
+ tagger.StartVerification(false, true);
+ }
+ }
+
public bool MenuEnabled(IWpfTextView activeTextView)
{
return activeTextView != null && activeTextView.TextBuffer.ContentType.DisplayName == "dafny";
@@ -80,6 +97,14 @@ namespace DafnyLanguage
&& resolver.Program != null;
}
+ public bool DiagnoseTimeoutsCommandEnabled(IWpfTextView activeTextView)
+ {
+ ResolverTagger resolver;
+ return activeTextView != null
+ && DafnyLanguage.ResolverTagger.ResolverTaggers.TryGetValue(activeTextView.TextBuffer, out resolver)
+ && resolver.VerificationErrors.Any(err => err.Message.Contains("timed out"));
+ }
+
public void Compile(IWpfTextView activeTextView)
{
ResolverTagger resolver;
diff --git a/Source/DafnyExtension/OutliningTagger.cs b/Source/DafnyExtension/OutliningTagger.cs
index 85771e94..fc06d4bf 100644
--- a/Source/DafnyExtension/OutliningTagger.cs
+++ b/Source/DafnyExtension/OutliningTagger.cs
@@ -80,7 +80,7 @@ namespace DafnyLanguage
if (start == end) yield break;
foreach (var r in _regions) {
- if (0 <= r.Length && r.Start >= start && r.Start + r.Length <= end) {
+ if (0 <= r.Length && r.Start <= end && start <= r.Start + r.Length) {
yield return new TagSpan<OutliningRegionTag>(
new SnapshotSpan(_snapshot, r.Start, r.Length),
new OutliningRegionTag(false, false, "...", r.HoverText));
@@ -130,18 +130,18 @@ namespace DafnyLanguage
if (module.IsAbstract) {
nm = "abstract " + nm;
}
- newRegions.Add(new OutliningRegion(module, nm));
+ OutliningRegion.Add(newRegions, program, module, nm);
}
foreach (Dafny.TopLevelDecl d in module.TopLevelDecls) {
if (!HasBodyTokens(d) && !(d is Dafny.ClassDecl)) {
continue;
}
if (d is Dafny.OpaqueTypeDecl) {
- newRegions.Add(new OutliningRegion(d, "type"));
+ OutliningRegion.Add(newRegions, program, d, "type");
} else if (d is Dafny.CoDatatypeDecl) {
- newRegions.Add(new OutliningRegion(d, "codatatype"));
+ OutliningRegion.Add(newRegions, program, d, "codatatype");
} else if (d is Dafny.DatatypeDecl) {
- newRegions.Add(new OutliningRegion(d, "datatype"));
+ OutliningRegion.Add(newRegions, program, d, "datatype");
} else if (d is Dafny.ModuleDecl) {
// do nothing here, since the outer loop handles modules
} else {
@@ -149,9 +149,9 @@ namespace DafnyLanguage
if (cl.IsDefaultClass) {
// do nothing
} else if (cl is Dafny.IteratorDecl) {
- newRegions.Add(new OutliningRegion(cl, "iterator"));
+ OutliningRegion.Add(newRegions, program, cl, "iterator");
} else {
- newRegions.Add(new OutliningRegion(cl, "class"));
+ OutliningRegion.Add(newRegions, program, cl, "class");
}
// do the class members (in particular, functions and methods)
foreach (Dafny.MemberDecl m in cl.Members) {
@@ -168,7 +168,7 @@ namespace DafnyLanguage
if (!m.IsGhost) {
nm += " method";
}
- newRegions.Add(new OutliningRegion(m, nm));
+ OutliningRegion.Add(newRegions, program, m, nm);
} else if (m is Dafny.Method && ((Dafny.Method)m).Body != null) {
var nm =
m is Dafny.Constructor ? "constructor" :
@@ -179,7 +179,7 @@ namespace DafnyLanguage
if (m.IsGhost && !(m is Dafny.CoLemma)) {
nm = "ghost " + nm;
}
- newRegions.Add(new OutliningRegion(m, nm));
+ OutliningRegion.Add(newRegions, program, m, nm);
}
}
}
@@ -196,12 +196,20 @@ namespace DafnyLanguage
return decl.BodyStartTok != Bpl.Token.NoToken && decl.BodyEndTok != Bpl.Token.NoToken;
}
- class OutliningRegion
+ class OutliningRegion : DafnyRegion
{
+ public static void Add(List<OutliningRegion> regions, Microsoft.Dafny.Program prog, Dafny.INamedRegion decl, string kind) {
+ Contract.Requires(regions != null);
+ Contract.Requires(prog != null);
+ if (InMainFileAndUserDefined(prog, decl.BodyStartTok)) {
+ regions.Add(new OutliningRegion(decl, kind));
+ }
+ }
+
public readonly int Start;
public readonly int Length;
public readonly string HoverText;
- public OutliningRegion(Dafny.INamedRegion decl, string kind) {
+ private OutliningRegion(Dafny.INamedRegion decl, string kind) {
int startPosition = decl.BodyStartTok.pos + 1; // skip the open-curly brace itself
int length = decl.BodyEndTok.pos - startPosition;
Start = startPosition;
diff --git a/Source/DafnyExtension/ProgressMargin.cs b/Source/DafnyExtension/ProgressMargin.cs
index b4e58d3d..c3f56259 100644
--- a/Source/DafnyExtension/ProgressMargin.cs
+++ b/Source/DafnyExtension/ProgressMargin.cs
@@ -197,6 +197,7 @@ namespace DafnyLanguage
bool verificationInProgress; // this field is protected by "this". Invariant: !verificationInProgress ==> bufferChangesPreVerificationStart.Count == 0
System.Threading.Tasks.Task verificationTask;
public bool VerificationDisabled { get; private set; }
+ bool isDiagnosingTimeouts;
string lastRequestId;
public static readonly IDictionary<ITextBuffer, ProgressTagger> ProgressTaggers = new ConcurrentDictionary<ITextBuffer, ProgressTagger>();
@@ -227,14 +228,21 @@ namespace DafnyLanguage
if (prog == null || VerificationDisabled) return;
// We have a successfully resolved program to verify
- var resolvedVersion = snap.Version.VersionNumber;
- if (bufferChangesPostVerificationStart.Count == 0) {
- // Nothing new to verify. No reason to start a new verification.
- return;
- } else if (!bufferChangesPostVerificationStart.TrueForAll(span => span.Snapshot.Version.VersionNumber <= resolvedVersion)) {
- // There have been buffer changes since the program that was resolved. Do nothing here,
- // and instead just await the next resolved program.
- return;
+ var dt = isDiagnosingTimeouts;
+ if (!dt)
+ {
+ var resolvedVersion = snap.Version.VersionNumber;
+ if (bufferChangesPostVerificationStart.Count == 0)
+ {
+ // Nothing new to verify. No reason to start a new verification.
+ return;
+ }
+ else if (!bufferChangesPostVerificationStart.TrueForAll(span => span.Snapshot.Version.VersionNumber <= resolvedVersion))
+ {
+ // There have been buffer changes since the program that was resolved. Do nothing here,
+ // and instead just await the next resolved program.
+ return;
+ }
}
// at this time, we're committed to running the verifier
@@ -254,10 +262,14 @@ namespace DafnyLanguage
}
verificationTask = System.Threading.Tasks.Task.Factory.StartNew(
- () => RunVerifier(prog, snap, lastRequestId, resolver),
+ () => RunVerifier(prog, snap, lastRequestId, resolver, dt),
TaskCreationOptions.LongRunning);
verificationInProgress = true;
+ if (dt)
+ {
+ isDiagnosingTimeouts = false;
+ }
// Change orange progress markers into yellow ones
Contract.Assert(bufferChangesPreVerificationStart.Count == 0); // follows from monitor invariant
@@ -293,7 +305,7 @@ namespace DafnyLanguage
}
}
- public void StartVerification()
+ public void StartVerification(bool clearCache = true, bool diagnoseTimeouts = false)
{
lock (this)
{
@@ -301,7 +313,11 @@ namespace DafnyLanguage
bufferChangesPostVerificationStart.Clear();
bufferChangesPostVerificationStart.Add(new SnapshotSpan(_buffer.CurrentSnapshot, 0, _buffer.CurrentSnapshot.Length));
VerificationDisabled = false;
- ClearCachedVerificationResults();
+ isDiagnosingTimeouts = diagnoseTimeouts;
+ if (clearCache)
+ {
+ ClearCachedVerificationResults();
+ }
NotifyAboutChangedTags(_buffer.CurrentSnapshot);
}
}
@@ -314,7 +330,7 @@ namespace DafnyLanguage
}
}
- void RunVerifier(Dafny.Program program, ITextSnapshot snapshot, string requestId, ResolverTagger errorListHolder) {
+ void RunVerifier(Dafny.Program program, ITextSnapshot snapshot, string requestId, ResolverTagger errorListHolder, bool diagnoseTimeouts) {
Contract.Requires(program != null);
Contract.Requires(snapshot != null);
Contract.Requires(requestId != null);
@@ -332,6 +348,8 @@ namespace DafnyLanguage
_version++;
}
+ DafnyDriver.SetDiagnoseTimeouts(diagnoseTimeouts);
+
try
{
bool success = DafnyDriver.Verify(program, errorListHolder, GetHashCode().ToString(), requestId, errorInfo =>
@@ -369,6 +387,10 @@ namespace DafnyLanguage
{
errorListHolder.AddError(new DafnyError("$$program$$", 0, 0, ErrorCategory.InternalError, "Verification process error: " + e.Message, snapshot, false), "$$program$$", requestId);
}
+ finally
+ {
+ DafnyDriver.SetDiagnoseTimeouts(!diagnoseTimeouts);
+ }
lock (this) {
bufferChangesPreVerificationStart.Clear();
diff --git a/Source/DafnyExtension/ResolverTagger.cs b/Source/DafnyExtension/ResolverTagger.cs
index 1fdd3827..4a50a4e8 100644
--- a/Source/DafnyExtension/ResolverTagger.cs
+++ b/Source/DafnyExtension/ResolverTagger.cs
@@ -68,26 +68,27 @@ namespace DafnyLanguage
{
Error = error;
}
-
- private static string ConvertToErrorType(DafnyError err)
- {
- string ty; // the COLORs below indicate what I see on my machine
- switch (err.Category)
- {
- default: // unexpected category
+
+ private static string ConvertToErrorType(DafnyError err) {
+ // the COLORs below indicate what I see on my machine
+ switch (err.Category) {
+ case ErrorCategory.ProcessError:
case ErrorCategory.ParseError:
- case ErrorCategory.ParseWarning:
- ty = "syntax error"; break; // COLOR: red
+ return "syntax error"; // COLOR: red
case ErrorCategory.ResolveError:
- ty = "compiler error"; break; // COLOR: blue
+ return "compiler error"; // COLOR: blue
+ case ErrorCategory.ParseWarning:
+ case ErrorCategory.ResolveWarning:
+ return "compiler warning"; // COLOR: blue
+ case ErrorCategory.InternalError:
case ErrorCategory.VerificationError:
- ty = "error"; break; // COLOR: red
+ return "error"; // COLOR: red
case ErrorCategory.AuxInformation:
- ty = "other error"; break; // COLOR: purple red
- case ErrorCategory.InternalError:
- ty = "error"; break; // COLOR: red
+ return "other error"; // COLOR: purple red
+ default:
+ Contract.Assert(false);
+ throw new InvalidOperationException();
}
- return ty;
}
}
@@ -188,7 +189,7 @@ namespace DafnyLanguage
{
get
{
- return _verificationErrors.Values.Where(ec => ec.RequestId == MostRecentRequestId).SelectMany(ec => ec.Errors.Reverse());
+ return _verificationErrors.Values.Where(ec => ec.RequestId == MostRecentRequestId).SelectMany(ec => ec.Errors.Reverse()).ToList();
}
}
@@ -198,13 +199,19 @@ namespace DafnyLanguage
{
lock (this)
{
- if (_resolutionErrors != null && _resolutionErrors.Any())
- {
- return _resolutionErrors;
+ bool anyResolutionErrors = false;
+ if (_resolutionErrors != null) {
+ foreach (var err in _resolutionErrors) {
+ if (CategoryConversion(err.Category) == TaskErrorCategory.Error) {
+ anyResolutionErrors = true;
+ }
+ yield return err;
+ }
}
- else
- {
- return VerificationErrors;
+ if (!anyResolutionErrors) {
+ foreach (var err in VerificationErrors) {
+ yield return err;
+ }
}
}
}
@@ -310,19 +317,18 @@ namespace DafnyLanguage
/// <summary>
/// Calls the Dafny parser/resolver/type checker on the contents of the buffer, updates the Error List accordingly.
/// </summary>
- void ResolveBuffer(object sender, EventArgs args)
- {
+ void ResolveBuffer(object sender, EventArgs args) {
ITextSnapshot snapshot = _buffer.CurrentSnapshot;
if (snapshot == Snapshot)
return; // we've already done this snapshot
string filename = _document != null ? _document.FilePath : "<program>";
- var driver = new DafnyDriver(snapshot, filename);
+ var driver = new DafnyDriver(_buffer, filename);
List<DafnyError> newErrors;
Dafny.Program program;
try
{
- program = driver.ProcessResolution();
+ program = driver.ProcessResolution(true);
newErrors = driver.Errors;
}
catch (Exception e)
@@ -353,49 +359,46 @@ namespace DafnyLanguage
public void UpdateErrorList(ITextSnapshot snapshot)
{
- lock (this)
+ if (_errorProvider != null && !m_disposed)
{
- if (_errorProvider != null && !m_disposed)
- {
- _errorProvider.SuspendRefresh(); // reduce flickering
- _errorProvider.Tasks.Clear();
- foreach (var err in AllErrors)
- {
- var lineNum = 0;
- var columnNum = 0;
- if (err.Span != null) {
- var span = err.Span.GetSpan(snapshot);
- lineNum = snapshot.GetLineNumberFromPosition(span.Start.Position);
- var line = snapshot.GetLineFromPosition(span.Start.Position);
- columnNum = span.Start - line.Start;
- } else {
- lineNum = err.Line;
- columnNum = err.Column;
- }
-
- ErrorTask task = new ErrorTask()
- {
- Category = TaskCategory.BuildCompile,
- ErrorCategory = CategoryConversion(err.Category),
- Text = err.Message,
- Line = lineNum,
- Column = columnNum
- };
- if (err.Filename != null) {
- task.Document = err.Filename;
- }
- else if (_document != null)
- {
- task.Document = _document.FilePath;
- }
- if (err.Category != ErrorCategory.ProcessError && err.Category != ErrorCategory.InternalError)
- {
- task.Navigate += new EventHandler(NavigateHandler);
- }
- _errorProvider.Tasks.Add(task);
+ _errorProvider.SuspendRefresh(); // reduce flickering
+ _errorProvider.Tasks.Clear();
+ foreach (var err in AllErrors)
+ {
+ var lineNum = 0;
+ var columnNum = 0;
+ if (err.Span != null) {
+ var span = err.Span.GetSpan(snapshot);
+ lineNum = snapshot.GetLineNumberFromPosition(span.Start.Position);
+ var line = snapshot.GetLineFromPosition(span.Start.Position);
+ columnNum = span.Start - line.Start;
+ } else {
+ lineNum = err.Line;
+ columnNum = err.Column;
+ }
+
+ ErrorTask task = new ErrorTask()
+ {
+ Category = TaskCategory.BuildCompile,
+ ErrorCategory = CategoryConversion(err.Category),
+ Text = err.Message,
+ Line = lineNum,
+ Column = columnNum
+ };
+ if (err.Filename != null) {
+ task.Document = err.Filename;
+ }
+ else if (_document != null)
+ {
+ task.Document = _document.FilePath;
+ }
+ if (err.Category != ErrorCategory.ProcessError && err.Category != ErrorCategory.InternalError)
+ {
+ task.Navigate += new EventHandler(NavigateHandler);
}
- _errorProvider.ResumeRefresh();
+ _errorProvider.Tasks.Add(task);
}
+ _errorProvider.ResumeRefresh();
}
var chng = TagsChanged;
if (chng != null)
@@ -412,6 +415,7 @@ namespace DafnyLanguage
case ErrorCategory.InternalError:
return TaskErrorCategory.Error;
case ErrorCategory.ParseWarning:
+ case ErrorCategory.ResolveWarning:
return TaskErrorCategory.Warning;
case ErrorCategory.AuxInformation:
return TaskErrorCategory.Message;
@@ -477,7 +481,7 @@ namespace DafnyLanguage
public enum ErrorCategory
{
- ProcessError, ParseWarning, ParseError, ResolveError, VerificationError, AuxInformation, InternalError
+ ProcessError, ParseWarning, ParseError, ResolveWarning, ResolveError, VerificationError, AuxInformation, InternalError
}
public class DafnyError
@@ -527,7 +531,7 @@ namespace DafnyLanguage
else
{
var line = Math.Max(0, int.Parse(match.Groups[1].Value) - 1);
- var column = Math.Max(0, int.Parse(match.Groups[2].Value) - 1);
+ var column = Math.Max(0, int.Parse(match.Groups[2].Value));
var sLine = Snapshot.GetLineFromLineNumber(line);
Contract.Assert(column <= sLine.Length);
var sLength = Math.Max(0, Math.Min(sLine.Length - column, 0));
diff --git a/Source/DafnyExtension/TokenTagger.cs b/Source/DafnyExtension/TokenTagger.cs
index af141ad7..7a5eb572 100644
--- a/Source/DafnyExtension/TokenTagger.cs
+++ b/Source/DafnyExtension/TokenTagger.cs
@@ -70,17 +70,34 @@ namespace DafnyLanguage
}
}
+
internal sealed class DafnyTokenTagger : ITagger<DafnyTokenTag>, IDisposable
{
+ internal sealed class ScanResult
+ {
+ internal ITextSnapshot _oldSnapshot;
+ internal ITextSnapshot _newSnapshot;
+ internal List<TokenRegion> _regions; // the regions computed for the _newSnapshot
+ internal NormalizedSnapshotSpanCollection _difference; // the difference between _oldSnapshot and _newSnapshot
+
+ internal ScanResult(ITextSnapshot oldSnapshot, ITextSnapshot newSnapshot, List<TokenRegion> regions, NormalizedSnapshotSpanCollection diffs) {
+ _oldSnapshot = oldSnapshot;
+ _newSnapshot = newSnapshot;
+ _regions = regions;
+ _difference = diffs;
+ }
+ }
+
ITextBuffer _buffer;
ITextSnapshot _snapshot;
List<TokenRegion> _regions;
+ static object bufferTokenTaggerKey = new object();
bool _disposed;
internal DafnyTokenTagger(ITextBuffer buffer) {
_buffer = buffer;
_snapshot = buffer.CurrentSnapshot;
- _regions = Rescan(_snapshot);
+ _regions = Scan(_snapshot);
_buffer.Changed += new EventHandler<TextContentChangedEventArgs>(ReparseFile);
}
@@ -89,6 +106,7 @@ namespace DafnyLanguage
lock (this) {
if (!_disposed) {
_buffer.Changed -= ReparseFile;
+ _buffer.Properties.RemoveProperty(bufferTokenTaggerKey);
_buffer = null;
_snapshot = null;
_regions = null;
@@ -127,24 +145,93 @@ namespace DafnyLanguage
ITextSnapshot snapshot = _buffer.CurrentSnapshot;
if (snapshot == _snapshot)
return; // we've already computed the regions for this snapshot
+
+ NormalizedSnapshotSpanCollection difference = new NormalizedSnapshotSpanCollection();
+ ScanResult result;
+ if (_buffer.Properties.TryGetProperty(bufferTokenTaggerKey, out result) &&
+ (result._oldSnapshot == _snapshot) &&
+ (result._newSnapshot == snapshot)) {
+ difference = result._difference;
+ // save the new baseline
+ _regions = result._regions;
+ _snapshot = snapshot;
+ } else {
+ List<TokenRegion> regions = new List<TokenRegion>();
+ List<SnapshotSpan> rescannedRegions = new List<SnapshotSpan>();
+
+ // loop through the changes and check for changes in comments first. If
+ // the change is in a comments, we need to rescan starting from the
+ // beginning of the comments (which in multi-lined comments, it can
+ // be a line that the changes are not on), otherwise, we can just rescan the lines
+ // that the changes are on.
+ bool done;
+ SnapshotPoint start, end;
+ for (int i = 0; i < args.Changes.Count; i++) {
+ done = false;
+ // get the span of the lines that the change is on.
+ int cStart = args.Changes[i].NewSpan.Start;
+ int cEnd = args.Changes[i].NewSpan.End;
+ start = snapshot.GetLineFromPosition(cStart).Start;
+ end = snapshot.GetLineFromPosition(cEnd).End;
+ SnapshotSpan newSpan = new SnapshotSpan(start, end);
+ foreach (TokenRegion r in _regions) {
+ if (r.Kind == DafnyTokenKind.Comment) {
+ // if the change is in the comments, we want to start scanning from the
+ // the beginning of the comments instead.
+ SnapshotSpan span = r.Span.TranslateTo(snapshot, SpanTrackingMode.EdgeExclusive);
+ if (span.IntersectsWith(newSpan)) {
+ start = span.Start.Position < newSpan.Start.Position ? span.Start : newSpan.Start;
+ end = span.End.Position > newSpan.End.Position ? span.End : newSpan.End;
+ end = Scan(snapshot.GetText(new SnapshotSpan(start, end)), start, regions, snapshot);
+ // record the regions that we rescanned.
+ rescannedRegions.Add(new SnapshotSpan(start, end));
+ done = true;
+ break;
+ }
+ }
+ }
+ if (!done) {
+ // scan the lines that the change is on to generate the new regions.
+ end = Scan(snapshot.GetText(new SnapshotSpan(start, end)), start, regions, snapshot);
+ // record the span that we rescanned.
+ rescannedRegions.Add(new SnapshotSpan(start, end));
+ }
+ }
- // get all of the outline regions in the snapshot
- List<TokenRegion> newRegions = Rescan(snapshot);
-
- // determine the changed span, and send a changed event with the new spans
- List<SnapshotSpan> oldSpans = new List<SnapshotSpan>(_regions.Select(r =>
- r.Span.TranslateTo(snapshot, SpanTrackingMode.EdgeExclusive)));
-
- List<SnapshotSpan> newSpans = new List<SnapshotSpan>(newRegions.Select(r => r.Span));
-
- NormalizedSnapshotSpanCollection oldSpanCollection = new NormalizedSnapshotSpanCollection(oldSpans);
- NormalizedSnapshotSpanCollection newSpanCollection = new NormalizedSnapshotSpanCollection(newSpans);
-
- NormalizedSnapshotSpanCollection difference = SymmetricDifference(oldSpanCollection, newSpanCollection);
-
- // save the new baseline
- _snapshot = snapshot;
- _regions = newRegions;
+ List<SnapshotSpan> oldSpans = new List<SnapshotSpan>();
+ List<SnapshotSpan> newSpans = new List<SnapshotSpan>();
+ // record the newly created spans.
+ foreach (TokenRegion r in regions) {
+ newSpans.Add(r.Span);
+ }
+ // loop through the old scan results and remove the ones that
+ // are in the regions that are rescanned.
+ foreach (TokenRegion r in _regions) {
+ SnapshotSpan origSpan = r.Span.TranslateTo(snapshot, SpanTrackingMode.EdgeExclusive);
+ bool obsolete = false;
+ foreach (SnapshotSpan span in rescannedRegions) {
+ if (origSpan.IntersectsWith(span)) {
+ oldSpans.Add(span);
+ obsolete = true;
+ break;
+ }
+ }
+ if (!obsolete) {
+ TokenRegion region = new TokenRegion(origSpan.Start, origSpan.End, r.Kind);
+ regions.Add(region);
+ }
+ }
+
+ NormalizedSnapshotSpanCollection oldSpanCollection = new NormalizedSnapshotSpanCollection(oldSpans);
+ NormalizedSnapshotSpanCollection newSpanCollection = new NormalizedSnapshotSpanCollection(newSpans);
+ difference = SymmetricDifference(oldSpanCollection, newSpanCollection);
+
+ // save the scan result
+ _buffer.Properties[bufferTokenTaggerKey] = new ScanResult(_snapshot, snapshot, regions, difference);
+ // save the new baseline
+ _snapshot = snapshot;
+ _regions = regions;
+ }
var chng = TagsChanged;
if (chng != null) {
@@ -160,38 +247,35 @@ namespace DafnyLanguage
NormalizedSnapshotSpanCollection.Difference(second, first));
}
- private static List<TokenRegion> Rescan(ITextSnapshot newSnapshot) {
- List<TokenRegion> newRegions = new List<TokenRegion>();
-
+ private static SnapshotPoint Scan(string txt, SnapshotPoint start, List<TokenRegion> newRegions, ITextSnapshot newSnapshot) {
int longCommentDepth = 0;
- SnapshotPoint commentStart = new SnapshotPoint(); // used only when longCommentDepth != 0
+ SnapshotPoint commentStart = new SnapshotPoint();
SnapshotPoint commentEndAsWeKnowIt = new SnapshotPoint(); // used only when longCommentDepth != 0
- foreach (ITextSnapshotLine line in newSnapshot.Lines) {
- string txt = line.GetText(); // the current line (without linebreak characters)
- int N = txt.Length; // length of the current line
- int cur = 0; // offset into the current line
-
+ int N = txt.Length;
+ bool done = false;
+ while (!done) {
+ N = txt.Length; // length of the current buffer
+ int cur = 0; // offset into the current buffer
if (longCommentDepth != 0) {
ScanForEndOfComment(txt, ref longCommentDepth, ref cur);
if (longCommentDepth == 0) {
// we just finished parsing a long comment
- newRegions.Add(new TokenRegion(commentStart, new SnapshotPoint(newSnapshot, line.Start + cur), DafnyTokenKind.Comment));
+ newRegions.Add(new TokenRegion(commentStart, new SnapshotPoint(newSnapshot, start + cur), DafnyTokenKind.Comment));
} else {
// we're still parsing the long comment
Contract.Assert(cur == txt.Length);
- commentEndAsWeKnowIt = new SnapshotPoint(newSnapshot, line.Start + cur);
+ commentEndAsWeKnowIt = new SnapshotPoint(newSnapshot, start + cur);
goto OUTER_CONTINUE;
}
}
-
- // repeatedly get the remaining tokens from this line
- int end; // offset into the current line
+ // repeatedly get the remaining tokens from this buffer
+ int end; // offset into the current buffer
for (; ; cur = end) {
// advance to the first character of a keyword or token
DafnyTokenKind ty = DafnyTokenKind.Keyword;
for (; ; cur++) {
if (N <= cur) {
- // we've looked at everything in this line
+ // we've looked at everything in this buffer
goto OUTER_CONTINUE;
}
char ch = txt[cur];
@@ -204,7 +288,7 @@ namespace DafnyLanguage
}
// advance to the end of the token
- end = cur + 1; // offset into the current line
+ end = cur + 1; // offset into the current buffer
if (ty == DafnyTokenKind.Number) {
// scan the rest of this number
for (; end < N; end++) {
@@ -213,7 +297,7 @@ namespace DafnyLanguage
} else break;
}
} else if (ty == DafnyTokenKind.String) {
- // scan the rest of this string, but not past the end-of-line
+ // scan the rest of this string, but not past the end-of-buffer
for (; end < N; end++) {
char ch = txt[end];
if (ch == '"') {
@@ -233,20 +317,20 @@ namespace DafnyLanguage
if (end == N) continue; // this was not the start of a comment; it was just a single "/" and we don't care to color it
char ch = txt[end];
if (ch == '/') {
- // a short comment
- end = N;
+ // a short comment, to the end of the line.
+ end = newSnapshot.GetLineFromPosition(start + end).End.Position - start;
} else if (ch == '*') {
// a long comment; find the matching "*/"
end++;
- commentStart = new SnapshotPoint(newSnapshot, line.Start + cur);
+ commentStart = new SnapshotPoint(newSnapshot, start + cur);
Contract.Assert(longCommentDepth == 0);
longCommentDepth = 1;
ScanForEndOfComment(txt, ref longCommentDepth, ref end);
if (longCommentDepth == 0) {
// we finished scanning a long comment, and "end" is set to right after it
- newRegions.Add(new TokenRegion(commentStart, new SnapshotPoint(newSnapshot, line.Start + end), DafnyTokenKind.Comment));
+ newRegions.Add(new TokenRegion(commentStart, new SnapshotPoint(newSnapshot, start + end), DafnyTokenKind.Comment));
} else {
- commentEndAsWeKnowIt = new SnapshotPoint(newSnapshot, line.Start + end);
+ commentEndAsWeKnowIt = new SnapshotPoint(newSnapshot, start + end);
}
continue;
} else {
@@ -305,6 +389,7 @@ namespace DafnyLanguage
case "ghost":
case "if":
case "imap":
+ case "iset":
case "import":
case "in":
case "include":
@@ -357,21 +442,54 @@ namespace DafnyLanguage
}
}
}
-
- newRegions.Add(new TokenRegion(new SnapshotPoint(newSnapshot, line.Start + cur), new SnapshotPoint(newSnapshot, line.Start + end), ty));
+ newRegions.Add(new TokenRegion(new SnapshotPoint(newSnapshot, start + cur), new SnapshotPoint(newSnapshot, start + end), ty));
+ }
+ OUTER_CONTINUE:
+ done = true;
+ if (longCommentDepth != 0) {
+ // we need to look into the next line
+ ITextSnapshotLine currLine = newSnapshot.GetLineFromPosition(start + N);
+ if ((currLine.LineNumber + 1) < newSnapshot.LineCount) {
+ ITextSnapshotLine nextLine = newSnapshot.GetLineFromLineNumber(currLine.LineNumber + 1);
+ txt = nextLine.GetText();
+ start = nextLine.Start;
+ // we are done scanning the current buffer, but not the whole file yet.
+ // we need to continue to find the enclosing "*/", or until the end of the file.
+ done = false;
+ } else {
+ // This was a malformed comment, running to the end of the buffer. Above, we let "commentEndAsWeKnowIt" be the end of the
+ // last line, so we can use it here.
+ newRegions.Add(new TokenRegion(commentStart, commentEndAsWeKnowIt, DafnyTokenKind.Comment));
+ }
}
- OUTER_CONTINUE: ;
}
+ return new SnapshotPoint(newSnapshot, start + N);
+ }
- if (longCommentDepth != 0) {
- // This was a malformed comment, running to the end of the buffer. Above, we let "commentEndAsWeKnowIt" be the end of the
- // last line, so we can use it here.
- newRegions.Add(new TokenRegion(commentStart, commentEndAsWeKnowIt, DafnyTokenKind.Comment));
+ private List<TokenRegion> Scan(ITextSnapshot newSnapshot) {
+ List<TokenRegion> newRegions;
+ ScanResult result;
+ if (_buffer.Properties.TryGetProperty(bufferTokenTaggerKey, out result) &&
+ result._newSnapshot == newSnapshot) {
+ newRegions = result._regions;
+ } else {
+ newRegions = new List<TokenRegion>();
+ int nextLineNumber = -1;
+ foreach (ITextSnapshotLine line in newSnapshot.Lines) {
+ if (line.LineNumber <= nextLineNumber) {
+ // the line is already processed.
+ continue;
+ }
+ string txt = line.GetText(); // the current line (without linebreak characters)
+ SnapshotPoint end = Scan(txt, line.Start, newRegions, newSnapshot);
+ nextLineNumber = newSnapshot.GetLineFromPosition(end).LineNumber;
+ }
+ _buffer.Properties[bufferTokenTaggerKey] = new ScanResult(null, newSnapshot, newRegions, null);
}
-
return newRegions;
}
+
/// <summary>
/// Scans "txt" beginning with depth "depth", which is assumed to be non-0. Any occurrences of "/*" or "*/"
/// increment or decrement "depth". If "depth" ever reaches 0, then "end" returns as the number of characters
diff --git a/Source/DafnyExtension/packages.config b/Source/DafnyExtension/packages.config
new file mode 100644
index 00000000..60f856c5
--- /dev/null
+++ b/Source/DafnyExtension/packages.config
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="VSSDK.ComponentModelHost" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.ComponentModelHost.11" version="11.0.3" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.CoreUtility" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.CoreUtility.11" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.DTE" version="7.0.4" targetFramework="net45" />
+ <package id="VSSDK.Editor" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.IDE" version="7.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.IDE.10" version="10.0.4" targetFramework="net45" />
+ <package id="VSSDK.IDE.11" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.IDE.8" version="8.0.4" targetFramework="net45" />
+ <package id="VSSDK.IDE.9" version="9.0.4" targetFramework="net45" />
+ <package id="VSSDK.Language" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.Language.11" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.OLE.Interop" version="7.0.4" targetFramework="net45" />
+ <package id="VSSDK.Shell.11" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.Shell.Immutable.10" version="10.0.4" targetFramework="net45" />
+ <package id="VSSDK.Shell.Immutable.11" version="11.0.4" targetFramework="net45" />
+ <package id="VSSDK.Shell.Interop" version="7.0.4" targetFramework="net45" />
+ <package id="VSSDK.Shell.Interop.10" version="10.0.4" targetFramework="net45" />
+ <package id="VSSDK.Shell.Interop.11" version="11.0.4" targetFramework="net45" />
+ <package id="VSSDK.Shell.Interop.8" version="8.0.4" targetFramework="net45" />
+ <package id="VSSDK.Shell.Interop.9" version="9.0.4" targetFramework="net45" />
+ <package id="VSSDK.Text" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.Text.11" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.TextManager.Interop" version="7.0.4" targetFramework="net45" />
+ <package id="VSSDK.TextManager.Interop.8" version="8.0.4" targetFramework="net45" />
+</packages>
diff --git a/Source/DafnyExtension/source.extension.vsixmanifest b/Source/DafnyExtension/source.extension.vsixmanifest
index b87e2452..a3987456 100644
--- a/Source/DafnyExtension/source.extension.vsixmanifest
+++ b/Source/DafnyExtension/source.extension.vsixmanifest
@@ -7,10 +7,11 @@
<License>Z3-LICENSE.txt</License>
</Metadata>
<Installation InstalledByMsi="false">
- <InstallationTarget Version="[11.0,13.0)" Id="Microsoft.VisualStudio.Pro" />
+ <InstallationTarget Version="[11.0,15.0)" Id="Microsoft.VisualStudio.Pro" />
+ <InstallationTarget Version="[14.0,15.0)" Id="Microsoft.VisualStudio.Community" />
</Installation>
<Dependencies>
- <Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="4.5" />
+ <Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="[4.5,]" />
<Dependency Id="Microsoft.VisualStudio.MPF.11.0" DisplayName="Visual Studio MPF 11.0" d:Source="Installed" Version="11.0" />
</Dependencies>
<Assets>
diff --git a/Source/DafnyMenu/DafnyMenu.csproj b/Source/DafnyMenu/DafnyMenu.csproj
index 503b3d0c..2a222038 100644
--- a/Source/DafnyMenu/DafnyMenu.csproj
+++ b/Source/DafnyMenu/DafnyMenu.csproj
@@ -62,22 +62,50 @@
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.Shell.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <Reference Include="Microsoft.VisualStudio.Editor, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.ComponentModelHost, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.Shell.11.0, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Editor, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Editor.11.0.4\lib\net45\Microsoft.VisualStudio.Editor.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.ComponentModelHost, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.ComponentModelHost.11.0.4\lib\net45\Microsoft.VisualStudio.ComponentModelHost.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.11.0, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Shell.11.11.0.4\lib\net45\Microsoft.VisualStudio.Shell.11.0.dll</HintPath>
+ </Reference>
<Reference Include="Microsoft.VisualStudio.Shell.Immutable.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.OLE.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <Reference Include="Microsoft.VisualStudio.Shell.Interop.11.0, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <Reference Include="Microsoft.VisualStudio.Shell.Interop.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <Reference Include="Microsoft.VisualStudio.Shell.Interop.8.0, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <Reference Include="Microsoft.VisualStudio.Shell.Interop.9.0, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <Reference Include="Microsoft.VisualStudio.CoreUtility, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.OLE.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\packages\VSSDK.OLE.Interop.7.0.4\lib\net20\Microsoft.VisualStudio.OLE.Interop.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.11.0, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Shell.Interop.11.11.0.4\lib\net20\Microsoft.VisualStudio.Shell.Interop.11.0.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Shell.Interop.10.10.0.4\lib\net20\Microsoft.VisualStudio.Shell.Interop.10.0.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.8.0, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\packages\VSSDK.Shell.Interop.8.8.0.4\lib\net20\Microsoft.VisualStudio.Shell.Interop.8.0.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.9.0, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\packages\VSSDK.Shell.Interop.9.9.0.4\lib\net20\Microsoft.VisualStudio.Shell.Interop.9.0.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.CoreUtility, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.CoreUtility.11.0.4\lib\net45\Microsoft.VisualStudio.CoreUtility.dll</HintPath>
+ </Reference>
<Reference Include="Microsoft.VisualStudio.TextManager.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <Reference Include="Microsoft.VisualStudio.Text.Data, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.Text.Logic, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.Text.UI, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.Text.UI.Wpf, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
- <Reference Include="Microsoft.VisualStudio.Language.StandardClassification, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Text.Data, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Text.11.0.4\lib\net45\Microsoft.VisualStudio.Text.Data.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.Logic, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Text.11.0.4\lib\net45\Microsoft.VisualStudio.Text.Logic.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.UI, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Text.11.0.4\lib\net45\Microsoft.VisualStudio.Text.UI.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.UI.Wpf, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Text.11.0.4\lib\net45\Microsoft.VisualStudio.Text.UI.Wpf.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Language.StandardClassification, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Language.11.0.4\lib\net45\Microsoft.VisualStudio.Language.StandardClassification.dll</HintPath>
+ </Reference>
</ItemGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
@@ -127,6 +155,9 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
+ <None Include="packages.config">
+ <SubType>Designer</SubType>
+ </None>
<None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
</None>
diff --git a/Source/DafnyMenu/DafnyMenu.vsct b/Source/DafnyMenu/DafnyMenu.vsct
index 4c4b1403..02ffcf48 100644
--- a/Source/DafnyMenu/DafnyMenu.vsct
+++ b/Source/DafnyMenu/DafnyMenu.vsct
@@ -112,6 +112,15 @@
</Strings>
</Button>
+ <Button guid="guidDafnyMenuCmdSet" id="cmdidToggleAutomaticInduction" priority="0x0106" type="Button">
+ <Parent guid="guidDafnyMenuCmdSet" id="DafnyMenuGroup" />
+ <CommandFlag>DynamicVisibility</CommandFlag>
+ <CommandFlag>TextChanges</CommandFlag>
+ <Strings>
+ <ButtonText>Disable automatic induction</ButtonText>
+ </Strings>
+ </Button>
+
<Button guid="guidDafnyMenuCmdSet" id="cmdidToggleBVD" priority="0x010a" type="Button">
<Parent guid="guidDafnyMenuCmdSet" id="DafnyMenuGroup" />
<CommandFlag>DynamicVisibility</CommandFlag>
@@ -122,6 +131,16 @@
</Strings>
</Button>
+ <Button guid="guidDafnyMenuCmdSet" id="cmdidDiagnoseTimeouts" priority="0x0107" type="Button">
+ <Parent guid="guidDafnyMenuCmdSet" id="DafnyMenuGroup" />
+ <CommandFlag>DynamicVisibility</CommandFlag>
+ <CommandFlag>DefaultInvisible</CommandFlag>
+ <CommandFlag>TextChanges</CommandFlag>
+ <Strings>
+ <ButtonText>Re-verify to diagnose timeouts</ButtonText>
+ </Strings>
+ </Button>
+
</Buttons>
<!--The bitmaps section is used to define the bitmaps that are used for the commands.-->
@@ -154,6 +173,8 @@
<IDSymbol name="cmdidToggleSnapshotVerification" value="0x0103" />
<IDSymbol name="cmdidToggleBVD" value="0x0104" />
<IDSymbol name="cmdidToggleMoreAdvancedSnapshotVerification" value="0x0105" />
+ <IDSymbol name="cmdidToggleAutomaticInduction" value="0x0106" />
+ <IDSymbol name="cmdidDiagnoseTimeouts" value="0x0107" />
</GuidSymbol>
<!--
diff --git a/Source/DafnyMenu/DafnyMenuPackage.cs b/Source/DafnyMenu/DafnyMenuPackage.cs
index 0acf3554..0addda47 100644
--- a/Source/DafnyMenu/DafnyMenuPackage.cs
+++ b/Source/DafnyMenu/DafnyMenuPackage.cs
@@ -28,6 +28,12 @@ namespace DafnyLanguage.DafnyMenu
bool MoreAdvancedSnapshotVerificationCommandEnabled(IWpfTextView activeTextView);
+ bool ToggleAutomaticInduction(IWpfTextView activeTextView);
+
+
+ bool AutomaticInductionCommandEnabled(IWpfTextView activeTextView);
+
+
bool StopVerifierCommandEnabled(IWpfTextView activeTextView);
@@ -53,6 +59,12 @@ namespace DafnyLanguage.DafnyMenu
void ShowErrorModel(IWpfTextView activeTextView);
+
+
+ bool DiagnoseTimeoutsCommandEnabled(IWpfTextView activeTextView);
+
+
+ void DiagnoseTimeouts(IWpfTextView activeTextView);
}
@@ -87,7 +99,9 @@ namespace DafnyLanguage.DafnyMenu
private OleMenuCommand stopVerifierCommand;
private OleMenuCommand toggleSnapshotVerificationCommand;
private OleMenuCommand toggleMoreAdvancedSnapshotVerificationCommand;
+ private OleMenuCommand toggleAutomaticInductionCommand;
private OleMenuCommand toggleBVDCommand;
+ private OleMenuCommand diagnoseTimeoutsCommand;
bool BVDDisabled;
@@ -151,12 +165,24 @@ namespace DafnyLanguage.DafnyMenu
toggleMoreAdvancedSnapshotVerificationCommand.BeforeQueryStatus += toggleMoreAdvancedSnapshotVerificationCommand_BeforeQueryStatus;
mcs.AddCommand(toggleMoreAdvancedSnapshotVerificationCommand);
+ var toggleAutomaticInductionCommandID = new CommandID(GuidList.guidDafnyMenuCmdSet, (int)PkgCmdIDList.cmdidToggleAutomaticInduction);
+ toggleAutomaticInductionCommand = new OleMenuCommand(ToggleAutomaticInductionCallback, toggleAutomaticInductionCommandID);
+ toggleAutomaticInductionCommand.Enabled = true;
+ toggleAutomaticInductionCommand.BeforeQueryStatus += toggleAutomaticInductionCommand_BeforeQueryStatus;
+ mcs.AddCommand(toggleAutomaticInductionCommand);
+
var showErrorModelCommandID = new CommandID(GuidList.guidDafnyMenuCmdSet, (int)PkgCmdIDList.cmdidToggleBVD);
toggleBVDCommand = new OleMenuCommand(ToggleBVDCallback, showErrorModelCommandID);
toggleBVDCommand.Enabled = true;
toggleBVDCommand.BeforeQueryStatus += showErrorModelCommand_BeforeQueryStatus;
mcs.AddCommand(toggleBVDCommand);
+ var diagnoseTimeoutsCommandID = new CommandID(GuidList.guidDafnyMenuCmdSet, (int)PkgCmdIDList.cmdidDiagnoseTimeouts);
+ diagnoseTimeoutsCommand = new OleMenuCommand(DiagnoseTimeoutsCallback, diagnoseTimeoutsCommandID);
+ diagnoseTimeoutsCommand.Enabled = true;
+ diagnoseTimeoutsCommand.BeforeQueryStatus += diagnoseTimeoutsCommand_BeforeQueryStatus;
+ mcs.AddCommand(diagnoseTimeoutsCommand);
+
var menuCommandID = new CommandID(GuidList.guidDafnyMenuPkgSet, (int)PkgCmdIDList.cmdidMenu);
menuCommand = new OleMenuCommand(new EventHandler((sender, e) => { }), menuCommandID);
menuCommand.BeforeQueryStatus += menuCommand_BeforeQueryStatus;
@@ -221,6 +247,14 @@ namespace DafnyLanguage.DafnyMenu
}
}
+ void ToggleAutomaticInductionCallback(object sender, EventArgs e) {
+ var atv = ActiveTextView;
+ if (MenuProxy != null && atv != null) {
+ var nowAutomatic = MenuProxy.ToggleAutomaticInduction(atv);
+ toggleAutomaticInductionCommand.Text = (nowAutomatic ? "Disable" : "Enable") + " automatic induction";
+ }
+ }
+
void stopVerifierCommand_BeforeQueryStatus(object sender, EventArgs e)
{
var atv = ActiveTextView;
@@ -301,6 +335,16 @@ namespace DafnyLanguage.DafnyMenu
}
}
+ void diagnoseTimeoutsCommand_BeforeQueryStatus(object sender, EventArgs e)
+ {
+ var atv = ActiveTextView;
+ if (MenuProxy != null && atv != null)
+ {
+ var visible = MenuProxy.DiagnoseTimeoutsCommandEnabled(atv);
+ diagnoseTimeoutsCommand.Visible = visible;
+ }
+ }
+
private void toggleMoreAdvancedSnapshotVerificationCommand_BeforeQueryStatus(object sender, EventArgs e)
{
var atv = ActiveTextView;
@@ -311,12 +355,29 @@ namespace DafnyLanguage.DafnyMenu
}
}
+ private void toggleAutomaticInductionCommand_BeforeQueryStatus(object sender, EventArgs e) {
+ var atv = ActiveTextView;
+ if (MenuProxy != null && atv != null) {
+ var visible = MenuProxy.AutomaticInductionCommandEnabled(atv);
+ toggleAutomaticInductionCommand.Visible = visible;
+ }
+ }
+
void ToggleBVDCallback(object sender, EventArgs e)
{
BVDDisabled = !BVDDisabled;
toggleBVDCommand.Text = (BVDDisabled ? "Enable" : "Disable") + " BVD";
}
+ void DiagnoseTimeoutsCallback(object sender, EventArgs e)
+ {
+ var atv = ActiveTextView;
+ if (MenuProxy != null && atv != null)
+ {
+ MenuProxy.DiagnoseTimeouts(atv);
+ }
+ }
+
public void ExecuteAsCompiling(Action action, TextWriter outputWriter)
{
IVsStatusbar statusBar = (IVsStatusbar)GetGlobalService(typeof(SVsStatusbar));
@@ -347,7 +408,7 @@ namespace DafnyLanguage.DafnyMenu
var window = this.FindToolWindow(typeof(BvdToolWindow), 0, true);
if ((window == null) || (window.Frame == null))
{
- throw new NotSupportedException("Can not create BvdToolWindow.");
+ throw new NotSupportedException("Cannot create BvdToolWindow.");
}
BvdToolWindow.BVD.HideMenuStrip();
diff --git a/Source/DafnyMenu/PkgCmdID.cs b/Source/DafnyMenu/PkgCmdID.cs
index b6f30145..ba4b786d 100644
--- a/Source/DafnyMenu/PkgCmdID.cs
+++ b/Source/DafnyMenu/PkgCmdID.cs
@@ -13,5 +13,7 @@ namespace DafnyLanguage.DafnyMenu
public static uint cmdidToggleSnapshotVerification = 0x103;
public const uint cmdidToggleBVD = 0x104;
public static uint cmdidToggleMoreAdvancedSnapshotVerification = 0x105;
+ public static uint cmdidToggleAutomaticInduction = 0x106;
+ public static uint cmdidDiagnoseTimeouts = 0x107;
};
} \ No newline at end of file
diff --git a/Source/DafnyMenu/packages.config b/Source/DafnyMenu/packages.config
new file mode 100644
index 00000000..60f856c5
--- /dev/null
+++ b/Source/DafnyMenu/packages.config
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="VSSDK.ComponentModelHost" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.ComponentModelHost.11" version="11.0.3" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.CoreUtility" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.CoreUtility.11" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.DTE" version="7.0.4" targetFramework="net45" />
+ <package id="VSSDK.Editor" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.IDE" version="7.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.IDE.10" version="10.0.4" targetFramework="net45" />
+ <package id="VSSDK.IDE.11" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.IDE.8" version="8.0.4" targetFramework="net45" />
+ <package id="VSSDK.IDE.9" version="9.0.4" targetFramework="net45" />
+ <package id="VSSDK.Language" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.Language.11" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.OLE.Interop" version="7.0.4" targetFramework="net45" />
+ <package id="VSSDK.Shell.11" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.Shell.Immutable.10" version="10.0.4" targetFramework="net45" />
+ <package id="VSSDK.Shell.Immutable.11" version="11.0.4" targetFramework="net45" />
+ <package id="VSSDK.Shell.Interop" version="7.0.4" targetFramework="net45" />
+ <package id="VSSDK.Shell.Interop.10" version="10.0.4" targetFramework="net45" />
+ <package id="VSSDK.Shell.Interop.11" version="11.0.4" targetFramework="net45" />
+ <package id="VSSDK.Shell.Interop.8" version="8.0.4" targetFramework="net45" />
+ <package id="VSSDK.Shell.Interop.9" version="9.0.4" targetFramework="net45" />
+ <package id="VSSDK.Text" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.Text.11" version="11.0.4" targetFramework="net45" developmentDependency="true" />
+ <package id="VSSDK.TextManager.Interop" version="7.0.4" targetFramework="net45" />
+ <package id="VSSDK.TextManager.Interop.8" version="8.0.4" targetFramework="net45" />
+</packages>
diff --git a/Source/DafnyMenu/source.extension.vsixmanifest b/Source/DafnyMenu/source.extension.vsixmanifest
index 7beeb69e..814374ee 100644
--- a/Source/DafnyMenu/source.extension.vsixmanifest
+++ b/Source/DafnyMenu/source.extension.vsixmanifest
@@ -6,7 +6,8 @@
<Description xml:space="preserve">This is a menu for interacting with Dafny.</Description>
</Metadata>
<Installation InstalledByMsi="false">
- <InstallationTarget Version="[11.0,13.0)" Id="Microsoft.VisualStudio.Pro" />
+ <InstallationTarget Version="[11.0,15.0)" Id="Microsoft.VisualStudio.Pro" />
+ <InstallationTarget Version="[14.0,15.0)" Id="Microsoft.VisualStudio.Community" />
</Installation>
<Dependencies>
<Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="4.5" />
diff --git a/Source/DafnyServer/App.config b/Source/DafnyServer/App.config
new file mode 100644
index 00000000..fad249e4
--- /dev/null
+++ b/Source/DafnyServer/App.config
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+ <startup>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
+ </startup>
+</configuration> \ No newline at end of file
diff --git a/Source/DafnyServer/DafnyHelper.cs b/Source/DafnyServer/DafnyHelper.cs
new file mode 100644
index 00000000..e54e2b48
--- /dev/null
+++ b/Source/DafnyServer/DafnyHelper.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Boogie;
+using Bpl = Microsoft.Boogie;
+
+namespace Microsoft.Dafny {
+ // FIXME: This should not be duplicated here
+ class DafnyConsolePrinter : ConsolePrinter {
+ public override void ReportBplError(IToken tok, string message, bool error, TextWriter tw, string category = null) {
+ // Dafny has 0-indexed columns, but Boogie counts from 1
+ var realigned_tok = new Token(tok.line, tok.col - 1);
+ realigned_tok.kind = tok.kind;
+ realigned_tok.pos = tok.pos;
+ realigned_tok.val = tok.val;
+ realigned_tok.filename = tok.filename;
+ base.ReportBplError(realigned_tok, message, error, tw, category);
+
+ if (tok is Dafny.NestedToken) {
+ var nt = (Dafny.NestedToken)tok;
+ ReportBplError(nt.Inner, "Related location", false, tw);
+ }
+ }
+ }
+
+ class DafnyHelper {
+ private string fname;
+ private string source;
+ private string[] args;
+
+ private readonly Dafny.ErrorReporter reporter;
+ private Dafny.Program dafnyProgram;
+ private Bpl.Program boogieProgram;
+
+ public DafnyHelper(string[] args, string fname, string source) {
+ this.args = args;
+ this.fname = fname;
+ this.source = source;
+ this.reporter = new Dafny.ConsoleErrorReporter();
+ }
+
+ public bool Verify() {
+ ServerUtils.ApplyArgs(args, reporter);
+ return Parse() && Resolve() && Translate() && Boogie();
+ }
+
+ private bool Parse() {
+ Dafny.ModuleDecl module = new Dafny.LiteralModuleDecl(new Dafny.DefaultModuleDecl(), null);
+ Dafny.BuiltIns builtIns = new Dafny.BuiltIns();
+ var success = (Dafny.Parser.Parse(source, fname, fname, module, builtIns, new Dafny.Errors(reporter)) == 0 &&
+ Dafny.Main.ParseIncludes(module, builtIns, new List<string>(), new Dafny.Errors(reporter)) == null);
+ if (success) {
+ dafnyProgram = new Dafny.Program(fname, module, builtIns, reporter);
+ }
+ return success;
+ }
+
+ private bool Resolve() {
+ var resolver = new Dafny.Resolver(dafnyProgram);
+ resolver.ResolveProgram(dafnyProgram);
+ return reporter.Count(ErrorLevel.Error) == 0;
+ }
+
+ private bool Translate() {
+ var translator = new Dafny.Translator(reporter) { InsertChecksums = true, UniqueIdPrefix = fname };
+ boogieProgram = translator.Translate(dafnyProgram); // FIXME how are translation errors reported?
+ return true;
+ }
+
+ private bool Boogie() {
+ if (boogieProgram.Resolve() == 0 && boogieProgram.Typecheck() == 0) { //FIXME ResolveAndTypecheck?
+ ExecutionEngine.EliminateDeadVariables(boogieProgram);
+ ExecutionEngine.CollectModSets(boogieProgram);
+ ExecutionEngine.CoalesceBlocks(boogieProgram);
+ ExecutionEngine.Inline(boogieProgram);
+
+ //NOTE: We could capture errors instead of printing them (pass a delegate instead of null)
+ switch (ExecutionEngine.InferAndVerify(boogieProgram, new PipelineStatistics(), "ServerProgram", null, DateTime.UtcNow.Ticks.ToString())) {
+ case PipelineOutcome.Done:
+ case PipelineOutcome.VerificationCompleted:
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/Source/DafnyServer/DafnyServer.csproj b/Source/DafnyServer/DafnyServer.csproj
new file mode 100644
index 00000000..af262fc3
--- /dev/null
+++ b/Source/DafnyServer/DafnyServer.csproj
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>DafnyServer</RootNamespace>
+ <AssemblyName>DafnyServer</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <StartupObject>Microsoft.Dafny.Server</StartupObject>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>..\..\Binaries\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>..\..\Binaries\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Checked|AnyCPU'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>..\..\Binaries\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <DebugType>full</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisIgnoreBuiltInRuleSets>false</CodeAnalysisIgnoreBuiltInRuleSets>
+ <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking>
+ <CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
+ <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
+ <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
+ <CodeContractsRuntimeSkipQuantifiers>False</CodeContractsRuntimeSkipQuantifiers>
+ <CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis>
+ <CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
+ <CodeContractsBoundsObligations>False</CodeContractsBoundsObligations>
+ <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
+ <CodeContractsEnumObligations>False</CodeContractsEnumObligations>
+ <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions>
+ <CodeContractsRunInBackground>True</CodeContractsRunInBackground>
+ <CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
+ <CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
+ <CodeContractsEmitXMLDocs>False</CodeContractsEmitXMLDocs>
+ <CodeContractsCustomRewriterAssembly />
+ <CodeContractsCustomRewriterClass />
+ <CodeContractsLibPaths />
+ <CodeContractsExtraRewriteOptions />
+ <CodeContractsExtraAnalysisOptions />
+ <CodeContractsBaseLineFile />
+ <CodeContractsCacheAnalysisResults>False</CodeContractsCacheAnalysisResults>
+ <CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
+ <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly>
+ <CodeContractsAnalysisWarningLevel>0</CodeContractsAnalysisWarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Core">
+ <HintPath>..\..\..\boogie\Binaries\Core.dll</HintPath>
+ </Reference>
+ <Reference Include="ExecutionEngine">
+ <HintPath>..\..\..\boogie\Binaries\ExecutionEngine.dll</HintPath>
+ </Reference>
+ <Reference Include="ParserHelper">
+ <HintPath>..\..\..\boogie\Binaries\ParserHelper.dll</HintPath>
+ </Reference>
+ <Reference Include="Provers.SMTLib">
+ <HintPath>..\..\..\boogie\Binaries\Provers.SMTLib.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Runtime.Serialization" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="DafnyHelper.cs" />
+ <Compile Include="Utilities.cs" />
+ <Compile Include="VerificationTask.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Server.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="App.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Dafny\DafnyPipeline.csproj">
+ <Project>{fe44674a-1633-4917-99f4-57635e6fa740}</Project>
+ <Name>DafnyPipeline</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/Source/DafnyServer/Properties/AssemblyInfo.cs b/Source/DafnyServer/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..5b996543
--- /dev/null
+++ b/Source/DafnyServer/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Dafny Server")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft Research")]
+[assembly: AssemblyProduct("Dafny Server")]
+[assembly: AssemblyCopyright("Copyright © 2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("44d9c1c1-773b-47c1-b876-2de17e70152e")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Source/DafnyServer/Server.cs b/Source/DafnyServer/Server.cs
new file mode 100644
index 00000000..8c94ad5b
--- /dev/null
+++ b/Source/DafnyServer/Server.cs
@@ -0,0 +1,100 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Json;
+
+using Dafny = Microsoft.Dafny;
+using Bpl = Microsoft.Boogie;
+using Microsoft.Boogie;
+
+namespace Microsoft.Dafny {
+ class Server {
+ private bool running;
+
+ static void Main(string[] args) {
+ Server server = new Server();
+
+ var hasArg = args.Length > 0;
+ var arg = hasArg ? args[0] : null;
+
+ if (hasArg && args[0] == "selftest") {
+ VerificationTask.SelfTest();
+ } else if (hasArg && File.Exists(arg)) {
+ Console.WriteLine("# Reading from {0}", Path.GetFileName(arg));
+ Console.SetIn(new StreamReader(arg, Encoding.UTF8));
+ server.Loop();
+ } else {
+ server.Loop();
+ }
+ }
+
+ private void SetupConsole() {
+ // Setting InputEncoding to UTF8 causes z3 to choke.
+ Console.OutputEncoding = new UTF8Encoding(false, true);
+ }
+
+ public Server() {
+ this.running = true;
+ ExecutionEngine.printer = new DafnyConsolePrinter();
+ SetupConsole();
+ }
+
+ bool EndOfPayload(out string line) {
+ line = Console.ReadLine();
+ return line == null || line == Interaction.CLIENT_EOM_TAG;
+ }
+
+ string ReadPayload() {
+ StringBuilder buffer = new StringBuilder();
+ string line = null;
+ while (!EndOfPayload(out line)) {
+ buffer.Append(line);
+ }
+ return buffer.ToString();
+ }
+
+ void Loop() {
+ for (int cycle = 0; running; cycle++) {
+ var line = Console.ReadLine() ?? "quit";
+ if (line != String.Empty && !line.StartsWith("#")) {
+ var command = line.Split();
+ Respond(command);
+ }
+ }
+ }
+
+ void Respond(string[] command) {
+ try {
+ if (command.Length == 0) {
+ throw new ServerException("Empty command");
+ }
+
+ var verb = command[0];
+ if (verb == "verify") {
+ ServerUtils.checkArgs(command, 0);
+ var payload = ReadPayload();
+ VerificationTask.ReadTask(payload).Run();
+ } else if (verb == "quit") {
+ ServerUtils.checkArgs(command, 0);
+ Exit();
+ } else {
+ throw new ServerException("Unknown verb '{0}'", verb);
+ }
+
+ Interaction.EOM(Interaction.SUCCESS, "Verification completed successfully!");
+ } catch (ServerException ex) {
+ Interaction.EOM(Interaction.FAILURE, ex);
+ } catch (Exception ex) {
+ Interaction.EOM(Interaction.FAILURE, ex, "[FATAL]");
+ running = false;
+ }
+ }
+
+ void Exit() {
+ this.running = false;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/Source/DafnyServer/Utilities.cs b/Source/DafnyServer/Utilities.cs
new file mode 100644
index 00000000..48bea01a
--- /dev/null
+++ b/Source/DafnyServer/Utilities.cs
@@ -0,0 +1,61 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Json;
+using Microsoft.Boogie;
+
+namespace Microsoft.Dafny {
+ class Interaction {
+ internal static string SUCCESS = "SUCCESS";
+ internal static string FAILURE = "FAILURE";
+ internal static string SERVER_EOM_TAG = "[[DAFNY-SERVER: EOM]]";
+ internal static string CLIENT_EOM_TAG = "[[DAFNY-CLIENT: EOM]]";
+
+ internal static void EOM(string header, string msg) {
+ var trailer = (msg == null) ? "" : "\n";
+ Console.Write("{0}{1}[{2}] {3}\n", msg ?? "", trailer, header, SERVER_EOM_TAG);
+ Console.Out.Flush();
+ }
+
+ internal static void EOM(string header, Exception ex, string subHeader = "") {
+ var aggregate = ex as AggregateException;
+ subHeader = String.IsNullOrEmpty(subHeader) ? "" : subHeader + " ";
+
+ if (aggregate == null) {
+ EOM(header, subHeader + ex.Message);
+ } else {
+ EOM(header, subHeader + aggregate.InnerExceptions.MapConcat(exn => exn.Message, ", "));
+ }
+ }
+ }
+
+ class ServerException : Exception {
+ internal ServerException(string message) : base(message) { }
+ internal ServerException(string message, params object[] args) : base(String.Format(message, args)) { }
+ }
+
+ class ServerUtils {
+ internal static void checkArgs(string[] command, int expectedLength) {
+ if (command.Length - 1 != expectedLength) {
+ throw new ServerException("Invalid argument count (got {0}, expected {1})", command.Length - 1, expectedLength);
+ }
+ }
+
+ internal static void ApplyArgs(string[] args, ErrorReporter reporter) {
+ Dafny.DafnyOptions.Install(new Dafny.DafnyOptions(reporter));
+ Dafny.DafnyOptions.O.ProverKillTime = 10; //This is just a default; it can be overriden
+
+ if (CommandLineOptions.Clo.Parse(args)) {
+ DafnyOptions.O.VerifySnapshots = 2; // Use caching
+ DafnyOptions.O.VcsCores = Math.Max(1, System.Environment.ProcessorCount / 2); // Don't use too many cores
+ DafnyOptions.O.PrintTooltips = true; // Dump tooptips (ErrorLevel.Info) to stdout
+ //DafnyOptions.O.UnicodeOutput = true; // Use pretty warning signs
+ DafnyOptions.O.TraceProofObligations = true; // Show which method is being verified, but don't show duration of verification
+ } else {
+ throw new ServerException("Invalid command line options");
+ }
+ }
+ }
+}
diff --git a/Source/DafnyServer/VerificationTask.cs b/Source/DafnyServer/VerificationTask.cs
new file mode 100644
index 00000000..eb740e70
--- /dev/null
+++ b/Source/DafnyServer/VerificationTask.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Json;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.Dafny {
+ [Serializable]
+ class VerificationTask {
+ [DataMember]
+ string[] args = null;
+
+ [DataMember]
+ string filename = null;
+
+ [DataMember]
+ string source = null;
+
+ [DataMember]
+ bool sourceIsFile = false;
+
+ string ProgramSource { get { return sourceIsFile ? File.ReadAllText(source) : source; } }
+
+ internal static VerificationTask ReadTask(string b64_repr) {
+ try {
+ var json = Encoding.UTF8.GetString(System.Convert.FromBase64String(b64_repr));
+ using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json))) {
+ DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(VerificationTask));
+ return (VerificationTask)serializer.ReadObject(ms);
+ }
+ } catch (Exception ex) {
+ throw new ServerException("Deserialization failed: {0}.", ex.Message);
+ }
+ }
+
+ internal static void SelfTest() {
+ var task = new VerificationTask() {
+ filename = "<none>",
+ sourceIsFile = false,
+ args = new string[] { },
+ source = "method selftest() { assert true; }"
+ };
+ try {
+ task.Run();
+ Interaction.EOM(Interaction.SUCCESS, null);
+ } catch (Exception ex) {
+ Interaction.EOM(Interaction.FAILURE, ex);
+ }
+ }
+
+ internal void Run() {
+ new DafnyHelper(args, filename, ProgramSource).Verify();
+ }
+ }
+}
diff --git a/Source/version.cs b/Source/version.cs
index 1234a75b..24de7cec 100644
--- a/Source/version.cs
+++ b/Source/version.cs
@@ -1,4 +1,4 @@
using System.Reflection;
-// Version 1.9.5, year 2013+2 month 05 day 11
-[assembly: AssemblyVersion("1.9.5.20511")]
-[assembly: AssemblyFileVersion("1.9.5.20511")]
+// Version 1.9.7, year 2013+3 month 04 day 01
+[assembly: AssemblyVersion("1.9.7.30401")]
+[assembly: AssemblyFileVersion("1.9.7.30401")]
diff --git a/Test/VSComp2010/Problem2-Invert.dfy b/Test/VSComp2010/Problem2-Invert.dfy
index 274d86de..0cf93061 100644
--- a/Test/VSComp2010/Problem2-Invert.dfy
+++ b/Test/VSComp2010/Problem2-Invert.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %dafny /compile:0 /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// VSComp 2010, problem 2, compute the inverse 'B' of a permutation 'A' and prove that 'B' is
diff --git a/Test/VSI-Benchmarks/b3.dfy b/Test/VSI-Benchmarks/b3.dfy
index f59b0c3e..d47c4d4d 100644
--- a/Test/VSI-Benchmarks/b3.dfy
+++ b/Test/VSI-Benchmarks/b3.dfy
@@ -96,7 +96,7 @@ class Benchmark3 {
if x < m { k := j; m := x; }
j := j+1;
}
-
+
j := 0;
while j < k
invariant j <= k;
@@ -108,10 +108,16 @@ class Benchmark3 {
RotationLemma(old(q.contents), j, qc0, q.contents);
j := j+1;
}
-
+
assert j == k;
assert q.contents == old(q.contents)[k..] + old(q.contents)[..k];
+ ghost var qq := q.contents;
m := q.Dequeue();
+ assert q.contents == qq[1..] && m == qq[0];
+ assert [m] + q.contents == qq;
+ assert |old(q.contents)| == |q.contents| + 1;
+
+ assert q.contents == old(q.contents)[k+1..] + old(q.contents)[..k];
}
lemma RotationLemma(O: seq, j: nat, A: seq, C: seq)
diff --git a/Test/VSI-Benchmarks/b8.dfy b/Test/VSI-Benchmarks/b8.dfy
index ea1911fe..a44ff5c3 100644
--- a/Test/VSI-Benchmarks/b8.dfy
+++ b/Test/VSI-Benchmarks/b8.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %dafny /compile:0 /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// Benchmark 8
diff --git a/Test/VerifyThis2015/Problem1.dfy b/Test/VerifyThis2015/Problem1.dfy
index 2e8a5243..1b54e918 100644
--- a/Test/VerifyThis2015/Problem1.dfy
+++ b/Test/VerifyThis2015/Problem1.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:3 /autoTriggers:1 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// Rustan Leino
@@ -161,18 +161,13 @@ lemma Same2<T>(pat: seq<T>, a: seq<T>)
ensures IRP_Alt(pat, a)
{
if pat == [] {
- assert pat <= a;
} else if a != [] && pat[0] == a[0] {
- assert IsRelaxedPrefixAux(pat[1..], a[1..], 1);
- Same2(pat[1..], a[1..]);
if pat[1..] <= a[1..] {
- assert pat <= a;
} else {
var k :| 0 <= k < |pat[1..]| && pat[1..][..k] + pat[1..][k+1..] <= a[1..];
assert 0 <= k+1 < |pat| && pat[..k+1] + pat[k+2..] <= a;
}
} else {
- assert IsRelaxedPrefixAux(pat[1..], a, 0);
Same2_Prefix(pat[1..], a);
assert pat[1..] <= a;
assert 0 <= 0 < |pat| && pat[..0] + pat[0+1..] <= a;
@@ -182,7 +177,4 @@ lemma Same2_Prefix<T>(pat: seq<T>, a: seq<T>)
requires IsRelaxedPrefixAux(pat, a, 0)
ensures pat <= a
{
- if pat != [] {
- Same2_Prefix(pat[1..], a[1..]);
- }
}
diff --git a/Test/VerifyThis2015/Problem2.dfy b/Test/VerifyThis2015/Problem2.dfy
index 1c7deffd..2bb63e7b 100644
--- a/Test/VerifyThis2015/Problem2.dfy
+++ b/Test/VerifyThis2015/Problem2.dfy
@@ -114,7 +114,7 @@ method ParallelGcd(A: int, B: int) returns (gcd: int)
var pc0, pc1 := 0, 0; // program counter for the two processes
var a0, b0, a1, b1; // local variables for the two processes
// To model fairness of scheduling, these "budget" variable give a bound on the number of times the
- // scheduler will repeatedly schedule on process to execute its "compare a and b" test. When a
+ // scheduler will repeatedly schedule one process to execute its "compare a and b" test. When a
// process executes its comparison, its budget is decreased and the budget for the other process
// is set to some arbitrary positive amount.
var budget0, budget1 :| budget0 > 0 && budget1 > 0;
@@ -315,7 +315,7 @@ lemma GcdDecrease(a: int, b: int)
ensures Gcd(a, b) == Gcd(a - b, b)
{
var k := Gcd(a - b, b);
- assert DividesBoth(k, a-b, b) && forall m :: DividesBoth(m, a-b, b) ==> m <= k;
+ assert DividesBoth(k, a-b, b) && forall m, mm :: mm == a - b ==> DividesBoth(m, mm, b) ==> m <= k; // WISH: auto-generate 'mm'
var n := DividesProperty(k, a-b);
assert n*k == a-b;
var p := DividesProperty(k, b);
diff --git a/Test/VerifyThis2015/Problem3.dfy b/Test/VerifyThis2015/Problem3.dfy
index 4205035d..60506a33 100644
--- a/Test/VerifyThis2015/Problem3.dfy
+++ b/Test/VerifyThis2015/Problem3.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" /vcsMaxKeepGoingSplits:5 "%s" > "%t"
+// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// Rustan Leino
@@ -92,6 +92,21 @@ class DoublyLinkedList {
}
Nodes := nodes;
}
+
+ function PopMiddle<T>(s: seq<T>, k: nat) : seq<T>
+ requires k < |s| {
+ s[..k] + s[k+1..]
+ }
+
+ predicate Injective<T>(s: seq<T>) {
+ forall j, k :: 0 <= j < k < |s| ==> s[j] != s[k]
+ }
+
+ lemma InjectiveAfterPop<T>(s: seq<T>, k: nat)
+ requires k < |s|
+ requires Injective(s)
+ ensures Injective(PopMiddle(s, k)) { }
+
method Remove(x: Node) returns (ghost k: int)
requires Valid()
requires x in Nodes && x != Nodes[0] && x != Nodes[|Nodes|-1] // not allowed to remove end nodes; you may think of them as a sentinel nodes
@@ -103,8 +118,11 @@ class DoublyLinkedList {
k :| 1 <= k < |Nodes|-1 && Nodes[k] == x;
x.R.L := x.L;
x.L.R := x.R;
+
+ InjectiveAfterPop(Nodes, k);
Nodes := Nodes[..k] + Nodes[k+1..];
}
+
// One might consider have a precondition that says there exists a "k" with the properties given here.
// However, we want to be able to refer to "k" in the postcondition as well, so it's convenient to
// burden the client with having to pass in "k" as a ghost parameter. This, however, is really no
diff --git a/Test/VerifyThis2015/Problem3.dfy.expect b/Test/VerifyThis2015/Problem3.dfy.expect
index 4035605c..d3a9554b 100644
--- a/Test/VerifyThis2015/Problem3.dfy.expect
+++ b/Test/VerifyThis2015/Problem3.dfy.expect
@@ -1,5 +1,5 @@
-Dafny program verifier finished with 15 verified, 0 errors
+Dafny program verifier finished with 19 verified, 0 errors
Program compiled successfully
Running...
diff --git a/Test/cloudmake/CloudMake-CachedBuilds.dfy b/Test/cloudmake/CloudMake-CachedBuilds.dfy
index 9e1b511e..5f16da90 100644
--- a/Test/cloudmake/CloudMake-CachedBuilds.dfy
+++ b/Test/cloudmake/CloudMake-CachedBuilds.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// This module proves the correctness of the algorithms. It leaves a number of things undefined.
diff --git a/Test/cloudmake/CloudMake-ConsistentBuilds.dfy b/Test/cloudmake/CloudMake-ConsistentBuilds.dfy
index 815352f6..c2fa4205 100644
--- a/Test/cloudmake/CloudMake-ConsistentBuilds.dfy
+++ b/Test/cloudmake/CloudMake-ConsistentBuilds.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
/******* State *******/
@@ -23,7 +23,7 @@ function Union(st: State, st': State): State
(p in DomSt(st) ==> GetSt(p, result) == GetSt(p, st)) &&
(p in DomSt(st') ==> GetSt(p, result) == GetSt(p, st'));
-ghost method StateEqualityProperty(st: State, st': State)
+lemma StateEqualityProperty(st: State, st': State)
requires DomSt(st) == DomSt(st');
requires forall p :: p in DomSt(st) ==> GetSt(p, st) == GetSt(p, st');
ensures st == st';
@@ -60,7 +60,7 @@ function UpdateC(cmd: Expression, deps: Expression, exts: Expression, stC: State
UpdateC(cmd, deps, exts', S(stC.st, c'))
}
-ghost method UpdateCLemma(cmd: Expression, deps: Expression, exts: Expression, stC: StateC)
+lemma UpdateCLemma(cmd: Expression, deps: Expression, exts: Expression, stC: StateC)
requires
cmd.exprLiteral? && cmd.lit.litString? &&
deps.exprLiteral? && deps.lit.litArrOfPaths? &&
@@ -136,7 +136,7 @@ function CombineC(stsC: set<StateC>): StateC
UnionC(stC, CombineC(stsC - {stC}))
}
-ghost method CombineCLemma(stsC: set<StateC>)
+lemma CombineCLemma(stsC: set<StateC>)
requires stsC != {};
requires forall stC :: stC in stsC ==> ConsistentCache(stC);
ensures
@@ -174,7 +174,7 @@ function SetEnv(id: Identifier, expr: Expression, env: Env): Env
/******* Primitive function 'exec' *******/
function exec(cmd: Expression, deps: Expression, exts: Expression, st: State): Tuple<Expression, State>
-ghost method ExecProperty(cmd: Expression, deps: Expression, exts: Expression, st: State)
+lemma ExecProperty(cmd: Expression, deps: Expression, exts: Expression, st: State)
requires
cmd.exprLiteral? && cmd.lit.litString? &&
deps.exprLiteral? && deps.lit.litArrOfPaths? &&
@@ -244,7 +244,7 @@ function execC(cmd: Expression, deps: Expression, exts: Expression, stC: StateC)
Pair(expr', stC')
}
-ghost method ExecCProperty(cmd: Expression, deps: Expression, exts: Expression, stC: StateC)
+lemma ExecCProperty(cmd: Expression, deps: Expression, exts: Expression, stC: StateC)
requires
cmd.exprLiteral? && cmd.lit.litString? &&
deps.exprLiteral? && deps.lit.litArrOfPaths? &&
@@ -305,7 +305,7 @@ predicate PostC(cmd: Expression, deps: Expression, exts: Expression, stC: StateC
function Hash(p: Path): HashValue
-ghost method HashProperty(cmd: Expression, deps: Expression, ext: string, cmd': Expression, deps': Expression, ext': string)
+lemma HashProperty(cmd: Expression, deps: Expression, ext: string, cmd': Expression, deps': Expression, ext': string)
requires Hash(Loc(cmd, deps, ext)) == Hash(Loc(cmd', deps', ext'));
ensures cmd == cmd' && deps == deps' && ext == ext';
@@ -509,7 +509,7 @@ predicate ValidArgsC(prim: Primitive, args: seq<Expression>, stC: StateC)
}
/******* {consistent_cache} buildC {no_bad_cache_error /\ consistent_cache} *******/
-ghost method CachedBuildsTheorem(prog: Program, stC: StateC)
+lemma CachedBuildsTheorem(prog: Program, stC: StateC)
requires Legal(prog.stmts);
requires ConsistentCache(stC);
ensures
@@ -521,7 +521,7 @@ ghost method CachedBuildsTheorem(prog: Program, stC: StateC)
BuildCLemma(prog, stC);
}
-ghost method BuildCLemma(prog: Program, stC: StateC)
+lemma BuildCLemma(prog: Program, stC: StateC)
requires Legal(prog.stmts);
requires ConsistentCache(stC);
ensures
@@ -532,7 +532,7 @@ ghost method BuildCLemma(prog: Program, stC: StateC)
DoCLemma(prog.stmts, stC, EmptyEnv());
}
-ghost method DoCLemma(stmts: seq<Statement>, stC: StateC, env: Env)
+lemma DoCLemma(stmts: seq<Statement>, stC: StateC, env: Env)
requires Legal(stmts);
requires ConsistentCache(stC);
ensures
@@ -558,7 +558,7 @@ ghost method DoCLemma(stmts: seq<Statement>, stC: StateC, env: Env)
}
}
-ghost method {:induction expr} EvalCLemma(expr: Expression, stC: StateC, env: Env)
+lemma {:induction expr} EvalCLemma(expr: Expression, stC: StateC, env: Env)
requires ConsistentCache(stC);
ensures
var result := evalC(expr, stC, env);
@@ -627,7 +627,7 @@ ghost method {:induction expr} EvalCLemma(expr: Expression, stC: StateC, env: En
} else { }
}
-ghost method EvalArgsCLemma(expr: Expression, args: seq<Expression>, stC: StateC, env: Env)
+lemma EvalArgsCLemma(expr: Expression, args: seq<Expression>, stC: StateC, env: Env)
requires ConsistentCache(stC);
requires forall arg :: arg in args ==> arg < expr;
ensures
@@ -640,7 +640,7 @@ ghost method EvalArgsCLemma(expr: Expression, args: seq<Expression>, stC: StateC
EvalArgsC'Lemma(expr, args, stC, env, [], {});
}
-ghost method EvalArgsC'Lemma(expr: Expression, args: seq<Expression>, stC: StateC, env: Env,
+lemma EvalArgsC'Lemma(expr: Expression, args: seq<Expression>, stC: StateC, env: Env,
args': seq<Expression>, stsC': set<StateC>)
requires ConsistentCache(stC);
requires forall stC' :: stC' in stsC' ==> ConsistentCache(stC');
diff --git a/Test/cloudmake/CloudMake-ParallelBuilds.dfy b/Test/cloudmake/CloudMake-ParallelBuilds.dfy
index 07cae317..5cc70994 100644
--- a/Test/cloudmake/CloudMake-ParallelBuilds.dfy
+++ b/Test/cloudmake/CloudMake-ParallelBuilds.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// This module proves the correctness of the algorithms. It leaves a number of things undefined.
diff --git a/Test/dafny0/AdvancedLHS.dfy.expect b/Test/dafny0/AdvancedLHS.dfy.expect
index cb817605..aab12cfc 100644
--- a/Test/dafny0/AdvancedLHS.dfy.expect
+++ b/Test/dafny0/AdvancedLHS.dfy.expect
@@ -1,4 +1,4 @@
-AdvancedLHS.dfy(34,23): Error: target object may be null
+AdvancedLHS.dfy(34,22): Error: target object may be null
Execution trace:
(0,0): anon0
(0,0): anon15_Else
diff --git a/Test/dafny0/Array.dfy b/Test/dafny0/Array.dfy
index 391ca5f7..309e9248 100644
--- a/Test/dafny0/Array.dfy
+++ b/Test/dafny0/Array.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
class A {
@@ -327,3 +327,5 @@ module DtypeRegression {
}
}
}
+
+// WISH: autoTriggers disabled because of induction
diff --git a/Test/dafny0/Array.dfy.expect b/Test/dafny0/Array.dfy.expect
index bf4da25f..40fb318d 100644
--- a/Test/dafny0/Array.dfy.expect
+++ b/Test/dafny0/Array.dfy.expect
@@ -1,16 +1,16 @@
-Array.dfy(13,8): Error: assignment may update an array element not in the enclosing context's modifies clause
+Array.dfy(13,7): Error: assignment may update an array element not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
(0,0): anon5_Then
(0,0): anon2
(0,0): anon6_Then
-Array.dfy(20,16): Error: target object may be null
+Array.dfy(20,15): Error: target object may be null
Execution trace:
(0,0): anon0
-Array.dfy(27,6): Error: index out of range
+Array.dfy(27,5): Error: index out of range
Execution trace:
(0,0): anon0
-Array.dfy(51,20): Error: assertion violation
+Array.dfy(51,19): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon12_Then
@@ -21,19 +21,19 @@ Execution trace:
(0,0): anon16_Then
(0,0): anon9
(0,0): anon11
-Array.dfy(59,8): Error: assignment may update an array element not in the enclosing context's modifies clause
+Array.dfy(59,7): Error: assignment may update an array element not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
(0,0): anon5_Then
(0,0): anon2
(0,0): anon6_Then
-Array.dfy(66,8): Error: assignment may update an array element not in the enclosing context's modifies clause
+Array.dfy(66,7): Error: assignment may update an array element not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
(0,0): anon5_Then
(0,0): anon2
(0,0): anon6_Then
-Array.dfy(110,21): Error: upper bound below lower bound or above length of array
+Array.dfy(110,20): Error: upper bound below lower bound or above length of array
Execution trace:
(0,0): anon0
(0,0): anon14_Else
@@ -41,71 +41,77 @@ Execution trace:
(0,0): anon19_Then
(0,0): anon20_Then
(0,0): anon11
-Array.dfy(120,8): Error: insufficient reads clause to read the indicated range of array elements
+Array.dfy(120,7): Error: insufficient reads clause to read the indicated range of array elements
Execution trace:
(0,0): anon0
- (0,0): anon9_Else
- (0,0): anon10_Then
+ (0,0): anon10_Else
(0,0): anon11_Then
(0,0): anon12_Then
-Array.dfy(122,8): Error: insufficient reads clause to read the indicated range of array elements
+ (0,0): anon13_Then
+ (0,0): anon9
+Array.dfy(122,7): Error: insufficient reads clause to read the indicated range of array elements
Execution trace:
(0,0): anon0
- (0,0): anon9_Else
- (0,0): anon10_Then
+ (0,0): anon10_Else
(0,0): anon11_Then
- (0,0): anon12_Else
-Array.dfy(123,8): Error: insufficient reads clause to read the indicated range of array elements
+ (0,0): anon12_Then
+ (0,0): anon13_Else
+ (0,0): anon9
+Array.dfy(123,7): Error: insufficient reads clause to read the indicated range of array elements
Execution trace:
(0,0): anon0
- (0,0): anon9_Else
- (0,0): anon10_Then
+ (0,0): anon10_Else
(0,0): anon11_Then
- (0,0): anon12_Else
-Array.dfy(124,8): Error: insufficient reads clause to read the indicated range of array elements
+ (0,0): anon12_Then
+ (0,0): anon13_Else
+ (0,0): anon9
+Array.dfy(124,7): Error: insufficient reads clause to read the indicated range of array elements
Execution trace:
(0,0): anon0
- (0,0): anon9_Else
- (0,0): anon10_Then
+ (0,0): anon10_Else
(0,0): anon11_Then
- (0,0): anon12_Else
-Array.dfy(163,6): Error: insufficient reads clause to read array element
+ (0,0): anon12_Then
+ (0,0): anon13_Else
+ (0,0): anon9
+Array.dfy(163,5): Error: insufficient reads clause to read array element
Execution trace:
(0,0): anon0
- (0,0): anon7_Else
- (0,0): anon8_Then
+ (0,0): anon8_Else
(0,0): anon9_Then
-Array.dfy(171,6): Error: insufficient reads clause to read array element
+ (0,0): anon10_Then
+ (0,0): anon7
+Array.dfy(171,5): Error: insufficient reads clause to read array element
Execution trace:
(0,0): anon0
- (0,0): anon7_Else
- (0,0): anon8_Then
+ (0,0): anon8_Else
(0,0): anon9_Then
-Array.dfy(187,6): Error: assignment may update an array element not in the enclosing context's modifies clause
+ (0,0): anon10_Then
+ (0,0): anon7
+Array.dfy(187,5): Error: assignment may update an array element not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
-Array.dfy(194,6): Error: assignment may update an array element not in the enclosing context's modifies clause
+Array.dfy(194,5): Error: assignment may update an array element not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
-Array.dfy(219,1): Error BP5003: A postcondition might not hold on this return path.
-Array.dfy(218,11): Related location: This is the postcondition that might not hold.
+Array.dfy(219,0): Error BP5003: A postcondition might not hold on this return path.
+Array.dfy(218,10): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-Array.dfy(243,1): Error BP5003: A postcondition might not hold on this return path.
-Array.dfy(242,11): Related location: This is the postcondition that might not hold.
+Array.dfy(243,0): Error BP5003: A postcondition might not hold on this return path.
+Array.dfy(242,10): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-Array.dfy(249,1): Error BP5003: A postcondition might not hold on this return path.
-Array.dfy(248,11): Related location: This is the postcondition that might not hold.
+Array.dfy(249,0): Error BP5003: A postcondition might not hold on this return path.
+Array.dfy(248,10): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-Array.dfy(264,10): Error: value assigned to a nat must be non-negative
+Array.dfy(264,9): Error: value assigned to a nat must be non-negative
Execution trace:
(0,0): anon0
(0,0): anon5_Then
(0,0): anon2
(0,0): anon6_Then
-Array.dfy(265,5): Error: value assigned to a nat must be non-negative
+Array.dfy(265,4): Error: value assigned to a nat must be non-negative
Execution trace:
(0,0): anon0
(0,0): anon5_Then
diff --git a/Test/dafny0/AssumptionVariables0.dfy b/Test/dafny0/AssumptionVariables0.dfy
index a3e23b73..b9acc522 100644
--- a/Test/dafny0/AssumptionVariables0.dfy
+++ b/Test/dafny0/AssumptionVariables0.dfy
@@ -6,7 +6,7 @@ method test0(x: int)
ghost var {:assumption} a0 := false; // error
ghost var a1, {:assumption} a2 := true, false; // error
ghost var {:assumption} a3: bool;
- var {:assumption} a4; // 2 errors
+ ghost var {:assumption} a4; // error: type must be bool
a0 := a0 && (0 < x);
@@ -54,7 +54,7 @@ method test2()
if (false)
{
- var {:assumption} a0: bool; // error
+ ghost var {:assumption} a0: bool;
if (false)
{
@@ -73,3 +73,7 @@ method test2()
}
}
}
+
+method test3() {
+ var {:assumption} a: bool; // error: assumption variable must be ghost
+}
diff --git a/Test/dafny0/AssumptionVariables0.dfy.expect b/Test/dafny0/AssumptionVariables0.dfy.expect
index f2d43fe1..83eb8a73 100644
--- a/Test/dafny0/AssumptionVariables0.dfy.expect
+++ b/Test/dafny0/AssumptionVariables0.dfy.expect
@@ -1,14 +1,13 @@
AssumptionVariables0.dfy(6,29): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a0 && <boolean expression>"
AssumptionVariables0.dfy(7,33): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a2 && <boolean expression>"
-AssumptionVariables0.dfy(9,20): Error: assumption variable must be ghost
-AssumptionVariables0.dfy(9,2): Error: assumption variable must be of type 'bool'
+AssumptionVariables0.dfy(9,26): Error: assumption variable must be of type 'bool'
AssumptionVariables0.dfy(15,5): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a3 && <boolean expression>"
AssumptionVariables0.dfy(17,5): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a3 && <boolean expression>"
AssumptionVariables0.dfy(27,5): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a0 && <boolean expression>"
AssumptionVariables0.dfy(31,5): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a0 && <boolean expression>"
AssumptionVariables0.dfy(53,9): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a0 && <boolean expression>"
-AssumptionVariables0.dfy(57,26): Error: assumption variable must be ghost
AssumptionVariables0.dfy(61,37): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a0 && <boolean expression>"
-AssumptionVariables0.dfy(61,10): Error: assumption variable must be of type 'bool'
+AssumptionVariables0.dfy(61,34): Error: assumption variable must be of type 'bool'
AssumptionVariables0.dfy(69,15): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a0 && <boolean expression>"
-13 resolution/type errors detected in AssumptionVariables0.dfy
+AssumptionVariables0.dfy(78,20): Error: assumption variable must be ghost
+12 resolution/type errors detected in AssumptionVariables0.dfy
diff --git a/Test/dafny0/AutoReq.dfy b/Test/dafny0/AutoReq.dfy
index acfe6b8d..d7c87e6d 100644
--- a/Test/dafny0/AutoReq.dfy
+++ b/Test/dafny0/AutoReq.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
function f(x:int) : bool
@@ -313,3 +313,8 @@ module OpaqueTest {
}
}
+
+// autoTriggers added because it causes an extra error message related to
+// violated preconditions to appear. That extra message is due to the extra
+// precondition involving a split quantifier: the user now gets two traces, one
+// for each conjunct.
diff --git a/Test/dafny0/AutoReq.dfy.expect b/Test/dafny0/AutoReq.dfy.expect
index 547b676d..25f00797 100644
--- a/Test/dafny0/AutoReq.dfy.expect
+++ b/Test/dafny0/AutoReq.dfy.expect
@@ -1,45 +1,52 @@
-AutoReq.dfy(247,5): Error: possible violation of function precondition
-AutoReq.dfy(239,14): Related location
+AutoReq.dfy(247,4): Error: possible violation of function precondition
+AutoReq.dfy(239,13): Related location
+AutoReq.dfy(239,59): Related location
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-AutoReq.dfy(13,3): Error: possible violation of function precondition
-AutoReq.dfy(5,14): Related location
+ (0,0): anon4_Else
+AutoReq.dfy(247,4): Error: possible violation of function precondition
+AutoReq.dfy(239,13): Related location
+AutoReq.dfy(239,35): Related location
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-AutoReq.dfy(25,3): Error: possible violation of function precondition
-AutoReq.dfy(5,14): Related location
+ (0,0): anon4_Else
+AutoReq.dfy(13,2): Error: possible violation of function precondition
+AutoReq.dfy(5,13): Related location
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-AutoReq.dfy(38,12): Error: assertion violation
-AutoReq.dfy(31,13): Related location
-AutoReq.dfy(7,5): Related location
+ (0,0): anon4_Else
+AutoReq.dfy(25,2): Error: possible violation of function precondition
+AutoReq.dfy(5,13): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon4_Else
+AutoReq.dfy(38,11): Error: assertion violation
+AutoReq.dfy(31,12): Related location
+AutoReq.dfy(7,4): Related location
Execution trace:
(0,0): anon0
(0,0): anon9_Then
-AutoReq.dfy(38,12): Error: possible violation of function precondition
-AutoReq.dfy(5,14): Related location
+AutoReq.dfy(38,11): Error: possible violation of function precondition
+AutoReq.dfy(5,13): Related location
Execution trace:
(0,0): anon0
(0,0): anon9_Then
-AutoReq.dfy(40,12): Error: assertion violation
-AutoReq.dfy(31,27): Related location
-AutoReq.dfy(7,5): Related location
+AutoReq.dfy(40,11): Error: assertion violation
+AutoReq.dfy(31,26): Related location
+AutoReq.dfy(7,4): Related location
Execution trace:
(0,0): anon0
(0,0): anon10_Then
-AutoReq.dfy(40,12): Error: possible violation of function precondition
-AutoReq.dfy(5,14): Related location
+AutoReq.dfy(40,11): Error: possible violation of function precondition
+AutoReq.dfy(5,13): Related location
Execution trace:
(0,0): anon0
(0,0): anon10_Then
-AutoReq.dfy(45,12): Error: assertion violation
-AutoReq.dfy(31,13): Related location
-AutoReq.dfy(7,5): Related location
+AutoReq.dfy(45,11): Error: assertion violation
+AutoReq.dfy(31,12): Related location
+AutoReq.dfy(7,4): Related location
Execution trace:
(0,0): anon0
(0,0): anon11_Then
-Dafny program verifier finished with 52 verified, 8 errors
+Dafny program verifier finished with 52 verified, 9 errors
diff --git a/Test/dafny0/Backticks.dfy.expect b/Test/dafny0/Backticks.dfy.expect
index ab2bbc52..58977413 100644
--- a/Test/dafny0/Backticks.dfy.expect
+++ b/Test/dafny0/Backticks.dfy.expect
@@ -1,9 +1,10 @@
-Backticks.dfy(38,5): Error: insufficient reads clause to invoke function
+Backticks.dfy(38,4): Error: insufficient reads clause to invoke function
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
- (0,0): anon6_Else
-Backticks.dfy(77,8): Error: call may violate context's modifies clause
+ (0,0): anon7_Else
+ (0,0): anon8_Else
+ (0,0): anon6
+Backticks.dfy(77,7): Error: call may violate context's modifies clause
Execution trace:
(0,0): anon0
(0,0): anon3_Then
diff --git a/Test/dafny0/BadFunction.dfy.expect b/Test/dafny0/BadFunction.dfy.expect
index 7127b60b..1af2608d 100644
--- a/Test/dafny0/BadFunction.dfy.expect
+++ b/Test/dafny0/BadFunction.dfy.expect
@@ -1,6 +1,6 @@
-BadFunction.dfy(9,3): Error: failure to decrease termination measure
+BadFunction.dfy(9,2): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
+ (0,0): anon4_Else
Dafny program verifier finished with 2 verified, 1 error
diff --git a/Test/dafny0/Basics.dfy b/Test/dafny0/Basics.dfy
index c8fa76c8..7b8b632b 100644
--- a/Test/dafny0/Basics.dfy
+++ b/Test/dafny0/Basics.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
class Global {
@@ -100,7 +100,7 @@ method ExpliesAssociativityM(A: bool, B: bool, C: bool) {
}
}
-method ExpliesShortCircuiting(a: array<T>)
+method ExpliesShortCircuiting(a: array)
{
assert a == null || 0 <= a.Length; // (W)
assert a != null ==> 0 <= a.Length; // (X) -- same as (W)
diff --git a/Test/dafny0/Basics.dfy.expect b/Test/dafny0/Basics.dfy.expect
index f28df20a..65d5d101 100644
--- a/Test/dafny0/Basics.dfy.expect
+++ b/Test/dafny0/Basics.dfy.expect
@@ -1,8 +1,8 @@
-Basics.dfy(45,14): Error: assertion violation
+Basics.dfy(45,13): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Else
-Basics.dfy(69,42): Error: assertion violation
+Basics.dfy(69,41): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon13_Then
@@ -12,7 +12,7 @@ Execution trace:
Basics.dfy(69,82): anon17_Else
Basics.dfy(69,95): anon18_Else
(0,0): anon12
-Basics.dfy(93,14): Error: assertion violation
+Basics.dfy(93,13): Error: assertion violation
Execution trace:
(0,0): anon0
Basics.dfy(83,14): anon27_Else
@@ -27,7 +27,7 @@ Execution trace:
Basics.dfy(91,13): anon34_Else
(0,0): anon35_Then
(0,0): anon15
-Basics.dfy(99,14): Error: assertion violation
+Basics.dfy(99,13): Error: assertion violation
Execution trace:
(0,0): anon0
Basics.dfy(83,14): anon27_Else
@@ -42,7 +42,7 @@ Execution trace:
Basics.dfy(97,19): anon40_Else
(0,0): anon41_Then
(0,0): anon26
-Basics.dfy(112,28): Error: target object may be null
+Basics.dfy(112,27): Error: target object may be null
Execution trace:
(0,0): anon0
Basics.dfy(105,20): anon13_Else
@@ -52,7 +52,7 @@ Execution trace:
Basics.dfy(107,24): anon15_Else
(0,0): anon6
(0,0): anon16_Then
-Basics.dfy(114,14): Error: target object may be null
+Basics.dfy(114,13): Error: target object may be null
Execution trace:
(0,0): anon0
Basics.dfy(105,20): anon13_Else
@@ -62,11 +62,11 @@ Execution trace:
Basics.dfy(107,24): anon15_Else
(0,0): anon6
(0,0): anon16_Else
-Basics.dfy(149,16): Error: assertion violation
+Basics.dfy(149,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon10_Then
-Basics.dfy(168,10): Error: when left-hand sides 0 and 1 may refer to the same location, they must be assigned the same value
+Basics.dfy(168,9): Error: when left-hand sides 0 and 1 may refer to the same location, they must be assigned the same value
Execution trace:
(0,0): anon0
(0,0): anon10_Then
@@ -75,28 +75,28 @@ Execution trace:
(0,0): anon6
(0,0): anon12_Then
(0,0): anon9
-Basics.dfy(182,10): Error: when left-hand sides 0 and 1 refer to the same location, they must be assigned the same value
+Basics.dfy(182,9): Error: when left-hand sides 0 and 1 refer to the same location, they must be assigned the same value
Execution trace:
(0,0): anon0
-Basics.dfy(194,19): Error: assertion violation
+Basics.dfy(194,18): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon11_Then
-Basics.dfy(196,10): Error: assignment may update an object not in the enclosing context's modifies clause
+Basics.dfy(196,9): Error: assignment may update an object not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
(0,0): anon3
-Basics.dfy(196,10): Error: target object may be null
+Basics.dfy(196,9): Error: target object may be null
Execution trace:
(0,0): anon0
(0,0): anon3
-Basics.dfy(201,12): Error: left-hand sides 0 and 1 may refer to the same location
+Basics.dfy(201,11): Error: left-hand sides 0 and 1 may refer to the same location
Execution trace:
(0,0): anon0
(0,0): anon11_Then
(0,0): anon3
(0,0): anon12_Then
-Basics.dfy(212,15): Error: assertion violation
+Basics.dfy(212,14): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon11_Then
@@ -106,19 +106,19 @@ Execution trace:
(0,0): anon13_Then
(0,0): anon8
(0,0): anon14_Then
-Basics.dfy(274,10): Error: when left-hand sides 0 and 1 refer to the same location, they must be assigned the same value
+Basics.dfy(274,9): Error: when left-hand sides 0 and 1 refer to the same location, they must be assigned the same value
Execution trace:
(0,0): anon0
-Basics.dfy(465,12): Error: assertion violation
+Basics.dfy(465,11): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon4_Then
(0,0): anon3
-Basics.dfy(476,19): Error: assertion violation
+Basics.dfy(476,18): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon4_Else
-Basics.dfy(478,12): Error: assertion violation
+Basics.dfy(478,11): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon4_Then
diff --git a/Test/dafny0/BindingGuards.dfy b/Test/dafny0/BindingGuards.dfy
new file mode 100644
index 00000000..0706fc5b
--- /dev/null
+++ b/Test/dafny0/BindingGuards.dfy
@@ -0,0 +1,159 @@
+// RUN: %dafny /dprint:- /env:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+predicate P(n: int)
+{
+ n % 2 == 0
+}
+
+predicate R(r: real)
+{
+ 0.0 <= r
+}
+
+method M0()
+{
+ if x :| P(x) {
+ var y := x + 3;
+ }
+}
+
+method M1()
+{
+ if x: int :| P(x) {
+ }
+}
+
+method M2()
+{
+ var x := true;
+ if x, y :| P(x) && R(y) { // this declares a new 'x'
+ var z := x + 12;
+ }
+ x := x && false;
+}
+
+method M3()
+{
+ var x := true;
+ if x: int, y :| P(x) && R(y) {
+ var z := x + y.Trunc;
+ var w := real(x) + y;
+ }
+}
+
+method M4()
+{
+ if x, y: real :| P(x) && R(y) {
+ }
+}
+
+method M5()
+{
+ if x: int, y: real :| P(x) && R(y) {
+ }
+}
+
+method M6()
+{
+ if x {:myattribute x, "hello"} :| P(x) {
+ }
+ if x, y {:myattribute y, "sveika"} :| P(x) && R(y) {
+ }
+ if x: int {:myattribute x, "chello"} :| P(x) {
+ }
+ if x {:myattribute x, "hola"} {:yourattribute x + x, "hej"} :| P(x) {
+ }
+}
+
+ghost method M7() returns (z: real, w: real)
+ ensures -2.0 <= z
+ ensures z == w // error: does not hold
+{
+ var k;
+ if x :| P(x) {
+ k, z := 4, 18.0;
+ } else if * {
+ z := z + -z;
+ } else if y :| R(y) {
+ z := y;
+ } else if y :| P(y) {
+ k := y;
+ } else {
+ z :| R(z);
+ }
+ if P(k) {
+ z := 18.0;
+ }
+}
+
+ghost method M8(m: int, n: int)
+ requires forall y :: m <= y < n ==> P(y)
+{
+ var t := -1;
+ var u;
+ if y :| m <= y < n && P(y) {
+ u := y;
+ if * {
+ t := n - y;
+ } else if * {
+ t := y - m;
+ } else if P(y) {
+ t := 8;
+ } else {
+ t := -100; // will never happen
+ }
+ }
+ if t < 0 && m < n {
+ assert P(m) && !P(m);
+ assert false;
+ }
+ assert t < 0 ==> n <= m;
+}
+
+method P0(m: int, n: int)
+ requires m < n
+{
+ ghost var even, alsoEven := 4, 8;
+ if {
+ case x :| P(x) =>
+ even := x;
+ case x: int :| P(x) =>
+ even := x;
+ case x, y :| P(x) && R(y) =>
+ even, alsoEven := x, y.Trunc; // this assigns to 'alsoEven' a possibly odd number
+ case x: int, y :| P(x) && R(y) =>
+ even := x;
+ case m < n => // just to be different
+ case x, y: real :| P(x) && R(y) =>
+ even := x;
+ case x: int, y: real :| P(x) && R(y) =>
+ even := x;
+ }
+ assert P(even);
+ assert P(alsoEven); // error: may not hold
+}
+
+method P1(m: int, n: int)
+{
+ if { // error: missing case
+ case x :| m <= x < n && P(x) =>
+ }
+}
+
+method P2(m: int, n: int)
+ requires forall y :: m <= y < n ==> P(y)
+{
+ if { // error: missing case
+ case x :| m <= x < n && P(x) =>
+ }
+}
+
+method P3(m: int, n: int)
+ requires m < n && forall y :: m <= y < n ==> P(y)
+{
+ assert P(m); // lemma that proves that the following 'if' covers all possibilities
+ if {
+ case x :| m <= x < n && P(x) =>
+ }
+}
diff --git a/Test/dafny0/BindingGuards.dfy.expect b/Test/dafny0/BindingGuards.dfy.expect
new file mode 100644
index 00000000..b7da7b9e
--- /dev/null
+++ b/Test/dafny0/BindingGuards.dfy.expect
@@ -0,0 +1,183 @@
+// BindingGuards.dfy
+
+predicate P(n: int)
+{
+ n % 2 == 0
+}
+
+predicate R(r: real)
+{
+ 0.0 <= r
+}
+
+method M0()
+{
+ if x :| P(x) {
+ var y := x + 3;
+ }
+}
+
+method M1()
+{
+ if x: int :| P(x) {
+ }
+}
+
+method M2()
+{
+ var x := true;
+ if x, y :| P(x) && R(y) {
+ var z := x + 12;
+ }
+ x := x && false;
+}
+
+method M3()
+{
+ var x := true;
+ if x: int, y :| P(x) && R(y) {
+ var z := x + y.Trunc;
+ var w := real(x) + y;
+ }
+}
+
+method M4()
+{
+ if x, y: real :| P(x) && R(y) {
+ }
+}
+
+method M5()
+{
+ if x: int, y: real :| P(x) && R(y) {
+ }
+}
+
+method M6()
+{
+ if x {:myattribute x, "hello"} :| P(x) {
+ }
+ if x, y {:myattribute y, "sveika"} :| P(x) && R(y) {
+ }
+ if x: int {:myattribute x, "chello"} :| P(x) {
+ }
+ if x {:myattribute x, "hola"} {:yourattribute x + x, "hej"} :| P(x) {
+ }
+}
+
+ghost method M7() returns (z: real, w: real)
+ ensures -2.0 <= z
+ ensures z == w
+{
+ var k;
+ if x :| P(x) {
+ k, z := 4, 18.0;
+ } else if * {
+ z := z + -z;
+ } else if y :| R(y) {
+ z := y;
+ } else if y :| P(y) {
+ k := y;
+ } else {
+ z :| R(z);
+ }
+ if P(k) {
+ z := 18.0;
+ }
+}
+
+ghost method M8(m: int, n: int)
+ requires forall y :: m <= y < n ==> P(y)
+{
+ var t := -1;
+ var u;
+ if y :| m <= y < n && P(y) {
+ u := y;
+ if * {
+ t := n - y;
+ } else if * {
+ t := y - m;
+ } else if P(y) {
+ t := 8;
+ } else {
+ t := -100;
+ }
+ }
+ if t < 0 && m < n {
+ assert P(m) && !P(m);
+ assert false;
+ }
+ assert t < 0 ==> n <= m;
+}
+
+method P0(m: int, n: int)
+ requires m < n
+{
+ ghost var even, alsoEven := 4, 8;
+ if {
+ case x :| P(x) =>
+ even := x;
+ case x: int :| P(x) =>
+ even := x;
+ case x, y :| P(x) && R(y) =>
+ even, alsoEven := x, y.Trunc;
+ case x: int, y :| P(x) && R(y) =>
+ even := x;
+ case m < n =>
+ case x, y: real :| P(x) && R(y) =>
+ even := x;
+ case x: int, y: real :| P(x) && R(y) =>
+ even := x;
+ }
+ assert P(even);
+ assert P(alsoEven);
+}
+
+method P1(m: int, n: int)
+{
+ if {
+ case x :| m <= x < n && P(x) =>
+ }
+}
+
+method P2(m: int, n: int)
+ requires forall y :: m <= y < n ==> P(y)
+{
+ if {
+ case x :| m <= x < n && P(x) =>
+ }
+}
+
+method P3(m: int, n: int)
+ requires m < n && forall y :: m <= y < n ==> P(y)
+{
+ assert P(m);
+ if {
+ case x :| m <= x < n && P(x) =>
+ }
+}
+BindingGuards.dfy(85,10): Error BP5003: A postcondition might not hold on this return path.
+BindingGuards.dfy(71,12): Related location: This is the postcondition that might not hold.
+Execution trace:
+ (0,0): anon0
+ (0,0): anon12_Then
+ (0,0): anon9
+ (0,0): anon16_Then
+BindingGuards.dfy(134,9): Error: assertion violation
+BindingGuards.dfy(6,8): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon20_Then
+ (0,0): anon21_Then
+ (0,0): anon5
+ (0,0): anon17
+BindingGuards.dfy(139,2): Error: alternative cases fail to cover all possibilties
+Execution trace:
+ (0,0): anon0
+ (0,0): anon7_Else
+BindingGuards.dfy(147,2): Error: alternative cases fail to cover all possibilties
+Execution trace:
+ (0,0): anon0
+ (0,0): anon7_Else
+
+Dafny program verifier finished with 24 verified, 4 errors
diff --git a/Test/dafny0/BindingGuardsResolution.dfy b/Test/dafny0/BindingGuardsResolution.dfy
new file mode 100644
index 00000000..e2b55a99
--- /dev/null
+++ b/Test/dafny0/BindingGuardsResolution.dfy
@@ -0,0 +1,154 @@
+// RUN: %dafny /dprint:- /env:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+predicate P(n: int)
+
+predicate R(r: real)
+
+method M0()
+{
+ if x :| P(x) {
+ var y := x + 3;
+ var x := true; // error: 'x' is already declared in this scope
+ }
+}
+
+method M1()
+{
+ if x: int :| P(x) {
+ x := x + 1; // error: 'x' is an immutable variable
+ }
+}
+
+method M2()
+{
+ var x := true;
+ if x, y :| P(x) && R(y) { // this declares a new 'x'
+ var z := x + 12;
+ }
+ x := x && false;
+}
+
+method M3()
+{
+ var x := true;
+ if x: int, y :| P(x) && R(y) {
+ var z := x + int(y);
+ var w := real(x) + y;
+ }
+ var x := 0.0; // error: 'x' is already declared in this scope
+}
+
+method M4()
+{
+ if x, y: real :| P(x) && R(y) {
+ }
+}
+
+method M5()
+{
+ if x: int, y: real :| P(x) && R(y) {
+ }
+}
+
+method M6()
+{
+ if x {:myattribute x, "hello"} :| P(x) {
+ }
+ if x, y {:myattribute y, "sveika"} :| P(x) && R(y) {
+ }
+ if x: int {:myattribute x, "chello"} :| P(x) {
+ }
+ if x {:myattribute x, "hola"} {:yourattribute x + x, "hej"} :| P(x) {
+ }
+}
+
+method M7()
+{
+ if x :| P(x) {
+ } else if * {
+ } else if y :| R(y) {
+ } else if y :| P(y) {
+ }
+}
+
+method P0(m: int, n: int)
+ requires m < n
+{
+ var x := true;
+ if {
+ case x :| P(x) =>
+ var t := 3 * x;
+ case x: int :| P(x) =>
+ case x, y :| P(x) && R(y) =>
+ y := y + 1.0; // error: 'y' is an immutable variable
+ case x: int, y :| P(x) && R(y) =>
+ case m < n =>
+ x := x || m + 5 == n;
+ case x, y: real :| P(x) && R(y) =>
+ case x: int, y: real :| P(x) && R(y) =>
+ }
+ assert x;
+}
+
+method P1(m: int, n: int)
+ requires m < n
+{
+ if {
+ case x {:myattribute x, "hello"} :| P(x) =>
+ case x, y {:myattribute y, "sveika"} :| P(x) && R(y) =>
+ case x: int {:myattribute x, "chello"} :| P(x) =>
+ case x {:myattribute x, "hola"} {:yourattribute x + x, "hej"} :| P(x) =>
+ case m < n =>
+ }
+}
+
+module TypesNotFullyDetermined {
+ method T0()
+ {
+ if x :| true { // error: type not entirely resolved
+ }
+ }
+ method T1()
+ {
+ if x :| true {
+ var y := x + 3;
+ }
+ }
+}
+
+module Ghost {
+ predicate P(x: int) // note, P is ghost
+ predicate method R(x: int)
+ method M7() returns (z: int, b: bool)
+ {
+ if * {
+ z := z + -z;
+ } else if y :| 1000 <= y < 2000 && R(y) {
+ z := y;
+ } else if y :| 0 <= y < 100 && P(y) {
+ z := y; // error: not allowed, because the P in the guard makes this a ghost context
+ } else if y :| 0 <= y < 100 && R(y) {
+ z := y; // error: this is also in a ghost context, because it depends on the P above
+ }
+
+ if * {
+ z := z + -z;
+ } else if exists y :: 1000 <= y < 2000 && R(y) {
+ z := 0;
+ } else if exists y :: 0 <= y < 100 && P(y) {
+ z := 0; // error: not allowed, because the P in the guard makes this a ghost context
+ } else if exists y :: 0 <= y < 100 && R(y) {
+ z := 0; // error: this is also in a ghost context, because it depends on the P above
+ }
+
+ if P(z) {
+ z := 20; // error: blatant ghost context
+ }
+
+ b := exists y :: 0 <= y < 100 && P(y); // error: assignment to non-ghost of something that depends on ghost
+ ghost var c;
+ c := exists y :: 0 <= y < 100 && P(y);
+ b := exists y {:myattribute P(y)} :: 0 <= y < 100;
+ }
+}
diff --git a/Test/dafny0/BindingGuardsResolution.dfy.expect b/Test/dafny0/BindingGuardsResolution.dfy.expect
new file mode 100644
index 00000000..b6f23805
--- /dev/null
+++ b/Test/dafny0/BindingGuardsResolution.dfy.expect
@@ -0,0 +1,165 @@
+// BindingGuardsResolution.dfy
+
+
+module TypesNotFullyDetermined {
+ method T0()
+ {
+ if x :| true {
+ }
+ }
+
+ method T1()
+ {
+ if x :| true {
+ var y := x + 3;
+ }
+ }
+}
+
+module Ghost {
+ predicate P(x: int)
+
+ predicate method R(x: int)
+
+ method M7() returns (z: int, b: bool)
+ {
+ if * {
+ z := z + -z;
+ } else if y :| 1000 <= y < 2000 && R(y) {
+ z := y;
+ } else if y :| 0 <= y < 100 && P(y) {
+ z := y;
+ } else if y :| 0 <= y < 100 && R(y) {
+ z := y;
+ }
+ if * {
+ z := z + -z;
+ } else if exists y :: 1000 <= y < 2000 && R(y) {
+ z := 0;
+ } else if exists y :: 0 <= y < 100 && P(y) {
+ z := 0;
+ } else if exists y :: 0 <= y < 100 && R(y) {
+ z := 0;
+ }
+ if P(z) {
+ z := 20;
+ }
+ b := exists y :: 0 <= y < 100 && P(y);
+ ghost var c;
+ c := exists y :: 0 <= y < 100 && P(y);
+ b := exists y {:myattribute P(y)} :: 0 <= y < 100;
+ }
+}
+predicate P(n: int)
+
+predicate R(r: real)
+
+method M0()
+{
+ if x :| P(x) {
+ var y := x + 3;
+ var x := true;
+ }
+}
+
+method M1()
+{
+ if x: int :| P(x) {
+ x := x + 1;
+ }
+}
+
+method M2()
+{
+ var x := true;
+ if x, y :| P(x) && R(y) {
+ var z := x + 12;
+ }
+ x := x && false;
+}
+
+method M3()
+{
+ var x := true;
+ if x: int, y :| P(x) && R(y) {
+ var z := x + int(y);
+ var w := real(x) + y;
+ }
+ var x := 0.0;
+}
+
+method M4()
+{
+ if x, y: real :| P(x) && R(y) {
+ }
+}
+
+method M5()
+{
+ if x: int, y: real :| P(x) && R(y) {
+ }
+}
+
+method M6()
+{
+ if x {:myattribute x, "hello"} :| P(x) {
+ }
+ if x, y {:myattribute y, "sveika"} :| P(x) && R(y) {
+ }
+ if x: int {:myattribute x, "chello"} :| P(x) {
+ }
+ if x {:myattribute x, "hola"} {:yourattribute x + x, "hej"} :| P(x) {
+ }
+}
+
+method M7()
+{
+ if x :| P(x) {
+ } else if * {
+ } else if y :| R(y) {
+ } else if y :| P(y) {
+ }
+}
+
+method P0(m: int, n: int)
+ requires m < n
+{
+ var x := true;
+ if {
+ case x :| P(x) =>
+ var t := 3 * x;
+ case x: int :| P(x) =>
+ case x, y :| P(x) && R(y) =>
+ y := y + 1.0;
+ case x: int, y :| P(x) && R(y) =>
+ case m < n =>
+ x := x || m + 5 == n;
+ case x, y: real :| P(x) && R(y) =>
+ case x: int, y: real :| P(x) && R(y) =>
+ }
+ assert x;
+}
+
+method P1(m: int, n: int)
+ requires m < n
+{
+ if {
+ case x {:myattribute x, "hello"} :| P(x) =>
+ case x, y {:myattribute y, "sveika"} :| P(x) && R(y) =>
+ case x: int {:myattribute x, "chello"} :| P(x) =>
+ case x {:myattribute x, "hola"} {:yourattribute x + x, "hej"} :| P(x) =>
+ case m < n =>
+ }
+}
+BindingGuardsResolution.dfy(109,7): Error: type of bound variable 'x' could not be determined; please specify the type explicitly
+BindingGuardsResolution.dfy(130,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+BindingGuardsResolution.dfy(132,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+BindingGuardsResolution.dfy(140,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+BindingGuardsResolution.dfy(142,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+BindingGuardsResolution.dfy(146,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+BindingGuardsResolution.dfy(149,37): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method')
+BindingGuardsResolution.dfy(12,8): Error: Duplicate local-variable name: x
+BindingGuardsResolution.dfy(19,4): Error: LHS of assignment must denote a mutable variable
+BindingGuardsResolution.dfy(39,6): Error: Duplicate local-variable name: x
+BindingGuardsResolution.dfy(84,6): Error: LHS of assignment must denote a mutable variable
+11 resolution/type errors detected in BindingGuardsResolution.dfy
diff --git a/Test/dafny0/Calculations.dfy b/Test/dafny0/Calculations.dfy
index c77bced7..eb4ff1b9 100644
--- a/Test/dafny0/Calculations.dfy
+++ b/Test/dafny0/Calculations.dfy
@@ -1,4 +1,5 @@
-// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint.dfy" "%s" > "%t"; %dafny /noVerify /compile:0 "%t.dprint.dfy" >> "%t"
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint.dfy" /autoTriggers:0 "%s" > "%t"
+// RUN: %dafny /noVerify /compile:0 "%t.dprint.dfy" >> "%t"
// RUN: %diff "%s.expect" "%t"
method CalcTest0(s: seq<int>) {
diff --git a/Test/dafny0/Calculations.dfy.expect b/Test/dafny0/Calculations.dfy.expect
index 3f6ef226..d4559f53 100644
--- a/Test/dafny0/Calculations.dfy.expect
+++ b/Test/dafny0/Calculations.dfy.expect
@@ -1,24 +1,24 @@
-Calculations.dfy(6,6): Error: index out of range
+Calculations.dfy(7,5): Error: index out of range
Execution trace:
(0,0): anon0
(0,0): anon24_Then
-Calculations.dfy(11,15): Error: index out of range
+Calculations.dfy(12,14): Error: index out of range
Execution trace:
(0,0): anon0
(0,0): anon26_Then
-Calculations.dfy(11,19): Error: assertion violation
+Calculations.dfy(12,18): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon26_Then
-Calculations.dfy(55,12): Error: assertion violation
+Calculations.dfy(56,11): Error: assertion violation
Execution trace:
(0,0): anon0
- Calculations.dfy(50,3): anon5_Else
-Calculations.dfy(78,15): Error: index out of range
+ Calculations.dfy(51,3): anon5_Else
+Calculations.dfy(79,14): Error: index out of range
Execution trace:
(0,0): anon0
(0,0): anon12_Then
-Calculations.dfy(78,19): Error: assertion violation
+Calculations.dfy(79,18): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon12_Then
diff --git a/Test/dafny0/CallStmtTests.dfy b/Test/dafny0/CallStmtTests.dfy
index 67e66b34..46c466ff 100644
--- a/Test/dafny0/CallStmtTests.dfy
+++ b/Test/dafny0/CallStmtTests.dfy
@@ -1,23 +1,27 @@
// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
-method testing1(t: int)
-{
- t := m(); // error: should be checked at the Dafny level, not fall to Boogie.
-}
+module M0 {
+ method testing1(t: int)
+ {
+ t := m(); // error: should be checked at the Dafny level, not fall to Boogie.
+ }
-method m() returns (r: int)
-{
- return 3;
+ method m() returns (r: int)
+ {
+ return 3;
+ }
}
-method testing2()
-{
- var v;
- v := m2(); // error: v needs to be ghost because r is.
-}
+module M1 {
+ method testing2()
+ {
+ var v;
+ v := m2(); // error: v needs to be ghost because r is.
+ }
-method m2() returns (ghost r: int)
-{
- r := 23;
+ method m2() returns (ghost r: int)
+ {
+ r := 23;
+ }
}
diff --git a/Test/dafny0/CallStmtTests.dfy.expect b/Test/dafny0/CallStmtTests.dfy.expect
index 8a334754..246b89f8 100644
--- a/Test/dafny0/CallStmtTests.dfy.expect
+++ b/Test/dafny0/CallStmtTests.dfy.expect
@@ -1,3 +1,3 @@
-CallStmtTests.dfy(6,3): Error: LHS of assignment must denote a mutable variable
-CallStmtTests.dfy(17,10): Error: actual out-parameter 0 is required to be a ghost variable
+CallStmtTests.dfy(7,4): Error: LHS of assignment must denote a mutable variable
+CallStmtTests.dfy(20,11): Error: actual out-parameter 0 is required to be a ghost variable
2 resolution/type errors detected in CallStmtTests.dfy
diff --git a/Test/dafny0/Char.dfy.expect b/Test/dafny0/Char.dfy.expect
index 55418934..874aaa65 100644
--- a/Test/dafny0/Char.dfy.expect
+++ b/Test/dafny0/Char.dfy.expect
@@ -1,14 +1,14 @@
-Char.dfy(48,21): Error: assertion violation
+Char.dfy(48,20): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon9_Then
(0,0): anon10_Then
-Char.dfy(52,21): Error: assertion violation
+Char.dfy(52,20): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon9_Then
(0,0): anon11_Else
-Char.dfy(63,17): Error: assertion violation
+Char.dfy(63,16): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon5_Else
diff --git a/Test/dafny0/CoPrefix.dfy b/Test/dafny0/CoPrefix.dfy
index 0becb24d..3b6bd670 100644
--- a/Test/dafny0/CoPrefix.dfy
+++ b/Test/dafny0/CoPrefix.dfy
@@ -192,3 +192,35 @@ module Recursion {
}
}
}
+
+module PrefixEquality {
+ codatatype Stream<T> = Cons(head: T, Stream)
+
+ colemma Test0(s: Stream, t: Stream)
+ requires s.head == t.head
+ {
+ calc {
+ s;
+ ==#[_k-1]
+ t; // error: this step might not hold
+ ==#[if 2 <= _k then _k-2 else _k-1]
+ s; // error: this step might not hold
+ ==#[0]
+ t;
+ }
+ }
+
+ colemma Test1(s: Stream, t: Stream)
+ requires s == t
+ {
+ calc {
+ s;
+ ==#[_k-1]
+ t;
+ ==#[_k-2] // error: prefix-equality limit must be at least 0
+ s;
+ ==#[0]
+ t;
+ }
+ }
+}
diff --git a/Test/dafny0/CoPrefix.dfy.expect b/Test/dafny0/CoPrefix.dfy.expect
index c92a09c1..b42f2593 100644
--- a/Test/dafny0/CoPrefix.dfy.expect
+++ b/Test/dafny0/CoPrefix.dfy.expect
@@ -1,50 +1,65 @@
-CoPrefix.dfy(164,3): Error BP5003: A postcondition might not hold on this return path.
-CoPrefix.dfy(163,15): Related location: This is the postcondition that might not hold.
+CoPrefix.dfy(164,2): Error BP5003: A postcondition might not hold on this return path.
+CoPrefix.dfy(163,14): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
(0,0): anon3_Else
-CoPrefix.dfy(169,3): Error BP5003: A postcondition might not hold on this return path.
-CoPrefix.dfy(168,15): Related location: This is the postcondition that might not hold.
+CoPrefix.dfy(169,2): Error BP5003: A postcondition might not hold on this return path.
+CoPrefix.dfy(168,14): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
(0,0): anon3_Else
-CoPrefix.dfy(176,11): Error: cannot prove termination; try supplying a decreases clause
+CoPrefix.dfy(176,10): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-CoPrefix.dfy(63,57): Error: failure to decrease termination measure
+CoPrefix.dfy(205,6): Error: the calculation step between the previous line and this line might not hold
+Execution trace:
+ (0,0): anon0
+ (0,0): anon8_Then
+ (0,0): anon10_Then
+CoPrefix.dfy(207,6): Error: the calculation step between the previous line and this line might not hold
+Execution trace:
+ (0,0): anon0
+ (0,0): anon8_Then
+ (0,0): anon11_Then
+CoPrefix.dfy(220,12): Error: prefix-equality limit must be at least 0
+Execution trace:
+ (0,0): anon0
+ (0,0): anon8_Then
+ (0,0): anon11_Then
+CoPrefix.dfy(63,56): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
(0,0): anon7_Then
(0,0): anon8_Else
(0,0): anon9_Then
-CoPrefix.dfy(76,56): Error: cannot prove termination; try supplying a decreases clause
+CoPrefix.dfy(76,55): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
(0,0): anon7_Then
(0,0): anon8_Else
(0,0): anon9_Then
-CoPrefix.dfy(114,1): Error BP5003: A postcondition might not hold on this return path.
-CoPrefix.dfy(113,11): Related location: This is the postcondition that might not hold.
-CoPrefix.dfy(101,17): Related location
+CoPrefix.dfy(114,0): Error BP5003: A postcondition might not hold on this return path.
+CoPrefix.dfy(113,10): Related location: This is the postcondition that might not hold.
+CoPrefix.dfy(101,16): Related location
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-CoPrefix.dfy(138,25): Error: assertion violation
+CoPrefix.dfy(138,24): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon9_Then
(0,0): anon10_Then
-CoPrefix.dfy(142,25): Error: assertion violation
-CoPrefix.dfy(117,23): Related location
+CoPrefix.dfy(142,24): Error: assertion violation
+CoPrefix.dfy(117,22): Related location
Execution trace:
(0,0): anon0
(0,0): anon9_Then
(0,0): anon12_Then
-CoPrefix.dfy(151,1): Error BP5003: A postcondition might not hold on this return path.
-CoPrefix.dfy(150,11): Related location: This is the postcondition that might not hold.
+CoPrefix.dfy(151,0): Error BP5003: A postcondition might not hold on this return path.
+CoPrefix.dfy(150,10): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
(0,0): anon3_Else
-Dafny program verifier finished with 41 verified, 9 errors
+Dafny program verifier finished with 43 verified, 12 errors
diff --git a/Test/dafny0/CoinductiveProofs.dfy b/Test/dafny0/CoinductiveProofs.dfy
index d990ae51..c8bb45c7 100644
--- a/Test/dafny0/CoinductiveProofs.dfy
+++ b/Test/dafny0/CoinductiveProofs.dfy
@@ -12,6 +12,7 @@ copredicate Pos(s: Stream<int>)
{
0 < s.head && Pos(s.tail)
}
+predicate FullPos(s: Stream<int>) { Pos(s) } // a way in the test file to sidestep focal-predicate rewrites
colemma {:induction false} PosLemma0(n: int)
requires 1 <= n;
@@ -26,7 +27,25 @@ colemma {:induction false} PosLemma1(n: int)
{
PosLemma1(n + 1);
if (*) {
- assert Pos(Upward(n + 1)); // error: cannot conclude this here, because we only have prefix predicates
+ assert FullPos(Upward(n + 1)); // error: cannot conclude this here, because we only have prefix predicates
+ }
+}
+
+colemma {:induction false} PosLemma2(n: int)
+ requires 1 <= n;
+ ensures Pos(Upward(n));
+{
+ PosLemma2(n + 1);
+ if (*) {
+ assert Pos(Upward(n + 1)); // Pos gets rewritten to Pos#[_k-1], which does hold
+ } else if (*) {
+ assert Pos#[_k-1](Upward(n + 1)); // explicitly saying Pos#[_k-1] also holds
+ } else if (*) {
+ assert Pos#[_k](Upward(n + 1)); // error: this is not known to hold for _k and n+1
+ } else if (*) {
+ assert Pos#[_k](Upward(n)); // but it does hold with Pos#[_k] and n (which is the postcondition of the prefix lemma)
+ } else if (*) {
+ assert Pos#[_k+1](Upward(n)); // error: this is too much to ask for
}
}
@@ -65,13 +84,29 @@ colemma {:induction false} AlwaysLemma_X1(s: Stream)
{
AlwaysLemma_X1(s); // this is the right proof
}
+predicate FullX(s: Stream) { X(s) } // a way in the test file to sidestep focal-predicate rewrites
colemma {:induction false} AlwaysLemma_X2(s: Stream)
ensures X(s);
{
AlwaysLemma_X2(s);
if (*) {
- assert X(s); // error: cannot conclude the full predicate here
+ assert FullX(s); // error: cannot conclude the full predicate here
+ }
+}
+
+colemma {:induction false} AlwaysLemma_X3(s: Stream)
+ ensures X(s);
+{
+ AlwaysLemma_X3(s);
+ if (*) {
+ assert X(s); // holds, because it gets rewritten to X#[_k-1]
+ } else if (*) {
+ assert X#[_k-1](s); // explicitly saying X#[_k-1] also holds
+ } else if (*) {
+ assert X#[_k](s); // in fact, X#[_k] holds, too (which is the postcondition of the prefix lemma)
+ } else if (*) {
+ assert X#[_k+1](s); // as it turns out, this holds too, since the definition of X makes X#[_k+1] equal X#[_k]
}
}
@@ -79,6 +114,7 @@ copredicate Y(s: Stream) // this is equivalent to always returning 'true'
{
Y(s.tail)
}
+predicate FullY(s: Stream) { Y(s) } // a way in the test file to sidestep focal-predicate rewrites
colemma {:induction false} AlwaysLemma_Y0(s: Stream)
ensures Y(s); // prove that Y(s) really is always 'true'
@@ -97,7 +133,24 @@ colemma {:induction false} AlwaysLemma_Y2(s: Stream)
{
AlwaysLemma_Y2(s.tail);
if (*) {
- assert Y(s.tail); // error: not provable here
+ assert FullY(s.tail); // error: not provable here
+ }
+}
+
+colemma {:induction false} AlwaysLemma_Y3(s: Stream)
+ ensures Y(s);
+{
+ AlwaysLemma_Y3(s.tail);
+ if (*) {
+ assert Y(s.tail); // this holds, because it's rewritten to Y#[_k-1]
+ } else if (*) {
+ assert Y#[_k-1](s.tail);
+ } else if (*) {
+ assert Y#[_k](s.tail); // error: not known to hold for _k and s.tail
+ } else if (*) {
+ assert Y#[_k](s); // this is also the postcondition of the prefix lemma
+ } else if (*) {
+ assert Y#[_k+1](s); // error: this is too much to ask for
}
}
diff --git a/Test/dafny0/CoinductiveProofs.dfy.expect b/Test/dafny0/CoinductiveProofs.dfy.expect
index 12ce2f01..c4f4c405 100644
--- a/Test/dafny0/CoinductiveProofs.dfy.expect
+++ b/Test/dafny0/CoinductiveProofs.dfy.expect
@@ -1,50 +1,77 @@
-CoinductiveProofs.dfy(29,12): Error: assertion violation
-CoinductiveProofs.dfy(13,17): Related location
+CoinductiveProofs.dfy(30,11): Error: assertion violation
+CoinductiveProofs.dfy(15,36): Related location
+CoinductiveProofs.dfy(13,16): Related location
Execution trace:
(0,0): anon0
(0,0): anon5_Then
(0,0): anon6_Then
-CoinductiveProofs.dfy(59,1): Error BP5003: A postcondition might not hold on this return path.
-CoinductiveProofs.dfy(58,11): Related location: This is the postcondition that might not hold.
-CoinductiveProofs.dfy(54,3): Related location
+CoinductiveProofs.dfy(44,11): Error: assertion violation
+CoinductiveProofs.dfy(13,16): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon13_Then
+ (0,0): anon16_Then
+CoinductiveProofs.dfy(48,11): Error: assertion violation
+CoinductiveProofs.dfy(13,16): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon13_Then
+ (0,0): anon18_Then
+CoinductiveProofs.dfy(78,0): Error BP5003: A postcondition might not hold on this return path.
+CoinductiveProofs.dfy(77,10): Related location: This is the postcondition that might not hold.
+CoinductiveProofs.dfy(73,2): Related location
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-CoinductiveProofs.dfy(74,12): Error: assertion violation
-CoinductiveProofs.dfy(54,3): Related location
+CoinductiveProofs.dfy(94,11): Error: assertion violation
+CoinductiveProofs.dfy(87,29): Related location
+CoinductiveProofs.dfy(73,2): Related location
Execution trace:
(0,0): anon0
(0,0): anon5_Then
(0,0): anon6_Then
-CoinductiveProofs.dfy(91,1): Error BP5003: A postcondition might not hold on this return path.
-CoinductiveProofs.dfy(90,11): Related location: This is the postcondition that might not hold.
-CoinductiveProofs.dfy(80,3): Related location
+CoinductiveProofs.dfy(127,0): Error BP5003: A postcondition might not hold on this return path.
+CoinductiveProofs.dfy(126,10): Related location: This is the postcondition that might not hold.
+CoinductiveProofs.dfy(115,2): Related location
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-CoinductiveProofs.dfy(100,12): Error: assertion violation
-CoinductiveProofs.dfy(80,3): Related location
+CoinductiveProofs.dfy(136,11): Error: assertion violation
+CoinductiveProofs.dfy(117,29): Related location
+CoinductiveProofs.dfy(115,2): Related location
Execution trace:
(0,0): anon0
(0,0): anon5_Then
(0,0): anon6_Then
-CoinductiveProofs.dfy(111,1): Error BP5003: A postcondition might not hold on this return path.
-CoinductiveProofs.dfy(110,11): Related location: This is the postcondition that might not hold.
-CoinductiveProofs.dfy(106,3): Related location
+CoinductiveProofs.dfy(149,11): Error: assertion violation
+CoinductiveProofs.dfy(115,2): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon13_Then
+ (0,0): anon16_Then
+CoinductiveProofs.dfy(153,11): Error: assertion violation
+CoinductiveProofs.dfy(115,2): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon13_Then
+ (0,0): anon18_Then
+CoinductiveProofs.dfy(164,0): Error BP5003: A postcondition might not hold on this return path.
+CoinductiveProofs.dfy(163,10): Related location: This is the postcondition that might not hold.
+CoinductiveProofs.dfy(159,2): Related location
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-CoinductiveProofs.dfy(150,1): Error BP5003: A postcondition might not hold on this return path.
-CoinductiveProofs.dfy(149,22): Related location: This is the postcondition that might not hold.
-CoinductiveProofs.dfy(4,24): Related location
+CoinductiveProofs.dfy(203,0): Error BP5003: A postcondition might not hold on this return path.
+CoinductiveProofs.dfy(202,21): Related location: This is the postcondition that might not hold.
+CoinductiveProofs.dfy(4,23): Related location
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-CoinductiveProofs.dfy(156,1): Error BP5003: A postcondition might not hold on this return path.
-CoinductiveProofs.dfy(155,22): Related location: This is the postcondition that might not hold.
-CoinductiveProofs.dfy(4,24): Related location
+CoinductiveProofs.dfy(209,0): Error BP5003: A postcondition might not hold on this return path.
+CoinductiveProofs.dfy(208,21): Related location: This is the postcondition that might not hold.
+CoinductiveProofs.dfy(4,23): Related location
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-Dafny program verifier finished with 35 verified, 8 errors
+Dafny program verifier finished with 42 verified, 12 errors
diff --git a/Test/dafny0/Compilation.dfy b/Test/dafny0/Compilation.dfy
index a2b96996..213ace54 100644
--- a/Test/dafny0/Compilation.dfy
+++ b/Test/dafny0/Compilation.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny "%s" > "%t"
+// RUN: %dafny /compile:3 /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// The tests in this file are designed to run through the compiler. They contain
@@ -43,7 +43,9 @@ module CoRecursion {
// 40
// 41
// 42
- method Main() {
+ // 9
+ // 9
+ method TestMain() {
var m := 17;
var cell := new Cell;
cell.data := 40;
@@ -58,6 +60,37 @@ module CoRecursion {
print l.car, "\n";
l := l.cdr;
}
+ var nio := OneLess(0, 10);
+ print nio, "\n";
+ nio := OneLess'(0, 10);
+ print nio, "\n";
+ }
+
+ method OneLess(lo: int, hi: int) returns (m: int)
+ requires lo < hi
+ // This method ensures m == hi - 1, but we don't care to prove it
+ decreases hi - lo
+ {
+ if y :| lo < y < hi {
+ m := OneLess(y, hi);
+ } else {
+ m := lo;
+ }
+ }
+
+ method OneLess'(lo: int, hi: int) returns (m: int)
+ requires lo < hi
+ // This method ensures m == hi - 1, but we don't care to prove it
+ decreases hi - lo
+ {
+ if {
+ case y :| lo < y < hi =>
+ m := OneLess'(y, hi);
+ case lo+1 < hi =>
+ m := OneLess'(lo+1, hi);
+ case lo + 1 == hi =>
+ m := lo;
+ }
}
}
@@ -76,8 +109,8 @@ module T refines S {
}
}
module A {
- import X as S default T
- import Y as S default T
+ import X : T
+ import Y : T
import Z = T
method run() {
var x := new X.C;
@@ -95,7 +128,7 @@ method NotMain() {
abstract module S1 {
- import B as S default T
+ import B : T
method do()
}
@@ -105,7 +138,7 @@ module T1 refines S1 {
}
}
module A1 {
- import X as S1 default T1
+ import X : T1
method run() {
X.do();
var x := new X.B.C;
@@ -227,3 +260,56 @@ class DigitUnderscore_Names {
this.10 := 20;
}
}
+
+// ------------------------------------------------------------------
+
+method Main()
+{
+ CoRecursion.TestMain();
+ EqualityTests.TestMain();
+}
+
+// ------------------------------------------------------------------
+
+module EqualityTests {
+ class C<T> {
+ }
+
+ method TestMain()
+ {
+ // regression tests:
+ var a: C<int>, b: C<int> := null, null;
+ if a == null {
+ print "a is null\n";
+ }
+ if a != null {
+ print "a is not null\n";
+ }
+ if a == b {
+ print "a and b are equal\n";
+ }
+ if a != b {
+ print "a and b are not equal\n";
+ }
+
+ var H := new real[10];
+ ArrayTests(H);
+ }
+
+ method ArrayTests<T>(H: array<T>)
+ {
+ var G := new int[10];
+ if G == H { // this comparison is allowed in Dafny, but requires a cast in C#
+ print "this would be highly suspicious\n";
+ }
+ if G != H { // this comparison is allowed in Dafny, but requires a cast in C#
+ print "good world order\n";
+ }
+ if null == H {
+ print "given array is null\n";
+ }
+ if null != H {
+ print "given array is non-null\n";
+ }
+ }
+}
diff --git a/Test/dafny0/Compilation.dfy.expect b/Test/dafny0/Compilation.dfy.expect
index 0a1938ae..0a934a63 100644
--- a/Test/dafny0/Compilation.dfy.expect
+++ b/Test/dafny0/Compilation.dfy.expect
@@ -1,3 +1,16 @@
-Dafny program verifier finished with 46 verified, 0 errors
-Compiled assembly into Compilation.exe
+Dafny program verifier finished with 56 verified, 0 errors
+Program compiled successfully
+Running...
+
+400
+320
+40
+41
+42
+9
+9
+a is null
+a and b are equal
+good world order
+given array is non-null
diff --git a/Test/dafny0/Comprehensions.dfy b/Test/dafny0/Comprehensions.dfy
index d0436815..dd83e46c 100644
--- a/Test/dafny0/Comprehensions.dfy
+++ b/Test/dafny0/Comprehensions.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
method M()
@@ -19,18 +19,18 @@ datatype D = A | B
// have to run the resulting program to check that the compiler is doing the right thing.
method Main()
{
- var q := set i,j | 0 <= i && i < 10 && 0 <= j && j < 3 :: i+j;
+ var q := set i,j | 0 <= i < 10 && 0 <= j < 3 :: i+j;
PrintSet(q);
q := set b: bool | true :: if b then 3 else 7;
var d := set b:D | true;
- var test := forall d:D :: d == A || d == B;
+ var test := forall d:D {:nowarn} :: d == A || d == B; // Ignoring the warning as we're only compiling here
PrintSet(q);
var m := set k | k in q :: 2*k;
PrintSet(m);
PrintSet(set k | k in q && k % 2 == 0);
var sq := [30, 40, 20];
- PrintSet(set k, i | k in sq && 0 <= i && i < k && i % 7 == 0 :: k + i);
- var bb := forall k, i | k in sq && 0 <= i && i < k && i % 7 == 0 :: k + i == 17;
+ PrintSet(set k, i | k in sq && 0 <= i < k && i % 7 == 0 :: k + i);
+ var bb := forall k, i {:nowarn} | k in sq && 0 <= i < k && i % 7 == 0 :: k + i == 17; // Ignoring the warning as we're only compiling here
}
method PrintSet<T>(s: set<T>) {
diff --git a/Test/dafny0/Comprehensions.dfy.expect b/Test/dafny0/Comprehensions.dfy.expect
index 88873fd8..887a3249 100644
--- a/Test/dafny0/Comprehensions.dfy.expect
+++ b/Test/dafny0/Comprehensions.dfy.expect
@@ -1,4 +1,4 @@
-Comprehensions.dfy(12,14): Error: assertion violation
+Comprehensions.dfy(12,13): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon9_Then
diff --git a/Test/dafny0/ComputationsLoop.dfy.expect b/Test/dafny0/ComputationsLoop.dfy.expect
index d9d48024..84674030 100644
--- a/Test/dafny0/ComputationsLoop.dfy.expect
+++ b/Test/dafny0/ComputationsLoop.dfy.expect
@@ -1,8 +1,8 @@
-ComputationsLoop.dfy(7,3): Error: failure to decrease termination measure
+ComputationsLoop.dfy(7,2): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-ComputationsLoop.dfy(12,26): Error: assertion violation
+ (0,0): anon4_Else
+ComputationsLoop.dfy(12,25): Error: assertion violation
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/ComputationsLoop2.dfy.expect b/Test/dafny0/ComputationsLoop2.dfy.expect
index 0a45e6d0..48fc618f 100644
--- a/Test/dafny0/ComputationsLoop2.dfy.expect
+++ b/Test/dafny0/ComputationsLoop2.dfy.expect
@@ -1,12 +1,12 @@
-ComputationsLoop2.dfy(6,3): Error: cannot prove termination; try supplying a decreases clause
+ComputationsLoop2.dfy(6,2): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-ComputationsLoop2.dfy(11,3): Error: cannot prove termination; try supplying a decreases clause
+ (0,0): anon4_Else
+ComputationsLoop2.dfy(11,2): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-ComputationsLoop2.dfy(16,26): Error: assertion violation
+ (0,0): anon4_Else
+ComputationsLoop2.dfy(16,25): Error: assertion violation
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/ComputationsNeg.dfy b/Test/dafny0/ComputationsNeg.dfy
index 0c539117..b9425d64 100644
--- a/Test/dafny0/ComputationsNeg.dfy
+++ b/Test/dafny0/ComputationsNeg.dfy
@@ -16,7 +16,7 @@ predicate ThProperty(step: nat, t: Nat, r: nat)
{
match t
case Zero => true
- case Succ(o) => step>0 && exists ro:nat :: ThProperty(step-1, o, ro)
+ case Succ(o) => step>0 && exists ro:nat, ss :: ss == step-1 ==> ThProperty(ss, o, ro) // WISH: auto-generate ss
}
ghost method test_ThProperty()
ensures ThProperty(10, Succ(Zero), 0);
diff --git a/Test/dafny0/ComputationsNeg.dfy.expect b/Test/dafny0/ComputationsNeg.dfy.expect
index 16c8963f..598e9fa5 100644
--- a/Test/dafny0/ComputationsNeg.dfy.expect
+++ b/Test/dafny0/ComputationsNeg.dfy.expect
@@ -1,19 +1,19 @@
-ComputationsNeg.dfy(7,3): Error: failure to decrease termination measure
+ComputationsNeg.dfy(7,2): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-ComputationsNeg.dfy(11,1): Error BP5003: A postcondition might not hold on this return path.
-ComputationsNeg.dfy(10,17): Related location: This is the postcondition that might not hold.
+ (0,0): anon4_Else
+ComputationsNeg.dfy(11,0): Error BP5003: A postcondition might not hold on this return path.
+ComputationsNeg.dfy(10,16): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-ComputationsNeg.dfy(23,1): Error BP5003: A postcondition might not hold on this return path.
-ComputationsNeg.dfy(22,11): Related location: This is the postcondition that might not hold.
+ComputationsNeg.dfy(23,0): Error BP5003: A postcondition might not hold on this return path.
+ComputationsNeg.dfy(22,10): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-ComputationsNeg.dfy(36,13): Error: assertion violation
+ComputationsNeg.dfy(36,12): Error: assertion violation
Execution trace:
(0,0): anon0
-ComputationsNeg.dfy(45,13): Error: assertion violation
+ComputationsNeg.dfy(45,12): Error: assertion violation
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/ContainerRanks.dfy b/Test/dafny0/ContainerRanks.dfy
new file mode 100644
index 00000000..df35e214
--- /dev/null
+++ b/Test/dafny0/ContainerRanks.dfy
@@ -0,0 +1,33 @@
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+datatype Abc = End | Wrapper(seq<Abc>)
+
+lemma SeqRank0(a: Abc)
+ ensures a != Wrapper([a])
+{
+ assert [a][0] == a; // TODO: one could consider strengthening axioms to eliminate the need for this assert
+ // The reason we need the assert is to match the trigger in the rank axioms produced
+ // for datatypes containing sequences.
+ // See "is SeqType" case of AddDatatype in Translator.cs
+}
+
+lemma SeqRank1(s: seq<Abc>)
+ requires s != []
+ ensures s[0] != Wrapper(s)
+{
+}
+
+datatype Def = End | MultiWrapper(multiset<Def>)
+
+lemma MultisetRank(a: Def)
+ ensures a != MultiWrapper(multiset{a})
+{
+}
+
+datatype Ghi = End | SetWrapper(set<Ghi>)
+
+lemma SetRank(a: Ghi)
+ ensures a != SetWrapper({a})
+{
+}
diff --git a/Test/dafny0/ContainerRanks.dfy.expect b/Test/dafny0/ContainerRanks.dfy.expect
new file mode 100644
index 00000000..42fd56a5
--- /dev/null
+++ b/Test/dafny0/ContainerRanks.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 8 verified, 0 errors
diff --git a/Test/dafny0/ControlStructures.dfy.expect b/Test/dafny0/ControlStructures.dfy.expect
index 43124912..5638bcbc 100644
--- a/Test/dafny0/ControlStructures.dfy.expect
+++ b/Test/dafny0/ControlStructures.dfy.expect
@@ -1,41 +1,41 @@
-ControlStructures.dfy(8,3): Error: missing case in case statement: Purple
+ControlStructures.dfy(8,2): Error: missing case in case statement: Purple
Execution trace:
(0,0): anon0
(0,0): anon6_Else
(0,0): anon7_Else
(0,0): anon8_Then
-ControlStructures.dfy(8,3): Error: missing case in case statement: Blue
+ControlStructures.dfy(8,2): Error: missing case in case statement: Blue
Execution trace:
(0,0): anon0
(0,0): anon6_Else
(0,0): anon7_Else
(0,0): anon8_Else
(0,0): anon9_Then
-ControlStructures.dfy(17,3): Error: missing case in case statement: Purple
+ControlStructures.dfy(17,2): Error: missing case in case statement: Purple
Execution trace:
(0,0): anon0
(0,0): anon6_Else
(0,0): anon7_Else
(0,0): anon8_Then
-ControlStructures.dfy(46,5): Error: missing case in case statement: Red
+ControlStructures.dfy(46,4): Error: missing case in case statement: Red
Execution trace:
(0,0): anon0
(0,0): anon8_Then
(0,0): anon9_Else
(0,0): anon10_Then
-ControlStructures.dfy(54,3): Error: missing case in case statement: Red
+ControlStructures.dfy(54,2): Error: missing case in case statement: Red
Execution trace:
(0,0): anon0
- (0,0): anon8_Else
(0,0): anon9_Else
(0,0): anon10_Else
(0,0): anon11_Else
- (0,0): anon12_Then
-ControlStructures.dfy(75,3): Error: alternative cases fail to cover all possibilties
+ (0,0): anon12_Else
+ (0,0): anon13_Then
+ControlStructures.dfy(75,2): Error: alternative cases fail to cover all possibilties
Execution trace:
(0,0): anon0
(0,0): anon5_Else
-ControlStructures.dfy(218,18): Error: assertion violation
+ControlStructures.dfy(218,17): Error: assertion violation
Execution trace:
(0,0): anon0
ControlStructures.dfy(197,3): anon59_LoopHead
@@ -51,7 +51,7 @@ Execution trace:
(0,0): anon69_LoopBody
ControlStructures.dfy(213,9): anon70_Else
(0,0): anon71_Then
-ControlStructures.dfy(235,21): Error: assertion violation
+ControlStructures.dfy(235,20): Error: assertion violation
Execution trace:
(0,0): anon0
ControlStructures.dfy(197,3): anon59_LoopHead
@@ -77,7 +77,7 @@ Execution trace:
(0,0): anon38
(0,0): anon83_Then
(0,0): anon52
-ControlStructures.dfy(238,30): Error: assertion violation
+ControlStructures.dfy(238,29): Error: assertion violation
Execution trace:
(0,0): anon0
ControlStructures.dfy(197,3): anon59_LoopHead
@@ -92,7 +92,7 @@ Execution trace:
(0,0): anon84_Then
(0,0): anon85_Then
(0,0): anon56
-ControlStructures.dfy(241,17): Error: assertion violation
+ControlStructures.dfy(241,16): Error: assertion violation
Execution trace:
(0,0): anon0
ControlStructures.dfy(197,3): anon59_LoopHead
diff --git a/Test/dafny0/Corecursion.dfy.expect b/Test/dafny0/Corecursion.dfy.expect
index e30f6f1a..a6b3fdce 100644
--- a/Test/dafny0/Corecursion.dfy.expect
+++ b/Test/dafny0/Corecursion.dfy.expect
@@ -1,36 +1,36 @@
-Corecursion.dfy(17,13): Error: cannot prove termination; try supplying a decreases clause (note that only functions without side effects can be called co-recursively)
+Corecursion.dfy(17,12): Error: cannot prove termination; try supplying a decreases clause (note that only functions without side effects can be called co-recursively)
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-Corecursion.dfy(23,13): Error: cannot prove termination; try supplying a decreases clause (note that only functions without any ensures clause can be called co-recursively)
+ (0,0): anon4_Else
+Corecursion.dfy(23,12): Error: cannot prove termination; try supplying a decreases clause (note that only functions without any ensures clause can be called co-recursively)
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-Corecursion.dfy(58,5): Error: cannot prove termination; try supplying a decreases clause
+ (0,0): anon4_Else
+Corecursion.dfy(58,4): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-Corecursion.dfy(71,16): Error: cannot prove termination; try supplying a decreases clause (note that calls cannot be co-recursive in this context)
+ (0,0): anon4_Else
+Corecursion.dfy(71,15): Error: cannot prove termination; try supplying a decreases clause (note that calls cannot be co-recursive in this context)
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
-Corecursion.dfy(93,15): Error: cannot prove termination; try supplying a decreases clause (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts)
+ (0,0): anon7_Else
+Corecursion.dfy(93,14): Error: cannot prove termination; try supplying a decreases clause (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts)
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
- (0,0): anon6_Then
-Corecursion.dfy(103,15): Error: cannot prove termination; try supplying a decreases clause (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts)
+ (0,0): anon7_Else
+ (0,0): anon8_Then
+Corecursion.dfy(103,14): Error: cannot prove termination; try supplying a decreases clause (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts)
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
- (0,0): anon6_Then
-Corecursion.dfy(148,13): Error: failure to decrease termination measure (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts)
+ (0,0): anon7_Else
+ (0,0): anon8_Then
+Corecursion.dfy(148,12): Error: failure to decrease termination measure (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts)
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-Corecursion.dfy(161,13): Error: failure to decrease termination measure (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts)
+ (0,0): anon4_Else
+Corecursion.dfy(161,12): Error: failure to decrease termination measure (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts)
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
+ (0,0): anon4_Else
Dafny program verifier finished with 20 verified, 8 errors
diff --git a/Test/dafny0/DTypes.dfy b/Test/dafny0/DTypes.dfy
index c8c893a0..9891c040 100644
--- a/Test/dafny0/DTypes.dfy
+++ b/Test/dafny0/DTypes.dfy
@@ -1,11 +1,11 @@
-// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /autoTriggers:1 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
class C {
var n: set<Node>;
method M(v: Stack)
- requires v != null;
+ requires v != null
{
var o: object := v;
assert o !in n; // should be known from the types involved
@@ -28,12 +28,12 @@ class C {
method A1(a: CP<int,C>)
{
var x: object := a;
- assert (forall b: CP<int,Stack> :: x == b ==> b == null); // follows from type antecedents
+ assert (forall b: CP<int,Stack> {:nowarn} :: x == b ==> b == null); // follows from type antecedents
}
var a2x: set<CP<C,Node>>;
method A2(b: set<CP<Node,C>>)
- requires null !in b;
+ requires null !in b
{
var x: set<object> := a2x;
var y: set<object> := b;
@@ -81,7 +81,7 @@ class CP<T,U> {
datatype Data = Lemon | Kiwi(int)
function G(d: Data): int
- requires d != Data.Lemon;
+ requires d != Data.Lemon
{
match d
case Lemon => G(d)
@@ -101,28 +101,28 @@ class DatatypeInduction<T> {
}
method Theorem0(tree: Tree<T>)
- ensures 1 <= LeafCount(tree);
+ ensures 1 <= LeafCount(tree)
{
assert (forall t: Tree<T> :: 1 <= LeafCount(t));
}
// also make sure it works for an instantiated generic datatype
method Theorem1(bt: Tree<bool>, it: Tree<int>)
- ensures 1 <= LeafCount(bt);
- ensures 1 <= LeafCount(it);
+ ensures 1 <= LeafCount(bt)
+ ensures 1 <= LeafCount(it)
{
assert (forall t: Tree<bool> :: 1 <= LeafCount(t));
assert (forall t: Tree<int> :: 1 <= LeafCount(t));
}
method NotATheorem0(tree: Tree<T>)
- ensures LeafCount(tree) % 2 == 1;
+ ensures LeafCount(tree) % 2 == 1
{
assert (forall t: Tree<T> :: LeafCount(t) % 2 == 1); // error: fails for Branch case
}
method NotATheorem1(tree: Tree<T>)
- ensures 2 <= LeafCount(tree);
+ ensures 2 <= LeafCount(tree)
{
assert (forall t: Tree<T> :: 2 <= LeafCount(t)); // error: fails for Leaf case
}
@@ -140,22 +140,22 @@ class DatatypeInduction<T> {
// ----- here is a test for induction over integers
method IntegerInduction_Succeeds(a: array<int>)
- requires a != null;
- requires a.Length == 0 || a[0] == 0;
- requires forall j :: 1 <= j && j < a.Length ==> a[j] == a[j-1]+2*j-1;
+ requires a != null
+ requires a.Length == 0 || a[0] == 0
+ requires forall j {:nowarn} :: 1 <= j < a.Length ==> a[j] == a[j-1]+2*j-1 // WISH: If induction was more powerful, we wouldn't need to rely on the quantifier to produce the j-1 term.
{
// The following assertion can be proved by induction:
- assert forall n {:induction} :: 0 <= n && n < a.Length ==> a[n] == n*n;
+ assert forall n {:induction} :: 0 <= n < a.Length ==> a[n] == n*n;
}
method IntegerInduction_Fails(a: array<int>)
- requires a != null;
- requires a.Length == 0 || a[0] == 0;
- requires forall j :: 1 <= j && j < a.Length ==> a[j] == a[j-1]+2*j-1;
+ requires a != null
+ requires a.Length == 0 || a[0] == 0
+ requires forall j {:nowarn} :: 1 <= j < a.Length ==> a[j] == a[j-1]+2*j-1 // WISH: Same as above
{
// ...but the induction heuristics don't recognize the situation as one where
// applying induction would be profitable:
- assert forall n :: 0 <= n && n < a.Length ==> a[n] == n*n; // error reported
+ assert forall n :: 0 <= n < a.Length ==> a[n] == n*n; // error reported
}
}
@@ -171,7 +171,7 @@ abstract module OpaqueTypesWithParameters {
}
method DifferentTypes(a: array<P<int>>, b: array<P<bool>>)
- requires a != null && b != null;
+ requires a != null && b != null
// If P were a known type, then it would also be known that P<int> and P<bool>
// would be different types, and then the types of 'a' and 'b' would be different,
// which would imply that the following postcondition would hold.
diff --git a/Test/dafny0/DTypes.dfy.expect b/Test/dafny0/DTypes.dfy.expect
index 9b4288e9..76088e9b 100644
--- a/Test/dafny0/DTypes.dfy.expect
+++ b/Test/dafny0/DTypes.dfy.expect
@@ -1,27 +1,27 @@
-DTypes.dfy(182,3): Error BP5003: A postcondition might not hold on this return path.
-DTypes.dfy(181,15): Related location: This is the postcondition that might not hold.
+DTypes.dfy(182,2): Error BP5003: A postcondition might not hold on this return path.
+DTypes.dfy(181,14): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-DTypes.dfy(18,14): Error: assertion violation
+DTypes.dfy(18,13): Error: assertion violation
Execution trace:
(0,0): anon0
-DTypes.dfy(56,18): Error: assertion violation
+DTypes.dfy(56,17): Error: assertion violation
Execution trace:
(0,0): anon0
-DTypes.dfy(121,13): Error: assertion violation
-DTypes.dfy(93,30): Related location
+DTypes.dfy(121,12): Error: assertion violation
+DTypes.dfy(93,29): Related location
Execution trace:
(0,0): anon0
-DTypes.dfy(127,13): Error: assertion violation
-DTypes.dfy(93,20): Related location
+DTypes.dfy(127,12): Error: assertion violation
+DTypes.dfy(93,19): Related location
Execution trace:
(0,0): anon0
-DTypes.dfy(137,12): Error: assertion violation
-DTypes.dfy(132,6): Related location
-DTypes.dfy(93,20): Related location
+DTypes.dfy(137,11): Error: assertion violation
+DTypes.dfy(132,5): Related location
+DTypes.dfy(93,19): Related location
Execution trace:
(0,0): anon0
-DTypes.dfy(158,12): Error: assertion violation
+DTypes.dfy(158,11): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon5_Then
diff --git a/Test/dafny0/DatatypeUpdate.dfy b/Test/dafny0/DatatypeUpdate.dfy
index 76cce5ce..b7905928 100644
--- a/Test/dafny0/DatatypeUpdate.dfy
+++ b/Test/dafny0/DatatypeUpdate.dfy
@@ -1,6 +1,6 @@
// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
-
+module OldSyntax {
datatype MyDataType = MyConstructor(myint:int, mybool:bool)
| MyOtherConstructor(otherbool:bool)
| MyNumericConstructor(42:int)
@@ -35,3 +35,51 @@ method UpdateNumNam(nn: NumericNames, y: int) returns (pp: NumericNames)
{
pp := nn[010 := y]; // not to be confused with a field name 10
}
+}
+
+module NewSyntax {
+datatype MyDataType = MyConstructor(myint:int, mybool:bool)
+ | MyOtherConstructor(otherbool:bool)
+ | MyNumericConstructor(42:int)
+
+method test(foo:MyDataType, x:int) returns (abc:MyDataType, def:MyDataType, ghi:MyDataType, jkl:MyDataType)
+ requires foo.MyConstructor?;
+ ensures abc == foo.(myint := x + 2);
+ ensures def == foo.(otherbool := !foo.mybool);
+ ensures ghi == foo.(myint := 2).(mybool := false);
+ //ensures jkl == foo.(non_destructor := 5); // Resolution error: no non_destructor in MyDataType
+ ensures jkl == foo.(42 := 7);
+{
+ abc := MyConstructor(x + 2, foo.mybool);
+ abc := foo.(myint := x + 2);
+ def := MyOtherConstructor(!foo.mybool);
+ ghi := MyConstructor(2, false);
+ jkl := foo.(42 := 7);
+
+ assert abc.(myint := abc.myint - 2) == foo.(myint := x);
+}
+
+// regression test (for a previous bug in the Translator.Substituter):
+datatype Dt = Ctor(x: int, y: bool)
+function F(d: Dt): Dt
+{
+ d.(x := 5)
+}
+
+datatype NumericNames = NumNam(010: int, 10: real, f: bool)
+
+method UpdateNumNam(nn: NumericNames, y: int) returns (pp: NumericNames)
+{
+ pp := nn.(010 := y); // not to be confused with a field name 10
+}
+
+method MultipleUpdates(nn: NumericNames, y: int) returns (pp: NumericNames)
+ ensures pp.010 == y
+{
+ if * {
+ pp := nn.(10 := 0.10, 010 := y);
+ } else {
+ pp := nn.(010 := y, f := true, 10 := 0.10);
+ }
+}
+}
diff --git a/Test/dafny0/DatatypeUpdate.dfy.expect b/Test/dafny0/DatatypeUpdate.dfy.expect
index 790f6509..9a924214 100644
--- a/Test/dafny0/DatatypeUpdate.dfy.expect
+++ b/Test/dafny0/DatatypeUpdate.dfy.expect
@@ -1,2 +1,13 @@
+DatatypeUpdate.dfy(10,22): Warning: datatype update syntax D[f := E] is deprecated; the new syntax is D.(f := E)
+DatatypeUpdate.dfy(11,22): Warning: datatype update syntax D[f := E] is deprecated; the new syntax is D.(f := E)
+DatatypeUpdate.dfy(12,22): Warning: datatype update syntax D[f := E] is deprecated; the new syntax is D.(f := E)
+DatatypeUpdate.dfy(12,34): Warning: datatype update syntax D[f := E] is deprecated; the new syntax is D.(f := E)
+DatatypeUpdate.dfy(14,22): Warning: datatype update syntax D[f := E] is deprecated; the new syntax is D.(f := E)
+DatatypeUpdate.dfy(17,14): Warning: datatype update syntax D[f := E] is deprecated; the new syntax is D.(f := E)
+DatatypeUpdate.dfy(20,14): Warning: datatype update syntax D[f := E] is deprecated; the new syntax is D.(f := E)
+DatatypeUpdate.dfy(22,14): Warning: datatype update syntax D[f := E] is deprecated; the new syntax is D.(f := E)
+DatatypeUpdate.dfy(22,45): Warning: datatype update syntax D[f := E] is deprecated; the new syntax is D.(f := E)
+DatatypeUpdate.dfy(29,3): Warning: datatype update syntax D[f := E] is deprecated; the new syntax is D.(f := E)
+DatatypeUpdate.dfy(36,10): Warning: datatype update syntax D[f := E] is deprecated; the new syntax is D.(f := E)
-Dafny program verifier finished with 5 verified, 0 errors
+Dafny program verifier finished with 12 verified, 0 errors
diff --git a/Test/dafny0/DatatypeUpdateResolution.dfy b/Test/dafny0/DatatypeUpdateResolution.dfy
new file mode 100644
index 00000000..26142fa8
--- /dev/null
+++ b/Test/dafny0/DatatypeUpdateResolution.dfy
@@ -0,0 +1,20 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+datatype MyDataType = MyConstructor(myint:int, mybool:bool)
+ | MyOtherConstructor(otherbool:bool)
+ | MyNumericConstructor(42:int)
+datatype SomeOtherType = S_O_T(non_destructor: int)
+
+method test(foo:MyDataType, x:int) returns (abc:MyDataType, def:MyDataType, ghi:MyDataType, jkl:MyDataType)
+ requires foo.MyConstructor?
+ ensures abc == foo.(myint := x + 2)
+ ensures jkl == foo.(non_destructor := 5) // error: 'non_destructor' is not a destructor in MyDataType
+ ensures jkl == foo.(mybool := true, 40 := 100, myint := 200) // error: '40' is not a destructor
+{
+ abc := MyConstructor(x + 2, foo.mybool).(myint := x + 3);
+ abc := foo.(myint := x + 2, mybool := true).(mybool := false); // allowed
+ def := MyOtherConstructor(!foo.mybool).(otherbool := true, otherbool := true); // error: duplicated member
+ ghi := MyConstructor(2, false).(otherbool := true); // allowed, and will generate verification error
+ jkl := foo.(42 := 7, otherbool := true); // error: members are from different constructors
+}
diff --git a/Test/dafny0/DatatypeUpdateResolution.dfy.expect b/Test/dafny0/DatatypeUpdateResolution.dfy.expect
new file mode 100644
index 00000000..db3e1fe2
--- /dev/null
+++ b/Test/dafny0/DatatypeUpdateResolution.dfy.expect
@@ -0,0 +1,5 @@
+DatatypeUpdateResolution.dfy(12,22): Error: member 'non_destructor' does not exist in datatype 'MyDataType'
+DatatypeUpdateResolution.dfy(13,38): Error: member '40' does not exist in datatype 'MyDataType'
+DatatypeUpdateResolution.dfy(17,61): Error: duplicate update member 'otherbool'
+DatatypeUpdateResolution.dfy(19,23): Error: updated datatype members must belong to the same constructor ('otherbool' belongs to 'MyOtherConstructor' and '42' belongs to 'MyNumericConstructor'
+4 resolution/type errors detected in DatatypeUpdateResolution.dfy
diff --git a/Test/dafny0/Datatypes.dfy.expect b/Test/dafny0/Datatypes.dfy.expect
index 874df45e..7147ca60 100644
--- a/Test/dafny0/Datatypes.dfy.expect
+++ b/Test/dafny0/Datatypes.dfy.expect
@@ -1,43 +1,43 @@
-Datatypes.dfy(297,10): Error BP5003: A postcondition might not hold on this return path.
-Datatypes.dfy(295,15): Related location: This is the postcondition that might not hold.
+Datatypes.dfy(297,9): Error BP5003: A postcondition might not hold on this return path.
+Datatypes.dfy(295,14): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
(0,0): anon13_Then
(0,0): anon14_Else
(0,0): anon15_Then
(0,0): anon6
-Datatypes.dfy(298,12): Error: missing case in case statement: Appendix
+Datatypes.dfy(298,11): Error: missing case in case statement: Appendix
Execution trace:
(0,0): anon0
(0,0): anon13_Then
(0,0): anon14_Else
(0,0): anon15_Else
(0,0): anon16_Then
-Datatypes.dfy(349,5): Error: missing case in case statement: Cons
+Datatypes.dfy(349,4): Error: missing case in case statement: Cons
Execution trace:
(0,0): anon0
- (0,0): anon6_Else
- (0,0): anon7_Then
-Datatypes.dfy(349,5): Error: missing case in case statement: Nil
+ (0,0): anon7_Else
+ (0,0): anon8_Then
+Datatypes.dfy(349,4): Error: missing case in case statement: Nil
Execution trace:
(0,0): anon0
- (0,0): anon6_Else
(0,0): anon7_Else
- (0,0): anon8_Then
-Datatypes.dfy(356,8): Error: missing case in case statement: Cons
+ (0,0): anon8_Else
+ (0,0): anon9_Then
+Datatypes.dfy(356,7): Error: missing case in case statement: Cons
Execution trace:
(0,0): anon0
- (0,0): anon9_Else
- (0,0): anon10_Then
+ (0,0): anon10_Else
(0,0): anon11_Then
-Datatypes.dfy(356,8): Error: missing case in case statement: Nil
+ (0,0): anon12_Then
+Datatypes.dfy(356,7): Error: missing case in case statement: Nil
Execution trace:
(0,0): anon0
- (0,0): anon9_Else
- (0,0): anon10_Then
- (0,0): anon11_Else
- (0,0): anon12_Then
-Datatypes.dfy(82,20): Error: assertion violation
+ (0,0): anon10_Else
+ (0,0): anon11_Then
+ (0,0): anon12_Else
+ (0,0): anon13_Then
+Datatypes.dfy(82,19): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon20_Else
@@ -47,23 +47,23 @@ Execution trace:
(0,0): anon23_Then
(0,0): anon24_Else
(0,0): anon25_Then
-Datatypes.dfy(170,16): Error: assertion violation
+Datatypes.dfy(170,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon4_Then
-Datatypes.dfy(172,16): Error: assertion violation
+Datatypes.dfy(172,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon4_Else
(0,0): anon5_Then
-Datatypes.dfy(201,13): Error: destructor 'Car' can only be applied to datatype values constructed by 'XCons'
+Datatypes.dfy(201,12): Error: destructor 'Car' can only be applied to datatype values constructed by 'XCons'
Execution trace:
(0,0): anon0
-Datatypes.dfy(204,17): Error: destructor 'Car' can only be applied to datatype values constructed by 'XCons'
+Datatypes.dfy(204,16): Error: destructor 'Car' can only be applied to datatype values constructed by 'XCons'
Execution trace:
(0,0): anon0
(0,0): anon6_Then
-Datatypes.dfy(225,17): Error: destructor 'c' can only be applied to datatype values constructed by 'T''
+Datatypes.dfy(225,16): Error: destructor 'c' can only be applied to datatype values constructed by 'T''
Execution trace:
(0,0): anon0
(0,0): anon5_Then
diff --git a/Test/dafny0/Definedness.dfy.expect b/Test/dafny0/Definedness.dfy.expect
index 41073c0e..b5b015ad 100644
--- a/Test/dafny0/Definedness.dfy.expect
+++ b/Test/dafny0/Definedness.dfy.expect
@@ -1,87 +1,87 @@
-Definedness.dfy(11,7): Error: possible division by zero
+Definedness.dfy(11,6): Error: possible division by zero
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-Definedness.dfy(18,16): Error: possible division by zero
+ (0,0): anon4_Else
+Definedness.dfy(18,15): Error: possible division by zero
Execution trace:
(0,0): anon0
-Definedness.dfy(27,16): Error: target object may be null
+Definedness.dfy(27,15): Error: target object may be null
Execution trace:
(0,0): anon0
-Definedness.dfy(28,21): Error: target object may be null
+Definedness.dfy(28,20): Error: target object may be null
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-Definedness.dfy(29,17): Error: possible division by zero
+Definedness.dfy(29,16): Error: possible division by zero
Execution trace:
(0,0): anon0
-Definedness.dfy(36,16): Error: target object may be null
+Definedness.dfy(36,15): Error: target object may be null
Execution trace:
(0,0): anon0
-Definedness.dfy(45,16): Error: target object may be null
+Definedness.dfy(45,15): Error: target object may be null
Execution trace:
(0,0): anon0
-Definedness.dfy(53,18): Error: target object may be null
+Definedness.dfy(53,17): Error: target object may be null
Execution trace:
(0,0): anon0
-Definedness.dfy(54,3): Error BP5003: A postcondition might not hold on this return path.
-Definedness.dfy(53,22): Related location: This is the postcondition that might not hold.
+Definedness.dfy(54,2): Error BP5003: A postcondition might not hold on this return path.
+Definedness.dfy(53,21): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-Definedness.dfy(60,18): Error: target object may be null
+Definedness.dfy(60,17): Error: target object may be null
Execution trace:
(0,0): anon0
-Definedness.dfy(61,3): Error BP5003: A postcondition might not hold on this return path.
-Definedness.dfy(60,22): Related location: This is the postcondition that might not hold.
+Definedness.dfy(61,2): Error BP5003: A postcondition might not hold on this return path.
+Definedness.dfy(60,21): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-Definedness.dfy(68,3): Error BP5003: A postcondition might not hold on this return path.
-Definedness.dfy(67,22): Related location: This is the postcondition that might not hold.
+Definedness.dfy(68,2): Error BP5003: A postcondition might not hold on this return path.
+Definedness.dfy(67,21): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-Definedness.dfy(88,7): Error: target object may be null
+Definedness.dfy(88,6): Error: target object may be null
Execution trace:
(0,0): anon0
-Definedness.dfy(89,5): Error: possible violation of function precondition
-Definedness.dfy(79,16): Related location
+Definedness.dfy(89,4): Error: possible violation of function precondition
+Definedness.dfy(79,15): Related location
Execution trace:
(0,0): anon0
-Definedness.dfy(89,10): Error: assignment may update an object not in the enclosing context's modifies clause
+Definedness.dfy(89,9): Error: assignment may update an object not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
-Definedness.dfy(89,10): Error: target object may be null
+Definedness.dfy(89,9): Error: target object may be null
Execution trace:
(0,0): anon0
-Definedness.dfy(90,10): Error: possible violation of function precondition
-Definedness.dfy(79,16): Related location
+Definedness.dfy(90,9): Error: possible violation of function precondition
+Definedness.dfy(79,15): Related location
Execution trace:
(0,0): anon0
-Definedness.dfy(95,14): Error: possible division by zero
+Definedness.dfy(95,13): Error: possible division by zero
Execution trace:
(0,0): anon0
-Definedness.dfy(95,23): Error: possible division by zero
+Definedness.dfy(95,22): Error: possible division by zero
Execution trace:
(0,0): anon0
-Definedness.dfy(96,15): Error: possible division by zero
+Definedness.dfy(96,14): Error: possible division by zero
Execution trace:
(0,0): anon0
-Definedness.dfy(101,12): Error: possible division by zero
+Definedness.dfy(101,11): Error: possible division by zero
Execution trace:
(0,0): anon0
-Definedness.dfy(108,15): Error: possible division by zero
+Definedness.dfy(108,14): Error: possible division by zero
Execution trace:
Definedness.dfy(108,5): anon7_LoopHead
(0,0): anon7_LoopBody
Definedness.dfy(108,5): anon8_Else
-Definedness.dfy(117,23): Error: possible violation of function precondition
-Definedness.dfy(79,16): Related location
+Definedness.dfy(117,22): Error: possible violation of function precondition
+Definedness.dfy(79,15): Related location
Execution trace:
(0,0): anon0
Definedness.dfy(116,5): anon12_LoopHead
(0,0): anon12_LoopBody
(0,0): anon13_Then
-Definedness.dfy(123,17): Error: possible violation of function precondition
-Definedness.dfy(79,16): Related location
+Definedness.dfy(123,16): Error: possible violation of function precondition
+Definedness.dfy(79,15): Related location
Execution trace:
(0,0): anon0
Definedness.dfy(116,5): anon12_LoopHead
@@ -91,30 +91,30 @@ Execution trace:
Definedness.dfy(122,5): anon15_LoopHead
(0,0): anon15_LoopBody
(0,0): anon16_Then
-Definedness.dfy(133,17): Error: possible violation of function precondition
-Definedness.dfy(79,16): Related location
+Definedness.dfy(133,16): Error: possible violation of function precondition
+Definedness.dfy(79,15): Related location
Execution trace:
(0,0): anon0
Definedness.dfy(132,5): anon6_LoopHead
(0,0): anon6_LoopBody
(0,0): anon7_Then
-Definedness.dfy(133,22): Error BP5004: This loop invariant might not hold on entry.
+Definedness.dfy(133,21): Error BP5004: This loop invariant might not hold on entry.
Execution trace:
(0,0): anon0
-Definedness.dfy(134,17): Error: possible violation of function precondition
-Definedness.dfy(79,16): Related location
+Definedness.dfy(134,16): Error: possible violation of function precondition
+Definedness.dfy(79,15): Related location
Execution trace:
(0,0): anon0
Definedness.dfy(132,5): anon6_LoopHead
(0,0): anon6_LoopBody
(0,0): anon7_Then
-Definedness.dfy(143,15): Error: possible division by zero
+Definedness.dfy(143,14): Error: possible division by zero
Execution trace:
(0,0): anon0
Definedness.dfy(143,5): anon8_LoopHead
(0,0): anon8_LoopBody
Definedness.dfy(143,5): anon9_Else
-Definedness.dfy(162,15): Error: possible division by zero
+Definedness.dfy(162,14): Error: possible division by zero
Execution trace:
(0,0): anon0
Definedness.dfy(156,5): anon16_LoopHead
@@ -126,11 +126,11 @@ Execution trace:
Definedness.dfy(162,5): anon20_LoopHead
(0,0): anon20_LoopBody
Definedness.dfy(162,5): anon21_Else
-Definedness.dfy(175,28): Error BP5004: This loop invariant might not hold on entry.
+Definedness.dfy(175,27): Error BP5004: This loop invariant might not hold on entry.
Execution trace:
(0,0): anon0
-Definedness.dfy(181,17): Error: possible violation of function precondition
-Definedness.dfy(79,16): Related location
+Definedness.dfy(181,16): Error: possible violation of function precondition
+Definedness.dfy(79,15): Related location
Execution trace:
(0,0): anon0
Definedness.dfy(173,5): anon18_LoopHead
@@ -142,38 +142,34 @@ Execution trace:
(0,0): anon22_Then
(0,0): anon23_Then
(0,0): anon11
-Definedness.dfy(196,19): Error: possible division by zero
+Definedness.dfy(196,18): Error: possible division by zero
Execution trace:
(0,0): anon0
Definedness.dfy(194,5): anon6_LoopHead
(0,0): anon6_LoopBody
(0,0): anon7_Then
-Definedness.dfy(196,23): Error BP5004: This loop invariant might not hold on entry.
+Definedness.dfy(196,22): Error BP5004: This loop invariant might not hold on entry.
Execution trace:
(0,0): anon0
-Definedness.dfy(196,28): Error: possible division by zero
+Definedness.dfy(196,27): Error: possible division by zero
Execution trace:
(0,0): anon0
Definedness.dfy(194,5): anon6_LoopHead
(0,0): anon6_LoopBody
(0,0): anon7_Then
-Definedness.dfy(215,10): Error BP5003: A postcondition might not hold on this return path.
-Definedness.dfy(217,46): Related location: This is the postcondition that might not hold.
+Definedness.dfy(215,9): Error BP5003: A postcondition might not hold on this return path.
+Definedness.dfy(217,45): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
-Definedness.dfy(224,22): Error: target object may be null
+ (0,0): anon4_Else
+Definedness.dfy(224,21): Error: target object may be null
Execution trace:
(0,0): anon0
- (0,0): anon5_Then
- (0,0): anon2
- (0,0): anon6_Then
-Definedness.dfy(237,10): Error BP5003: A postcondition might not hold on this return path.
-Definedness.dfy(240,24): Related location: This is the postcondition that might not hold.
+ (0,0): anon4_Then
+Definedness.dfy(237,9): Error BP5003: A postcondition might not hold on this return path.
+Definedness.dfy(240,23): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
- (0,0): anon7_Then
- (0,0): anon2
- (0,0): anon8_Else
+ (0,0): anon4_Else
Dafny program verifier finished with 21 verified, 37 errors
diff --git a/Test/dafny0/DeterministicPick.dfy b/Test/dafny0/DeterministicPick.dfy
index a7ec55fa..13db1bfc 100644
--- a/Test/dafny0/DeterministicPick.dfy
+++ b/Test/dafny0/DeterministicPick.dfy
@@ -29,6 +29,7 @@ module Attempt_Smallest refines Specification {
var z :| z in s;
if s != {z} {
var s' := s - {z};
+ assert forall y :: y in s ==> y in s' || y == z;
ASmallestToPick(s');
}
}
diff --git a/Test/dafny0/DeterministicPick.dfy.expect b/Test/dafny0/DeterministicPick.dfy.expect
index f8b779ef..aef97ebd 100644
--- a/Test/dafny0/DeterministicPick.dfy.expect
+++ b/Test/dafny0/DeterministicPick.dfy.expect
@@ -1,6 +1,6 @@
-DeterministicPick.dfy(13,5): Error: to be compilable, the value of a let-such-that expression must be uniquely determined
+DeterministicPick.dfy(13,4): Error: to be compilable, the value of a let-such-that expression must be uniquely determined
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
+ (0,0): anon4_Else
Dafny program verifier finished with 6 verified, 1 error
diff --git a/Test/dafny0/DiamondImports.dfy.expect b/Test/dafny0/DiamondImports.dfy.expect
index e9e8c2b9..1acca075 100644
--- a/Test/dafny0/DiamondImports.dfy.expect
+++ b/Test/dafny0/DiamondImports.dfy.expect
@@ -1,12 +1,12 @@
-DiamondImports.dfy(34,16): Error: assertion violation
+DiamondImports.dfy(34,15): Error: assertion violation
Execution trace:
(0,0): anon0
-DiamondImports.dfy(50,16): Error: assertion violation
+DiamondImports.dfy(50,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Then
(0,0): anon2
-DiamondImports.dfy(101,16): Error: assertion violation
+DiamondImports.dfy(101,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon7_Then
@@ -14,7 +14,7 @@ Execution trace:
(0,0): anon8_Then
(0,0): anon9_Then
(0,0): anon6
-DiamondImports.dfy(120,16): Error: assertion violation
+DiamondImports.dfy(120,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon9_Then
@@ -25,7 +25,7 @@ Execution trace:
(0,0): anon6
(0,0): anon12_Then
(0,0): anon8
-DiamondImports.dfy(140,26): Error: assertion violation
+DiamondImports.dfy(140,25): Error: assertion violation
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/DirtyLoops.dfy b/Test/dafny0/DirtyLoops.dfy
index 265fadb5..1a61a7e6 100644
--- a/Test/dafny0/DirtyLoops.dfy
+++ b/Test/dafny0/DirtyLoops.dfy
@@ -1,4 +1,5 @@
-// RUN: %dafny /compile:0 /dprint:"%t.dprint.dfy" "%s" > "%t"; %dafny /noVerify /compile:1 "%t.dprint.dfy" >> "%t"
+// RUN: %dafny /compile:0 /dprint:"%t.dprint.dfy" "%s" > "%t"
+// RUN: %dafny /noVerify /compile:1 "%t.dprint.dfy" >> "%t"
// RUN: %diff "%s.expect" "%t"
class MyClass {
diff --git a/Test/dafny0/DiscoverBounds.dfy.expect b/Test/dafny0/DiscoverBounds.dfy.expect
index ee816683..34003053 100644
--- a/Test/dafny0/DiscoverBounds.dfy.expect
+++ b/Test/dafny0/DiscoverBounds.dfy.expect
@@ -1,4 +1,4 @@
-DiscoverBounds.dfy(36,7): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'o''
-DiscoverBounds.dfy(39,7): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'r'
-DiscoverBounds.dfy(40,7): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'r''
+DiscoverBounds.dfy(36,7): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'o''
+DiscoverBounds.dfy(39,7): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'r'
+DiscoverBounds.dfy(40,7): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'r''
3 resolution/type errors detected in DiscoverBounds.dfy
diff --git a/Test/dafny0/EqualityTypes.dfy b/Test/dafny0/EqualityTypes.dfy
index b2812759..c510cfb1 100644
--- a/Test/dafny0/EqualityTypes.dfy
+++ b/Test/dafny0/EqualityTypes.dfy
@@ -241,3 +241,115 @@ module Deep {
var m1 := map[ko := 5]; // error: bad type
}
}
+
+//--------------------------
+
+module UnderspecifiedTypeParameters {
+ method UP<T>()
+ function method UG<T>(): int
+ method Callee<T(==)>()
+ class TakesParam<U> { }
+
+ method MPG()
+ {
+ var g := UG(); // error: type parameter underspecified
+ UP(); // error: type parameter underspecified
+ }
+ method M() {
+ var zs: set; // error: type is underspecified
+ Callee<(int)>();
+ Callee<set>(); // error: type is underspecified
+ Callee<()>();
+ // The following
+ Callee<TakesParam>(); // error: type is underspecified
+ }
+}
+
+module EqualitySupportingTypes {
+ method P<T>()
+ function method G<T>(): int
+ class AClass<V(==),Y> {
+ static function method H<W,X(==)>(): bool
+ static method Q<A,B(==)>()
+ }
+
+ method Callee<T(==)>()
+ function method FCallee<T>(): T
+
+ datatype Dt = Dt(f: int -> int)
+ codatatype Stream<T> = Cons(T, Stream)
+
+ method M<ArbitraryTypeArg>()
+ {
+ Callee<Dt>(); // error: Dt is not an equality-supporting type
+ Callee<Stream<int>>(); // error: specified type does not support equality
+
+ // set<X> is allowed in a non-ghost context only if X is equality supporting.
+ // Ditto for multiset<X> and map<X,Y>.
+ var s3x: set<Dt>; // error: this type not allowed in a non-ghost context
+ var is3x: iset<Dt>; // error: this type not allowed in a non-ghost context
+ var mast: multiset<ArbitraryTypeArg>; // error: this type not allowed in a non-ghost context
+ var qt: seq<Stream<int>>; // allowed
+ var mp0: map<Dt,int>; // error: this type not allowed in a non-ghost context
+ var mp1: map<int,Dt>; // allowed
+ var imp0: imap<Dt,int>; // error: this type not allowed in a non-ghost context
+ var imp1: imap<int,Dt>; // allowed
+
+ var S := FCallee<set>(); // this gives s:set<?>
+ if 4 in S { // this constrains the type further to be s:set<int>
+ }
+
+ var xy: set<set<int>>;
+ var xz: set<set<Stream<int>>>; // error: set type argument must support equality
+
+ Callee<set<Stream<int>>>(); // bogus: a set shouldn't ever be allowed to take a Stream as an argument (this check seems to be missing for explicit type arguments) -- Note: language definition should be changed, because it doesn't make sense for it to talk about a type appearing in a ghost or non-ghost context. Instead, set/iset/multiset/map/imap should always be allowed to take any type argument, but these types may or may not support equality.
+ var xg := G<set<Stream<int>>>();
+
+ var ac0: AClass<int,int>;
+ var ac1: AClass<Stream<int>,int>; // error: type parameter 0 is required to support equality
+ var ac2: AClass<int,Stream<int>>;
+ var xd0 := ac0.H<real,real>();
+ var xd1 := ac1.H<Stream<real>,real>(); // error (remnant of the fact that the type of ac1 is not allowed)
+ var xd2 := ac2.H<real,Stream<real>>(); // error: type parameter 1 is required to support equality
+ var xe0 := ac0.H<real,real>;
+ var xe1 := ac1.H<Stream<real>,real>; // error (remnant of the fact that the type of ac1 is not allowed)
+ var xe2 := ac2.H<real,Stream<real>>; // error: type parameter 1 is required to support equality
+ var xh0 := AClass<int,int>.H<real,real>();
+ var xh1 := AClass<int,int>.H<Stream<real>,real>();
+ var xh2 := AClass<int,int>.H<real,Stream<real>>(); // error: type parameter 1 is required to support equality
+ var xk0 := AClass<real,real>.H<int,int>;
+ var xk1 := AClass<Stream<real>,real>.H<int,int>; // error: class type param 0 wants an equality-supporting type
+ var xk2 := AClass<real,Stream<real>>.H<int,int>;
+ AClass<Stream<int>,int>.Q<real,real>(); // error: class type param 0 wants an equality-supporting type
+ AClass<int,Stream<int>>.Q<real,real>();
+ AClass<int,Stream<int>>.Q<Stream<real>,real>();
+ AClass<int,Stream<int>>.Q<real,Stream<real>>(); // error: method type param 1 wants an equality-supporting type
+
+/*************************** TESTS YET TO COME
+ var ac8: AClass<real,real>;
+ var xd8 := (if 5/0 == 3 then ac0 else ac8).H<real,real>(); // error: this should be checked by the verifier
+
+ AClass<int,set<Stream<int>>>.Q<real,real>(); // error: cannot utter "set<Stream<int>>" Or is that okay???
+ AClass<int,int>.Q<set<Stream<real>>,real>(); // error: cannot utter "set<Stream<real>>" Or is that okay???
+ var xi0 := AClass<int,set<Stream<int>>>.H<real,real>(); // error: cannot utter "set<Stream<int>>" Or is that okay???
+ var xi1 := AClass<int,int>.H<real,set<Stream<real>>>(); // error: cannot utter "set<Stream<real>>" Or is that okay???
+
+ var x, t, s: seq<int -> int>, fii: int -> int;
+ if s == t {
+ x := 5; // error: assigning to non-ghost variable in ghost context
+ }
+ if fii in s {
+ x := 4; // error: assigning to non-ghost variable in ghost context
+ }
+ if !(fii in s) {
+ x := 3; // error: assigning to non-ghost variable in ghost context
+ }
+
+ ghost var ghostset: set<Stream<int>> := {}; // fine, since this is ghost
+ forall u | 0 <= u < 100
+ ensures var lets: set<Stream<int>> := {}; lets == lets // this is ghost, so the equality requirement doesn't apply
+ {
+ }
+*********************************************/
+ }
+}
diff --git a/Test/dafny0/EqualityTypes.dfy.expect b/Test/dafny0/EqualityTypes.dfy.expect
index 9f277582..1c02f3a0 100644
--- a/Test/dafny0/EqualityTypes.dfy.expect
+++ b/Test/dafny0/EqualityTypes.dfy.expect
@@ -35,4 +35,26 @@ EqualityTypes.dfy(238,24): Error: set argument type must support equality (got C
EqualityTypes.dfy(239,21): Error: multiset argument type must support equality (got Co)
EqualityTypes.dfy(241,8): Error: map domain type must support equality (got Co)
EqualityTypes.dfy(241,14): Error: map domain type must support equality (got Co)
-37 resolution/type errors detected in EqualityTypes.dfy
+EqualityTypes.dfy(255,13): Error: type variable 'T' in the function call to 'UG' could not be determined
+EqualityTypes.dfy(256,4): Error: type '?' to the method 'UP' is not determined
+EqualityTypes.dfy(259,8): Error: the type of this local variable is underspecified
+EqualityTypes.dfy(261,4): Error: type 'set<?>' to the method 'Callee' is not determined
+EqualityTypes.dfy(264,4): Error: type 'TakesParam<?>' to the method 'Callee' is not determined
+EqualityTypes.dfy(284,14): Error: type parameter 0 (T) passed to method Callee must support equality (got Dt)
+EqualityTypes.dfy(285,23): Error: type parameter 0 (T) passed to method Callee must support equality (got Stream<int>)
+EqualityTypes.dfy(289,8): Error: set argument type must support equality (got Dt)
+EqualityTypes.dfy(290,8): Error: iset argument type must support equality (got Dt)
+EqualityTypes.dfy(291,8): Error: multiset argument type must support equality (got ArbitraryTypeArg) (perhaps try declaring type parameter 'ArbitraryTypeArg' on line 282 as 'ArbitraryTypeArg(==)', which says it can only be instantiated with a type that supports equality)
+EqualityTypes.dfy(293,8): Error: map domain type must support equality (got Dt)
+EqualityTypes.dfy(295,8): Error: imap domain type must support equality (got Dt)
+EqualityTypes.dfy(303,8): Error: set argument type must support equality (got Stream<int>)
+EqualityTypes.dfy(309,8): Error: type parameter 0 (V) passed to type AClass must support equality (got Stream<int>)
+EqualityTypes.dfy(312,19): Error: type parameter 0 (V) passed to type AClass must support equality (got Stream<int>)
+EqualityTypes.dfy(313,19): Error: type parameter 1 (X) passed to function H must support equality (got Stream<real>)
+EqualityTypes.dfy(315,19): Error: type parameter 0 (V) passed to type AClass must support equality (got Stream<int>)
+EqualityTypes.dfy(316,19): Error: type parameter 1 (X) passed to function 'H' must support equality (got Stream<real>)
+EqualityTypes.dfy(319,31): Error: type parameter 1 (X) passed to function H must support equality (got Stream<real>)
+EqualityTypes.dfy(321,41): Error: type parameter 0 (V) passed to type AClass must support equality (got Stream<real>)
+EqualityTypes.dfy(323,28): Error: type parameter 0 (V) passed to type AClass must support equality (got Stream<int>)
+EqualityTypes.dfy(326,48): Error: type parameter 1 (B) passed to method Q must support equality (got Stream<real>)
+59 resolution/type errors detected in EqualityTypes.dfy
diff --git a/Test/dafny0/Extern.dfy b/Test/dafny0/Extern.dfy
new file mode 100644
index 00000000..cbdffe34
--- /dev/null
+++ b/Test/dafny0/Extern.dfy
@@ -0,0 +1,27 @@
+// RUN: %dafny /compile:1 /print:"%t.print" /dprint:"%t.dprint" "%s" "%S\Extern2.cs" "%S\ExternHelloLibrary.dll" > "%t"
+// RUN: %diff "%s.expect" "%t"
+extern "Modx" module Mod1
+{
+ extern "classx" class Class1
+ {
+ extern "Fun1x" static function method Fun1() : int
+ ensures Fun1() > 0
+ extern "Method1x" static method Method1() returns (x: int)
+ ensures x > 0
+ static function method Fun2() : int
+ ensures Fun2() > 0
+ {
+ Fun1()
+ }
+ static method Method2() returns (x: int)
+ ensures x > 0
+ {
+ x := Method1();
+ }
+ }
+ method Main()
+ {
+ var m2 := Class1.Method2();
+ print ("Fun2() = ", Class1.Fun2(), "Method2() = ", m2, "\n");
+ }
+}
diff --git a/Test/dafny0/Extern.dfy.expect b/Test/dafny0/Extern.dfy.expect
new file mode 100644
index 00000000..25c1c3ee
--- /dev/null
+++ b/Test/dafny0/Extern.dfy.expect
@@ -0,0 +1,4 @@
+
+Dafny program verifier finished with 7 verified, 0 errors
+Compiled program written to Extern.cs
+Compiled assembly into Extern.exe
diff --git a/Test/dafny0/Extern2.cs b/Test/dafny0/Extern2.cs
new file mode 100644
index 00000000..2fcaf18b
--- /dev/null
+++ b/Test/dafny0/Extern2.cs
@@ -0,0 +1,14 @@
+using System.Numerics;
+namespace @Modx {
+
+ public partial class @classx {
+ public static BigInteger @Fun1x() {
+ return BigInteger.One;
+ }
+ public static void @Method1x(out BigInteger @x)
+ {
+ ExternHelloLibrary.ExternHelloLibrary.SayHello();
+ @x = BigInteger.One;
+ }
+ }
+}
diff --git a/Test/dafny0/ExternHelloLibrary.cs b/Test/dafny0/ExternHelloLibrary.cs
new file mode 100644
index 00000000..81163997
--- /dev/null
+++ b/Test/dafny0/ExternHelloLibrary.cs
@@ -0,0 +1,15 @@
+// Note that ExternHelloLibrary.dll was produced from this file using
+// csc /t:library ExternHelloLibrary.cs
+
+using System;
+
+namespace ExternHelloLibrary
+{
+ public static class ExternHelloLibrary
+ {
+ public static void SayHello()
+ {
+ Console.WriteLine("Hello from ExternHelloLibrary.");
+ }
+ }
+}
diff --git a/Test/dafny0/ExternHelloLibrary.dll b/Test/dafny0/ExternHelloLibrary.dll
new file mode 100644
index 00000000..914e4248
--- /dev/null
+++ b/Test/dafny0/ExternHelloLibrary.dll
Binary files differ
diff --git a/Test/dafny0/ExternNegative.dfy b/Test/dafny0/ExternNegative.dfy
new file mode 100644
index 00000000..4ae73232
--- /dev/null
+++ b/Test/dafny0/ExternNegative.dfy
@@ -0,0 +1,26 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+extern "Modx" module Mod1
+{
+ extern "classx" class Class1
+ {
+ extern "Fun1x" static function method Fun1() : int
+ ensures Fun1() > 0
+ extern "Method1x" static method Method1() returns (x: int)
+ ensures x > 0
+ static abstract function method Fun2() : int
+ ensures Fun2() > 0
+ {
+ Fun1()
+ }
+ static static method Method2() returns (x: int)
+ ensures x > 0
+ {
+ x := Method1();
+ }
+ }
+}
+// Will give error about duplicate CompileName for module.
+extern "Modx" module Mod2
+{
+}
diff --git a/Test/dafny0/ExternNegative.dfy.expect b/Test/dafny0/ExternNegative.dfy.expect
new file mode 100644
index 00000000..5d95ced7
--- /dev/null
+++ b/Test/dafny0/ExternNegative.dfy.expect
@@ -0,0 +1,3 @@
+ExternNegative.dfy(11,11): Error: Function methods cannot be declared 'abstract'.
+ExternNegative.dfy(16,11): Error: Duplicate declaration modifier: static
+2 parse errors detected in ExternNegative.dfy
diff --git a/Test/dafny0/ExternNegative2.dfy b/Test/dafny0/ExternNegative2.dfy
new file mode 100644
index 00000000..3d09913b
--- /dev/null
+++ b/Test/dafny0/ExternNegative2.dfy
@@ -0,0 +1,26 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+extern "Modx" module Mod1
+{
+ extern "classx" class Class1
+ {
+ extern "Fun1x" static function method Fun1() : int
+ ensures Fun1() > 0
+ extern "Method1x" static method Method1() returns (x: int)
+ ensures x > 0
+ static function method Fun2() : int
+ ensures Fun2() > 0
+ {
+ Fun1()
+ }
+ static method Method2() returns (x: int)
+ ensures x > 0
+ {
+ x := Method1();
+ }
+ }
+}
+// Will give error about duplicate CompileName for module.
+extern "Modx" module Mod2
+{
+}
diff --git a/Test/dafny0/ExternNegative2.dfy.expect b/Test/dafny0/ExternNegative2.dfy.expect
new file mode 100644
index 00000000..64194de0
--- /dev/null
+++ b/Test/dafny0/ExternNegative2.dfy.expect
@@ -0,0 +1,2 @@
+ExternNegative2.dfy(24,21): Error: Modules 'Mod1' and 'Mod2' both have CompileName 'Modx'.
+1 resolution/type errors detected in ExternNegative2.dfy
diff --git a/Test/dafny0/ForallCompilation.dfy b/Test/dafny0/ForallCompilation.dfy
index c812983a..4d89f70d 100644
--- a/Test/dafny0/ForallCompilation.dfy
+++ b/Test/dafny0/ForallCompilation.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
method Main() {
diff --git a/Test/dafny0/Fuel.dfy b/Test/dafny0/Fuel.dfy
new file mode 100644
index 00000000..a768db02
--- /dev/null
+++ b/Test/dafny0/Fuel.dfy
@@ -0,0 +1,462 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+module TestModule1 {
+ function pos(x:int) : int
+ {
+ if x < 0 then 0
+ else 1 + pos(x - 1)
+ }
+
+ method test(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert pos(z) == 0;
+ assert pos(-1) == 0;
+ assert pos(y) == 3 + pos(y - 3); // error: Should fail, due to lack of fuel
+ assert pos(y) == 4 + pos(y - 4); // Succeeds, thanks to the assume from the preceding assert
+ }
+}
+
+// Test with function-level fuel boost
+module TestModule2 {
+ function {:fuel 3} pos1(x:int) : int
+ {
+ if x < 0 then 0
+ else 1 + pos1(x - 1)
+ }
+
+ function {:fuel 3,5} pos2(x:int) : int
+ {
+ if x < 0 then 0
+ else 1 + pos2(x - 1)
+ }
+
+ function {:fuel 3,5} pos3(x:int) : int
+ {
+ if x < 0 then 0
+ else 1 + pos3(x - 1)
+ }
+
+ function {:opaque} {:fuel 3,5} pos4(x:int) : int
+ {
+ if x < 0 then 0
+ else 1 + pos3(x - 1)
+ }
+
+ method test(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert pos1(z) == 0;
+ assert pos1(-1) == 0;
+ assert pos1(y) == 3 + pos1(y - 3);
+ assert pos1(y) == 4 + pos1(y - 4);
+
+ assert pos2(z) == 0;
+ assert pos2(-1) == 0;
+ assert pos2(y) == 3 + pos2(y - 3);
+ assert pos2(y) == 4 + pos2(y - 4);
+
+ if (*) {
+ assert pos3(y) == 5 + pos3(y - 5); // Just enough fuel to get here
+ } else {
+ assert pos3(y) == 6 + pos3(y - 6); // error: Should fail even with a boost, since boost is too small
+ }
+
+ if (*) {
+ assert pos4(z) == 0; // error: Fuel shouldn't overcome opaque
+ } else {
+ reveal_pos4();
+ assert pos4(y) == 5 + pos4(y - 5); // With reveal, everything should work as above
+ }
+
+
+ }
+}
+
+
+module TestModule3 {
+ // This fuel setting is equivalent to opaque, except for literals
+ function {:fuel 0,0} pos(x:int) : int
+ {
+ if x < 0 then 0
+ else 1 + pos(x - 1)
+ }
+
+ method test(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert pos(z) == 0; // error: Opaque setting hides body
+ assert pos(-1) == 0; // Passes, since Dafny's computation mode for lits ignore fuel
+ assert pos(y) == 3 + pos(y - 3);// error: Opaque setting hides body
+ }
+}
+
+// Test fuel settings via different contexts
+module TestModule4 {
+ function pos(x:int) : int
+ {
+ if x < 0 then 0
+ else 1 + pos(x - 1)
+ }
+
+ // Should pass
+ method {:fuel pos,3,5} test1(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert pos(z) == 0;
+ assert pos(-1) == 0;
+ assert pos(y) == 3 + pos(y - 3);
+ }
+
+ method {:fuel pos,0,0} test2(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert pos(z) == 0; // error: Should fail due to "opaque" fuel setting
+ assert pos(-1) == 0;
+ assert pos(y) == 3 + pos(y - 3); // error: Should fail due to "opaque" fuel setting
+ }
+
+ method test3(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert {:fuel pos,0,0} pos(z) == 0; // error: fuel can't be decreased
+ assert pos(-1) == 0;
+ if (*) {
+ assert pos(y) == 3 + pos(y - 3); // error: Should fail without extra fuel setting
+ assert pos(y) == 6 + pos(y - 6); // error: Should fail even with previous assert turned into assume
+ } else {
+ assert {:fuel pos,3,5} pos(y) == 3 + pos(y - 3); // Should succeed with extra fuel setting
+ assert pos(y) == 6 + pos(y - 6); // Should succeed thanks to previous assert turned into assume
+ }
+ }
+
+ method test4(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ forall t:int {:fuel pos,3} | t > 0
+ ensures true;
+ {
+ assert pos(y) == 3 + pos(y - 3); // Expected to pass, due to local fuel boost
+ }
+
+ if (*) {
+ calc {:fuel pos,3} {
+ pos(y);
+ 3 + pos(y - 3);
+ }
+ }
+
+ assert pos(y) == 3 + pos(y - 3); // error: Should fail, due to lack of fuel outside the forall
+ }
+}
+
+// Test fuel settings via different module contexts
+module TestModule5 {
+ // Test module level fuel settings, with nested modules
+
+ module TestModule5a {
+ module {:fuel TestModule5aiA.pos,3} TestModule5ai {
+ module TestModule5aiA {
+ function pos(x:int) : int
+ {
+ if x < 0 then 0
+ else 1 + pos(x - 1)
+ }
+
+ method test(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert pos(z) == 0;
+ assert pos(-1) == 0;
+ assert pos(y) == 3 + pos(y - 3); // Should pass due to intermediate module's fuel setting
+ }
+ }
+
+ method test(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert TestModule5aiA.pos(z) == 0;
+ assert TestModule5aiA.pos(-1) == 0;
+ assert TestModule5aiA.pos(y) == 3 + TestModule5aiA.pos(y - 3); // Should pass due to module level fuel
+ }
+ }
+
+ method test(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert TestModule5ai.TestModule5aiA.pos(z) == 0;
+ assert TestModule5ai.TestModule5aiA.pos(-1) == 0;
+ assert TestModule5ai.TestModule5aiA.pos(y) == 3 + TestModule5ai.TestModule5aiA.pos(y - 3); // error: Should fail, due to lack of fuel
+ }
+ }
+
+ module {:fuel TestModule5bi.TestModule5biA.pos,3} TestModule5b {
+ module TestModule5bi {
+ module TestModule5biA {
+ function pos(x:int) : int
+ {
+ if x < 0 then 0
+ else 1 + pos(x - 1)
+ }
+
+ method test(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert pos(z) == 0;
+ assert pos(-1) == 0;
+ assert pos(y) == 3 + pos(y - 3); // Should succceed due to outer module fuel setting
+ }
+ }
+ }
+ }
+}
+
+// Test fuel setting for multiple functions
+module TestModule6 {
+ function pos(x:int) : int
+ {
+ if x < 0 then 0
+ else 1 + pos(x - 1)
+ }
+
+ function neg(x:int) : int
+ decreases 1 - x;
+ {
+ if x > 0 then 0
+ else 1 + neg(x + 1)
+ }
+
+ method test1(y:int, z:int)
+ requires y > 5;
+ requires z < 5;
+ {
+ assert pos(y) == 3 + pos(y - 3); // error: Should fail, due to lack of fuel
+
+ assert neg(z) == 3 + neg(z + 3); // error: Should fail, due to lack of fuel
+ }
+
+ method {:fuel pos,3} {:fuel neg,4} test2(y:int, z:int)
+ requires y > 5;
+ requires z < -5;
+ {
+ assert pos(y) == 3 + pos(y - 3);
+
+ assert neg(z) == 3 + neg(z + 3);
+ }
+}
+
+// Test fuel settings with multiple overlapping contexts
+module TestModule7 {
+ function {:fuel 3} pos(x:int) : int
+ {
+ if x < 0 then 0
+ else 1 + pos(x - 1)
+ }
+
+ function {:fuel 0,0} neg(x:int) : int
+ decreases 1 - x;
+ {
+ if x > 0 then 0
+ else 1 + neg(x + 1)
+ }
+
+ method {:fuel neg,4} {:fuel pos,0,0} test1(y:int, z:int)
+ requires y > 5;
+ requires z < -5;
+ {
+ if (*) {
+ assert pos(y) == 3 + pos(y - 3); // error: Method fuel should override function fuel, so this should fail
+ assert neg(z) == 3 + neg(z + 3); // Method fuel should override function fuel, so this succeeds
+ }
+
+ forall t:int {:fuel pos,3} | t > 0
+ ensures true;
+ {
+ assert pos(y) == 3 + pos(y - 3); // Statement fuel should override method fuel, so this should succeed
+ }
+ }
+}
+
+// Test fuel in a slightly more complicated setting
+module TestModule8 {
+
+ newtype byte = i:int | 0 <= i < 0x100
+ newtype uint64 = i:int | 0 <= i < 0x10000000000000000
+
+ datatype G = GUint64
+ | GArray(elt:G)
+ | GTuple(t:seq<G>)
+ | GByteArray
+ | GTaggedUnion(cases:seq<G>)
+
+ datatype V = VUint64(u:uint64)
+ | VTuple(t:seq<V>)
+ | VCase(c:uint64, val:V)
+
+ predicate {:fuel 2} ValInGrammar(val:V, grammar:G)
+ {
+ match val
+ case VUint64(_) => grammar.GUint64?
+ case VTuple(t) => grammar.GTuple? && |t| == |grammar.t|
+ && forall i :: 0 <= i < |t| ==> ValInGrammar(t[i], grammar.t[i])
+ case VCase(c, val) => grammar.GTaggedUnion? && int(c) < |grammar.cases| && ValInGrammar(val, grammar.cases[c])
+ }
+
+ datatype CRequest = CRequest(client:EndPoint, seqno:uint64, request:CAppMessage) | CRequestNoOp()
+
+ type EndPoint
+ function method EndPoint_grammar() : G { GUint64 }
+ function method CRequest_grammar() : G { GTaggedUnion([ GTuple([EndPoint_grammar(), GUint64, CAppMessage_grammar()]), GUint64]) }
+
+ function method parse_EndPoint(val:V) : EndPoint
+ requires ValInGrammar(val, EndPoint_grammar());
+
+ type CAppMessage
+ function method CAppMessage_grammar() : G { GTaggedUnion([GUint64, GUint64, GUint64]) }
+ function method parse_AppMessage(val:V) : CAppMessage
+ requires ValInGrammar(val, CAppMessage_grammar());
+
+ function method {:fuel ValInGrammar,1,2} parse_Request1(val:V) : CRequest
+ requires ValInGrammar(val, CRequest_grammar());
+ {
+ if val.c == 0 then
+ var ep := parse_EndPoint(val.val.t[0]); // With default fuel, error: function precondition, destructor, index
+ CRequest(ep, val.val.t[1].u, parse_AppMessage(val.val.t[2])) // error: index out of range, destructor
+ else
+ CRequestNoOp()
+ }
+
+ function method parse_Request2(val:V) : CRequest
+ requires ValInGrammar(val, CRequest_grammar());
+ {
+ if val.c == 0 then
+ var ep := parse_EndPoint(val.val.t[0]); // With fuel boosted to 2 this succeeds
+ CRequest(ep, val.val.t[1].u, parse_AppMessage(val.val.t[2])) // error: destructor
+ else
+ CRequestNoOp()
+ }
+
+ function method {:fuel ValInGrammar,3} parse_Request3(val:V) : CRequest
+ requires ValInGrammar(val, CRequest_grammar());
+ {
+ if val.c == 0 then
+ var ep := parse_EndPoint(val.val.t[0]);
+ CRequest(ep, val.val.t[1].u, parse_AppMessage(val.val.t[2])) // With one more boost, everything succeeds
+ else
+ CRequestNoOp()
+ }
+
+ // With the method, everything succeeds with one less fuel boost (i.e., 2, rather than 3, as in parse_Request3)
+ method parse_Request4(val:V) returns (req:CRequest)
+ requires ValInGrammar(val, CRequest_grammar());
+ {
+ if val.c == 0 {
+ var ep := parse_EndPoint(val.val.t[0]);
+ req := CRequest(ep, val.val.t[1].u, parse_AppMessage(val.val.t[2]));
+ } else {
+ req := CRequestNoOp();
+ }
+ }
+}
+
+
+// Test fuel when it's applied to a non-recursive function
+module TestModule9 {
+ function abs(x:int) : int
+ {
+ if x < 0 then -1 * x else x
+ }
+
+ // All should pass.
+ method test1(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert abs(z) == -1*z;
+ assert abs(y) == y;
+ assert abs(-1) == 1;
+ }
+
+ // Method-level fuel override
+ method {:fuel abs,0,0} test2(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert abs(z) == -1*z; // error: Cannot see the body of abs
+ assert abs(y) == y; // error: Cannot see the body of abs
+ assert abs(-1) == 1; // lit bypasses fuel, so this should succeed
+ }
+
+ // Statement-level fuel override
+ method test3(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert {:fuel abs,0,0} abs(z) == -1*z; // error: fuel can't be decreased
+ assert abs(y) == y; // Normal success
+ assert abs(-1) == 1; // lit bypasses fuel, so this should succeed
+ }
+
+ // Giving more fuel to a non-recursive function won't help,
+ // but it shouldn't hurt either.
+ method {:fuel abs,5,7} test4(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert abs(z) == -1*z;
+ assert abs(y) == y;
+ assert abs(-1) == 1;
+ }
+}
+
+// Test fuel when it's applied to a non-recursive function directly (to simulate opaque)
+module TestModule10 {
+ function {:fuel 0,0} abs(x:int) : int
+ {
+ if x < 0 then -1 * x else x
+ }
+
+ method test1(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert abs(z) == -1*z; // error: Cannot see the body of abs
+ assert abs(y) == y; // error: Cannot see the body of abs
+ assert abs(-1) == 1; // lit bypasses fuel, so this should succeed
+ }
+}
+
+// Test fuel when it's mentioned in other functions function to simulate a local opaque
+module TestModule11 {
+ function abs(x:int) : int
+ {
+ if x < 0 then -1 * x else x
+ }
+
+ function {:fuel abs,0,0} abs'(x:int) : int
+ {
+ abs(x)
+ }
+
+ method test1(y:int, z:int)
+ requires y > 5;
+ requires z < 0;
+ {
+ assert abs'(z) == -1*z; // error: Cannot see the body of abs
+ assert abs'(y) == y; // error: Cannot see the body of abs
+ assert abs'(-1) == 1; // lit bypasses fuel, so this should succeed
+ }
+}
+
diff --git a/Test/dafny0/Fuel.dfy.expect b/Test/dafny0/Fuel.dfy.expect
new file mode 100644
index 00000000..275be237
--- /dev/null
+++ b/Test/dafny0/Fuel.dfy.expect
@@ -0,0 +1,113 @@
+Fuel.dfy(129,8): Error: Fuel can only increase within a given scope.
+Fuel.dfy(407,8): Error: Fuel can only increase within a given scope.
+Fuel.dfy(17,22): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Fuel.dfy(65,27): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon6_Else
+Fuel.dfy(69,27): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon6_Then
+ (0,0): anon7_Then
+Fuel.dfy(92,22): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Fuel.dfy(94,22): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Fuel.dfy(120,22): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Fuel.dfy(122,22): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Fuel.dfy(129,38): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon7_Then
+Fuel.dfy(132,26): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ Fuel.dfy(129,9): anon7_Else
+ (0,0): anon8_Then
+Fuel.dfy(133,26): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ Fuel.dfy(129,9): anon7_Else
+ (0,0): anon8_Then
+Fuel.dfy(157,22): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon10_Else
+ (0,0): anon9
+Fuel.dfy(200,55): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Fuel.dfy(245,22): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Fuel.dfy(247,22): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Fuel.dfy(280,26): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon7_Then
+Fuel.dfy(335,26): Error: possible violation of function precondition
+Fuel.dfy(324,21): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon7_Else
+ (0,0): anon8_Then
+Fuel.dfy(335,49): Error: destructor 't' can only be applied to datatype values constructed by 'VTuple'
+Execution trace:
+ (0,0): anon0
+ (0,0): anon7_Else
+ (0,0): anon8_Then
+Fuel.dfy(335,50): Error: index out of range
+Execution trace:
+ (0,0): anon0
+ (0,0): anon7_Else
+ (0,0): anon8_Then
+Fuel.dfy(336,38): Error: index out of range
+Execution trace:
+ (0,0): anon0
+ (0,0): anon7_Else
+ (0,0): anon8_Then
+Fuel.dfy(336,42): Error: destructor 'u' can only be applied to datatype values constructed by 'VUint64'
+Execution trace:
+ (0,0): anon0
+ (0,0): anon7_Else
+ (0,0): anon8_Then
+Fuel.dfy(346,42): Error: destructor 'u' can only be applied to datatype values constructed by 'VUint64'
+Execution trace:
+ (0,0): anon0
+ (0,0): anon7_Else
+ (0,0): anon8_Then
+Fuel.dfy(397,22): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Fuel.dfy(398,22): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Fuel.dfy(407,38): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon3_Then
+Fuel.dfy(435,22): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Fuel.dfy(436,22): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Fuel.dfy(457,23): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Fuel.dfy(458,23): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 56 verified, 28 errors
diff --git a/Test/dafny0/FunctionSpecifications.dfy.expect b/Test/dafny0/FunctionSpecifications.dfy.expect
index 4b9aa202..078afaef 100644
--- a/Test/dafny0/FunctionSpecifications.dfy.expect
+++ b/Test/dafny0/FunctionSpecifications.dfy.expect
@@ -1,70 +1,70 @@
-FunctionSpecifications.dfy(35,25): Error BP5003: A postcondition might not hold on this return path.
-FunctionSpecifications.dfy(31,13): Related location: This is the postcondition that might not hold.
+FunctionSpecifications.dfy(29,9): Error BP5003: A postcondition might not hold on this return path.
+FunctionSpecifications.dfy(31,12): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
- (0,0): anon8_Else
- (0,0): anon9_Else
- (0,0): anon10_Then
+ (0,0): anon10_Else
(0,0): anon11_Else
-FunctionSpecifications.dfy(45,3): Error BP5003: A postcondition might not hold on this return path.
-FunctionSpecifications.dfy(40,24): Related location: This is the postcondition that might not hold.
+ (0,0): anon12_Then
+ (0,0): anon13_Else
+ (0,0): anon9
+FunctionSpecifications.dfy(38,9): Error BP5003: A postcondition might not hold on this return path.
+FunctionSpecifications.dfy(40,23): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
- (0,0): anon11_Else
- (0,0): anon14_Else
- (0,0): anon15_Then
-FunctionSpecifications.dfy(53,11): Error: cannot prove termination; try supplying a decreases clause
+ (0,0): anon15_Else
+ (0,0): anon18_Else
+ (0,0): anon19_Then
+ (0,0): anon14
+FunctionSpecifications.dfy(53,10): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon8_Then
- (0,0): anon3
-FunctionSpecifications.dfy(59,10): Error BP5003: A postcondition might not hold on this return path.
-FunctionSpecifications.dfy(60,22): Related location: This is the postcondition that might not hold.
+ (0,0): anon11_Then
+ (0,0): anon5
+FunctionSpecifications.dfy(59,9): Error BP5003: A postcondition might not hold on this return path.
+FunctionSpecifications.dfy(60,21): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
-FunctionSpecifications.dfy(108,23): Error: assertion violation
+ (0,0): anon7_Else
+FunctionSpecifications.dfy(108,22): Error: assertion violation
Execution trace:
(0,0): anon0
-FunctionSpecifications.dfy(111,23): Error: assertion violation
+FunctionSpecifications.dfy(111,22): Error: assertion violation
Execution trace:
(0,0): anon0
-FunctionSpecifications.dfy(126,27): Error: assertion violation
+FunctionSpecifications.dfy(126,26): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-FunctionSpecifications.dfy(130,27): Error: assertion violation
+FunctionSpecifications.dfy(130,26): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Else
-FunctionSpecifications.dfy(158,3): Error: cannot prove termination; try supplying a decreases clause
+FunctionSpecifications.dfy(158,2): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-FunctionSpecifications.dfy(167,11): Error: cannot prove termination; try supplying a decreases clause
+ (0,0): anon4_Else
+FunctionSpecifications.dfy(167,10): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-FunctionSpecifications.dfy(135,20): Error BP5003: A postcondition might not hold on this return path.
-FunctionSpecifications.dfy(137,29): Related location: This is the postcondition that might not hold.
+ (0,0): anon4_Else
+FunctionSpecifications.dfy(135,19): Error BP5003: A postcondition might not hold on this return path.
+FunctionSpecifications.dfy(137,28): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
- (0,0): anon5_Then
- (0,0): anon2
- (0,0): anon6_Else
-FunctionSpecifications.dfy(146,3): Error: failure to decrease termination measure
+ (0,0): anon4_Else
+FunctionSpecifications.dfy(146,2): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-FunctionSpecifications.dfy(153,3): Error: failure to decrease termination measure
+ (0,0): anon4_Else
+FunctionSpecifications.dfy(153,2): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-FunctionSpecifications.dfy(174,3): Error: cannot prove termination; try supplying a decreases clause
+ (0,0): anon4_Else
+FunctionSpecifications.dfy(174,2): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-FunctionSpecifications.dfy(171,20): Error: cannot prove termination; try supplying a decreases clause
+ (0,0): anon4_Else
+FunctionSpecifications.dfy(171,19): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/IMaps.dfy.expect b/Test/dafny0/IMaps.dfy.expect
index c2da9505..28ca8ca3 100644
--- a/Test/dafny0/IMaps.dfy.expect
+++ b/Test/dafny0/IMaps.dfy.expect
@@ -1,4 +1,4 @@
-IMaps.dfy(52,8): Error: element may not be in domain
+IMaps.dfy(52,7): Error: element may not be in domain
Execution trace:
(0,0): anon0
(0,0): anon5_Then
diff --git a/Test/dafny0/ISets.dfy b/Test/dafny0/ISets.dfy
new file mode 100644
index 00000000..703039c8
--- /dev/null
+++ b/Test/dafny0/ISets.dfy
@@ -0,0 +1,43 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+ghost method M()
+{
+ ghost var s := iset{2};
+ // test "in"
+ if(2 in s)
+ {
+ }
+ else
+ { assert false; }
+ // test "!in"
+ if(3 !in s)
+ {
+ }
+ else
+ { assert false; }
+
+ if(s == iset{2})
+ {
+ }
+ else
+ { assert false; }
+}
+
+ghost method m1() {
+ var s1:iset<int> := iset{}; // the empty set
+ var s2 := iset{1, 2, 3}; // set contains exactly 1, 2, and 3
+ assert s2 == iset{1,1,2,3,3,3,3}; // same as before
+ var s3, s4 := iset{1,2}, iset{1,4};
+
+ assert s2 + s4 == iset{1,2,3,4}; // set union
+ assert s2 * s3 == iset{1,2} && s2 * s4 == iset{1}; // set intersection
+ assert s2 - s3 == iset{3}; // set difference
+
+ assert (iset x | x in s2 :: x+1) == iset{2,3,4}; // set comprehension
+ assert 17 in (iset x: int | true :: x); // set comprehension
+
+ assert (imap x: int | true :: x+1)[14] == 15;
+}
+
+
diff --git a/Test/dafny0/ISets.dfy.expect b/Test/dafny0/ISets.dfy.expect
new file mode 100644
index 00000000..73ba063c
--- /dev/null
+++ b/Test/dafny0/ISets.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 4 verified, 0 errors
diff --git a/Test/dafny0/Include.dfy.expect b/Test/dafny0/Include.dfy.expect
index cb329398..d4543afe 100644
--- a/Test/dafny0/Include.dfy.expect
+++ b/Test/dafny0/Include.dfy.expect
@@ -1,13 +1,13 @@
-Include.dfy(19,19): Error BP5003: A postcondition might not hold on this return path.
-Includee.dfy(17,20): Related location: This is the postcondition that might not hold.
+Include.dfy(19,18): Error BP5003: A postcondition might not hold on this return path.
+Includee.dfy(17,19): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-Includee.dfy[Concrete](22,16): Error: assertion violation
+ (0,0): anon4_Else
+Includee.dfy[Concrete](22,15): Error: assertion violation
Execution trace:
(0,0): anon0
-Include.dfy(27,7): Error BP5003: A postcondition might not hold on this return path.
-Includee.dfy[Concrete](20,15): Related location: This is the postcondition that might not hold.
+Include.dfy(27,6): Error BP5003: A postcondition might not hold on this return path.
+Includee.dfy[Concrete](20,14): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
(0,0): anon6_Then
diff --git a/Test/dafny0/Includee.dfy.expect b/Test/dafny0/Includee.dfy.expect
index e0f0689c..ce61e32a 100644
--- a/Test/dafny0/Includee.dfy.expect
+++ b/Test/dafny0/Includee.dfy.expect
@@ -1,12 +1,12 @@
-Includee.dfy(21,3): Error BP5003: A postcondition might not hold on this return path.
-Includee.dfy(20,15): Related location: This is the postcondition that might not hold.
+Includee.dfy(21,2): Error BP5003: A postcondition might not hold on this return path.
+Includee.dfy(20,14): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-Includee.dfy(24,18): Error: assertion violation
+Includee.dfy(24,17): Error: assertion violation
Execution trace:
(0,0): anon0
-Includee.dfy(6,1): Error BP5003: A postcondition might not hold on this return path.
-Includee.dfy(5,13): Related location: This is the postcondition that might not hold.
+Includee.dfy(6,0): Error BP5003: A postcondition might not hold on this return path.
+Includee.dfy(5,12): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/IndexIntoUpdate.dfy b/Test/dafny0/IndexIntoUpdate.dfy
new file mode 100644
index 00000000..01359e04
--- /dev/null
+++ b/Test/dafny0/IndexIntoUpdate.dfy
@@ -0,0 +1,9 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+method M() {
+ var s := [1, 2, 3, 4];
+ assert 3 in s;
+ s := s[0 := 1];
+ if * { assert 3 in s; } // FIXME: This should verify
+ else { assert s[2] == 3; assert 3 in s; }
+}
diff --git a/Test/dafny0/IndexIntoUpdate.dfy.expect b/Test/dafny0/IndexIntoUpdate.dfy.expect
new file mode 100644
index 00000000..2db3aa0a
--- /dev/null
+++ b/Test/dafny0/IndexIntoUpdate.dfy.expect
@@ -0,0 +1,6 @@
+IndexIntoUpdate.dfy(7,18): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon3_Then
+
+Dafny program verifier finished with 1 verified, 1 error
diff --git a/Test/dafny0/InductivePredicates.dfy b/Test/dafny0/InductivePredicates.dfy
index 424118e7..e9aa7604 100644
--- a/Test/dafny0/InductivePredicates.dfy
+++ b/Test/dafny0/InductivePredicates.dfy
@@ -18,7 +18,7 @@ lemma M(x: natinf)
}
// yay! my first proof involving an inductive predicate :)
-lemma M'(k: nat, x: natinf)
+lemma {:induction false} M'(k: nat, x: natinf)
requires Even#[k](x)
ensures x.N? && x.n % 2 == 0
{
@@ -32,8 +32,14 @@ lemma M'(k: nat, x: natinf)
}
}
+lemma M'_auto(k: nat, x: natinf)
+ requires Even#[k](x)
+ ensures x.N? && x.n % 2 == 0
+{
+}
+
// Here is the same proof as in M / M', but packaged into a single "inductive lemma":
-inductive lemma IL(x: natinf)
+inductive lemma {:induction false} IL(x: natinf)
requires Even(x)
ensures x.N? && x.n % 2 == 0
{
@@ -45,18 +51,24 @@ inductive lemma IL(x: natinf)
}
}
-inductive lemma IL_EvenBetter(x: natinf)
+inductive lemma {:induction false} IL_EvenBetter(x: natinf)
requires Even(x)
ensures x.N? && x.n % 2 == 0
{
if {
case x.N? && x.n == 0 =>
// trivial
- case x.N? && 2 <= x.n && Even(N(x.n - 2)) =>
+ case x.N? && 2 <= x.n && Even(N(x.n - 2)) => // syntactic rewrite makes this like in IL
IL_EvenBetter(N(x.n - 2));
}
}
+inductive lemma IL_Best(x: natinf)
+ requires Even(x)
+ ensures x.N? && x.n % 2 == 0
+{
+}
+
inductive lemma IL_Bad(x: natinf)
requires Even(x)
ensures x.N? && x.n % 2 == 0
@@ -107,7 +119,7 @@ module Alt {
{
match x
case N(n) => N(n+1)
- case Inf => Inf
+ case Inf => Inf
}
inductive predicate Even(x: natinf)
@@ -116,7 +128,7 @@ module Alt {
exists y :: x == S(S(y)) && Even(y)
}
- inductive lemma MyLemma_NotSoNice(x: natinf)
+ inductive lemma {:induction false} MyLemma_NotSoNice(x: natinf)
requires Even(x)
ensures x.N? && x.n % 2 == 0
{
@@ -130,7 +142,7 @@ module Alt {
}
}
- inductive lemma MyLemma_NiceButNotFast(x: natinf)
+ inductive lemma {:induction false} MyLemma_Nicer(x: natinf) // same as MyLemma_NotSoNice but relying on syntactic rewrites
requires Even(x)
ensures x.N? && x.n % 2 == 0
{
@@ -139,11 +151,17 @@ module Alt {
// trivial
case exists y :: x == S(S(y)) && Even(y) =>
var y :| x == S(S(y)) && Even(y);
- MyLemma_NiceButNotFast(y);
+ MyLemma_Nicer(y);
assert x.n == y.n + 2;
}
}
-
+
+ inductive lemma MyLemma_RealNice_AndFastToo(x: natinf)
+ requires Even(x)
+ ensures x.N? && x.n % 2 == 0
+ {
+ }
+
lemma InfNotEven()
ensures !Even(Inf)
{
@@ -156,15 +174,6 @@ module Alt {
requires Even(Inf)
ensures false
{
- var x := Inf;
- if {
- case x.N? && x.n == 0 =>
- assert false; // this case is absurd
- case exists y :: x == S(S(y)) && Even(y) =>
- var y :| x == S(S(y)) && Even(y);
- assert y == Inf;
- InfNotEven_Aux();
- }
}
lemma NextEven(x: natinf)
diff --git a/Test/dafny0/InductivePredicates.dfy.expect b/Test/dafny0/InductivePredicates.dfy.expect
index b09b7903..48beade5 100644
--- a/Test/dafny0/InductivePredicates.dfy.expect
+++ b/Test/dafny0/InductivePredicates.dfy.expect
@@ -1,9 +1,9 @@
-InductivePredicates.dfy(64,10): Error: assertion violation
+InductivePredicates.dfy(76,9): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-InductivePredicates.dfy(76,11): Error: assertion violation
+InductivePredicates.dfy(88,10): Error: assertion violation
Execution trace:
(0,0): anon0
-Dafny program verifier finished with 29 verified, 2 errors
+Dafny program verifier finished with 35 verified, 2 errors
diff --git a/Test/dafny0/Inverses.dfy b/Test/dafny0/Inverses.dfy
index 7995255a..b424cfd9 100644
--- a/Test/dafny0/Inverses.dfy
+++ b/Test/dafny0/Inverses.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// This identity function is used to so that if the occurrence of i below
@@ -110,3 +110,6 @@ method RotateD<T>(a: array<T>) returns (r: array<T>)
r[if a.Length - 1 == i then 0 else i + 1] := a[Id(i)]; // yes, Dafny can invert this one
}
}
+
+// autoTriggers added because it causes a slight rephrasing of an error
+// message.
diff --git a/Test/dafny0/Inverses.dfy.expect b/Test/dafny0/Inverses.dfy.expect
index a04f21dc..b9530e3f 100644
--- a/Test/dafny0/Inverses.dfy.expect
+++ b/Test/dafny0/Inverses.dfy.expect
@@ -1,10 +1,12 @@
-Inverses.dfy(70,1): Error BP5003: A postcondition might not hold on this return path.
-Inverses.dfy(69,11): Related location: This is the postcondition that might not hold.
+Inverses.dfy(70,0): Error BP5003: A postcondition might not hold on this return path.
+Inverses.dfy(69,10): Related location: This is the postcondition that might not hold.
+Inverses.dfy(69,66): Related location
Execution trace:
(0,0): anon0
(0,0): anon6_Else
-Inverses.dfy(83,1): Error BP5003: A postcondition might not hold on this return path.
-Inverses.dfy(82,11): Related location: This is the postcondition that might not hold.
+Inverses.dfy(83,0): Error BP5003: A postcondition might not hold on this return path.
+Inverses.dfy(82,10): Related location: This is the postcondition that might not hold.
+Inverses.dfy(82,66): Related location
Execution trace:
(0,0): anon0
(0,0): anon9_Else
diff --git a/Test/dafny0/Iterators.dfy.expect b/Test/dafny0/Iterators.dfy.expect
index f0c6e400..d9129e3e 100644
--- a/Test/dafny0/Iterators.dfy.expect
+++ b/Test/dafny0/Iterators.dfy.expect
@@ -1,55 +1,55 @@
-Iterators.dfy(251,10): Error: failure to decrease termination measure
+Iterators.dfy(251,9): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
(0,0): anon5_Else
(0,0): anon6_Else
-Iterators.dfy(274,10): Error: failure to decrease termination measure
+Iterators.dfy(274,9): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
(0,0): anon5_Else
(0,0): anon6_Else
-Iterators.dfy(284,32): Error: failure to decrease termination measure
+Iterators.dfy(284,31): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
-Iterators.dfy(296,10): Error: cannot prove termination; try supplying a decreases clause
+Iterators.dfy(296,9): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
(0,0): anon5_Else
(0,0): anon6_Else
-Iterators.dfy(317,10): Error: cannot prove termination; try supplying a decreases clause
+Iterators.dfy(317,9): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
(0,0): anon5_Else
(0,0): anon6_Else
-Iterators.dfy(326,32): Error: cannot prove termination; try supplying a decreases clause
+Iterators.dfy(326,31): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
-Iterators.dfy(343,10): Error: failure to decrease termination measure
+Iterators.dfy(343,9): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
(0,0): anon5_Else
(0,0): anon6_Else
-Iterators.dfy(353,32): Error: cannot prove termination; try supplying a decreases clause
+Iterators.dfy(353,31): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
-Iterators.dfy(370,10): Error: failure to decrease termination measure
+Iterators.dfy(370,9): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
(0,0): anon5_Else
(0,0): anon6_Else
-Iterators.dfy(103,22): Error: assertion violation
+Iterators.dfy(103,21): Error: assertion violation
Execution trace:
(0,0): anon0
-Iterators.dfy(106,14): Error: assertion violation
+Iterators.dfy(106,13): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon4_Then
(0,0): anon3
-Iterators.dfy(177,28): Error: assertion violation
+Iterators.dfy(177,27): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon15_Then
-Iterators.dfy(208,7): Error: an assignment to _new is only allowed to shrink the set
+Iterators.dfy(208,6): Error: an assignment to _new is only allowed to shrink the set
Execution trace:
(0,0): anon0
Iterators.dfy(197,3): anon16_LoopHead
@@ -57,7 +57,7 @@ Execution trace:
Iterators.dfy(197,3): anon17_Else
Iterators.dfy(197,3): anon19_Else
(0,0): anon20_Then
-Iterators.dfy(212,21): Error: assertion violation
+Iterators.dfy(212,20): Error: assertion violation
Execution trace:
(0,0): anon0
Iterators.dfy(197,3): anon16_LoopHead
@@ -65,8 +65,8 @@ Execution trace:
Iterators.dfy(197,3): anon17_Else
Iterators.dfy(197,3): anon19_Else
(0,0): anon21_Then
-Iterators.dfy(40,22): Error BP5002: A precondition for this call might not hold.
-Iterators.dfy(4,10): Related location: This is the precondition that might not hold.
+Iterators.dfy(40,21): Error BP5002: A precondition for this call might not hold.
+Iterators.dfy(4,9): Related location: This is the precondition that might not hold.
Execution trace:
(0,0): anon0
(0,0): anon35_Then
@@ -74,24 +74,24 @@ Execution trace:
(0,0): anon36_Then
(0,0): anon5
(0,0): anon37_Then
-Iterators.dfy(89,14): Error: assertion violation
+Iterators.dfy(89,13): Error: assertion violation
Execution trace:
(0,0): anon0
-Iterators.dfy(119,16): Error: assertion violation
+Iterators.dfy(119,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Else
-Iterators.dfy(150,16): Error: assertion violation
+Iterators.dfy(150,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon4_Else
-Iterators.dfy(155,24): Error BP5002: A precondition for this call might not hold.
-Iterators.dfy(125,10): Related location: This is the precondition that might not hold.
+Iterators.dfy(155,23): Error BP5002: A precondition for this call might not hold.
+Iterators.dfy(125,9): Related location: This is the precondition that might not hold.
Execution trace:
(0,0): anon0
(0,0): anon4_Then
(0,0): anon3
-Iterators.dfy(234,21): Error: assertion violation
+Iterators.dfy(234,20): Error: assertion violation
Execution trace:
(0,0): anon0
Iterators.dfy(225,3): anon14_LoopHead
diff --git a/Test/dafny0/JustWarnings.dfy b/Test/dafny0/JustWarnings.dfy
new file mode 100644
index 00000000..86523f5b
--- /dev/null
+++ b/Test/dafny0/JustWarnings.dfy
@@ -0,0 +1,19 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /warnShadowing "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file tests the behavior where the Resolver reports some warnings
+// but no errors. In the case of errors, resolution does not continue
+// to clone modules and resolve them, but the cloning does proceed if there
+// are only warnings. Dafny should report only one copy of these warnings,
+// and warnings are therefore turned off when processing the clones. This
+// test file makes sure the warnings don't appear twice.
+
+method M(x: int)
+{
+ var x := 10; // warning: this shadows the parameter 'x'
+}
+
+class C<T> {
+ var u: T
+ method P<T>(t: T) // warning: this shadows the type parameter 'T'
+}
diff --git a/Test/dafny0/JustWarnings.dfy.expect b/Test/dafny0/JustWarnings.dfy.expect
new file mode 100644
index 00000000..5f0e66d8
--- /dev/null
+++ b/Test/dafny0/JustWarnings.dfy.expect
@@ -0,0 +1,4 @@
+JustWarnings.dfy(18,11): Warning: Shadowed type-parameter name: T
+JustWarnings.dfy(13,6): Warning: Shadowed local-variable name: x
+
+Dafny program verifier finished with 3 verified, 0 errors
diff --git a/Test/dafny0/LetExpr.dfy b/Test/dafny0/LetExpr.dfy
index b8d68bd6..6a0ca66b 100644
--- a/Test/dafny0/LetExpr.dfy
+++ b/Test/dafny0/LetExpr.dfy
@@ -1,4 +1,5 @@
-// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint.dfy" "%s" > "%t"; %dafny /noVerify /compile:0 "%t.dprint.dfy" >> "%t"
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint.dfy" /autoTriggers:0 "%s" > "%t"
+// RUN: %dafny /noVerify /compile:0 "%t.dprint.dfy" >> "%t"
// RUN: %diff "%s.expect" "%t"
method M0(n: int)
diff --git a/Test/dafny0/LetExpr.dfy.expect b/Test/dafny0/LetExpr.dfy.expect
index 36fc9361..8f365da3 100644
--- a/Test/dafny0/LetExpr.dfy.expect
+++ b/Test/dafny0/LetExpr.dfy.expect
@@ -1,39 +1,40 @@
-LetExpr.dfy(108,23): Error: assertion violation
+LetExpr.dfy(109,22): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon11_Then
-LetExpr.dfy(8,12): Error: assertion violation
+LetExpr.dfy(9,11): Error: assertion violation
Execution trace:
(0,0): anon0
-LetExpr.dfy(253,19): Error: value assigned to a nat must be non-negative
+LetExpr.dfy(254,18): Error: value assigned to a nat must be non-negative
Execution trace:
(0,0): anon0
(0,0): anon5_Then
-LetExpr.dfy(256,19): Error: value assigned to a nat must be non-negative
+LetExpr.dfy(257,18): Error: value assigned to a nat must be non-negative
Execution trace:
(0,0): anon0
(0,0): anon6_Then
-LetExpr.dfy(258,24): Error: value assigned to a nat must be non-negative
+LetExpr.dfy(259,23): Error: value assigned to a nat must be non-negative
Execution trace:
(0,0): anon0
(0,0): anon6_Else
-LetExpr.dfy(287,14): Error: RHS is not certain to look like the pattern 'Agnes'
+LetExpr.dfy(288,13): Error: RHS is not certain to look like the pattern 'Agnes'
Execution trace:
(0,0): anon0
(0,0): anon3_Else
-LetExpr.dfy(304,42): Error: value assigned to a nat must be non-negative
+LetExpr.dfy(305,41): Error: value assigned to a nat must be non-negative
Execution trace:
(0,0): anon0
- (0,0): anon6_Else
-LetExpr.dfy(306,12): Error: assertion violation
+ (0,0): anon7_Else
+LetExpr.dfy(307,11): Error: assertion violation
Execution trace:
(0,0): anon0
- (0,0): anon6_Else
-LetExpr.dfy(316,12): Error: to be compilable, the value of a let-such-that expression must be uniquely determined
+ (0,0): anon7_Else
+LetExpr.dfy(317,11): Error: to be compilable, the value of a let-such-that expression must be uniquely determined
Execution trace:
(0,0): anon0
(0,0): anon10_Then
Dafny program verifier finished with 39 verified, 9 errors
+LetExpr.dfy.tmp.dprint.dfy(162,2): Warning: /!\ No terms found to trigger on.
Dafny program verifier finished with 0 verified, 0 errors
diff --git a/Test/dafny0/LhsDuplicates.dfy b/Test/dafny0/LhsDuplicates.dfy
index 6a84c5a5..8a57f6ce 100644
--- a/Test/dafny0/LhsDuplicates.dfy
+++ b/Test/dafny0/LhsDuplicates.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
class MyClass<T> {
diff --git a/Test/dafny0/LhsDuplicates.dfy.expect b/Test/dafny0/LhsDuplicates.dfy.expect
index a864390f..d6689047 100644
--- a/Test/dafny0/LhsDuplicates.dfy.expect
+++ b/Test/dafny0/LhsDuplicates.dfy.expect
@@ -1,27 +1,27 @@
-LhsDuplicates.dfy(18,10): Error: left-hand sides for different forall-statement bound variables may refer to the same location
+LhsDuplicates.dfy(18,9): Error: left-hand sides for different forall-statement bound variables may refer to the same location
Execution trace:
(0,0): anon0
(0,0): anon16_Else
(0,0): anon18_Else
(0,0): anon21_Then
(0,0): anon13
-LhsDuplicates.dfy(34,12): Error: left-hand sides for different forall-statement bound variables may refer to the same location
+LhsDuplicates.dfy(34,11): Error: left-hand sides for different forall-statement bound variables may refer to the same location
Execution trace:
(0,0): anon0
(0,0): anon16_Else
(0,0): anon18_Else
(0,0): anon21_Then
(0,0): anon13
-LhsDuplicates.dfy(42,12): Error: when left-hand sides 1 and 3 refer to the same location, they must be assigned the same value
+LhsDuplicates.dfy(42,11): Error: when left-hand sides 1 and 3 refer to the same location, they must be assigned the same value
Execution trace:
(0,0): anon0
-LhsDuplicates.dfy(51,18): Error: when left-hand sides 0 and 2 refer to the same location, they must be assigned the same value
+LhsDuplicates.dfy(51,17): Error: when left-hand sides 0 and 2 refer to the same location, they must be assigned the same value
Execution trace:
(0,0): anon0
-LhsDuplicates.dfy(60,16): Error: when left-hand sides 1 and 2 may refer to the same location, they must be assigned the same value
+LhsDuplicates.dfy(60,15): Error: when left-hand sides 1 and 2 may refer to the same location, they must be assigned the same value
Execution trace:
(0,0): anon0
-LhsDuplicates.dfy(69,20): Error: when left-hand sides 1 and 2 refer to the same location, they must be assigned the same value
+LhsDuplicates.dfy(69,19): Error: when left-hand sides 1 and 2 refer to the same location, they must be assigned the same value
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/LitTriggers.dfy b/Test/dafny0/LitTriggers.dfy
new file mode 100644
index 00000000..93e65643
--- /dev/null
+++ b/Test/dafny0/LitTriggers.dfy
@@ -0,0 +1,39 @@
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// Imported from bug 76. LitInt would be triggered on, causing matching failures.
+
+predicate P(x:int, y:int)
+
+lemma L1(x:int, y:int)
+ requires y == 2;
+ requires forall i :: P(i, 3);
+{
+ assert P(x, y + 1);
+}
+
+lemma L2(x:int, y:int)
+ requires y == 2;
+ requires forall i {:trigger P(i, 3)} :: P(i, 3);
+{
+ assert P(x, y + 1);
+}
+
+lemma L3(x:int, y:int)
+ requires y == 2;
+ requires forall i :: P(i, 3);
+{
+ var dummy := 3;
+ assert P(x, y + 1);
+}
+
+lemma L4(x:int, y:int)
+ requires y == 2;
+ requires forall i, j :: j == 3 ==> P(i, j);
+{
+ assert P(x, y + 1);
+}
+
+// Local Variables:
+// dafny-prover-local-args: ("/autoTriggers:1")
+// End:
diff --git a/Test/dafny0/LitTriggers.dfy.expect b/Test/dafny0/LitTriggers.dfy.expect
new file mode 100644
index 00000000..249e77e5
--- /dev/null
+++ b/Test/dafny0/LitTriggers.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 9 verified, 0 errors
diff --git a/Test/dafny0/LoopModifies.dfy.expect b/Test/dafny0/LoopModifies.dfy.expect
index 682975fb..a7ded8a4 100644
--- a/Test/dafny0/LoopModifies.dfy.expect
+++ b/Test/dafny0/LoopModifies.dfy.expect
@@ -1,38 +1,38 @@
-LoopModifies.dfy(8,5): Error: assignment may update an array element not in the enclosing context's modifies clause
+LoopModifies.dfy(8,4): Error: assignment may update an array element not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
-LoopModifies.dfy(19,8): Error: assignment may update an array element not in the enclosing context's modifies clause
+LoopModifies.dfy(19,7): Error: assignment may update an array element not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
LoopModifies.dfy(16,4): anon8_LoopHead
(0,0): anon8_LoopBody
LoopModifies.dfy(16,4): anon9_Else
LoopModifies.dfy(16,4): anon11_Else
-LoopModifies.dfy(48,8): Error: assignment may update an array element not in the enclosing context's modifies clause
+LoopModifies.dfy(48,7): Error: assignment may update an array element not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
LoopModifies.dfy(44,4): anon8_LoopHead
(0,0): anon8_LoopBody
LoopModifies.dfy(44,4): anon9_Else
LoopModifies.dfy(44,4): anon11_Else
-LoopModifies.dfy(63,8): Error: assignment may update an array element not in the enclosing context's modifies clause
+LoopModifies.dfy(63,7): Error: assignment may update an array element not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
LoopModifies.dfy(59,4): anon9_LoopHead
(0,0): anon9_LoopBody
LoopModifies.dfy(59,4): anon10_Else
LoopModifies.dfy(59,4): anon12_Else
-LoopModifies.dfy(76,4): Error: loop modifies clause may violate context's modifies clause
+LoopModifies.dfy(76,3): Error: loop modifies clause may violate context's modifies clause
Execution trace:
(0,0): anon0
-LoopModifies.dfy(100,8): Error: assignment may update an array element not in the enclosing context's modifies clause
+LoopModifies.dfy(100,7): Error: assignment may update an array element not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
LoopModifies.dfy(92,4): anon8_LoopHead
(0,0): anon8_LoopBody
LoopModifies.dfy(92,4): anon9_Else
LoopModifies.dfy(92,4): anon11_Else
-LoopModifies.dfy(148,11): Error: assignment may update an array element not in the enclosing context's modifies clause
+LoopModifies.dfy(148,10): Error: assignment may update an array element not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
LoopModifies.dfy(136,4): anon17_LoopHead
@@ -43,14 +43,14 @@ Execution trace:
(0,0): anon21_LoopBody
LoopModifies.dfy(141,7): anon22_Else
LoopModifies.dfy(141,7): anon24_Else
-LoopModifies.dfy(199,10): Error: assignment may update an array element not in the enclosing context's modifies clause
+LoopModifies.dfy(199,9): Error: assignment may update an array element not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
LoopModifies.dfy(195,4): anon8_LoopHead
(0,0): anon8_LoopBody
LoopModifies.dfy(195,4): anon9_Else
LoopModifies.dfy(195,4): anon11_Else
-LoopModifies.dfy(287,13): Error: assignment may update an array element not in the enclosing context's modifies clause
+LoopModifies.dfy(287,12): Error: assignment may update an array element not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
LoopModifies.dfy(275,4): anon16_LoopHead
diff --git a/Test/dafny0/Maps.dfy.expect b/Test/dafny0/Maps.dfy.expect
index f46549dd..8b4a6a36 100644
--- a/Test/dafny0/Maps.dfy.expect
+++ b/Test/dafny0/Maps.dfy.expect
@@ -1,7 +1,7 @@
-Maps.dfy(78,8): Error: element may not be in domain
+Maps.dfy(78,7): Error: element may not be in domain
Execution trace:
(0,0): anon0
-Maps.dfy(128,13): Error: assertion violation
+Maps.dfy(128,12): Error: assertion violation
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/Matrix-OOB.dfy b/Test/dafny0/Matrix-OOB.dfy
new file mode 100644
index 00000000..d7aacd79
--- /dev/null
+++ b/Test/dafny0/Matrix-OOB.dfy
@@ -0,0 +1,13 @@
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This is a regression test: OOB errors for matrices used to be reported on the
+// quantifier that introduced the variables that constituted the invalid indices.
+
+// WISH: It would be even better to report the error on the variables inside the
+// array instead of the array itself.
+
+method M(m: array2<int>)
+ requires m != null
+ ensures forall i, j :: m[i, j] == 0
+{ }
diff --git a/Test/dafny0/Matrix-OOB.dfy.expect b/Test/dafny0/Matrix-OOB.dfy.expect
new file mode 100644
index 00000000..e2920445
--- /dev/null
+++ b/Test/dafny0/Matrix-OOB.dfy.expect
@@ -0,0 +1,14 @@
+Matrix-OOB.dfy(12,10): Info: Selected triggers: {m[i, j]}
+Matrix-OOB.dfy(12,26): Error: index 0 out of range
+Execution trace:
+ (0,0): anon0
+Matrix-OOB.dfy(12,26): Error: index 1 out of range
+Execution trace:
+ (0,0): anon0
+Matrix-OOB.dfy(13,0): Error BP5003: A postcondition might not hold on this return path.
+Matrix-OOB.dfy(12,10): Related location: This is the postcondition that might not hold.
+Matrix-OOB.dfy(12,33): Related location
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 0 verified, 3 errors
diff --git a/Test/dafny0/ModifyStmt.dfy.expect b/Test/dafny0/ModifyStmt.dfy.expect
index 4ea872e0..019453d1 100644
--- a/Test/dafny0/ModifyStmt.dfy.expect
+++ b/Test/dafny0/ModifyStmt.dfy.expect
@@ -1,19 +1,19 @@
-ModifyStmt.dfy(27,14): Error: assertion violation
+ModifyStmt.dfy(27,13): Error: assertion violation
Execution trace:
(0,0): anon0
-ModifyStmt.dfy(42,5): Error: modify statement may violate context's modifies clause
+ModifyStmt.dfy(42,4): Error: modify statement may violate context's modifies clause
Execution trace:
(0,0): anon0
-ModifyStmt.dfy(48,5): Error: modify statement may violate context's modifies clause
+ModifyStmt.dfy(48,4): Error: modify statement may violate context's modifies clause
Execution trace:
(0,0): anon0
-ModifyStmt.dfy(61,5): Error: modify statement may violate context's modifies clause
+ModifyStmt.dfy(61,4): Error: modify statement may violate context's modifies clause
Execution trace:
(0,0): anon0
-ModifyStmt.dfy(70,14): Error: assertion violation
+ModifyStmt.dfy(70,13): Error: assertion violation
Execution trace:
(0,0): anon0
-ModifyStmt.dfy(89,14): Error: assertion violation
+ModifyStmt.dfy(89,13): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon9_Then
@@ -22,20 +22,20 @@ Execution trace:
ModifyStmt.dfy(81,7): anon11_Else
(0,0): anon12_Then
(0,0): anon8
-ModifyStmt.dfy(99,14): Error: assertion violation
+ModifyStmt.dfy(99,13): Error: assertion violation
Execution trace:
(0,0): anon0
-ModifyStmt.dfy(110,14): Error: assertion violation
+ModifyStmt.dfy(110,13): Error: assertion violation
Execution trace:
(0,0): anon0
-ModifyStmt.dfy(122,16): Error: assertion violation
+ModifyStmt.dfy(122,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-ModifyStmt.dfy(134,7): Error: assignment may update an object not in the enclosing context's modifies clause
+ModifyStmt.dfy(134,6): Error: assignment may update an object not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
-ModifyStmt.dfy(172,15): Error: assertion violation
+ModifyStmt.dfy(172,14): Error: assertion violation
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/ModuleExport.dfy b/Test/dafny0/ModuleExport.dfy
new file mode 100644
index 00000000..1e69764f
--- /dev/null
+++ b/Test/dafny0/ModuleExport.dfy
@@ -0,0 +1,105 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+module A {
+ default export Public { f, h}
+ export E1 { f, g}
+ export E2 extends Public, E1 {T}
+ export Friend extends Public {g, T}
+ export Fruit {Data}
+
+ method h() {}
+ function f(): int { 818 }
+ function g() : int { 819 }
+ function k() : int { 820 }
+
+ class T
+ {
+ static method l() {}
+ }
+
+ datatype Data = Lemon | Kiwi(int)
+
+}
+
+module B {
+ import X = A.Public
+ method m() {
+ X.h(); // OK
+ assert X.f() == 818; // OK
+ assert X.g() == 819; // error
+ assert X.k() == 820; // error
+ X.T.l(); // error
+ }
+}
+
+module C {
+ import X = A.Friend
+ method m() {
+ X.h(); // OK
+ assert X.f() == 818; // OK
+ assert X.g() == 819; // OK
+ assert X.k() == 820; // error
+ X.T.l(); // OK
+ }
+}
+
+module D {
+ import opened A
+ method m() {
+ h(); // OK
+ assert f() == 818; // OK
+ assert g() == 819; // error
+ assert k() == 820; // error
+ }
+}
+
+module E {
+ import opened A.Fruit
+
+ function G(d: Data): int
+ requires d != Data.Lemon
+ {
+ match d
+ case Lemon => G(d)
+ case Kiwi(x) => 7
+ case Orang => 8 // error
+ }
+}
+
+module F {
+ default export Public { f, h}
+ default export E1 { f, g}
+ export E2 extends Public2, E1 {T} // error: Public2 is not a exported view of F
+ export Friend extends Public {g2, T} // error: g2 is not a member of F
+ export Fruit {Data}
+
+ method h() {}
+ function f(): int { 818 }
+ function g() : int { 819 }
+ function k() : int { 820 }
+
+ class T
+ {
+ static method l() {}
+ }
+
+ datatype Data = Lemon | Kiwi(int)
+}
+
+module G {
+ export Public { f, h}
+
+ method h() {}
+ function f(): int { 818 }
+ function g() : int { 819 }
+ function k() : int { 820 }
+}
+
+module H {
+ import G // error: G has no default export
+}
+
+module I {
+ import G.Public // OK
+} \ No newline at end of file
diff --git a/Test/dafny0/ModuleExport.dfy.expect b/Test/dafny0/ModuleExport.dfy.expect
new file mode 100644
index 00000000..274c8f31
--- /dev/null
+++ b/Test/dafny0/ModuleExport.dfy.expect
@@ -0,0 +1,13 @@
+ModuleExport.dfy(30,11): Error: unresolved identifier: g
+ModuleExport.dfy(31,11): Error: unresolved identifier: k
+ModuleExport.dfy(32,4): Error: unresolved identifier: T
+ModuleExport.dfy(32,7): Error: expected method call, found expression
+ModuleExport.dfy(42,11): Error: unresolved identifier: k
+ModuleExport.dfy(52,9): Error: unresolved identifier: g
+ModuleExport.dfy(53,9): Error: unresolved identifier: k
+ModuleExport.dfy(66,2): Error: member Orang does not exist in datatype Data
+ModuleExport.dfy(70,7): Error: Public2 must be an export of F to be extended
+ModuleExport.dfy(74,9): Error: g2 must be a member of F to be exported
+ModuleExport.dfy(70,7): Error: more than one default export declared in module F
+ModuleExport.dfy(90,7): Error: no default export declared in module: G
+12 resolution/type errors detected in ModuleExport.dfy
diff --git a/Test/dafny0/Modules0.dfy b/Test/dafny0/Modules0.dfy
index 34aba3de..4b86d848 100644
--- a/Test/dafny0/Modules0.dfy
+++ b/Test/dafny0/Modules0.dfy
@@ -71,16 +71,17 @@ module X1 {
}
module X2 {
+ import opened X1
class MyClass2 {
- method Down(x1: MyClass1, x0: MyClass0) {
+ method Down(x1: MyClass1, x0: X0'.MyClass0) {
x1.Down(x0);
}
- method WayDown(x0: MyClass0) {
+ method WayDown(x0: X0'.MyClass0) {
x0.Down();
}
method Up() {
}
- method Somewhere(y: MyClassY) {
+ method Somewhere(y: MyClassY) { // error: no such type in scope
y.M();
}
}
@@ -97,8 +98,7 @@ module YY {
class ClassG {
method T() { }
function method TFunc(): int { 10 }
- method V(y: MyClassY) { // Note, MyClassY is in scope, since we are in the _default
- // module, which imports everything
+ method V(y: MyClassY) {
y.M();
}
}
@@ -141,10 +141,10 @@ class AClassWithSomeField {
SomeField := SomeField + 4;
var a := old(SomeField); // error: old can only be used in ghost contexts
var b := fresh(this); // error: fresh can only be used in ghost contexts
- var c := allocated(this); // error: allocated can only be used in ghost contexts
+// var c := allocated(this); // error: allocated can only be used in ghost contexts
if (fresh(this)) { // this guard makes the if statement a ghost statement
ghost var x := old(SomeField); // this is a ghost context, so it's okay
- ghost var y := allocated(this); // this is a ghost context, so it's okay
+// ghost var y := allocated(this); // this is a ghost context, so it's okay
}
}
}
@@ -335,3 +335,15 @@ module TopLevelStatics {
static method M() // error/warning: static keyword does not belong here
{ }
}
+
+module Library {
+ class T { }
+}
+
+module AA {
+ import opened Library
+}
+
+module B refines AA {
+ datatype T = MakeT(int) // illegal
+}
diff --git a/Test/dafny0/Modules0.dfy.expect b/Test/dafny0/Modules0.dfy.expect
index 5d11f9c9..f51e0f6c 100644
--- a/Test/dafny0/Modules0.dfy.expect
+++ b/Test/dafny0/Modules0.dfy.expect
@@ -1,27 +1,22 @@
-Modules0.dfy(333,3): warning: module-level functions are always non-instance, so the 'static' keyword is not allowed here
-Modules0.dfy(335,3): warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+Modules0.dfy(333,2): Warning: module-level functions are always non-instance, so the 'static' keyword is not allowed here
+Modules0.dfy(335,2): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
Modules0.dfy(8,8): Error: Duplicate name of top-level declaration: WazzupA
Modules0.dfy(9,11): Error: Duplicate name of top-level declaration: WazzupA
Modules0.dfy(10,7): Error: Duplicate name of top-level declaration: WazzupA
Modules0.dfy(13,7): Error: Duplicate name of top-level declaration: WazzupB
Modules0.dfy(14,8): Error: Duplicate name of top-level declaration: WazzupB
Modules0.dfy(15,11): Error: Duplicate name of top-level declaration: WazzupB
-Modules0.dfy(56,21): Error: Undeclared top-level type or type parameter: MyClass1 (did you forget to qualify a name?)
-Modules0.dfy(57,21): Error: Undeclared top-level type or type parameter: MyClass2 (did you forget to qualify a name?)
-Modules0.dfy(68,21): Error: Undeclared top-level type or type parameter: MyClass2 (did you forget to qualify a name?)
-Modules0.dfy(76,9): Error: type MyClass1 does not have a member Down
-Modules0.dfy(76,13): Error: expected method call, found expression
-Modules0.dfy(79,9): Error: type MyClass0 does not have a member Down
-Modules0.dfy(79,13): Error: expected method call, found expression
-Modules0.dfy(84,8): Error: type MyClassY does not have a member M
-Modules0.dfy(84,9): Error: expected method call, found expression
-Modules0.dfy(92,19): Error: Undeclared top-level type or type parameter: ClassG (did you forget to qualify a name?)
-Modules0.dfy(226,15): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name?)
+Modules0.dfy(56,21): Error: Undeclared top-level type or type parameter: MyClass1 (did you forget to qualify a name or declare a module import 'opened?')
+Modules0.dfy(57,21): Error: Undeclared top-level type or type parameter: MyClass2 (did you forget to qualify a name or declare a module import 'opened?')
+Modules0.dfy(68,21): Error: Undeclared top-level type or type parameter: MyClass2 (did you forget to qualify a name or declare a module import 'opened?')
+Modules0.dfy(84,24): Error: Undeclared top-level type or type parameter: MyClassY (did you forget to qualify a name or declare a module import 'opened?')
+Modules0.dfy(93,19): Error: Undeclared top-level type or type parameter: ClassG (did you forget to qualify a name or declare a module import 'opened?')
+Modules0.dfy(226,15): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?')
Modules0.dfy(226,8): Error: new can be applied only to reference types (got X)
Modules0.dfy(235,13): Error: module 'B' does not declare a type 'X'
Modules0.dfy(245,13): Error: unresolved identifier: X
Modules0.dfy(246,15): Error: member DoesNotExist does not exist in class X
-Modules0.dfy(285,19): Error: Undeclared top-level type or type parameter: D (did you forget to qualify a name?)
+Modules0.dfy(285,19): Error: Undeclared top-level type or type parameter: D (did you forget to qualify a name or declare a module import 'opened?')
Modules0.dfy(285,12): Error: new can be applied only to reference types (got D)
Modules0.dfy(288,25): Error: type of the receiver is not fully determined at this program point
Modules0.dfy(289,16): Error: type of the receiver is not fully determined at this program point
@@ -30,16 +25,11 @@ Modules0.dfy(290,16): Error: type of the receiver is not fully determined at thi
Modules0.dfy(290,17): Error: expected method call, found expression
Modules0.dfy(314,18): Error: second argument to "in" must be a set, multiset, or sequence with elements of type Q_Imp.Node, or a map with domain Q_Imp.Node (instead got set<Node>)
Modules0.dfy(318,13): Error: arguments must have the same type (got Q_Imp.Node and Node)
-Modules0.dfy(319,11): Error: Undeclared top-level type or type parameter: LongLostModule (did you forget to qualify a name?)
-Modules0.dfy(320,11): Error: Undeclared top-level type or type parameter: Wazzup (did you forget to qualify a name?)
+Modules0.dfy(319,11): Error: Undeclared top-level type or type parameter: LongLostModule (did you forget to qualify a name or declare a module import 'opened?')
+Modules0.dfy(320,11): Error: Undeclared top-level type or type parameter: Wazzup (did you forget to qualify a name or declare a module import 'opened?')
Modules0.dfy(321,17): Error: module 'Q_Imp' does not declare a type 'Edon'
Modules0.dfy(323,10): Error: new can be applied only to reference types (got Q_Imp.List<?>)
Modules0.dfy(324,30): Error: member Create does not exist in class Klassy
-Modules0.dfy(102,6): Error: type MyClassY does not have a member M
-Modules0.dfy(102,7): Error: expected method call, found expression
-Modules0.dfy(127,11): Error: ghost variables are allowed only in specification contexts
-Modules0.dfy(142,13): Error: old expressions are allowed only in specification and ghost contexts
-Modules0.dfy(143,13): Error: fresh expressions are allowed only in specification and ghost contexts
-Modules0.dfy(144,13): Error: unresolved identifier: allocated
-Modules0.dfy(147,21): Error: unresolved identifier: allocated
-42 resolution/type errors detected in Modules0.dfy
+Modules0.dfy(348,11): Error: a datatype declaration (T) in a refinement module can only replace an opaque type declaration
+Modules0.dfy(101,14): Error: Undeclared top-level type or type parameter: MyClassY (did you forget to qualify a name or declare a module import 'opened?')
+32 resolution/type errors detected in Modules0.dfy
diff --git a/Test/dafny0/Modules1.dfy b/Test/dafny0/Modules1.dfy
index 505d9b74..3025cc00 100644
--- a/Test/dafny0/Modules1.dfy
+++ b/Test/dafny0/Modules1.dfy
@@ -125,11 +125,11 @@ abstract module Regression {
predicate p<c,d>(m: map<c,d>)
lemma m<a,b>(m: map<a,b>)
- ensures exists m :: p(var m : map<a,b> := m; m);
+ ensures exists m {:nowarn} :: p(var m : map<a,b> := m; m) // WISH: Zeta-expanding the let binding would provide a good trigger
}
abstract module B
{
- import X as A
+ import X : A
}
}
diff --git a/Test/dafny0/Modules1.dfy.expect b/Test/dafny0/Modules1.dfy.expect
index 342b5808..feddf46a 100644
--- a/Test/dafny0/Modules1.dfy.expect
+++ b/Test/dafny0/Modules1.dfy.expect
@@ -1,20 +1,20 @@
-Modules1.dfy(79,16): Error: assertion violation
+Modules1.dfy(79,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-Modules1.dfy(92,16): Error: assertion violation
+Modules1.dfy(92,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-Modules1.dfy(94,18): Error: assertion violation
+Modules1.dfy(94,17): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Else
-Modules1.dfy(56,9): Error: decreases expression must be bounded below by 0
-Modules1.dfy(54,13): Related location
+Modules1.dfy(56,8): Error: decreases expression must be bounded below by 0
+Modules1.dfy(54,12): Related location
Execution trace:
(0,0): anon0
-Modules1.dfy(62,9): Error: failure to decrease termination measure
+Modules1.dfy(62,8): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/Modules2.dfy b/Test/dafny0/Modules2.dfy
index a8dde8ce..beb80546 100644
--- a/Test/dafny0/Modules2.dfy
+++ b/Test/dafny0/Modules2.dfy
@@ -31,7 +31,7 @@ module Test {
}
module Test2 {
- import opened B as A
+ import opened B : A
method m() {
var c := new C; // fine, as A was opened
var c' := new B.C;// also fine, as A is bound
diff --git a/Test/dafny0/MultiDimArray.dfy.expect b/Test/dafny0/MultiDimArray.dfy.expect
index 597ade30..f2bf74de 100644
--- a/Test/dafny0/MultiDimArray.dfy.expect
+++ b/Test/dafny0/MultiDimArray.dfy.expect
@@ -1,9 +1,9 @@
-MultiDimArray.dfy(56,21): Error: assertion violation
+MultiDimArray.dfy(56,20): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon11_Then
(0,0): anon12_Then
-MultiDimArray.dfy(83,25): Error: assertion violation
+MultiDimArray.dfy(83,24): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon5_Then
diff --git a/Test/dafny0/MultiSets.dfy b/Test/dafny0/MultiSets.dfy
index 3535f857..ba075fc3 100644
--- a/Test/dafny0/MultiSets.dfy
+++ b/Test/dafny0/MultiSets.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
method test1()
@@ -295,3 +295,6 @@ lemma Set_and_Multiset_Cardinalities(x: int, y: int)
assert |multiset{x,y}| == 2;
}
}
+
+// AutoTriggers explicitly removed, as simplifications of set expressions such
+// as x in {1,2} cause invalid terms to appear in the triggers
diff --git a/Test/dafny0/MultiSets.dfy.expect b/Test/dafny0/MultiSets.dfy.expect
index 30534b11..aed70bd2 100644
--- a/Test/dafny0/MultiSets.dfy.expect
+++ b/Test/dafny0/MultiSets.dfy.expect
@@ -1,24 +1,24 @@
-MultiSets.dfy(159,3): Error BP5003: A postcondition might not hold on this return path.
-MultiSets.dfy(158,15): Related location: This is the postcondition that might not hold.
+MultiSets.dfy(159,2): Error BP5003: A postcondition might not hold on this return path.
+MultiSets.dfy(158,14): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-MultiSets.dfy(165,3): Error BP5003: A postcondition might not hold on this return path.
-MultiSets.dfy(164,15): Related location: This is the postcondition that might not hold.
+MultiSets.dfy(165,2): Error BP5003: A postcondition might not hold on this return path.
+MultiSets.dfy(164,14): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-MultiSets.dfy(178,11): Error: new number of occurrences might be negative
+MultiSets.dfy(178,10): Error: new number of occurrences might be negative
Execution trace:
(0,0): anon0
(0,0): anon4_Then
(0,0): anon3
-MultiSets.dfy(269,24): Error: assertion violation
+MultiSets.dfy(269,23): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon11_Then
(0,0): anon3
(0,0): anon12_Then
(0,0): anon14_Else
-MultiSets.dfy(292,16): Error: assertion violation
+MultiSets.dfy(292,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon8_Then
diff --git a/Test/dafny0/NatTypes.dfy.expect b/Test/dafny0/NatTypes.dfy.expect
index 99fa16e5..2bc00e95 100644
--- a/Test/dafny0/NatTypes.dfy.expect
+++ b/Test/dafny0/NatTypes.dfy.expect
@@ -1,43 +1,43 @@
-NatTypes.dfy(35,12): Error: value assigned to a nat must be non-negative
+NatTypes.dfy(35,11): Error: value assigned to a nat must be non-negative
Execution trace:
(0,0): anon0
NatTypes.dfy(23,5): anon10_LoopHead
(0,0): anon10_LoopBody
NatTypes.dfy(23,5): anon11_Else
(0,0): anon12_Then
-NatTypes.dfy(10,5): Error: value assigned to a nat must be non-negative
+NatTypes.dfy(10,4): Error: value assigned to a nat must be non-negative
Execution trace:
(0,0): anon0
-NatTypes.dfy(43,14): Error: assertion violation
+NatTypes.dfy(43,13): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon4_Then
-NatTypes.dfy(45,14): Error: assertion violation
+NatTypes.dfy(45,13): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon4_Then
-NatTypes.dfy(62,16): Error: assertion violation
+NatTypes.dfy(62,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-NatTypes.dfy(76,16): Error: assertion violation
+NatTypes.dfy(76,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon5_Else
(0,0): anon6_Then
-NatTypes.dfy(94,22): Error: value assigned to a nat must be non-negative
+NatTypes.dfy(94,21): Error: value assigned to a nat must be non-negative
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-NatTypes.dfy(109,45): Error: value assigned to a nat must be non-negative
+NatTypes.dfy(109,44): Error: value assigned to a nat must be non-negative
Execution trace:
(0,0): anon0
- (0,0): anon6_Else
- (0,0): anon7_Else
- (0,0): anon8_Then
-NatTypes.dfy(132,35): Error: value assigned to a nat must be non-negative
+ (0,0): anon8_Else
+ (0,0): anon9_Else
+ (0,0): anon10_Then
+NatTypes.dfy(132,34): Error: value assigned to a nat must be non-negative
Execution trace:
(0,0): anon0
- (0,0): anon3_Then
+ (0,0): anon4_Then
Dafny program verifier finished with 15 verified, 9 errors
diff --git a/Test/dafny0/NestedMatch.dfy b/Test/dafny0/NestedMatch.dfy
new file mode 100644
index 00000000..81319b4a
--- /dev/null
+++ b/Test/dafny0/NestedMatch.dfy
@@ -0,0 +1,59 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+datatype Nat = Zero | Suc(Nat)
+
+predicate Even(n: Nat)
+{
+ match n
+ case Zero => true
+ case Suc(Zero) => false
+ case Suc(Suc(p)) => Even(p)
+}
+
+
+method checkEven(n: Nat) {
+ assert Even(Zero) == true;
+ assert Even(Suc(Zero)) == false;
+ assert Even(Suc(Suc(n))) == Even(n);
+}
+
+datatype List<T> = Nil | Cons(T, List<T>)
+
+function last<T>(xs: List<T>): T
+ requires xs != Nil
+{
+ match xs
+ case Cons(y, Nil) => y
+ case Cons(y, Cons(z, zs)) => last(Cons(z, zs))
+}
+
+method checkLast<T>(y: T) {
+ assert last(Cons(y, Nil)) == y;
+ assert last(Cons(y, Cons(y, Nil))) == last(Cons(y, Nil));
+}
+
+
+function minus(x: Nat, y: Nat): Nat
+{
+ match (x, y)
+ case (Zero, _) => Zero
+ case (Suc(_), Zero) => x
+ case (Suc(a), Suc(b)) => minus(a, b)
+}
+
+method checkMinus(x:Nat, y: Nat) {
+ assert minus(Suc(x), Suc(y)) == minus(x,y);
+}
+
+
+// nested match statement
+method Last<T>(xs: List<T>) returns (x: T)
+ requires xs != Nil
+{
+
+ match xs {
+ case Cons(y, Nil) => x:= y;
+ case Cons(y, Cons(z, zs)) => x:=Last(Cons(z, zs));
+ }
+}
diff --git a/Test/dafny0/NestedMatch.dfy.expect b/Test/dafny0/NestedMatch.dfy.expect
new file mode 100644
index 00000000..f3a9c95f
--- /dev/null
+++ b/Test/dafny0/NestedMatch.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 11 verified, 0 errors
diff --git a/Test/dafny0/NestedPatterns.dfy b/Test/dafny0/NestedPatterns.dfy
new file mode 100644
index 00000000..d1d88b2a
--- /dev/null
+++ b/Test/dafny0/NestedPatterns.dfy
@@ -0,0 +1,124 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+datatype List<T> = Nil | Cons(head: T, tail: List<T>)
+
+method MethodA<T>(xs: List<T>) returns (ys: List<T>)
+{
+ match xs
+ case Nil =>
+ ys := Nil;
+ case Cons(h, Nil) =>
+ ys := Nil;
+ case Cons(h, Cons(h', tt)) =>
+ ys := tt;
+}
+
+method MethodB<T>(xs: List<T>)
+{
+ match xs
+ case Nil =>
+ case Cons(h, Nil) =>
+ var x := 12;
+ var xxs := Cons(Nil, Nil);
+ case Cons(h, Cons(h', tt)) =>
+}
+
+method MethodC<T>(xs: List<T>) returns (ys: List<T>)
+ requires xs.Cons? ==> !xs.tail.Cons?;
+{
+ match xs
+ case Nil =>
+ ys := Nil;
+ case Cons(h, Nil) =>
+ ys := Nil;
+}
+
+method MethodD<T>(xs: List<T>) returns (ys: List<T>)
+{
+ match xs
+ case Nil =>
+ ys := Nil;
+ case Cons(h, Nil) =>
+ var xxs: List<List<T>> := Cons(Nil, Nil); // BUG: type inference is not doing the right thing on this lint
+ case Cons(h, Cons(h0, tt)) =>
+}
+
+method MethodE<T>(xs: List<T>) returns (ys: List<T>)
+{
+ var xxs: List<List<T>> := Cons(Nil, Nil); // here it works! (but the same line in MethodD does not work)
+}
+
+method MethodF<T>(xs: List<T>) returns (ys: List<T>)
+ requires xs.Cons? ==> !xs.tail.Cons?;
+{
+ match xs
+ case Nil =>
+ case Cons(h, Nil) =>
+ case Cons(h0, Cons(h1, tt)) => // BUG: Dafny complains that Cons appears in more than one case; it seems to be due to the
+ // fact that the previous case uses identifier "h" as the first argument to Cons, whereas this
+ // line uses "h0"
+}
+
+method MethodG<T>(xs: List<T>) returns (xxs: List<List<T>>)
+{
+ match xs
+ case Nil =>
+ xxs := Cons(Nil, Nil); // BUG: this causes there to be an "unresolved identifier: _mc#0" error; oddly enough, the error goes away if the third case is commented out
+ case Cons(h, t) =>
+ case Cons(h, Cons(ht, tt)) =>
+}
+
+method AssertionFailure(xs: List)
+{
+ match xs
+ case (Nil) => // BUG: this line causes an assertion in the Dafny implementation (what should happen is that "(Nil)" should not be allowed here)
+ case (Cons(h, t)) => // BUG: ditto
+}
+
+method DuplicateIdentifierInPattern0<T>(xs: List<T>)
+{
+ match xs
+ case Nil =>
+ case Cons(h, Nil) =>
+ case Cons(h, Cons(_, h)) => // BUG: this duplicate identifier name should give rise to an error (from the Resolver), but no error is reported
+}
+
+method DuplicateIdentifierInPattern1<T>(xs: List<T>)
+{
+ match xs
+ case Nil =>
+ case Cons(h, Nil) =>
+ case Cons(h, Cons(h, _)) => // BUG: this duplicate identifier name should give rise to an error (from the Resolver), but no error is reported
+}
+
+method DuplicateIdentifierInPattern2<T>(xs: List<T>)
+{
+ match xs
+ case Nil =>
+ case Cons(h, Nil) =>
+ case Cons(h, Cons(e, e)) => // BUG: here, the duplicate identifier is detected, but the error message is shown 3 times, which is less than ideal
+}
+
+method Tuples0(xs: List, ys: List)
+{
+ match (xs, ys)
+ case (Nil, Nil) =>
+ case (Cons(a, b), Nil) =>
+ case (Nil, Cons(x, y)) =>
+ case (Cons(a, b), Cons(x, y)) => // BUG: here and in some other places above, not all identifiers are highlighted in the Dafny IDE; it looks like
+ // only the identifiers in the last constructors are
+}
+
+method Tuples1(xs: List, ys: List)
+{
+ match (xs, ys, 4)
+ case (Nil, Nil) => // BUG: the mismatch of 3 versus 2 arguments in the previous line and this line causes Dafny to crash with an
+ // assertion failure "mc.CasePatterns.Count == e.Arguments.Count"
+}
+
+method Tuples2(xs: List, ys: List)
+{
+ match (xs, ys, ())
+ case (Nil, Nil, ()) => // BUG: Dafny crashes with an assertion failure "e.Arguments.Count >= 1"
+}
diff --git a/Test/dafny0/NestedPatterns.dfy.expect b/Test/dafny0/NestedPatterns.dfy.expect
new file mode 100644
index 00000000..d83a7da1
--- /dev/null
+++ b/Test/dafny0/NestedPatterns.dfy.expect
@@ -0,0 +1,9 @@
+NestedPatterns.dfy(69,2): Error: member Cons appears in more than one case
+NestedPatterns.dfy(75,2): Error: member does not exist in datatype List
+NestedPatterns.dfy(76,2): Error: member does not exist in datatype List
+NestedPatterns.dfy(84,23): Error: Duplicate parameter name: h
+NestedPatterns.dfy(92,20): Error: Duplicate parameter name: h
+NestedPatterns.dfy(100,23): Error: Duplicate parameter name: e
+NestedPatterns.dfy(116,2): Error: case arguments count does not match source arguments count
+NestedPatterns.dfy(122,2): Error: match source tuple needs at least 1 argument
+8 resolution/type errors detected in NestedPatterns.dfy
diff --git a/Test/dafny0/Newtypes.dfy.expect b/Test/dafny0/Newtypes.dfy.expect
index 8e6ff4c5..425ee9a9 100644
--- a/Test/dafny0/Newtypes.dfy.expect
+++ b/Test/dafny0/Newtypes.dfy.expect
@@ -1,54 +1,54 @@
-Newtypes.dfy(74,11): Error: cannot find witness that shows type is inhabited (sorry, for now, only tried 0)
+Newtypes.dfy(74,10): Error: cannot find witness that shows type is inhabited (sorry, for now, only tried 0)
Execution trace:
(0,0): anon0
-Newtypes.dfy(76,45): Error: possible division by zero
+Newtypes.dfy(76,44): Error: possible division by zero
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-Newtypes.dfy(87,14): Error: result of operation might violate newtype constraint
+Newtypes.dfy(87,13): Error: result of operation might violate newtype constraint
Execution trace:
(0,0): anon0
-Newtypes.dfy(95,12): Error: result of operation might violate newtype constraint
+Newtypes.dfy(95,11): Error: result of operation might violate newtype constraint
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-Newtypes.dfy(97,14): Error: result of operation might violate newtype constraint
+Newtypes.dfy(97,13): Error: result of operation might violate newtype constraint
Execution trace:
(0,0): anon0
(0,0): anon3_Else
-Newtypes.dfy(104,16): Error: result of operation might violate newtype constraint
+Newtypes.dfy(104,15): Error: result of operation might violate newtype constraint
Execution trace:
(0,0): anon0
-Newtypes.dfy(177,14): Error: result of operation might violate newtype constraint
+Newtypes.dfy(177,13): Error: result of operation might violate newtype constraint
Execution trace:
(0,0): anon0
-Newtypes.dfy(193,64): Error: index 0 out of range
+Newtypes.dfy(193,63): Error: index 0 out of range
Execution trace:
(0,0): anon0
(0,0): anon32_Then
(0,0): anon33_Then
(0,0): anon16
-Newtypes.dfy(194,67): Error: index 1 out of range
+Newtypes.dfy(194,66): Error: index 1 out of range
Execution trace:
(0,0): anon0
(0,0): anon34_Then
(0,0): anon35_Then
(0,0): anon19
-Newtypes.dfy(222,16): Error: new number of occurrences might be negative
+Newtypes.dfy(222,15): Error: new number of occurrences might be negative
Execution trace:
(0,0): anon0
(0,0): anon6_Then
-Newtypes.dfy(225,40): Error: result of operation might violate newtype constraint
+Newtypes.dfy(225,39): Error: result of operation might violate newtype constraint
Execution trace:
(0,0): anon0
(0,0): anon8_Then
-Newtypes.dfy(237,19): Error: result of operation might violate newtype constraint
+Newtypes.dfy(237,18): Error: result of operation might violate newtype constraint
Execution trace:
(0,0): anon0
Newtypes.dfy(236,5): anon9_LoopHead
(0,0): anon9_LoopBody
(0,0): anon10_Then
-Newtypes.dfy(277,19): Error: result of operation might violate newtype constraint
+Newtypes.dfy(277,18): Error: result of operation might violate newtype constraint
Execution trace:
(0,0): anon0
Newtypes.dfy(276,5): anon9_LoopHead
diff --git a/Test/dafny0/NonGhostQuantifiers.dfy b/Test/dafny0/NonGhostQuantifiers.dfy
index bff1d65b..e522d0fc 100644
--- a/Test/dafny0/NonGhostQuantifiers.dfy
+++ b/Test/dafny0/NonGhostQuantifiers.dfy
@@ -181,6 +181,12 @@ module DependencyOnAllAllocatedObjects {
forall c: SomeClass :: true // error: not allowed to dependend on which objects are allocated
}
+ class SomeClass {
+ var f: int;
+ }
+}
+
+module DependencyOnAllAllocatedObjects_More {
method M()
{
var b := forall c: SomeClass :: c != null ==> c.f == 0; // error: non-ghost code requires bounds
@@ -192,3 +198,4 @@ module DependencyOnAllAllocatedObjects {
var f: int;
}
}
+
diff --git a/Test/dafny0/NonGhostQuantifiers.dfy.expect b/Test/dafny0/NonGhostQuantifiers.dfy.expect
index 1e2fce17..0abf0b6c 100644
--- a/Test/dafny0/NonGhostQuantifiers.dfy.expect
+++ b/Test/dafny0/NonGhostQuantifiers.dfy.expect
@@ -6,16 +6,12 @@ NonGhostQuantifiers.dfy(167,4): Error: a quantifier involved in a function defin
NonGhostQuantifiers.dfy(171,4): Error: a quantifier involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'c'
NonGhostQuantifiers.dfy(176,4): Error: a quantifier involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'c'
NonGhostQuantifiers.dfy(181,4): Error: a quantifier involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'c'
-NonGhostQuantifiers.dfy(186,13): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'c'
-NonGhostQuantifiers.dfy(16,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'n'
-NonGhostQuantifiers.dfy(45,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'n'
-NonGhostQuantifiers.dfy(49,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'd'
-NonGhostQuantifiers.dfy(53,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'n'
-NonGhostQuantifiers.dfy(77,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'i'
-NonGhostQuantifiers.dfy(81,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'j'
-NonGhostQuantifiers.dfy(91,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'j'
-NonGhostQuantifiers.dfy(106,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'j'
-NonGhostQuantifiers.dfy(114,10): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'y'
-NonGhostQuantifiers.dfy(123,8): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'x'
+NonGhostQuantifiers.dfy(192,13): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'c'
+NonGhostQuantifiers.dfy(16,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'n'
+NonGhostQuantifiers.dfy(45,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'n'
+NonGhostQuantifiers.dfy(49,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'd'
+NonGhostQuantifiers.dfy(53,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'n'
+NonGhostQuantifiers.dfy(114,10): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'y'
+NonGhostQuantifiers.dfy(123,8): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'x'
NonGhostQuantifiers.dfy(140,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
-20 resolution/type errors detected in NonGhostQuantifiers.dfy
+16 resolution/type errors detected in NonGhostQuantifiers.dfy
diff --git a/Test/dafny0/OpaqueFunctions.dfy b/Test/dafny0/OpaqueFunctions.dfy
index e1c0756c..b3cde309 100644
--- a/Test/dafny0/OpaqueFunctions.dfy
+++ b/Test/dafny0/OpaqueFunctions.dfy
@@ -44,7 +44,7 @@ module A' refines A {
}
module B {
- import X as A
+ import X : A
method Main() {
var c := new X.C();
c.M(); // fine
@@ -68,7 +68,7 @@ module B {
}
}
module B_direct {
- import X as A'
+ import X : A'
method Main() {
var c := new X.C();
c.M(); // fine
diff --git a/Test/dafny0/OpaqueFunctions.dfy.expect b/Test/dafny0/OpaqueFunctions.dfy.expect
index 2fb1701f..e9f6e60c 100644
--- a/Test/dafny0/OpaqueFunctions.dfy.expect
+++ b/Test/dafny0/OpaqueFunctions.dfy.expect
@@ -1,86 +1,86 @@
-OpaqueFunctions.dfy(27,16): Error: assertion violation
+OpaqueFunctions.dfy(27,15): Error: assertion violation
Execution trace:
(0,0): anon0
-OpaqueFunctions.dfy(52,8): Error BP5002: A precondition for this call might not hold.
-OpaqueFunctions.dfy(24,16): Related location: This is the precondition that might not hold.
+OpaqueFunctions.dfy(52,7): Error BP5002: A precondition for this call might not hold.
+OpaqueFunctions.dfy(24,15): Related location: This is the precondition that might not hold.
Execution trace:
(0,0): anon0
-OpaqueFunctions.dfy(58,20): Error: assertion violation
+OpaqueFunctions.dfy(58,19): Error: assertion violation
Execution trace:
(0,0): anon0
-OpaqueFunctions.dfy(60,21): Error: assertion violation
+OpaqueFunctions.dfy(60,20): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon5_Then
-OpaqueFunctions.dfy(63,21): Error: assertion violation
+OpaqueFunctions.dfy(63,20): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon6_Then
-OpaqueFunctions.dfy(66,21): Error: assertion violation
+OpaqueFunctions.dfy(66,20): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon6_Else
-OpaqueFunctions.dfy(77,21): Error: assertion violation
+OpaqueFunctions.dfy(77,20): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-OpaqueFunctions.dfy(79,10): Error BP5002: A precondition for this call might not hold.
-OpaqueFunctions.dfy[A'](24,16): Related location: This is the precondition that might not hold.
+OpaqueFunctions.dfy(79,9): Error BP5002: A precondition for this call might not hold.
+OpaqueFunctions.dfy[A'](24,15): Related location: This is the precondition that might not hold.
Execution trace:
(0,0): anon0
(0,0): anon3_Else
-OpaqueFunctions.dfy(86,20): Error: assertion violation
+OpaqueFunctions.dfy(86,19): Error: assertion violation
Execution trace:
(0,0): anon0
-OpaqueFunctions.dfy(88,21): Error: assertion violation
+OpaqueFunctions.dfy(88,20): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon5_Then
-OpaqueFunctions.dfy(91,21): Error: assertion violation
+OpaqueFunctions.dfy(91,20): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon6_Then
-OpaqueFunctions.dfy(94,21): Error: assertion violation
+OpaqueFunctions.dfy(94,20): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon6_Else
-OpaqueFunctions.dfy(105,21): Error: assertion violation
+OpaqueFunctions.dfy(105,20): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-OpaqueFunctions.dfy(107,10): Error BP5002: A precondition for this call might not hold.
-OpaqueFunctions.dfy[A'](24,16): Related location: This is the precondition that might not hold.
+OpaqueFunctions.dfy(107,9): Error BP5002: A precondition for this call might not hold.
+OpaqueFunctions.dfy[A'](24,15): Related location: This is the precondition that might not hold.
Execution trace:
(0,0): anon0
(0,0): anon3_Else
-OpaqueFunctions.dfy(114,20): Error: assertion violation
+OpaqueFunctions.dfy(114,19): Error: assertion violation
Execution trace:
(0,0): anon0
-OpaqueFunctions.dfy(116,21): Error: assertion violation
+OpaqueFunctions.dfy(116,20): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon5_Then
-OpaqueFunctions.dfy(119,21): Error: assertion violation
+OpaqueFunctions.dfy(119,20): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon6_Then
-OpaqueFunctions.dfy(122,21): Error: assertion violation
+OpaqueFunctions.dfy(122,20): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon6_Else
-OpaqueFunctions.dfy(138,13): Error: assertion violation
+OpaqueFunctions.dfy(138,12): Error: assertion violation
Execution trace:
(0,0): anon0
-OpaqueFunctions.dfy(202,12): Error: assertion violation
+OpaqueFunctions.dfy(202,11): Error: assertion violation
Execution trace:
(0,0): anon0
-OpaqueFunctions.dfy(218,12): Error: assertion violation
+OpaqueFunctions.dfy(218,11): Error: assertion violation
Execution trace:
(0,0): anon0
-OpaqueFunctions.dfy(170,16): Error: assertion violation
+OpaqueFunctions.dfy(170,15): Error: assertion violation
Execution trace:
(0,0): anon0
-OpaqueFunctions.dfy(185,20): Error: assertion violation
+OpaqueFunctions.dfy(185,19): Error: assertion violation
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/Parallel.dfy b/Test/dafny0/Parallel.dfy
index 030eb350..00a1514c 100644
--- a/Test/dafny0/Parallel.dfy
+++ b/Test/dafny0/Parallel.dfy
@@ -1,14 +1,14 @@
-// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
class C {
- var data: int;
- var n: nat;
- var st: set<object>;
+ var data: int
+ var n: nat
+ var st: set<object>
ghost method CLemma(k: int)
- requires k != -23;
- ensures data < k; // magic, isn't it (or bogus, some would say)
+ requires k != -23
+ ensures data < k // magic, isn't it (or bogus, some would say)
}
// This method more or less just tests the syntax, resolution, and basic verification
@@ -19,31 +19,31 @@ method ParallelStatement_Resolve(
S: set<int>,
clx: C, cly: C, clk: int
)
- requires a != null && null !in spine;
- modifies a, spine;
+ requires a != null && null !in spine
+ modifies a, spine
{
- forall (i: int | 0 <= i < a.Length && i % 2 == 0) {
+ forall i | 0 <= i < a.Length && i % 2 == 0 {
a[i] := a[(i + 1) % a.Length] + 3;
}
- forall (o | o in spine) {
+ forall o | o in spine {
o.st := o.st + Repr;
}
- forall (x, y | x in S && 0 <= y+x < 100) {
+ forall x, y | x in S && 0 <= y+x < 100 {
Lemma(clx, x, y); // error: precondition does not hold (clx may be null)
}
- forall (x, y | x in S && 0 <= y+x < 100) {
+ forall x, y | x in S && 0 <= y+x < 100 {
cly.CLemma(x + y); // error: receiver might be null
}
- forall (p | 0 <= p)
- ensures F(p) <= Sum(p) + p - 1; // error (no connection is known between F and Sum)
+ forall p | 0 <= p
+ ensures F(p) <= Sum(p) + p - 1 // error (no connection is known between F and Sum)
{
assert 0 <= G(p);
ghost var t;
- if (p % 2 == 0) {
+ if p % 2 == 0 {
assert G(p) == F(p+2); // error (there's nothing that gives any relation between F and G)
t := p+p;
} else {
@@ -56,11 +56,11 @@ method ParallelStatement_Resolve(
}
}
-ghost method Lemma(c: C, x: int, y: int)
- requires c != null;
- ensures c.data <= x+y;
-ghost method PowerLemma(x: int, y: int)
- ensures Pred(x, y);
+lemma Lemma(c: C, x: int, y: int)
+ requires c != null
+ ensures c.data <= x+y
+lemma PowerLemma(x: int, y: int)
+ ensures Pred(x, y)
function F(x: int): int
function G(x: int): nat
@@ -71,54 +71,54 @@ function Pred(x: int, y: int): bool
// ---------------------------------------------------------------------
method M0(S: set<C>)
- requires null !in S;
- modifies S;
- ensures forall o :: o in S ==> o.data == 85;
- ensures forall o :: o != null && o !in S ==> o.data == old(o.data);
+ requires null !in S
+ modifies S
+ ensures forall o :: o in S ==> o.data == 85
+ ensures forall o :: o != null && o !in S ==> o.data == old(o.data)
{
- forall (s | s in S) {
+ forall s | s in S {
s.data := 85;
}
}
method M1(S: set<C>, x: C)
- requires null !in S && x in S;
+ requires null !in S && x in S
{
- forall (s | s in S)
- ensures s.data < 100;
+ forall s | s in S
+ ensures s.data < 100
{
assume s.data == 85;
}
- if (*) {
+ if * {
assert x.data == 85; // error (cannot be inferred from forall ensures clause)
} else {
assert x.data < 120;
}
- forall (s | s in S)
- ensures s.data < 70; // error
+ forall s | s in S
+ ensures s.data < 70 // error
{
assume s.data == 85;
}
}
method M2() returns (a: array<int>)
- ensures a != null;
- ensures forall i,j :: 0 <= i < a.Length/2 <= j < a.Length ==> a[i] < a[j];
+ ensures a != null
+ ensures forall i,j :: 0 <= i < a.Length/2 <= j < a.Length ==> a[i] < a[j]
{
a := new int[250];
- forall (i: nat | i < 125) {
+ forall i: nat | i < 125 {
a[i] := 423;
}
- forall (i | 125 <= i < 250) {
+ forall i | 125 <= i < 250 {
a[i] := 300 + i;
}
}
method M4(S: set<C>, k: int)
- modifies S;
+ modifies S
{
- forall (s | s in S && s != null) {
+ forall s | s in S && s != null {
s.n := k; // error: k might be negative
}
}
@@ -127,25 +127,25 @@ method M5()
{
if {
case true =>
- forall (x | 0 <= x < 100) {
+ forall x | 0 <= x < 100 {
PowerLemma(x, x);
}
assert Pred(34, 34);
case true =>
- forall (x,y | 0 <= x < 100 && y == x+1) {
+ forall x,y | 0 <= x < 100 && y == x+1 {
PowerLemma(x, y);
}
assert Pred(34, 35);
case true =>
- forall (x,y | 0 <= x < y < 100) {
+ forall x,y | 0 <= x < y < 100 {
PowerLemma(x, y);
}
assert Pred(34, 35);
case true =>
- forall (x | x in set k | 0 <= k < 100) {
+ forall x | x in set k | 0 <= k < 100 {
PowerLemma(x, x);
}
assert Pred(34, 34);
@@ -155,22 +155,22 @@ method M5()
method Main()
{
var a := new int[180];
- forall (i | 0 <= i < 180) {
+ forall i | 0 <= i < 180 {
a[i] := 2*i + 100;
}
var sq := [0, 0, 0, 2, 2, 2, 5, 5, 5];
- forall (i | 0 <= i < |sq|) {
+ forall i | 0 <= i < |sq| {
a[20+i] := sq[i];
}
- forall (t | t in sq) {
+ forall t | t in sq {
a[t] := 1000;
}
- forall (t,u | t in sq && t < 4 && 10 <= u < 10+t) {
+ forall t,u | t in sq && t < 4 && 10 <= u < 10+t {
a[u] := 6000 + t;
}
var k := 0;
- while (k < 180) {
- if (k != 0) { print ", "; }
+ while k < 180 {
+ if k != 0 { print ", "; }
print a[k];
k := k + 1;
}
@@ -180,50 +180,50 @@ method Main()
method DuplicateUpdate() {
var a := new int[180];
var sq := [0, 0, 0, 2, 2, 2, 5, 5, 5];
- if (*) {
- forall (t,u | t in sq && 10 <= u < 10+t) {
+ if * {
+ forall t,u | t in sq && 10 <= u < 10+t {
a[u] := 6000 + t; // error: a[10] (and a[11]) are assigned more than once
}
} else {
- forall (t,u | t in sq && t < 4 && 10 <= u < 10+t) {
+ forall t,u | t in sq && t < 4 && 10 <= u < 10+t {
a[u] := 6000 + t; // with the 't < 4' conjunct in the line above, this is fine
}
}
}
-ghost method DontDoMuch(x: int)
+lemma DontDoMuch(x: int)
{
}
method OmittedRange() {
- forall (x: int) { } // a type is still needed for the bound variable
- forall (x) {
+ forall x: int { } // a type is still needed for the bound variable
+ forall x {
DontDoMuch(x);
}
}
// ----------------------- two-state postconditions ---------------------------------
-class TwoState_C { ghost var data: int; }
+class TwoState_C { ghost var data: int }
// It is not possible to achieve this postcondition in a ghost method, because ghost
// contexts are not allowed to allocate state. Callers of this ghost method will know
// that the postcondition is tantamount to 'false'.
ghost method TwoState0(y: int)
- ensures exists o: TwoState_C :: o != null && fresh(o);
+ ensures exists o: TwoState_C {:nowarn} :: o != null && fresh(o)
method TwoState_Main0() {
- forall (x) { TwoState0(x); }
+ forall x { TwoState0(x); }
assert false; // no prob, because the postcondition of TwoState0 implies false
}
method X_Legit(c: TwoState_C)
- requires c != null;
- modifies c;
+ requires c != null
+ modifies c
{
c.data := c.data + 1;
- forall (x | c.data <= x)
- ensures old(c.data) < x; // note that the 'old' refers to the method's initial state
+ forall x | c.data <= x
+ ensures old(c.data) < x // note that the 'old' refers to the method's initial state
{
}
}
@@ -235,8 +235,8 @@ method X_Legit(c: TwoState_C)
// method, not the beginning of the 'forall' statement.
method TwoState_Main2()
{
- forall (x: int)
- ensures exists o: TwoState_C :: o != null && fresh(o);
+ forall x: int
+ ensures exists o: TwoState_C {:nowarn} :: o != null && fresh(o)
{
TwoState0(x);
}
@@ -251,8 +251,8 @@ method TwoState_Main2()
// statement's effect on the heap is not optimized away.
method TwoState_Main3()
{
- forall (x: int)
- ensures exists o: TwoState_C :: o != null && fresh(o);
+ forall x: int
+ ensures exists o: TwoState_C {:nowarn} :: o != null && fresh(o)
{
assume false; // (there's no other way to achieve this forall-statement postcondition)
}
@@ -262,11 +262,11 @@ method TwoState_Main3()
// ------- empty forall statement -----------------------------------------
class EmptyForallStatement {
- var emptyPar: int;
+ var emptyPar: int
method Empty_Parallel0()
- modifies this;
- ensures emptyPar == 8;
+ modifies this
+ ensures emptyPar == 8
{
forall () {
this.emptyPar := 8;
@@ -274,11 +274,11 @@ class EmptyForallStatement {
}
function EmptyPar_P(x: int): bool
- ghost method EmptyPar_Lemma(x: int)
- ensures EmptyPar_P(x);
+ lemma EmptyPar_Lemma(x: int)
+ ensures EmptyPar_P(x)
method Empty_Parallel1()
- ensures EmptyPar_P(8);
+ ensures EmptyPar_P(8)
{
forall {
EmptyPar_Lemma(8);
@@ -288,7 +288,7 @@ class EmptyForallStatement {
method Empty_Parallel2()
{
forall
- ensures exists k :: EmptyPar_P(k);
+ ensures exists k :: EmptyPar_P(k)
{
var y := 8;
assume EmptyPar_P(y);
@@ -309,12 +309,12 @@ predicate ThProperty(step: nat, t: Nat, r: nat)
{
match t
case Zero => true
- case Succ(o) => step>0 && exists ro:nat :: ThProperty(step-1, o, ro)
+ case Succ(o) => step>0 && exists ro:nat, ss | ss == step-1 :: ThProperty(ss, o, ro) //WISH: ss should be autogrnerated. Note that step is not a bound variable.
}
-ghost method Th(step: nat, t: Nat, r: nat)
- requires t.Succ? && ThProperty(step, t, r);
+lemma Th(step: nat, t: Nat, r: nat)
+ requires t.Succ? && ThProperty(step, t, r)
// the next line follows from the precondition and the definition of ThProperty
- ensures exists ro:nat :: ThProperty(step-1, t.tail, ro);
+ ensures exists ro:nat, ss | ss == step-1 :: ThProperty(ss, t.tail, ro) //WISH same as above
{
}
diff --git a/Test/dafny0/Parallel.dfy.expect b/Test/dafny0/Parallel.dfy.expect
index db551bba..5d9b044f 100644
--- a/Test/dafny0/Parallel.dfy.expect
+++ b/Test/dafny0/Parallel.dfy.expect
@@ -1,9 +1,9 @@
-Parallel.dfy(297,22): Error: assertion violation
+Parallel.dfy(297,21): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon4_Else
-Parallel.dfy(34,10): Error BP5002: A precondition for this call might not hold.
-Parallel.dfy(60,14): Related location: This is the precondition that might not hold.
+Parallel.dfy(34,9): Error BP5002: A precondition for this call might not hold.
+Parallel.dfy(60,13): Related location: This is the precondition that might not hold.
Execution trace:
(0,0): anon0
(0,0): anon29_Else
@@ -12,7 +12,7 @@ Execution trace:
(0,0): anon34_Then
(0,0): anon35_Then
(0,0): anon14
-Parallel.dfy(38,5): Error: target object may be null
+Parallel.dfy(38,4): Error: target object may be null
Execution trace:
(0,0): anon0
(0,0): anon29_Else
@@ -22,7 +22,7 @@ Execution trace:
(0,0): anon37_Then
(0,0): anon38_Then
(0,0): anon20
-Parallel.dfy(42,18): Error: possible violation of postcondition of forall statement
+Parallel.dfy(42,17): Error: possible violation of postcondition of forall statement
Execution trace:
(0,0): anon0
(0,0): anon29_Else
@@ -32,7 +32,7 @@ Execution trace:
(0,0): anon39_Then
(0,0): anon40_Then
(0,0): anon26
-Parallel.dfy(47,19): Error: assertion violation
+Parallel.dfy(47,18): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon29_Else
@@ -41,24 +41,24 @@ Execution trace:
(0,0): anon36_Else
(0,0): anon39_Then
(0,0): anon40_Then
-Parallel.dfy(93,19): Error: assertion violation
+Parallel.dfy(93,18): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon10_Else
(0,0): anon11_Then
-Parallel.dfy(99,20): Error: possible violation of postcondition of forall statement
+Parallel.dfy(99,19): Error: possible violation of postcondition of forall statement
Execution trace:
(0,0): anon0
(0,0): anon10_Else
(0,0): anon11_Then
(0,0): anon12_Then
-Parallel.dfy(122,12): Error: value assigned to a nat must be non-negative
+Parallel.dfy(122,11): Error: value assigned to a nat must be non-negative
Execution trace:
(0,0): anon0
(0,0): anon6_Then
(0,0): anon7_Then
(0,0): anon3
-Parallel.dfy(185,12): Error: left-hand sides for different forall-statement bound variables may refer to the same location
+Parallel.dfy(185,11): Error: left-hand sides for different forall-statement bound variables may refer to the same location
Execution trace:
(0,0): anon0
(0,0): anon19_Then
diff --git a/Test/dafny0/ParallelResolveErrors.dfy b/Test/dafny0/ParallelResolveErrors.dfy
index 5e01f019..8c48487d 100644
--- a/Test/dafny0/ParallelResolveErrors.dfy
+++ b/Test/dafny0/ParallelResolveErrors.dfy
@@ -7,7 +7,6 @@ class C {
ghost method Init_ModifyNothing() { }
ghost method Init_ModifyThis() modifies this;
{
- data := 6; // error: assignment to a non-ghost field
gdata := 7;
}
ghost method Init_ModifyStuff(c: C) modifies this, c; { }
@@ -40,8 +39,8 @@ method M0(IS: set<int>)
{
var x := i;
x := x + 1;
- y := 18; // (this statement is not allowed, since y is declared outside the forall, but that check happens only if the first resolution pass of the forall statement passes, which it doesn't in this case because of the next line)
- z := 20; // error: assigning to a non-ghost variable inside a ghost forall block
+ y := 18; // error: assigning to a (ghost) variable inside a ghost forall block
+ z := 20; // error: assigning to a (non-ghost) variable inside a ghost forall block
}
forall (i | 0 <= i)
@@ -120,3 +119,15 @@ method M3(c: C)
c.GhostMethodWithModifies(x); // error: not allowed to call method with nonempty modifies clause
}
}
+
+module AnotherModule {
+ class C {
+ var data: int;
+ ghost var gdata: int;
+ ghost method Init_ModifyThis() modifies this;
+ {
+ data := 6; // error: assignment to a non-ghost field
+ gdata := 7;
+ }
+ }
+}
diff --git a/Test/dafny0/ParallelResolveErrors.dfy.expect b/Test/dafny0/ParallelResolveErrors.dfy.expect
index 7305bfce..4d25ba11 100644
--- a/Test/dafny0/ParallelResolveErrors.dfy.expect
+++ b/Test/dafny0/ParallelResolveErrors.dfy.expect
@@ -1,22 +1,23 @@
-ParallelResolveErrors.dfy(10,9): Error: Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
-ParallelResolveErrors.dfy(21,4): Error: LHS of assignment must denote a mutable variable
-ParallelResolveErrors.dfy(26,6): Error: body of forall statement is attempting to update a variable declared outside the forall statement
-ParallelResolveErrors.dfy(44,6): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
-ParallelResolveErrors.dfy(56,13): Error: new allocation not supported in forall statements
+ParallelResolveErrors.dfy(129,11): Error: Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+ParallelResolveErrors.dfy(20,4): Error: LHS of assignment must denote a mutable variable
+ParallelResolveErrors.dfy(25,6): Error: body of forall statement is attempting to update a variable declared outside the forall statement
+ParallelResolveErrors.dfy(42,6): Error: body of forall statement is attempting to update a variable declared outside the forall statement
+ParallelResolveErrors.dfy(43,6): Error: body of forall statement is attempting to update a variable declared outside the forall statement
+ParallelResolveErrors.dfy(55,13): Error: new allocation not supported in forall statements
+ParallelResolveErrors.dfy(60,13): Error: new allocation not allowed in ghost context
ParallelResolveErrors.dfy(61,13): Error: new allocation not allowed in ghost context
ParallelResolveErrors.dfy(62,13): Error: new allocation not allowed in ghost context
ParallelResolveErrors.dfy(63,13): Error: new allocation not allowed in ghost context
-ParallelResolveErrors.dfy(64,13): Error: new allocation not allowed in ghost context
-ParallelResolveErrors.dfy(65,22): Error: the body of the enclosing forall statement is not allowed to update heap locations, so any call must be to a method with an empty modifies clause
-ParallelResolveErrors.dfy(66,20): Error: the body of the enclosing forall statement is not allowed to call non-ghost methods
-ParallelResolveErrors.dfy(73,19): Error: trying to break out of more loop levels than there are enclosing loops
-ParallelResolveErrors.dfy(77,18): Error: return statement is not allowed inside a forall statement
-ParallelResolveErrors.dfy(84,21): Error: trying to break out of more loop levels than there are enclosing loops
-ParallelResolveErrors.dfy(85,20): Error: trying to break out of more loop levels than there are enclosing loops
-ParallelResolveErrors.dfy(86,20): Error: break label is undefined or not in scope: OutsideLoop
-ParallelResolveErrors.dfy(95,24): Error: trying to break out of more loop levels than there are enclosing loops
-ParallelResolveErrors.dfy(96,24): Error: break label is undefined or not in scope: OutsideLoop
-ParallelResolveErrors.dfy(107,9): Error: the body of the enclosing forall statement is not allowed to update heap locations
-ParallelResolveErrors.dfy(115,29): Error: the body of the enclosing forall statement is not allowed to update heap locations, so any call must be to a method with an empty modifies clause
-ParallelResolveErrors.dfy(120,29): Error: the body of the enclosing forall statement is not allowed to update heap locations, so any call must be to a method with an empty modifies clause
-21 resolution/type errors detected in ParallelResolveErrors.dfy
+ParallelResolveErrors.dfy(64,22): Error: the body of the enclosing forall statement is not allowed to update heap locations, so any call must be to a method with an empty modifies clause
+ParallelResolveErrors.dfy(65,20): Error: the body of the enclosing forall statement is not allowed to call non-ghost methods
+ParallelResolveErrors.dfy(72,19): Error: trying to break out of more loop levels than there are enclosing loops
+ParallelResolveErrors.dfy(76,18): Error: return statement is not allowed inside a forall statement
+ParallelResolveErrors.dfy(83,21): Error: trying to break out of more loop levels than there are enclosing loops
+ParallelResolveErrors.dfy(84,20): Error: trying to break out of more loop levels than there are enclosing loops
+ParallelResolveErrors.dfy(85,20): Error: break label is undefined or not in scope: OutsideLoop
+ParallelResolveErrors.dfy(94,24): Error: trying to break out of more loop levels than there are enclosing loops
+ParallelResolveErrors.dfy(95,24): Error: break label is undefined or not in scope: OutsideLoop
+ParallelResolveErrors.dfy(106,9): Error: the body of the enclosing forall statement is not allowed to update heap locations
+ParallelResolveErrors.dfy(114,29): Error: the body of the enclosing forall statement is not allowed to update heap locations, so any call must be to a method with an empty modifies clause
+ParallelResolveErrors.dfy(119,29): Error: the body of the enclosing forall statement is not allowed to update heap locations, so any call must be to a method with an empty modifies clause
+22 resolution/type errors detected in ParallelResolveErrors.dfy
diff --git a/Test/dafny0/ParseErrors.dfy.expect b/Test/dafny0/ParseErrors.dfy.expect
index 30898479..660ed926 100644
--- a/Test/dafny0/ParseErrors.dfy.expect
+++ b/Test/dafny0/ParseErrors.dfy.expect
@@ -1,17 +1,17 @@
-ParseErrors.dfy(7,19): error: a chain cannot have more than one != operator
-ParseErrors.dfy(9,37): error: this operator chain cannot continue with a descending operator
-ParseErrors.dfy(10,38): error: this operator chain cannot continue with an ascending operator
-ParseErrors.dfy(15,24): error: this operator chain cannot continue with a descending operator
-ParseErrors.dfy(18,18): error: this operator cannot be part of a chain
-ParseErrors.dfy(19,19): error: this operator cannot be part of a chain
-ParseErrors.dfy(20,18): error: this operator cannot be part of a chain
-ParseErrors.dfy(21,18): error: chaining not allowed from the previous operator
-ParseErrors.dfy(28,19): error: chaining not allowed from the previous operator
-ParseErrors.dfy(31,20): error: can only chain disjoint (!!) with itself.
-ParseErrors.dfy(58,8): error: the main operator of a calculation must be transitive
-ParseErrors.dfy(74,2): error: this operator cannot continue this calculation
-ParseErrors.dfy(75,2): error: this operator cannot continue this calculation
-ParseErrors.dfy(80,2): error: this operator cannot continue this calculation
-ParseErrors.dfy(81,2): error: this operator cannot continue this calculation
-ParseErrors.dfy(87,2): error: this operator cannot continue this calculation
+ParseErrors.dfy(7,18): Error: a chain cannot have more than one != operator
+ParseErrors.dfy(9,36): Error: this operator chain cannot continue with a descending operator
+ParseErrors.dfy(10,37): Error: this operator chain cannot continue with an ascending operator
+ParseErrors.dfy(15,23): Error: this operator chain cannot continue with a descending operator
+ParseErrors.dfy(18,17): Error: this operator cannot be part of a chain
+ParseErrors.dfy(19,18): Error: this operator cannot be part of a chain
+ParseErrors.dfy(20,17): Error: this operator cannot be part of a chain
+ParseErrors.dfy(21,17): Error: chaining not allowed from the previous operator
+ParseErrors.dfy(28,18): Error: chaining not allowed from the previous operator
+ParseErrors.dfy(31,19): Error: can only chain disjoint (!!) with itself.
+ParseErrors.dfy(58,7): Error: the main operator of a calculation must be transitive
+ParseErrors.dfy(74,1): Error: this operator cannot continue this calculation
+ParseErrors.dfy(75,1): Error: this operator cannot continue this calculation
+ParseErrors.dfy(80,1): Error: this operator cannot continue this calculation
+ParseErrors.dfy(81,1): Error: this operator cannot continue this calculation
+ParseErrors.dfy(87,1): Error: this operator cannot continue this calculation
16 parse errors detected in ParseErrors.dfy
diff --git a/Test/dafny0/PredExpr.dfy.expect b/Test/dafny0/PredExpr.dfy.expect
index 07bd5f20..80f311cb 100644
--- a/Test/dafny0/PredExpr.dfy.expect
+++ b/Test/dafny0/PredExpr.dfy.expect
@@ -1,16 +1,16 @@
-PredExpr.dfy(7,12): Error: assertion violation
+PredExpr.dfy(7,11): Error: assertion violation
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-PredExpr.dfy(39,15): Error: value assigned to a nat must be non-negative
+ (0,0): anon4_Else
+PredExpr.dfy(39,14): Error: value assigned to a nat must be non-negative
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
- (0,0): anon6_Else
-PredExpr.dfy(52,17): Error: assertion violation
+ (0,0): anon7_Else
+ (0,0): anon8_Else
+PredExpr.dfy(52,16): Error: assertion violation
Execution trace:
(0,0): anon0
-PredExpr.dfy(77,14): Error: assertion violation
+PredExpr.dfy(77,13): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon8_Else
diff --git a/Test/dafny0/Predicates.dfy b/Test/dafny0/Predicates.dfy
index 737dacd2..f8b3355d 100644
--- a/Test/dafny0/Predicates.dfy
+++ b/Test/dafny0/Predicates.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
module A {
diff --git a/Test/dafny0/Predicates.dfy.expect b/Test/dafny0/Predicates.dfy.expect
index dac4eb3c..36c9dfdd 100644
--- a/Test/dafny0/Predicates.dfy.expect
+++ b/Test/dafny0/Predicates.dfy.expect
@@ -1,26 +1,28 @@
-Predicates.dfy[B](21,5): Error BP5003: A postcondition might not hold on this return path.
-Predicates.dfy[B](20,15): Related location: This is the postcondition that might not hold.
-Predicates.dfy(31,9): Related location
+Predicates.dfy[B](21,4): Error BP5003: A postcondition might not hold on this return path.
+Predicates.dfy[B](20,14): Related location: This is the postcondition that might not hold.
+Predicates.dfy(31,8): Related location
Execution trace:
(0,0): anon0
-Predicates.dfy(88,16): Error: assertion violation
+Predicates.dfy(88,15): Error: assertion violation
Execution trace:
(0,0): anon0
-Predicates.dfy(92,14): Error: assertion violation
+Predicates.dfy(92,13): Error: assertion violation
Execution trace:
(0,0): anon0
-Predicates.dfy[Tricky_Full](126,5): Error BP5003: A postcondition might not hold on this return path.
-Predicates.dfy[Tricky_Full](125,15): Related location: This is the postcondition that might not hold.
-Predicates.dfy(136,7): Related location
-Predicates.dfy[Tricky_Full](116,9): Related location
+Predicates.dfy[Tricky_Full](126,4): Error BP5003: A postcondition might not hold on this return path.
+Predicates.dfy[Tricky_Full](125,14): Related location: This is the postcondition that might not hold.
+Predicates.dfy(136,6): Related location
+Predicates.dfy[Tricky_Full](116,8): Related location
Execution trace:
(0,0): anon0
-Predicates.dfy(164,5): Error BP5003: A postcondition might not hold on this return path.
-Predicates.dfy(163,15): Related location: This is the postcondition that might not hold.
+Predicates.dfy(164,4): Error BP5003: A postcondition might not hold on this return path.
+Predicates.dfy(163,14): Related location: This is the postcondition that might not hold.
+Predicates.dfy(163,42): Related location
Execution trace:
(0,0): anon0
-Predicates.dfy[Q1](154,5): Error BP5003: A postcondition might not hold on this return path.
-Predicates.dfy[Q1](153,15): Related location: This is the postcondition that might not hold.
+Predicates.dfy[Q1](154,4): Error BP5003: A postcondition might not hold on this return path.
+Predicates.dfy[Q1](153,14): Related location: This is the postcondition that might not hold.
+Predicates.dfy[Q1](153,45): Related location
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/Protected.dfy.expect b/Test/dafny0/Protected.dfy.expect
index d50f2dd5..6796e847 100644
--- a/Test/dafny0/Protected.dfy.expect
+++ b/Test/dafny0/Protected.dfy.expect
@@ -1,20 +1,20 @@
-Protected.dfy(17,20): Error: assertion violation
+Protected.dfy(17,19): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon9_Then
-Protected.dfy(31,18): Error: assertion violation
+Protected.dfy(31,17): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon12_Then
(0,0): anon6
(0,0): anon13_Else
-Protected.dfy(35,16): Error: assertion violation
+Protected.dfy(35,15): Error: assertion violation
Execution trace:
(0,0): anon0
-Protected.dfy(48,20): Error: assertion violation
+Protected.dfy(48,19): Error: assertion violation
Execution trace:
(0,0): anon0
-Protected.dfy(55,20): Error: assertion violation
+Protected.dfy(55,19): Error: assertion violation
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/RangeCompilation.dfy b/Test/dafny0/RangeCompilation.dfy
new file mode 100644
index 00000000..de8ca68e
--- /dev/null
+++ b/Test/dafny0/RangeCompilation.dfy
@@ -0,0 +1,25 @@
+// RUN: %dafny /compile:3 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+newtype Byte = x | 0 <= x < 256
+predicate method GoodByte(b: Byte) {
+ b % 3 == 2
+}
+predicate method GoodInteger(i: int) {
+ i % 5 == 4
+}
+
+method Main() {
+ assert GoodByte(11) && GoodInteger(24);
+ var b: Byte :| GoodByte(b);
+ var i: int :| 0 <= i < 256 && GoodInteger(i);
+ print "b=", b, " i=", i, "\n";
+ var m0 := new MyClass;
+ var m17 := new M17.AnotherClass;
+}
+
+class MyClass { }
+
+module M17 {
+ class AnotherClass { }
+}
diff --git a/Test/dafny0/RangeCompilation.dfy.expect b/Test/dafny0/RangeCompilation.dfy.expect
new file mode 100644
index 00000000..c3275d12
--- /dev/null
+++ b/Test/dafny0/RangeCompilation.dfy.expect
@@ -0,0 +1,6 @@
+
+Dafny program verifier finished with 5 verified, 0 errors
+Program compiled successfully
+Running...
+
+b=2 i=4
diff --git a/Test/dafny0/RankNeg.dfy.expect b/Test/dafny0/RankNeg.dfy.expect
index d740f8a0..33cd4f1e 100644
--- a/Test/dafny0/RankNeg.dfy.expect
+++ b/Test/dafny0/RankNeg.dfy.expect
@@ -1,22 +1,22 @@
-RankNeg.dfy(10,26): Error: cannot prove termination; try supplying a decreases clause
+RankNeg.dfy(10,25): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
- (0,0): anon6_Then
-RankNeg.dfy(15,28): Error: cannot prove termination; try supplying a decreases clause
+ (0,0): anon7_Else
+ (0,0): anon8_Then
+RankNeg.dfy(15,27): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
- (0,0): anon6_Then
-RankNeg.dfy(22,31): Error: cannot prove termination; try supplying a decreases clause
+ (0,0): anon7_Else
+ (0,0): anon8_Then
+RankNeg.dfy(22,30): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
- (0,0): anon6_Then
-RankNeg.dfy(32,25): Error: cannot prove termination; try supplying a decreases clause
+ (0,0): anon7_Else
+ (0,0): anon8_Then
+RankNeg.dfy(32,24): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
- (0,0): anon6_Then
+ (0,0): anon7_Else
+ (0,0): anon8_Then
Dafny program verifier finished with 1 verified, 4 errors
diff --git a/Test/dafny0/Reads.dfy b/Test/dafny0/Reads.dfy
index 645494cb..6dedbada 100644
--- a/Test/dafny0/Reads.dfy
+++ b/Test/dafny0/Reads.dfy
@@ -55,3 +55,84 @@ function ok5(r : R):()
reads if r != null then {r, r.r} else {};
{()}
+// Reads checking where there are circularities among the expressions
+
+class CircularChecking {
+ ghost var Repr: set<object>
+
+ function F(): int
+ reads this, Repr
+
+ function F'(): int
+ reads Repr, this // this is also fine
+
+ function G0(): int
+ reads this
+ requires Repr == {} && F() == 100
+
+ function G1(): int
+ reads this
+ requires F() == 100 // fine, since the next line tells us that Repr is empty
+ requires Repr == {}
+
+ function H0(cell: Cell): int
+ reads Repr // by itself, this reads is not self-framing
+ requires this in Repr // lo and behold! So, reads clause is fine after all
+
+ function H1(cell: Cell): int
+ reads this, Repr
+ requires cell in Repr
+ requires cell != null && cell.data == 10
+
+ function H2(cell: Cell): int
+ reads this, Repr
+ requires cell != null && cell.data == 10 // this is okay, too, since reads checks are postponed
+ requires cell in Repr
+}
+
+class Cell { var data: int }
+
+// Test the benefits of the new reads checking for function checking
+
+function ApplyToSet<X>(S: set<X>, f: X -> X): set<X>
+ requires forall x :: x in S ==> f.reads(x) == {} && f.requires(x)
+{
+ if S == {} then {} else
+ var x :| x in S;
+ ApplyToSet(S - {x}, f) + {f(x)}
+}
+
+function ApplyToSet_AltSignature0<X>(S: set<X>, f: X -> X): set<X>
+ requires forall x :: x in S ==> f.requires(x) && f.reads(x) == {}
+
+function ApplyToSet_AltSignature1<X>(S: set<X>, f: X -> X): set<X>
+ requires forall x :: x in S ==> f.reads(x) == {}
+ requires forall x :: x in S ==> f.requires(x)
+
+function ApplyToSet_AltSignature2<X>(S: set<X>, f: X -> X): set<X>
+ requires (forall x :: x in S ==> f.reads(x) == {}) ==> forall x :: x in S ==> f.requires(x)
+ // (this precondition would not be good enough to check the body above)
+
+function FunctionInQuantifier0(): int
+ requires exists f: int -> int :: f(10) == 100 // error (x2): precondition violation and insufficient reads
+
+function FunctionInQuantifier1(): int
+ requires exists f: int -> int :: f.requires(10) && f(10) == 100 // error: insufficient reads
+
+function FunctionInQuantifier2(): int
+ requires exists f: int -> int :: f.reads(10) == {} && f.requires(10) && f(10) == 100
+ ensures FunctionInQuantifier2() == 100
+{
+ var f: int -> int :| f.reads(10) == {} && f.requires(10) && f(10) == 100; // fine :) :)
+ f(10)
+}
+
+class DynamicFramesIdiom {
+ ghost var Repr: set<object>
+ predicate IllFormed_Valid()
+ reads Repr // error: reads is not self framing (notice the absence of "this")
+ {
+ this in Repr // this says that the predicate returns true if "this in Repr", but the
+ // predicate can also be invoked in a state where its body will evaluate to false
+ }
+}
diff --git a/Test/dafny0/Reads.dfy.expect b/Test/dafny0/Reads.dfy.expect
index 090cf99d..0ef90aec 100644
--- a/Test/dafny0/Reads.dfy.expect
+++ b/Test/dafny0/Reads.dfy.expect
@@ -1,26 +1,33 @@
-Reads.dfy(9,30): Error: insufficient reads clause to read field
+Reads.dfy(133,10): Error: insufficient reads clause to read field
Execution trace:
(0,0): anon0
- (0,0): anon5_Then
-Reads.dfy(18,30): Error: insufficient reads clause to read field
+Reads.dfy(9,29): Error: insufficient reads clause to read field
Execution trace:
(0,0): anon0
- (0,0): anon11_Then
- (0,0): anon12_Then
-Reads.dfy(28,50): Error: insufficient reads clause to read field
+Reads.dfy(18,29): Error: insufficient reads clause to read field
Execution trace:
(0,0): anon0
- (0,0): anon7_Then
- (0,0): anon8_Then
-Reads.dfy(37,43): Error: insufficient reads clause to read field
+Reads.dfy(28,49): Error: insufficient reads clause to read field
+Execution trace:
+ (0,0): anon0
+Reads.dfy(37,42): Error: insufficient reads clause to read field
Execution trace:
(0,0): anon0
(0,0): anon7_Then
- (0,0): anon8_Then
-Reads.dfy(51,30): Error: insufficient reads clause to read field
+ (0,0): anon4
+Reads.dfy(51,29): Error: insufficient reads clause to read field
+Execution trace:
+ (0,0): anon0
+ (0,0): anon10_Then
+ (0,0): anon4
+Reads.dfy(117,35): Error: insufficient reads clause to invoke function
+Execution trace:
+ (0,0): anon0
+Reads.dfy(117,35): Error: possible violation of function precondition
+Execution trace:
+ (0,0): anon0
+Reads.dfy(120,37): Error: insufficient reads clause to invoke function
Execution trace:
(0,0): anon0
- (0,0): anon9_Then
- (0,0): anon3
-Dafny program verifier finished with 5 verified, 5 errors
+Dafny program verifier finished with 17 verified, 9 errors
diff --git a/Test/dafny0/RealCompare.dfy.expect b/Test/dafny0/RealCompare.dfy.expect
index 5b25fa25..48524bdf 100644
--- a/Test/dafny0/RealCompare.dfy.expect
+++ b/Test/dafny0/RealCompare.dfy.expect
@@ -1,19 +1,19 @@
-RealCompare.dfy(35,6): Error: failure to decrease termination measure
+RealCompare.dfy(35,5): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-RealCompare.dfy(50,4): Error: decreases expression must be bounded below by 0.0
-RealCompare.dfy(48,13): Related location
+RealCompare.dfy(50,3): Error: decreases expression must be bounded below by 0.0
+RealCompare.dfy(48,12): Related location
Execution trace:
(0,0): anon0
-RealCompare.dfy(141,12): Error: assertion violation
+RealCompare.dfy(141,11): Error: assertion violation
Execution trace:
(0,0): anon0
RealCompare.dfy(133,3): anon7_LoopHead
(0,0): anon7_LoopBody
RealCompare.dfy(133,3): anon8_Else
(0,0): anon9_Then
-RealCompare.dfy(156,12): Error: assertion violation
+RealCompare.dfy(156,11): Error: assertion violation
Execution trace:
(0,0): anon0
RealCompare.dfy(147,3): anon9_LoopHead
diff --git a/Test/dafny0/RealTypes.dfy.expect b/Test/dafny0/RealTypes.dfy.expect
index 0d132948..0fce4634 100644
--- a/Test/dafny0/RealTypes.dfy.expect
+++ b/Test/dafny0/RealTypes.dfy.expect
@@ -1,22 +1,22 @@
-RealTypes.dfy(12,16): Error: the real-based number must be an integer (if you want truncation, apply .Trunc to the real-based number)
+RealTypes.dfy(12,15): Error: the real-based number must be an integer (if you want truncation, apply .Trunc to the real-based number)
Execution trace:
(0,0): anon0
(0,0): anon6_Then
-RealTypes.dfy(14,28): Error: assertion violation
+RealTypes.dfy(14,27): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon7_Then
-RealTypes.dfy(21,12): Error: possible division by zero
+RealTypes.dfy(21,11): Error: possible division by zero
Execution trace:
(0,0): anon0
RealTypes.dfy(20,23): anon3_Else
(0,0): anon2
-RealTypes.dfy(21,20): Error: assertion violation
+RealTypes.dfy(21,19): Error: assertion violation
Execution trace:
(0,0): anon0
RealTypes.dfy(20,23): anon3_Else
(0,0): anon2
-RealTypes.dfy(29,12): Error: assertion violation
+RealTypes.dfy(29,11): Error: assertion violation
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/Refinement.dfy.expect b/Test/dafny0/Refinement.dfy.expect
index 93d59873..339c86b4 100644
--- a/Test/dafny0/Refinement.dfy.expect
+++ b/Test/dafny0/Refinement.dfy.expect
@@ -1,40 +1,40 @@
-Refinement.dfy(15,5): Error BP5003: A postcondition might not hold on this return path.
-Refinement.dfy(14,17): Related location: This is the postcondition that might not hold.
+Refinement.dfy(15,4): Error BP5003: A postcondition might not hold on this return path.
+Refinement.dfy(14,16): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-Refinement.dfy[B](15,5): Error BP5003: A postcondition might not hold on this return path.
-Refinement.dfy(33,20): Related location: This is the postcondition that might not hold.
+Refinement.dfy[B](15,4): Error BP5003: A postcondition might not hold on this return path.
+Refinement.dfy(33,19): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-Refinement.dfy(69,16): Error: assertion violation
+Refinement.dfy(69,15): Error: assertion violation
Execution trace:
(0,0): anon0
-Refinement.dfy(80,17): Error: assertion violation
+Refinement.dfy(80,16): Error: assertion violation
Execution trace:
(0,0): anon0
-Refinement.dfy(99,12): Error BP5003: A postcondition might not hold on this return path.
-Refinement.dfy(78,15): Related location: This is the postcondition that might not hold.
+Refinement.dfy(99,11): Error BP5003: A postcondition might not hold on this return path.
+Refinement.dfy(78,14): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-Refinement.dfy(102,3): Error BP5003: A postcondition might not hold on this return path.
-Refinement.dfy(83,15): Related location: This is the postcondition that might not hold.
+ (0,0): anon4_Else
+Refinement.dfy(102,2): Error BP5003: A postcondition might not hold on this return path.
+Refinement.dfy(83,14): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-Refinement.dfy(189,5): Error BP5003: A postcondition might not hold on this return path.
-Refinement.dfy[IncorrectConcrete](121,15): Related location: This is the postcondition that might not hold.
-Refinement.dfy(186,9): Related location
+Refinement.dfy(189,4): Error BP5003: A postcondition might not hold on this return path.
+Refinement.dfy[IncorrectConcrete](121,14): Related location: This is the postcondition that might not hold.
+Refinement.dfy(186,8): Related location
Execution trace:
(0,0): anon0
-Refinement.dfy(193,5): Error BP5003: A postcondition might not hold on this return path.
-Refinement.dfy[IncorrectConcrete](129,15): Related location: This is the postcondition that might not hold.
-Refinement.dfy(186,9): Related location
+Refinement.dfy(193,4): Error BP5003: A postcondition might not hold on this return path.
+Refinement.dfy[IncorrectConcrete](129,14): Related location: This is the postcondition that might not hold.
+Refinement.dfy(186,8): Related location
Execution trace:
(0,0): anon0
(0,0): anon4_Then
(0,0): anon3
-Refinement.dfy(199,7): Error: assertion violation
-Refinement.dfy[IncorrectConcrete](137,24): Related location
+Refinement.dfy(199,6): Error: assertion violation
+Refinement.dfy[IncorrectConcrete](137,23): Related location
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/RefinementErrors.dfy b/Test/dafny0/RefinementErrors.dfy
index 121b33aa..8d60a8e4 100644
--- a/Test/dafny0/RefinementErrors.dfy
+++ b/Test/dafny0/RefinementErrors.dfy
@@ -59,3 +59,40 @@ module BB refines B {
{ 10 }
}
}
+
+module Forall0 {
+ class C {
+ var a: int
+ method M()
+ modifies this
+ {
+ }
+ lemma Lemma(x: int)
+ {
+ }
+ }
+}
+module Forall1 refines Forall0 {
+ class C {
+ var b: int
+ method M...
+ {
+ forall x { Lemma(x); } // allowed
+ var s := {4};
+ forall x | x in s ensures x == 4 { } // allowed
+ forall x { // allowed
+ calc {
+ x in s;
+ ==
+ x == 4;
+ }
+ }
+ forall c | c in {this} {
+ c.b := 17; // allowed
+ }
+ forall c | c in {this} {
+ c.a := 17; // error: not allowed to update previously defined field
+ }
+ }
+ }
+}
diff --git a/Test/dafny0/RefinementErrors.dfy.expect b/Test/dafny0/RefinementErrors.dfy.expect
index 40cdb081..bac6612d 100644
--- a/Test/dafny0/RefinementErrors.dfy.expect
+++ b/Test/dafny0/RefinementErrors.dfy.expect
@@ -9,4 +9,5 @@ RefinementErrors.dfy(38,13): Error: type parameters are not allowed to be rename
RefinementErrors.dfy(39,23): Error: the type of parameter 'z' is different from the type of the same parameter in the corresponding function in the module it refines ('seq<C>' instead of 'set<C>')
RefinementErrors.dfy(40,9): Error: there is a difference in name of parameter 3 ('k' versus 'b') of function F compared to corresponding function in the module it refines
RefinementErrors.dfy(57,20): Error: a function can be changed into a function method in a refining module only if the function has not yet been given a body: G
-11 resolution/type errors detected in RefinementErrors.dfy
+RefinementErrors.dfy(94,10): Error: refinement method cannot assign to a field defined in parent module ('a')
+12 resolution/type errors detected in RefinementErrors.dfy
diff --git a/Test/dafny0/ResolutionErrors.dfy b/Test/dafny0/ResolutionErrors.dfy
index 761cffa0..8dceb6ba 100644
--- a/Test/dafny0/ResolutionErrors.dfy
+++ b/Test/dafny0/ResolutionErrors.dfy
@@ -9,9 +9,9 @@ method GhostDivergentLoop()
a[1] := -1;
ghost var i := 0;
while (i < 2)
- decreases *; // error: not allowed on a ghost loop
- invariant i <= 2;
- invariant (forall j :: 0 <= j && j < i ==> a[j] > 0);
+ decreases * // error: not allowed on a ghost loop
+ invariant i <= 2
+ invariant (forall j :: 0 <= j && j < i ==> a[j] > 0)
{
i := 0;
}
@@ -91,9 +91,9 @@ class EE {
var b3 := Benny;
var d0 := David(20); // error: constructor name David is ambiguous
var d1 := David; // error: constructor name David is ambiguous (never mind that the signature does
- // not match either of them)
+ // not match either of them)
var d2 := David(20, 40); // error: constructor name Davis is ambiguous (never mind that the given
- // parameters match the signature of only one of those constructors)
+ // parameters match the signature of only one of those constructors)
var d3 := Abc.David(20, 40); // error: wrong number of parameters
var d4 := Rst.David(20, 40);
var e := Eleanor; // this resolves to the field, not the Abc datatype constructor
@@ -102,7 +102,7 @@ class EE {
}
// --------------- ghost tests -------------------------------------
-
+module HereAreMoreGhostTests {
datatype GhostDt =
Nil(ghost extraInfo: int) |
Cons(data: int, tail: GhostDt, ghost moreInfo: int)
@@ -150,14 +150,6 @@ class GhostTests {
r := r + g; // fine, for the same reason
r := N(20, 20); // error: call to non-ghost method from ghost method is not okay
}
- ghost method NiceTry()
- ensures false;
- {
- while (true)
- decreases *; // error: not allowed in ghost context
- {
- }
- }
ghost method BreaksAreFineHere(t: int)
{
var n := 0;
@@ -195,6 +187,57 @@ class GhostTests {
decreases 112 - n;
{
label MyStructure: {
+ k := k + 1;
+ }
+ label MyOtherStructure:
+ if (k % 17 == 0) {
+ break MyOtherStructure; // this break is fine
+ } else {
+ k := k + 1;
+ }
+
+ var dontKnow;
+ if (n == 112) {
+ ghost var m := 0;
+ label LoopLabel0:
+ label LoopLabel1:
+ while (m < 200) {
+ if (m % 103 == 0) {
+ if {
+ case true => break; // fine, since this breaks out of the enclosing ghost loop
+ case true => break LoopLabel0; // fine
+ case true => break LoopLabel1; // fine
+ }
+ } else if (m % 101 == 0) {
+ }
+ m := m + 3;
+ }
+ break;
+ } else if (dontKnow == 708) {
+ var q := 0;
+ while (q < 1) {
+ label IfNest:
+ if (p == 67) {
+ break break; // fine, since this is not a ghost context
+ }
+ q := q + 1;
+ }
+ } else if (n == t) {
+ }
+ n := n + 1;
+ p := p + 1;
+ }
+ }
+ method BreakMayNotBeFineHere_Ghost(ghost t: int)
+ {
+ var n := 0;
+ ghost var k := 0;
+ var p := 0;
+ while (true)
+ invariant n <= 112;
+ decreases 112 - n;
+ {
+ label MyStructure: {
if (k % 17 == 0) { break MyStructure; } // error: break from ghost to non-ghost point
k := k + 1;
}
@@ -230,8 +273,6 @@ class GhostTests {
if (p == 67) {
break break; // fine, since this is not a ghost context
} else if (*) {
- break break break; // error: tries to break out of more loop levels than there are
- } else if (*) {
break break; // fine, since this is not a ghost context
} else if (k == 67) {
break break; // error, because this is a ghost context
@@ -246,7 +287,7 @@ class GhostTests {
}
}
}
-
+} //HereAreMoreGhostTests
method DuplicateLabels(n: int) {
var x;
if (n < 7) {
@@ -310,18 +351,17 @@ method DatatypeDestructors(d: DTD_List) {
assert d.DTD_Cons? == d.Car; // type error
assert d == DTD_Cons(hd, tl, 5);
ghost var g0 := d.g; // fine
- var g1 := d.g; // error: cannot use ghost member in non-ghost code
}
}
// ------------------- print statements ---------------------------------------
-
+module GhostPrintAttempts {
method PrintOnlyNonGhosts(a: int, ghost b: int)
{
print "a: ", a, "\n";
print "b: ", b, "\n"; // error: print statement cannot take ghosts
}
-
+}
// ------------------- auto-added type arguments ------------------------------
class GenericClass<T> { var data: T; }
@@ -381,48 +421,45 @@ method TestCalc(m: int, n: int, a: bool, b: bool)
n + m + 1;
==> n + m + 2; // error: ==> operator requires boolean lines
}
- calc {
- n + m;
- { print n + m; } // error: non-ghost statements are not allowed in hints
- m + n;
- }
}
-class SideEffectChecks {
- ghost var ycalc: int;
+module MyOwnModule {
+ class SideEffectChecks {
+ ghost var ycalc: int;
- ghost method Mod(a: int)
- modifies this;
- ensures ycalc == a;
- {
- ycalc := a;
- }
+ ghost method Mod(a: int)
+ modifies this;
+ ensures ycalc == a;
+ {
+ ycalc := a;
+ }
- ghost method Bad()
- modifies this;
- ensures 0 == 1;
- {
- var x: int;
- calc {
- 0;
- { Mod(0); } // methods with side-effects are not allowed
- ycalc;
- { ycalc := 1; } // heap updates are not allowed
- 1;
- { x := 1; } // updates to locals defined outside of the hint are not allowed
- x;
- {
- var x: int;
- x := 1; // this is OK
+ ghost method Bad()
+ modifies this;
+ ensures 0 == 1;
+ {
+ var x: int;
+ calc {
+ 0;
+ { Mod(0); } // error: methods with side-effects are not allowed
+ ycalc;
+ { ycalc := 1; } // error: heap updates are not allowed
+ 1;
+ { x := 1; } // error: updates to locals defined outside of the hint are not allowed
+ x;
+ {
+ var x: int;
+ x := 1; // this is OK
+ }
+ 1;
}
- 1;
}
}
}
-
+
// ------------------- nameless constructors ------------------------------
-class YHWH {
+class Y {
var data: int;
constructor (x: int)
modifies this;
@@ -433,22 +470,22 @@ class YHWH {
{
}
method Test() {
- var IAmWhoIAm := new YHWH(5);
- IAmWhoIAm := new YHWH._ctor(7); // but, in fact, it is also possible to use the underlying name
- IAmWhoIAm := new YHWH; // error: the class has a constructor, so one must be used
- var s := new Lucifer.Init(5);
- s := new Lucifer.FromArray(null);
- s := new Lucifer(false);
- s := new Lucifer._ctor(false);
- s := new Lucifer.M(); // error: there is a constructor, so one must be called
- s := new Lucifer; // error: there is a constructor, so one must be called
+ var i := new Y(5);
+ i := new Y._ctor(7); // but, in fact, it is also possible to use the underlying name
+ i := new Y; // error: the class has a constructor, so one must be used
+ var s := new Luci.Init(5);
+ s := new Luci.FromArray(null);
+ s := new Luci(false);
+ s := new Luci._ctor(false);
+ s := new Luci.M(); // error: there is a constructor, so one must be called
+ s := new Luci; // error: there is a constructor, so one must be called
var l := new Lamb;
l := new Lamb(); // error: there is no default constructor
l := new Lamb.Gwen();
}
}
-class Lucifer {
+class Luci {
constructor Init(y: int) { }
constructor (nameless: bool) { }
constructor FromArray(a: array<int>) { }
@@ -456,7 +493,7 @@ class Lucifer {
}
class Lamb {
- method Jesus() { }
+ method Jess() { }
method Gwen() { }
}
@@ -467,14 +504,10 @@ method AssignSuchThatFromGhost()
var x: int;
ghost var g: int;
- x := g; // error: ghost cannot flow into non-ghost
-
x := *;
assume x == g; // this mix of ghosts and non-ghosts is cool (but, of course,
// the compiler will complain)
- x :| x == g; // error: left-side has non-ghost, so RHS must be non-ghost as well
-
x :| assume x == g; // this is cool, since it's an assume (but, of course, the
// compiler will complain)
@@ -543,8 +576,6 @@ method LetSuchThat(ghost z: int, n: nat)
var x: int;
x := var y :| y < 0; y; // fine for the resolver (but would give a verification error for not being deterministic)
- x := var y :| y < z; y; // error: contraint depend on ghost (z)
-
x := var w :| w == 2*w; w; // fine (even for the verifier, this one)
x := var w := 2*w; w; // error: the 'w' in the RHS of the assignment is not in scope
ghost var xg := var w :| w == 2*w; w;
@@ -555,16 +586,16 @@ method LetSuchThat(ghost z: int, n: nat)
module NonInferredType {
predicate P<T>(x: T)
- method NonInferredType0(x: int)
+ method InferredType(x: int)
{
var t;
- assume forall z :: P(z) && z == t; // It would be nice to allow the following example, but the implementation calls DiscoverBounds before CheckInference for quantifiers.
+ assume forall z :: P(z) && z == t;
assume t == x; // this statement determines the type of t and z
}
- method NonInferredType1(x: int)
+ method NonInferredType(x: int)
{
- var t;
+ var t; // error: the type of t is not determined
assume forall z :: P(z) && z == t; // error: the type of z is not determined
}
}
@@ -582,20 +613,7 @@ module GhostAllocationTests {
p := new G; // error: ditto
}
- method GhostNew1(n: nat)
- {
- var a := new G[n];
- forall i | 0 <= i < n {
- a[i] := new G; // error: 'new' is currently not supported in forall statements
- }
- forall i | 0 <= i < n
- ensures true; // this makes the whole 'forall' statement into a ghost statement
- {
- a[i] := new G; // error: 'new' not allowed in ghost contexts, and proof-forall cannot update state
- }
- }
-
- method GhostNew2(n: nat, ghost g: int) returns (t: G, z: int)
+ method GhostNew1(n: nat, ghost g: int) returns (t: G, z: int)
{
if n < 0 {
z, t := 5, new G; // fine
@@ -605,33 +623,45 @@ module GhostAllocationTests {
}
}
- method GhostNew3(ghost b: bool)
+ method GhostNew2(ghost b: bool)
{
if (b) {
var y := new GIter(); // error: 'new' not allowed in ghost contexts (and a non-ghost method is not allowed to be called here either)
}
}
- method GhostNew4(n: nat)
+ method GhostNew3(n: nat)
{
var g := new G;
calc {
5;
{ var y := new G; } // error: 'new' not allowed in ghost contexts
2 + 3;
- { if n != 0 { GhostNew4(n-1); } } // error: cannot call non-ghost method in a ghost context
- 1 + 4;
- { GhostNew5(g); } // error: cannot call method with nonempty modifies
- -5 + 10;
}
}
- ghost method GhostNew5(g: G)
+ ghost method GhostNew4(g: G)
modifies g;
{
}
}
+module NewForall {
+ class G { }
+ method NewForallTest(n: nat)
+ {
+ var a := new G[n];
+ forall i | 0 <= i < n {
+ a[i] := new G; // error: 'new' is currently not supported in forall statements
+ }
+ forall i | 0 <= i < n
+ ensures true; // this makes the whole 'forall' statement into a ghost statement
+ {
+ a[i] := new G; // error: 'new' not allowed in ghost contexts, and proof-forall cannot update state
+ }
+ }
+}
+
// ------------------------- underspecified types ------------------------------
module UnderspecifiedTypes {
@@ -672,46 +702,31 @@ module StatementsInExpressions {
{
}
- ghost method M()
- modifies this;
- {
- calc {
- 5;
- { SideEffect(); } // error: cannot call method with side effects
- 5;
- }
- }
-
function F(): int
{
calc {
- 6;
- { assert 6 < 8; }
- { NonGhostMethod(); } // error: cannot call non-ghost method
- { var x := 8;
- while x != 0
- decreases *; // error: cannot use 'decreases *' in a ghost context
- {
- x := x - 1;
- }
- }
- { var x := 8;
- while x != 0
- {
- x := x - 1;
- }
- }
- { MyField := 12; } // error: cannot assign to a field
- { MyGhostField := 12; } // error: cannot assign to any field
- { SideEffect(); } // error: cannot call (ghost) method with a modifies clause
- { var x := 8;
- while x != 0
- modifies this; // error: cannot use a modifies clause on a loop
- {
- x := x - 1;
- }
- }
- 6;
+ 6;
+ { assert 6 < 8; }
+ { var x := 8;
+ while x != 0
+ decreases * // error: cannot use 'decreases *' here
+ {
+ x := x - 1;
+ }
+ }
+ { var x := 8;
+ while x != 0
+ {
+ x := x - 1;
+ }
+ }
+ { var x := 8;
+ while x != 0
+ {
+ x := x - 1;
+ }
+ }
+ 6;
}
5
}
@@ -723,33 +738,28 @@ module StatementsInExpressions {
{
var y :=
calc {
- 6;
- { assert 6 < 8; }
- { NonGhostMethod(); } // error: cannot call non-ghost method
- { var x := 8;
- while x != 0
- decreases *; // error: cannot use 'decreases *' in a ghost context
- {
- x := x - 1;
- }
- }
- { MyField := 12; } // error: cannot assign to a field
- { MyGhostField := 12; } // error: cannot assign to any field
- { M(); } // error: cannot call (ghost) method with a modifies clause
- { var x := 8;
- while x != 0
- modifies this; // error: cannot use a modifies clause on a loop
- {
- x := x - 1;
- }
- }
- { var x := 8;
- while x != 0
- {
- x := x - 1;
- }
- }
- 6;
+ 6;
+ { assert 6 < 8; }
+ { var x := 8;
+ while x != 0
+ decreases * // error: cannot use 'decreases *' here
+ {
+ x := x - 1;
+ }
+ }
+ { var x := 8;
+ while x != 0
+ {
+ x := x - 1;
+ }
+ }
+ { var x := 8;
+ while x != 0
+ {
+ x := x - 1;
+ }
+ }
+ 6;
}
5;
}
@@ -764,7 +774,6 @@ module StatementsInExpressions {
{
MyLemma();
MyGhostMethod(); // error: modifi2es state
- OrdinaryMethod(); // error: not a ghost
OutParamMethod(); // error: has out-parameters
10
}
@@ -846,40 +855,48 @@ class ModifyStatementClass {
ghost method G0()
modifies `g;
modifies `x; // error: non-ghost field mentioned in ghost context
- {
- modify `g;
- modify `x; // error: non-ghost field mentioned in ghost context
- }
- method G1()
- modifies this;
- {
- modify `x;
- if g < 100 {
- // we are now in a ghost context
+}
+module ModifyStatementClass_More {
+ class C {
+ var x: int;
+ ghost var g: int;
+ ghost method G0()
+ modifies `g;
+ {
+ modify `g;
modify `x; // error: non-ghost field mentioned in ghost context
}
- }
- method G2(y: nat)
- modifies this;
- {
- if g < 100 {
- // we're now in a ghost context
- var n := 0;
- while n < y
- modifies `x; // error: non-ghost field mentioned in ghost context
- {
- if * {
- g := g + 1; // if we got as far as verification, this would be flagged as an error too
- }
- n := n + 1;
+ method G1()
+ modifies this;
+ {
+ modify `x;
+ if g < 100 {
+ // we are now in a ghost context
+ modify `x; // error: non-ghost field mentioned in ghost context
}
}
- modify `x; // fine
- ghost var i := 0;
- while i < y
- modifies `x; // error: non-ghost field mentioned in ghost context
+ method G2(y: nat)
+ modifies this;
{
- i := i + 1;
+ if g < 100 {
+ // we're now in a ghost context
+ var n := 0;
+ while n < y
+ modifies `x; // error: non-ghost field mentioned in ghost context
+ {
+ if * {
+ g := g + 1; // if we got as far as verification, this would be flagged as an error too
+ }
+ n := n + 1;
+ }
+ }
+ modify `x; // fine
+ ghost var i := 0;
+ while i < y
+ modifies `x; // error: non-ghost field mentioned in ghost context
+ {
+ i := i + 1;
+ }
}
}
}
@@ -1109,15 +1126,15 @@ method TraitSynonym()
// ----- set comprehensions where the term type is finite -----
module ObjectSetComprehensions {
- // allowed in non-ghost context:
- function A() : set<object> { set o : object | true :: o }
+ // the following set comprehensions are known to be finite
+ function A() : set<object> { set o : object | true :: o } // error: a function is not allowed to depend on the allocated state
- lemma B() { var x := set o : object | true :: o; }
+ function method B() : set<object> { set o : object | true :: o } // error: a function is not allowed to depend on the allocated state
- // not allowed in non-ghost context:
- function method C() : set<object> { set o : object | true :: o }
+ // outside functions, the comprehension is permitted, but it cannot be compiled
+ lemma C() { var x := set o : object | true :: o; }
- method D() { var x := set o : object | true :: o; }
+ method D() { var x := set o : object | true :: o; } // error: not (easily) compilable
}
// ------ regression test for type checking of integer division -----
@@ -1211,9 +1228,9 @@ module NonInferredTypeVariables {
method BadClient(n: nat)
{
var p := P(n); // error: cannot infer the type argument for P
- ghost var q := Q(n); // error: cannot infer the type argument for Q
+ ghost var q := Q(n); // error: cannot infer the type argument for Q (and thus q's type cannot be determined either)
M(n); // error: cannot infer the type argument for M
- var x := N(n); // error: cannot infer the type argument for N
+ var x := N(n); // error: cannot infer the type argument for N (and thus x's type cannot be determined either)
var a := new array; // error: cannot infer the type argument for 'array'
var c := new C; // error: cannot infer the type argument for 'C'
var s: set; // type argument for 'set'
@@ -1231,7 +1248,7 @@ module NonInferredTypeVariables {
ghost var d0 := forall s :: s == {7} ==> s != {};
var d1 := forall s: set :: s in S ==> s == {};
var ggcc0: C;
- var ggcc1: C;
+ var ggcc1: C; // error: full type cannot be determined
ghost var d2 := forall c: C :: c != null ==> c.f == 10;
ghost var d2' := forall c :: c == ggcc0 && c != null ==> c.f == 10;
ghost var d2'' := forall c :: c == ggcc1 && c != null ==> c.f == c.f; // error: here, type of c is not determined
@@ -1253,14 +1270,14 @@ module SignatureCompletion {
datatype Dt = Ctor(X -> Dt) // error: X is not a declared type
datatype Et<Y> = Ctor(X -> Et, Y) // error: X is not a declared type
- // For methods and functions, signatures can auto-declare type parameters
- method My0(s: set, x: A -> B)
- method My1(x: A -> B, s: set)
+
+ method My0<A,B>(s: set, x: A -> B)
+ method My1<A,B>(x: A -> B, s: set)
method My2<A,B>(s: set, x: A -> B)
method My3<A,B>(x: A -> B, s: set)
- function F0(s: set, x: A -> B): int
- function F1(x: A -> B, s: set): int
+ function F0<A,B>(s: set, x: A -> B): int
+ function F1<A,B>(x: A -> B, s: set): int
function F2<A,B>(s: set, x: A -> B): int
function F3<A,B>(x: A -> B, s: set): int
}
@@ -1292,7 +1309,7 @@ module FrameTargetFields {
modifies `z // cool
{
}
-
+} } module FrameTargetFields_More { class C { var x: int var y: int ghost var z: int
method P()
modifies this
{
@@ -1349,3 +1366,349 @@ module TupleEqualitySupport {
datatype GoodRecord = GoodRecord(set<(int,int)>)
datatype BadRecord = BadRecord(set<(int, int->bool)>) // error: this tuple type does not support equality
}
+
+// ------------------- non-type variable names -------------------
+
+module NonTypeVariableNames {
+ type X = int
+
+ module Y { }
+
+ method M(m: map<real,string>)
+ {
+ assert X == X; // error (x2): type name used as variable
+ assert Y == Y; // error (x2): module name used as variable
+ assert X in m; // error (x2): type name used as variable
+ assert Y in m; // error (x2): module name used as variable
+ }
+
+ method N(k: int)
+ {
+ assert k == X; // error (x2): type name used as variable
+ assert k == Y; // error (x2): module name used as variable
+ X := k; // error: type name used as variable
+ Y := k; // error: module name used as variable
+ }
+}
+
+// ------------------- assign-such-that and let-such-that -------------------
+
+module SuchThat {
+ method M() {
+ var x: int;
+ x :| 5 + 7; // error: constraint should be boolean
+ x :| x; // error: constraint should be boolean
+ var y :| 4; // error: constraint should be boolean
+ }
+ function F(): int {
+ var w :| 6 + 8; // error: constraint should be boolean
+ w
+ }
+}
+
+// ---------------------- NEW STUFF ----------------------------------------
+
+module GhostTests {
+ class G { }
+
+ method GhostNew3(n: nat)
+ {
+ var g := new G;
+ calc {
+ 5;
+ 2 + 3;
+ { if n != 0 { GhostNew3(n-1); } } // error: cannot call non-ghost method in a ghost context
+ 1 + 4;
+ { GhostNew4(g); } // error: cannot call method with nonempty modifies
+ -5 + 10;
+ }
+ }
+
+ ghost method GhostNew4(g: G)
+ modifies g;
+ {
+ }
+
+ class MyClass {
+ ghost method SideEffect()
+ modifies this;
+ {
+ }
+
+ method NonGhostMethod()
+ {
+ }
+
+ ghost method M()
+ modifies this;
+ {
+ calc {
+ 5;
+ { SideEffect(); } // error: cannot call method with side effects
+ 5;
+ }
+ }
+ function F(): int
+ {
+ calc {
+ 6;
+ { assert 6 < 8; }
+ { NonGhostMethod(); } // error: cannot call non-ghost method
+ { var x := 8;
+ while x != 0
+ {
+ x := x - 1;
+ }
+ }
+ { var x := 8;
+ while x != 0
+ {
+ x := x - 1;
+ }
+ }
+ { MyField := 12; } // error: cannot assign to a field, and especially not a non-ghost field
+ { MyGhostField := 12; } // error: cannot assign to any field
+ { SideEffect(); } // error: cannot call (ghost) method with a modifies clause
+ { var x := 8;
+ while x != 0
+ modifies this; // error: cannot use a modifies clause on a loop
+ {
+ x := x - 1;
+ }
+ }
+ 6;
+ }
+ 5
+ }
+ var MyField: int;
+ ghost var MyGhostField: int;
+ method N()
+ {
+ var y :=
+ calc {
+ 6;
+ { assert 6 < 8; }
+ { NonGhostMethod(); } // error: cannot call non-ghost method
+ { var x := 8;
+ while x != 0
+ {
+ x := x - 1;
+ }
+ }
+ { MyField := 12; } // error: cannot assign to a field, and especially not a non-ghost field
+ { MyGhostField := 12; } // error: cannot assign to any field
+ { M(); } // error: cannot call (ghost) method with a modifies clause
+ { var x := 8;
+ while x != 0
+ modifies this; // error: cannot use a modifies clause on a loop
+ {
+ x := x - 1;
+ }
+ }
+ { var x := 8;
+ while x != 0
+ {
+ x := x - 1;
+ }
+ }
+ 6;
+ }
+ 5;
+ }
+ ghost method MyLemma()
+ ghost method MyGhostMethod()
+ modifies this;
+ method OrdinaryMethod()
+ ghost method OutParamMethod() returns (y: int)
+
+ function UseLemma(): int
+ {
+ MyLemma();
+ OrdinaryMethod(); // error: not a ghost
+ 10
+ }
+ }
+}
+
+module EvenMoreGhostTests {
+ ghost method NiceTry()
+ ensures false;
+ {
+ while (true)
+ decreases * // error: not allowed here
+ {
+ }
+ }
+ method BreakMayNotBeFineHere()
+ {
+ var n := 0;
+ var p := 0;
+ while (true)
+ {
+ var dontKnow;
+ if (n == 112) {
+ } else if (dontKnow == 708) {
+ while * {
+ label IfNest:
+ if (p == 67) {
+ break break; // fine, since this is not a ghost context
+ } else if (*) {
+ break break break; // error: tries to break out of more loop levels than there are
+ }
+ }
+ }
+ }
+ }
+}
+
+module BadGhostTransfer {
+ datatype DTD_List = DTD_Nil | DTD_Cons(Car: int, Cdr: DTD_List, ghost g: int)
+
+ method DatatypeDestructors_Ghost(d: DTD_List) {
+ var g1 := d.g; // error: cannot use ghost member in non-ghost code
+ }
+ method AssignSuchThatFromGhost()
+ {
+ var x: int;
+ ghost var g: int;
+
+ x := g; // error: ghost cannot flow into non-ghost
+
+ x := *;
+ assume x == g; // this mix of ghosts and non-ghosts is cool (but, of course,
+ // the compiler will complain)
+
+ x :| x == g; // error: left-side has non-ghost, so RHS must be non-ghost as well
+
+ x :| assume x == g; // this is cool, since it's an assume (but, of course, the
+ // compiler will complain)
+
+ x :| x == 5;
+ g :| g <= g;
+ g :| assume g < g; // the compiler will complain here, despite the LHS being
+ // ghost -- and rightly so, since an assume is used
+ }
+}
+
+module MoreGhostPrintAttempts {
+ method TestCalc_Ghost(m: int, n: int, a: bool, b: bool)
+ {
+ calc {
+ n + m;
+ { print n + m; } // error: non-ghost statements are not allowed in hints
+ m + n;
+ }
+ }
+}
+
+module MoreLetSuchThatExpr {
+ method LetSuchThat_Ghost(ghost z: int, n: nat)
+ {
+ var x := var y :| y < z; y; // error: contraint depend on ghost (z)
+ }
+}
+
+module UnderspecifiedTypedShouldBeResolvedOnlyOnce {
+ method CalcTest0(s: seq<int>) {
+ calc {
+ 2;
+ var t :| true; 2; // error: type of 't' is underspecified
+ }
+ }
+}
+
+module LoopResolutionTests {
+ class C {
+ var x: int
+ ghost var y: int
+ }
+
+ ghost method M(c: C)
+ requires c != null
+ modifies c
+ {
+ var n := 0;
+ while n < 100
+ modifies c`y
+ modifies c`x // error: not allowed to mention non-ghost field in modifies clause of ghost loops
+ {
+ c.x := c.x + 1; // error: assignment to non-ghost field not allowed here
+ }
+ }
+
+ method MM(c: C)
+ requires c != null
+ modifies c
+ {
+ var n := 0;
+ while
+ invariant n <= 100
+ modifies c // regression test
+ {
+ case n < 100 => n := n + 1;
+ }
+ }
+
+ method MMX(c: C, ghost g: int)
+ requires c != null
+ modifies c
+ {
+ var n := 0;
+ while
+ invariant n <= 100
+ modifies c`y
+ modifies c`x // error: not allowed to mention non-ghost field in modifies clause of ghost loops
+ {
+ case n < 100 => n := n + 1; // error: cannot assign to non-ghost in a ghost loop
+ case g < 56 && n != 100 => n := n + 1; // error: cannot assign to non-ghost in a ghost loop
+ }
+ }
+
+ method MD0(c: C, ghost g: nat)
+ requires c != null
+ modifies c
+ decreases *
+ {
+ var n := 0;
+ while n + g < 100
+ invariant n <= 100
+ decreases * // error: disallowed on ghost loops
+ {
+ n := n + 1; // error: cannot assign to non-ghost in a ghost loop
+ }
+ }
+
+ method MD1(c: C, ghost g: nat)
+ requires c != null
+ modifies c
+ decreases *
+ {
+ var n := 0;
+ while
+ invariant n <= 100
+ decreases * // error: disallowed on ghost loops
+ {
+ case n + g < 100 => n := n + 1; // error: cannot assign to non-ghost in a ghost loop
+ }
+ }
+}
+
+module UnderspecifiedTypesInAttributes {
+ function method P<T>(x: T): int
+ method M() {
+ var {:myattr var u :| true; 6} v: int; // error: type of u is underspecified
+ var j {:myattr var u :| true; 6} :| 0 <= j < 100; // error: type of u is underspecified
+
+ var a := new int[100];
+ forall lp {:myattr var u :| true; 6} | 0 <= lp < 100 { // error: type of u is underspecified
+ a[lp] := 0;
+ }
+
+ modify {:myattr P(10)} {:myattr var u :| true; 6} a; // error: type of u is underspecified
+
+ calc {:myattr P(10)} {:myattr var u :| true; 6} // error: type of u is underspecified
+ {
+ 5;
+ }
+ }
+}
diff --git a/Test/dafny0/ResolutionErrors.dfy.expect b/Test/dafny0/ResolutionErrors.dfy.expect
index c215d354..d1d826f4 100644
--- a/Test/dafny0/ResolutionErrors.dfy.expect
+++ b/Test/dafny0/ResolutionErrors.dfy.expect
@@ -1,102 +1,3 @@
-ResolutionErrors.dfy(502,7): Error: RHS (of type List<A>) not assignable to LHS (of type List<B>)
-ResolutionErrors.dfy(507,7): Error: RHS (of type List<A>) not assignable to LHS (of type List<B>)
-ResolutionErrors.dfy(521,23): Error: type of case bodies do not agree (found Tree<_T1,_T0>, previous types Tree<_T0,_T1>)
-ResolutionErrors.dfy(533,24): Error: Wrong number of type arguments (0 instead of 2) passed to datatype: Tree
-ResolutionErrors.dfy(561,25): Error: the type of this variable is underspecified
-ResolutionErrors.dfy(561,23): Error: type variable 'T' in the function call to 'P' could not be determined
-ResolutionErrors.dfy(568,25): Error: the type of this variable is underspecified
-ResolutionErrors.dfy(568,23): Error: type variable 'T' in the function call to 'P' could not be determined
-ResolutionErrors.dfy(581,13): Error: 'new' is not allowed in ghost contexts
-ResolutionErrors.dfy(582,9): Error: 'new' is not allowed in ghost contexts
-ResolutionErrors.dfy(589,14): Error: new allocation not supported in forall statements
-ResolutionErrors.dfy(594,11): Error: the body of the enclosing forall statement is not allowed to update heap locations
-ResolutionErrors.dfy(594,14): Error: new allocation not allowed in ghost context
-ResolutionErrors.dfy(604,23): Error: 'new' is not allowed in ghost contexts
-ResolutionErrors.dfy(611,15): Error: 'new' is not allowed in ghost contexts
-ResolutionErrors.dfy(611,15): Error: only ghost methods can be called from this context
-ResolutionErrors.dfy(620,17): Error: 'new' is not allowed in ghost contexts
-ResolutionErrors.dfy(622,29): Error: only ghost methods can be called from this context
-ResolutionErrors.dfy(624,17): Error: calls to methods with side-effects are not allowed inside a hint
-ResolutionErrors.dfy(642,21): Error: the type of this variable is underspecified
-ResolutionErrors.dfy(680,13): Error: calls to methods with side-effects are not allowed inside a hint
-ResolutionErrors.dfy(690,17): Error: only ghost methods can be called from this context
-ResolutionErrors.dfy(693,15): Error: 'decreases *' is not allowed on ghost loops
-ResolutionErrors.dfy(704,11): Error: Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
-ResolutionErrors.dfy(704,11): Error: a hint is not allowed to update heap locations
-ResolutionErrors.dfy(705,16): Error: a hint is not allowed to update heap locations
-ResolutionErrors.dfy(706,13): Error: calls to methods with side-effects are not allowed inside a hint
-ResolutionErrors.dfy(709,14): Error: a while statement used inside a hint is not allowed to have a modifies clause
-ResolutionErrors.dfy(728,17): Error: only ghost methods can be called from this context
-ResolutionErrors.dfy(731,15): Error: 'decreases *' is not allowed on ghost loops
-ResolutionErrors.dfy(736,11): Error: Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
-ResolutionErrors.dfy(736,11): Error: a hint is not allowed to update heap locations
-ResolutionErrors.dfy(737,16): Error: a hint is not allowed to update heap locations
-ResolutionErrors.dfy(738,4): Error: calls to methods with side-effects are not allowed inside a hint
-ResolutionErrors.dfy(741,14): Error: a while statement used inside a hint is not allowed to have a modifies clause
-ResolutionErrors.dfy(766,19): Error: calls to methods with side-effects are not allowed inside a statement expression
-ResolutionErrors.dfy(767,20): Error: only ghost methods can be called from this context
-ResolutionErrors.dfy(768,20): Error: wrong number of method result arguments (got 0, expected 1)
-ResolutionErrors.dfy(780,23): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method')
-ResolutionErrors.dfy(790,4): Error: ghost variables are allowed only in specification contexts
-ResolutionErrors.dfy(801,36): Error: ghost variables are allowed only in specification contexts
-ResolutionErrors.dfy(810,17): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method')
-ResolutionErrors.dfy(824,6): Error: RHS (of type B) not assignable to LHS (of type object)
-ResolutionErrors.dfy(825,6): Error: RHS (of type int) not assignable to LHS (of type object)
-ResolutionErrors.dfy(826,6): Error: RHS (of type B) not assignable to LHS (of type object)
-ResolutionErrors.dfy(831,6): Error: RHS (of type G) not assignable to LHS (of type object)
-ResolutionErrors.dfy(832,6): Error: RHS (of type Dt) not assignable to LHS (of type object)
-ResolutionErrors.dfy(833,6): Error: RHS (of type CoDt) not assignable to LHS (of type object)
-ResolutionErrors.dfy(895,4): Error: LHS of array assignment must denote an array element (found seq<int>)
-ResolutionErrors.dfy(896,4): Error: LHS of array assignment must denote an array element (found seq<int>)
-ResolutionErrors.dfy(901,10): Error: LHS of assignment must denote a mutable field
-ResolutionErrors.dfy(902,10): Error: LHS of assignment must denote a mutable field
-ResolutionErrors.dfy(903,9): Error: cannot assign to a range of array elements (try the 'forall' statement)
-ResolutionErrors.dfy(904,9): Error: cannot assign to a range of array elements (try the 'forall' statement)
-ResolutionErrors.dfy(905,5): Error: cannot assign to a range of array elements (try the 'forall' statement)
-ResolutionErrors.dfy(906,5): Error: cannot assign to a range of array elements (try the 'forall' statement)
-ResolutionErrors.dfy(987,11): Error: Wrong number of type arguments (2 instead of 1) passed to array type: array3
-ResolutionErrors.dfy(988,11): Error: Wrong number of type arguments (2 instead of 1) passed to class: C
-ResolutionErrors.dfy(999,7): Error: Duplicate name of top-level declaration: BadSyn2
-ResolutionErrors.dfy(996,17): Error: Wrong number of type arguments (0 instead of 1) passed to datatype: List
-ResolutionErrors.dfy(997,17): Error: Undeclared top-level type or type parameter: badName (did you forget to qualify a name?)
-ResolutionErrors.dfy(998,22): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name?)
-ResolutionErrors.dfy(1005,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> A
-ResolutionErrors.dfy(1008,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A
-ResolutionErrors.dfy(1012,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A
-ResolutionErrors.dfy(1021,11): Error: because of cyclic dependencies among constructor argument types, no instances of datatype 'D' can be constructed
-ResolutionErrors.dfy(1024,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A
-ResolutionErrors.dfy(1029,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A
-ResolutionErrors.dfy(1048,21): Error: unresolved identifier: x
-ResolutionErrors.dfy(1055,35): Error: Wrong number of type arguments (2 instead of 1) passed to opaque type: P
-ResolutionErrors.dfy(1067,13): Error: Undeclared top-level type or type parameter: BX (did you forget to qualify a name?)
-ResolutionErrors.dfy(1077,6): Error: RHS (of type P<int>) not assignable to LHS (of type P<bool>)
-ResolutionErrors.dfy(1082,6): Error: RHS (of type P<A>) not assignable to LHS (of type P<B>)
-ResolutionErrors.dfy(1087,6): Error: RHS (of type P<A>) not assignable to LHS (of type P<int>)
-ResolutionErrors.dfy(1088,6): Error: RHS (of type P<int>) not assignable to LHS (of type P<A>)
-ResolutionErrors.dfy(1093,13): Error: arguments must have the same type (got P<int> and P<X>)
-ResolutionErrors.dfy(1094,13): Error: arguments must have the same type (got P<bool> and P<X>)
-ResolutionErrors.dfy(1095,13): Error: arguments must have the same type (got P<int> and P<bool>)
-ResolutionErrors.dfy(1118,38): Error: a set comprehension must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'o'
-ResolutionErrors.dfy(1120,24): Error: a set comprehension must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'o'
-ResolutionErrors.dfy(1225,26): Error: the type of this variable is underspecified
-ResolutionErrors.dfy(1226,31): Error: the type of this variable is underspecified
-ResolutionErrors.dfy(1227,29): Error: the type of this variable is underspecified
-ResolutionErrors.dfy(1237,34): Error: the type of this variable is underspecified
-ResolutionErrors.dfy(1253,21): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name?)
-ResolutionErrors.dfy(1254,24): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name?)
-ResolutionErrors.dfy(1291,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (y)
-ResolutionErrors.dfy(1301,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x)
-ResolutionErrors.dfy(1329,15): Error: The name Inner ambiguously refers to a type in one of the modules A, B (try qualifying the type name with the module name)
-ResolutionErrors.dfy(1339,29): Error: ghost variables are allowed only in specification contexts
-ResolutionErrors.dfy(1341,49): Error: ghost variables are allowed only in specification contexts
-ResolutionErrors.dfy(1341,54): Error: ghost variables are allowed only in specification contexts
-ResolutionErrors.dfy(432,2): Error: More than one anonymous constructor
-ResolutionErrors.dfy(50,13): Error: 'this' is not allowed in a 'static' context
-ResolutionErrors.dfy(87,14): Error: the name 'Benny' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, 'Abc.Benny')
-ResolutionErrors.dfy(92,14): Error: the name 'David' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, 'Abc.David')
-ResolutionErrors.dfy(93,14): Error: the name 'David' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, 'Abc.David')
-ResolutionErrors.dfy(95,14): Error: the name 'David' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, 'Abc.David')
-ResolutionErrors.dfy(97,18): Error: wrong number of arguments to datatype constructor David (found 2, expected 1)
ResolutionErrors.dfy(113,9): Error: ghost variables are allowed only in specification contexts
ResolutionErrors.dfy(114,9): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method')
ResolutionErrors.dfy(118,11): Error: ghost variables are allowed only in specification contexts
@@ -107,31 +8,183 @@ ResolutionErrors.dfy(137,4): Error: ghost variables are allowed only in specific
ResolutionErrors.dfy(141,21): Error: ghost variables are allowed only in specification contexts
ResolutionErrors.dfy(142,35): Error: ghost variables are allowed only in specification contexts
ResolutionErrors.dfy(151,10): Error: only ghost methods can be called from this context
-ResolutionErrors.dfy(157,16): Error: 'decreases *' is not allowed on ghost loops
-ResolutionErrors.dfy(198,27): Error: ghost-context break statement is not allowed to break out of non-ghost structure
-ResolutionErrors.dfy(221,12): Error: ghost-context break statement is not allowed to break out of non-ghost loop
-ResolutionErrors.dfy(233,12): Error: trying to break out of more loop levels than there are enclosing loops
-ResolutionErrors.dfy(237,12): Error: ghost-context break statement is not allowed to break out of non-ghost loop
-ResolutionErrors.dfy(242,8): Error: return statement is not allowed in this context (because it is guarded by a specification-only expression)
-ResolutionErrors.dfy(408,11): Error: calls to methods with side-effects are not allowed inside a hint
-ResolutionErrors.dfy(410,14): Error: a hint is not allowed to update heap locations
-ResolutionErrors.dfy(412,10): Error: a hint is not allowed to update a variable declared outside the hint
-ResolutionErrors.dfy(438,14): Error: when allocating an object of type 'YHWH', one of its constructor methods must be called
-ResolutionErrors.dfy(443,6): Error: when allocating an object of type 'Lucifer', one of its constructor methods must be called
-ResolutionErrors.dfy(444,6): Error: when allocating an object of type 'Lucifer', one of its constructor methods must be called
-ResolutionErrors.dfy(446,9): Error: class Lamb does not have an anonymous constructor
-ResolutionErrors.dfy(844,11): Error: a modifies-clause expression must denote an object or a collection of objects (instead got int)
-ResolutionErrors.dfy(848,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x)
-ResolutionErrors.dfy(851,12): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x)
-ResolutionErrors.dfy(859,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x)
-ResolutionErrors.dfy(869,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x)
-ResolutionErrors.dfy(880,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x)
-ResolutionErrors.dfy(1036,23): Error: unresolved identifier: x
-ResolutionErrors.dfy(1039,20): Error: unresolved identifier: x
-ResolutionErrors.dfy(1042,23): Error: unresolved identifier: x
-ResolutionErrors.dfy(1044,19): Error: unresolved identifier: x
-ResolutionErrors.dfy(1046,19): Error: unresolved identifier: x
-ResolutionErrors.dfy(12,16): Error: 'decreases *' is not allowed on ghost loops
+ResolutionErrors.dfy(241,27): Error: ghost-context break statement is not allowed to break out of non-ghost structure
+ResolutionErrors.dfy(264,12): Error: ghost-context break statement is not allowed to break out of non-ghost loop
+ResolutionErrors.dfy(278,12): Error: ghost-context break statement is not allowed to break out of non-ghost loop
+ResolutionErrors.dfy(283,8): Error: return statement is not allowed in this context (because it is guarded by a specification-only expression)
+ResolutionErrors.dfy(362,15): Error: ghost variables are allowed only in specification contexts
+ResolutionErrors.dfy(444,13): Error: calls to methods with side-effects are not allowed inside a hint
+ResolutionErrors.dfy(446,16): Error: a hint is not allowed to update heap locations
+ResolutionErrors.dfy(448,12): Error: a hint is not allowed to update a variable declared outside the hint
+ResolutionErrors.dfy(535,7): Error: RHS (of type List<A>) not assignable to LHS (of type List<B>)
+ResolutionErrors.dfy(540,7): Error: RHS (of type List<A>) not assignable to LHS (of type List<B>)
+ResolutionErrors.dfy(554,23): Error: type of case bodies do not agree (found Tree<_T1,_T0>, previous types Tree<_T0,_T1>)
+ResolutionErrors.dfy(566,24): Error: Wrong number of type arguments (0 instead of 2) passed to datatype: Tree
+ResolutionErrors.dfy(598,8): Error: the type of this local variable is underspecified
+ResolutionErrors.dfy(599,23): Error: type variable 'T' in the function call to 'P' could not be determined
+ResolutionErrors.dfy(599,18): Error: type of bound variable 'z' could not be determined; please specify the type explicitly
+ResolutionErrors.dfy(612,13): Error: 'new' is not allowed in ghost contexts
+ResolutionErrors.dfy(613,9): Error: 'new' is not allowed in ghost contexts
+ResolutionErrors.dfy(622,23): Error: 'new' is not allowed in ghost contexts
+ResolutionErrors.dfy(629,15): Error: 'new' is not allowed in ghost contexts
+ResolutionErrors.dfy(638,17): Error: 'new' is not allowed in ghost contexts
+ResolutionErrors.dfy(655,14): Error: new allocation not supported in forall statements
+ResolutionErrors.dfy(660,11): Error: the body of the enclosing forall statement is not allowed to update heap locations
+ResolutionErrors.dfy(660,14): Error: new allocation not allowed in ghost context
+ResolutionErrors.dfy(672,21): Error: the type of this variable is underspecified
+ResolutionErrors.dfy(712,22): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating
+ResolutionErrors.dfy(745,22): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating
+ResolutionErrors.dfy(776,19): Error: calls to methods with side-effects are not allowed inside a statement expression
+ResolutionErrors.dfy(777,20): Error: wrong number of method result arguments (got 0, expected 1)
+ResolutionErrors.dfy(789,23): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method')
+ResolutionErrors.dfy(799,4): Error: ghost variables are allowed only in specification contexts
+ResolutionErrors.dfy(810,36): Error: ghost variables are allowed only in specification contexts
+ResolutionErrors.dfy(819,17): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method')
+ResolutionErrors.dfy(833,6): Error: RHS (of type B) not assignable to LHS (of type object)
+ResolutionErrors.dfy(834,6): Error: RHS (of type int) not assignable to LHS (of type object)
+ResolutionErrors.dfy(835,6): Error: RHS (of type B) not assignable to LHS (of type object)
+ResolutionErrors.dfy(840,6): Error: RHS (of type G) not assignable to LHS (of type object)
+ResolutionErrors.dfy(841,6): Error: RHS (of type Dt) not assignable to LHS (of type object)
+ResolutionErrors.dfy(842,6): Error: RHS (of type CoDt) not assignable to LHS (of type object)
+ResolutionErrors.dfy(867,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x)
+ResolutionErrors.dfy(875,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x)
+ResolutionErrors.dfy(885,20): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x)
+ResolutionErrors.dfy(896,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x)
+ResolutionErrors.dfy(912,4): Error: LHS of array assignment must denote an array element (found seq<int>)
+ResolutionErrors.dfy(913,4): Error: LHS of array assignment must denote an array element (found seq<int>)
+ResolutionErrors.dfy(918,10): Error: LHS of assignment must denote a mutable field
+ResolutionErrors.dfy(919,10): Error: LHS of assignment must denote a mutable field
+ResolutionErrors.dfy(920,9): Error: cannot assign to a range of array elements (try the 'forall' statement)
+ResolutionErrors.dfy(921,9): Error: cannot assign to a range of array elements (try the 'forall' statement)
+ResolutionErrors.dfy(922,5): Error: cannot assign to a range of array elements (try the 'forall' statement)
+ResolutionErrors.dfy(923,5): Error: cannot assign to a range of array elements (try the 'forall' statement)
+ResolutionErrors.dfy(1004,11): Error: Wrong number of type arguments (2 instead of 1) passed to array type: array3
+ResolutionErrors.dfy(1005,11): Error: Wrong number of type arguments (2 instead of 1) passed to class: C
+ResolutionErrors.dfy(1016,7): Error: Duplicate name of top-level declaration: BadSyn2
+ResolutionErrors.dfy(1013,17): Error: Wrong number of type arguments (0 instead of 1) passed to datatype: List
+ResolutionErrors.dfy(1014,17): Error: Undeclared top-level type or type parameter: badName (did you forget to qualify a name or declare a module import 'opened?')
+ResolutionErrors.dfy(1015,22): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?')
+ResolutionErrors.dfy(1022,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> A
+ResolutionErrors.dfy(1025,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A
+ResolutionErrors.dfy(1029,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A
+ResolutionErrors.dfy(1038,11): Error: because of cyclic dependencies among constructor argument types, no instances of datatype 'D' can be constructed
+ResolutionErrors.dfy(1041,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A
+ResolutionErrors.dfy(1046,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A
+ResolutionErrors.dfy(1065,21): Error: unresolved identifier: x
+ResolutionErrors.dfy(1072,35): Error: Wrong number of type arguments (2 instead of 1) passed to opaque type: P
+ResolutionErrors.dfy(1084,13): Error: Undeclared top-level type or type parameter: BX (did you forget to qualify a name or declare a module import 'opened?')
+ResolutionErrors.dfy(1094,6): Error: RHS (of type P<int>) not assignable to LHS (of type P<bool>)
+ResolutionErrors.dfy(1099,6): Error: RHS (of type P<A>) not assignable to LHS (of type P<B>)
+ResolutionErrors.dfy(1104,6): Error: RHS (of type P<A>) not assignable to LHS (of type P<int>)
+ResolutionErrors.dfy(1105,6): Error: RHS (of type P<int>) not assignable to LHS (of type P<A>)
+ResolutionErrors.dfy(1110,13): Error: arguments must have the same type (got P<int> and P<X>)
+ResolutionErrors.dfy(1111,13): Error: arguments must have the same type (got P<bool> and P<X>)
+ResolutionErrors.dfy(1112,13): Error: arguments must have the same type (got P<int> and P<bool>)
+ResolutionErrors.dfy(1130,31): Error: a set comprehension involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'o'
+ResolutionErrors.dfy(1132,38): Error: a set comprehension involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'o'
+ResolutionErrors.dfy(1137,24): Error: set comprehensions in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'o'
+ResolutionErrors.dfy(1230,13): Error: type variable 'PT' in the function call to 'P' could not be determined
+ResolutionErrors.dfy(1231,14): Error: the type of this variable is underspecified
+ResolutionErrors.dfy(1231,19): Error: type variable 'QT' in the function call to 'Q' could not be determined
+ResolutionErrors.dfy(1232,4): Error: type '?' to the method 'M' is not determined
+ResolutionErrors.dfy(1233,8): Error: the type of this variable is underspecified
+ResolutionErrors.dfy(1233,13): Error: type '?' to the method 'N' is not determined
+ResolutionErrors.dfy(1234,8): Error: the type of this variable is underspecified
+ResolutionErrors.dfy(1235,8): Error: the type of this variable is underspecified
+ResolutionErrors.dfy(1236,8): Error: the type of this local variable is underspecified
+ResolutionErrors.dfy(1237,8): Error: the type of this variable is underspecified
+ResolutionErrors.dfy(1238,8): Error: the type of this local variable is underspecified
+ResolutionErrors.dfy(1242,26): Error: the type of this variable is underspecified
+ResolutionErrors.dfy(1242,21): Error: type of bound variable 's' could not be determined; please specify the type explicitly
+ResolutionErrors.dfy(1243,31): Error: the type of this variable is underspecified
+ResolutionErrors.dfy(1243,21): Error: type of bound variable 's' could not be determined; please specify the type explicitly
+ResolutionErrors.dfy(1244,29): Error: the type of this variable is underspecified
+ResolutionErrors.dfy(1244,21): Error: type of bound variable 'c' could not be determined; please specify the type explicitly
+ResolutionErrors.dfy(1251,8): Error: the type of this local variable is underspecified
+ResolutionErrors.dfy(1254,29): Error: type of bound variable 'c' could not be determined; please specify the type explicitly
+ResolutionErrors.dfy(1270,21): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?')
+ResolutionErrors.dfy(1271,24): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?')
+ResolutionErrors.dfy(1308,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (y)
+ResolutionErrors.dfy(1318,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x)
+ResolutionErrors.dfy(1346,15): Error: The name Inner ambiguously refers to a type in one of the modules A, B (try qualifying the type name with the module name)
+ResolutionErrors.dfy(1356,29): Error: ghost variables are allowed only in specification contexts
+ResolutionErrors.dfy(1358,49): Error: ghost variables are allowed only in specification contexts
+ResolutionErrors.dfy(1358,54): Error: ghost variables are allowed only in specification contexts
+ResolutionErrors.dfy(1379,11): Error: name of type (X) is used as a variable
+ResolutionErrors.dfy(1379,16): Error: name of type (X) is used as a variable
+ResolutionErrors.dfy(1380,11): Error: name of module (Y) is used as a variable
+ResolutionErrors.dfy(1380,16): Error: name of module (Y) is used as a variable
+ResolutionErrors.dfy(1381,11): Error: name of type (X) is used as a variable
+ResolutionErrors.dfy(1381,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #type, or a map with domain #type (instead got map<real, string>)
+ResolutionErrors.dfy(1382,11): Error: name of module (Y) is used as a variable
+ResolutionErrors.dfy(1382,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #module, or a map with domain #module (instead got map<real, string>)
+ResolutionErrors.dfy(1387,16): Error: name of type (X) is used as a variable
+ResolutionErrors.dfy(1387,13): Error: arguments must have the same type (got int and #type)
+ResolutionErrors.dfy(1388,16): Error: name of module (Y) is used as a variable
+ResolutionErrors.dfy(1388,13): Error: arguments must have the same type (got int and #module)
+ResolutionErrors.dfy(1389,4): Error: name of type (X) is used as a variable
+ResolutionErrors.dfy(1390,4): Error: name of module (Y) is used as a variable
+ResolutionErrors.dfy(1399,11): Error: type of RHS of assign-such-that statement must be boolean (got int)
+ResolutionErrors.dfy(1400,9): Error: type of RHS of assign-such-that statement must be boolean (got int)
+ResolutionErrors.dfy(1401,13): Error: type of RHS of assign-such-that statement must be boolean (got int)
+ResolutionErrors.dfy(1404,15): Error: type of RHS of let-such-that expression must be boolean (got int)
+ResolutionErrors.dfy(1447,20): Error: calls to methods with side-effects are not allowed inside a hint
+ResolutionErrors.dfy(1469,18): Error: a hint is not allowed to update heap locations
+ResolutionErrors.dfy(1470,23): Error: a hint is not allowed to update heap locations
+ResolutionErrors.dfy(1471,20): Error: calls to methods with side-effects are not allowed inside a hint
+ResolutionErrors.dfy(1474,21): Error: a while statement used inside a hint is not allowed to have a modifies clause
+ResolutionErrors.dfy(1456,24): Error: only ghost methods can be called from this context
+ResolutionErrors.dfy(1469,18): Error: Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+ResolutionErrors.dfy(1498,18): Error: a hint is not allowed to update heap locations
+ResolutionErrors.dfy(1499,23): Error: a hint is not allowed to update heap locations
+ResolutionErrors.dfy(1500,11): Error: calls to methods with side-effects are not allowed inside a hint
+ResolutionErrors.dfy(1503,21): Error: a while statement used inside a hint is not allowed to have a modifies clause
+ResolutionErrors.dfy(1491,24): Error: only ghost methods can be called from this context
+ResolutionErrors.dfy(1498,18): Error: Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+ResolutionErrors.dfy(1527,20): Error: only ghost methods can be called from this context
+ResolutionErrors.dfy(1420,29): Error: only ghost methods can be called from this context
+ResolutionErrors.dfy(1422,17): Error: calls to methods with side-effects are not allowed inside a hint
+ResolutionErrors.dfy(1538,16): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating
+ResolutionErrors.dfy(1556,12): Error: trying to break out of more loop levels than there are enclosing loops
+ResolutionErrors.dfy(1568,16): Error: ghost fields are allowed only in specification contexts
+ResolutionErrors.dfy(1575,9): Error: ghost variables are allowed only in specification contexts
+ResolutionErrors.dfy(1581,4): Error: non-ghost variable cannot be assigned a value that depends on a ghost
+ResolutionErrors.dfy(1598,8): Error: print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+ResolutionErrors.dfy(1607,26): Error: ghost variables are allowed only in specification contexts
+ResolutionErrors.dfy(1615,6): Error: the type of the bound variable 't' could not be determined
+ResolutionErrors.dfy(1633,15): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x)
+ResolutionErrors.dfy(1635,10): Error: Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+ResolutionErrors.dfy(1660,15): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x)
+ResolutionErrors.dfy(1662,25): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+ResolutionErrors.dfy(1663,35): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+ResolutionErrors.dfy(1673,4): Error: 'decreases *' is not allowed on ghost loops
+ResolutionErrors.dfy(1677,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+ResolutionErrors.dfy(1687,4): Error: 'decreases *' is not allowed on ghost loops
+ResolutionErrors.dfy(1691,29): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+ResolutionErrors.dfy(1699,17): Error: the type of the bound variable 'u' could not be determined
+ResolutionErrors.dfy(1700,19): Error: the type of the bound variable 'u' could not be determined
+ResolutionErrors.dfy(1703,23): Error: the type of the bound variable 'u' could not be determined
+ResolutionErrors.dfy(1707,36): Error: the type of the bound variable 'u' could not be determined
+ResolutionErrors.dfy(1709,34): Error: the type of the bound variable 'u' could not be determined
+ResolutionErrors.dfy(469,2): Error: More than one anonymous constructor
+ResolutionErrors.dfy(50,13): Error: 'this' is not allowed in a 'static' context
+ResolutionErrors.dfy(87,14): Error: the name 'Benny' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, 'Abc.Benny')
+ResolutionErrors.dfy(92,14): Error: the name 'David' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, 'Abc.David')
+ResolutionErrors.dfy(93,14): Error: the name 'David' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, 'Abc.David')
+ResolutionErrors.dfy(95,14): Error: the name 'David' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, 'Abc.David')
+ResolutionErrors.dfy(97,18): Error: wrong number of arguments to datatype constructor David (found 2, expected 1)
+ResolutionErrors.dfy(475,6): Error: when allocating an object of type 'Y', one of its constructor methods must be called
+ResolutionErrors.dfy(480,6): Error: when allocating an object of type 'Luci', one of its constructor methods must be called
+ResolutionErrors.dfy(481,6): Error: when allocating an object of type 'Luci', one of its constructor methods must be called
+ResolutionErrors.dfy(483,9): Error: class Lamb does not have an anonymous constructor
+ResolutionErrors.dfy(853,11): Error: a modifies-clause expression must denote an object or a collection of objects (instead got int)
+ResolutionErrors.dfy(857,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x)
+ResolutionErrors.dfy(1053,23): Error: unresolved identifier: x
+ResolutionErrors.dfy(1056,20): Error: unresolved identifier: x
+ResolutionErrors.dfy(1059,23): Error: unresolved identifier: x
+ResolutionErrors.dfy(1061,19): Error: unresolved identifier: x
+ResolutionErrors.dfy(1063,19): Error: unresolved identifier: x
+ResolutionErrors.dfy(12,16): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating
ResolutionErrors.dfy(24,11): Error: array selection requires an array2 (got array3<T>)
ResolutionErrors.dfy(25,12): Error: sequence/array/multiset/map selection requires a sequence, array, multiset, or map (got array3<T>)
ResolutionErrors.dfy(26,11): Error: array selection requires an array4 (got array<T>)
@@ -144,42 +197,36 @@ ResolutionErrors.dfy(62,14): Error: accessing member 'M' requires an instance ex
ResolutionErrors.dfy(63,7): Error: unresolved identifier: N
ResolutionErrors.dfy(66,8): Error: non-function expression (of type int) is called with parameters
ResolutionErrors.dfy(67,14): Error: member 'z' does not exist in type 'Global'
-ResolutionErrors.dfy(260,4): Error: label shadows an enclosing label
-ResolutionErrors.dfy(265,2): Error: duplicate label
-ResolutionErrors.dfy(291,4): Error: when allocating an object of type 'ClassWithConstructor', one of its constructor methods must be called
-ResolutionErrors.dfy(292,4): Error: when allocating an object of type 'ClassWithConstructor', one of its constructor methods must be called
-ResolutionErrors.dfy(294,9): Error: a constructor is allowed to be called only when an object is being allocated
-ResolutionErrors.dfy(308,16): Error: arguments must have the same type (got int and DTD_List)
-ResolutionErrors.dfy(309,16): Error: arguments must have the same type (got DTD_List and int)
-ResolutionErrors.dfy(310,25): Error: arguments must have the same type (got bool and int)
-ResolutionErrors.dfy(313,18): Error: ghost fields are allowed only in specification contexts
-ResolutionErrors.dfy(322,15): Error: ghost variables are allowed only in specification contexts
-ResolutionErrors.dfy(347,5): Error: incorrect type of method in-parameter 1 (expected GenericClass<int>, got GenericClass<bool>)
-ResolutionErrors.dfy(359,18): Error: incorrect type of datatype constructor argument (found GList<_T0>, expected GList<int>)
-ResolutionErrors.dfy(367,6): Error: arguments to + must be of a numeric type or a collection type (instead got bool)
-ResolutionErrors.dfy(372,6): Error: all lines in a calculation must have the same type (got int after bool)
-ResolutionErrors.dfy(375,6): Error: first argument to ==> must be of type bool (instead got int)
-ResolutionErrors.dfy(375,6): Error: second argument to ==> must be of type bool (instead got int)
-ResolutionErrors.dfy(376,10): Error: first argument to ==> must be of type bool (instead got int)
-ResolutionErrors.dfy(376,10): Error: second argument to ==> must be of type bool (instead got int)
-ResolutionErrors.dfy(381,10): Error: first argument to ==> must be of type bool (instead got int)
-ResolutionErrors.dfy(381,10): Error: second argument to ==> must be of type bool (instead got int)
-ResolutionErrors.dfy(386,6): Error: print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
-ResolutionErrors.dfy(470,7): Error: ghost variables are allowed only in specification contexts
-ResolutionErrors.dfy(476,12): Error: ghost variables are allowed only in specification contexts
-ResolutionErrors.dfy(546,20): Error: ghost variables are allowed only in specification contexts
-ResolutionErrors.dfy(549,18): Error: unresolved identifier: w
-ResolutionErrors.dfy(656,11): Error: lemmas are not allowed to have modifies clauses
-ResolutionErrors.dfy(918,9): Error: unresolved identifier: s
-ResolutionErrors.dfy(929,32): Error: RHS (of type (int,int,real)) not assignable to LHS (of type (int,real,int))
-ResolutionErrors.dfy(930,37): Error: RHS (of type (int,real,int)) not assignable to LHS (of type (int,real,int,real))
-ResolutionErrors.dfy(936,16): Error: condition is expected to be of type bool, but is int
-ResolutionErrors.dfy(937,16): Error: member 3 does not exist in datatype _tuple#3
-ResolutionErrors.dfy(937,26): Error: member x does not exist in datatype _tuple#2
-ResolutionErrors.dfy(960,15): Error: arguments to / must have the same type (got real and int)
-ResolutionErrors.dfy(961,10): Error: second argument to % must be of type int (instead got real)
-ResolutionErrors.dfy(1106,8): Error: new cannot be applied to a trait
-ResolutionErrors.dfy(1127,13): Error: first argument to / must be of numeric type (instead got set<bool>)
-ResolutionErrors.dfy(1134,18): Error: a call to a possibly non-terminating method is allowed only if the calling method is also declared (with 'decreases *') to be possibly non-terminating
-ResolutionErrors.dfy(1149,14): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating
-184 resolution/type errors detected in ResolutionErrors.dfy
+ResolutionErrors.dfy(301,4): Error: label shadows an enclosing label
+ResolutionErrors.dfy(306,2): Error: duplicate label
+ResolutionErrors.dfy(332,4): Error: when allocating an object of type 'ClassWithConstructor', one of its constructor methods must be called
+ResolutionErrors.dfy(333,4): Error: when allocating an object of type 'ClassWithConstructor', one of its constructor methods must be called
+ResolutionErrors.dfy(335,9): Error: a constructor is allowed to be called only when an object is being allocated
+ResolutionErrors.dfy(349,16): Error: arguments must have the same type (got int and DTD_List)
+ResolutionErrors.dfy(350,16): Error: arguments must have the same type (got DTD_List and int)
+ResolutionErrors.dfy(351,25): Error: arguments must have the same type (got bool and int)
+ResolutionErrors.dfy(387,5): Error: incorrect type of method in-parameter 1 (expected GenericClass<int>, got GenericClass<bool>)
+ResolutionErrors.dfy(399,18): Error: incorrect type of datatype constructor argument (found GList<_T0>, expected GList<int>)
+ResolutionErrors.dfy(407,6): Error: arguments to + must be of a numeric type or a collection type (instead got bool)
+ResolutionErrors.dfy(412,6): Error: all lines in a calculation must have the same type (got int after bool)
+ResolutionErrors.dfy(415,6): Error: first argument to ==> must be of type bool (instead got int)
+ResolutionErrors.dfy(415,6): Error: second argument to ==> must be of type bool (instead got int)
+ResolutionErrors.dfy(416,10): Error: first argument to ==> must be of type bool (instead got int)
+ResolutionErrors.dfy(416,10): Error: second argument to ==> must be of type bool (instead got int)
+ResolutionErrors.dfy(421,10): Error: first argument to ==> must be of type bool (instead got int)
+ResolutionErrors.dfy(421,10): Error: second argument to ==> must be of type bool (instead got int)
+ResolutionErrors.dfy(580,18): Error: unresolved identifier: w
+ResolutionErrors.dfy(686,11): Error: lemmas are not allowed to have modifies clauses
+ResolutionErrors.dfy(935,9): Error: unresolved identifier: s
+ResolutionErrors.dfy(946,32): Error: RHS (of type (int,int,real)) not assignable to LHS (of type (int,real,int))
+ResolutionErrors.dfy(947,37): Error: RHS (of type (int,real,int)) not assignable to LHS (of type (int,real,int,real))
+ResolutionErrors.dfy(953,16): Error: condition is expected to be of type bool, but is int
+ResolutionErrors.dfy(954,16): Error: member 3 does not exist in datatype _tuple#3
+ResolutionErrors.dfy(954,26): Error: member x does not exist in datatype _tuple#2
+ResolutionErrors.dfy(977,15): Error: arguments to / must have the same type (got real and int)
+ResolutionErrors.dfy(978,10): Error: second argument to % must be of type int (instead got real)
+ResolutionErrors.dfy(1123,8): Error: new cannot be applied to a trait
+ResolutionErrors.dfy(1144,13): Error: first argument to / must be of numeric type (instead got set<bool>)
+ResolutionErrors.dfy(1151,18): Error: a call to a possibly non-terminating method is allowed only if the calling method is also declared (with 'decreases *') to be possibly non-terminating
+ResolutionErrors.dfy(1166,14): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating
+231 resolution/type errors detected in ResolutionErrors.dfy
diff --git a/Test/dafny0/SeqFromArray.dfy b/Test/dafny0/SeqFromArray.dfy
index aa131f98..cf889804 100644
--- a/Test/dafny0/SeqFromArray.dfy
+++ b/Test/dafny0/SeqFromArray.dfy
@@ -1,6 +1,8 @@
-// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
+// /autoTriggers:1 added to suppress instabilities
+
method Main() { }
method H(a: array<int>, c: array<int>, n: nat, j: nat)
@@ -51,7 +53,7 @@ method L(a: array<int>, c: array<int>, n: nat)
case A == C =>
assert forall i :: 0 <= i < h ==> A[i] == C[i];
case A == C =>
- assert forall i :: 0 <= i < h ==> a[n+i] == c[n+i];
+ assert forall i :: n <= i < n + h ==> a[i] == c[i];
case true =>
}
}
@@ -71,20 +73,24 @@ method M(a: array<int>, c: array<int>, m: nat, n: nat, k: nat, l: nat)
} else if * {
assert forall i :: 0 <= i < n ==> A[i] == C[i];
} else if * {
- assert forall i :: k <= i < k+n ==> A[i-k] == C[i-k];
+ assert forall i {:nowarn} :: k <= i < k+n ==> A[i-k] == C[i-k];
} else if * {
assert forall i :: 0 <= i < n ==> A[i] == a[k+i];
} else if * {
assert forall i :: 0 <= i < n ==> C[i] == c[l+i];
} else if * {
- assert forall i :: 0 <= i < n ==> a[k+i] == c[l+i];
+ assert forall i {:nowarn} :: 0 <= i < n ==> a[k+i] == c[l+i];
}
}
case l+m <= c.Length && forall i :: 0 <= i < m ==> a[i] == c[l+i] =>
assert a[..m] == c[l..l+m];
case l+a.Length <= c.Length && forall i :: k <= i < a.Length ==> a[i] == c[l+i] =>
- assert a[k..] == c[l+k..l+a.Length];
+ assert a[k..] == c[l+k..l+a.Length];
case l+k+m <= c.Length && forall i :: k <= i < k+m ==> a[i] == c[l+i] =>
- assert a[k..k+m] == c[l+k..l+k+m];
+ assert a[k..k+m] == c[l+k..l+k+m];
}
}
+
+// Local Variables:
+// dafny-prover-local-args: ("/autoTriggers:1")
+// End:
diff --git a/Test/dafny0/Shadows.dfy b/Test/dafny0/Shadows.dfy
new file mode 100644
index 00000000..da1e74d6
--- /dev/null
+++ b/Test/dafny0/Shadows.dfy
@@ -0,0 +1,42 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /warnShadowing "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+module Module0 {
+ class C<alpha> {
+ method M<beta, beta>(x: beta) // error: duplicate type parameter
+ method P<alpha>(x: alpha) // shadowed type parameter
+ function F<beta, beta>(x: beta): int // error: duplicate type parameter
+ function G<alpha>(x: alpha): int // shadowed type parameter
+
+ method Q0(x: int) returns (x: int) // error: duplicate variable name
+ }
+}
+module Module1 {
+ class D {
+ method Q1(x: int) returns (y: int)
+ {
+ var x; // shadowed
+ var y; // error: duplicate
+ }
+
+ var f: int
+ method R()
+ {
+ var f; // okay
+ var f; // error: duplicate
+ }
+ method S()
+ {
+ var x;
+ {
+ var x; // shadow
+ }
+ }
+ method T()
+ {
+ var x;
+ ghost var b := forall x :: x < 10; // shadow
+ ghost var c := forall y :: forall y :: y != y + 1; // shadow
+ }
+ }
+}
diff --git a/Test/dafny0/Shadows.dfy.expect b/Test/dafny0/Shadows.dfy.expect
new file mode 100644
index 00000000..5083ac64
--- /dev/null
+++ b/Test/dafny0/Shadows.dfy.expect
@@ -0,0 +1,12 @@
+Shadows.dfy(6,19): Error: Duplicate type-parameter name: beta
+Shadows.dfy(7,13): Warning: Shadowed type-parameter name: alpha
+Shadows.dfy(8,21): Error: Duplicate type-parameter name: beta
+Shadows.dfy(9,15): Warning: Shadowed type-parameter name: alpha
+Shadows.dfy(11,31): Error: Duplicate parameter name: x
+Shadows.dfy(18,10): Warning: Shadowed local-variable name: x
+Shadows.dfy(19,10): Error: Duplicate local-variable name: y
+Shadows.dfy(26,10): Error: Duplicate local-variable name: f
+Shadows.dfy(32,12): Warning: Shadowed local-variable name: x
+Shadows.dfy(38,28): Warning: Shadowed bound-variable name: x
+Shadows.dfy(39,40): Warning: Shadowed bound-variable name: y
+5 resolution/type errors detected in Shadows.dfy
diff --git a/Test/dafny0/Simple.dfy b/Test/dafny0/Simple.dfy
index f7bfcb70..0b6a620e 100644
--- a/Test/dafny0/Simple.dfy
+++ b/Test/dafny0/Simple.dfy
@@ -74,3 +74,30 @@ class CF {
static protected function method I(): real
protected static predicate method J()
}
+
+// test printing of various if statements, including with omitted guards
+module A {
+ method P(x: int, y: int) {
+ if x==2 {
+ } else if * {
+ }
+ if x==10 {
+ }
+ if y==0 {
+ } else if y==1 {
+ } else if * {
+ } else if y==2 {
+ } else if (*) {
+ } else if y == 3 {
+ } else {
+ }
+ }
+}
+module B refines A {
+ method P... {
+ if ... {
+ } else if x==3 {
+ }
+ ...;
+ }
+}
diff --git a/Test/dafny0/Simple.dfy.expect b/Test/dafny0/Simple.dfy.expect
index e6647c8a..d5eb6722 100644
--- a/Test/dafny0/Simple.dfy.expect
+++ b/Test/dafny0/Simple.dfy.expect
@@ -67,6 +67,35 @@ class CF {
static protected predicate method J()
}
+module A {
+ method P(x: int, y: int)
+ {
+ if x == 2 {
+ } else if * {
+ }
+ if x == 10 {
+ }
+ if y == 0 {
+ } else if y == 1 {
+ } else if * {
+ } else if y == 2 {
+ } else if * {
+ } else if y == 3 {
+ } else {
+ }
+ }
+}
+
+module B refines A {
+ method P ...
+ {
+ if ... {
+ } else if x == 3 {
+ }
+ ...;
+ }
+}
+
lemma M(x: int)
ensures x < 8
{
diff --git a/Test/dafny0/Skeletons.dfy.expect b/Test/dafny0/Skeletons.dfy.expect
index 43b372c3..4b48bad0 100644
--- a/Test/dafny0/Skeletons.dfy.expect
+++ b/Test/dafny0/Skeletons.dfy.expect
@@ -1,5 +1,5 @@
-Skeletons.dfy(45,3): Error BP5003: A postcondition might not hold on this return path.
-Skeletons.dfy(44,15): Related location: This is the postcondition that might not hold.
+Skeletons.dfy(45,2): Error BP5003: A postcondition might not hold on this return path.
+Skeletons.dfy(44,14): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
Skeletons.dfy[C0](32,5): anon11_LoopHead
diff --git a/Test/dafny0/SmallTests.dfy b/Test/dafny0/SmallTests.dfy
index 65db7f7f..ba009b83 100644
--- a/Test/dafny0/SmallTests.dfy
+++ b/Test/dafny0/SmallTests.dfy
@@ -1,4 +1,5 @@
-// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint.dfy" "%s" > "%t"; %dafny /noVerify /compile:0 "%t.dprint.dfy" >> "%t"
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint.dfy" /autoTriggers:1 "%s" > "%t"
+// RUN: %dafny /noVerify /compile:0 "%t.dprint.dfy" >> "%t"
// RUN: %diff "%s.expect" "%t"
class Node {
@@ -34,11 +35,11 @@ class Node {
}
method Sequence(s: seq<bool>, j: int, b: bool, c: bool) returns (t: seq<bool>)
- requires 10 <= |s|;
- requires 8 <= j && j < |s|;
- ensures |t| == |s|;
- ensures t[8] == s[8] || t[9] == s[9];
- ensures t[j] == b;
+ requires 10 <= |s|
+ requires 8 <= j < |s|
+ ensures |t| == |s|
+ ensures t[8] == s[8] || t[9] == s[9]
+ ensures t[j] == b
{
if (c) {
t := s[j := b];
@@ -48,14 +49,14 @@ class Node {
}
method Max0(x: int, y: int) returns (r: int)
- ensures r == (if x < y then y else x);
+ ensures r == (if x < y then y else x)
{
if (x < y) { r := y; } else { r := x; }
}
method Max1(x: int, y: int) returns (r: int)
- ensures r == x || r == y;
- ensures x <= r && y <= r;
+ ensures r == x || r == y
+ ensures x <= r && y <= r
{
r := if x < y then y else x;
}
@@ -121,12 +122,12 @@ class Modifies {
method C(b: bool)
modifies this;
- ensures !b ==> x == old(x) && next == old(next);
+ ensures !b ==> x == old(x) && next == old(next)
{
}
method D(p: Modifies, y: int)
- requires p != null;
+ requires p != null
{
if (y == 3) {
p.C(true); // error: may violate modifies clause
@@ -229,15 +230,15 @@ class InitCalls {
method Init(y: int)
modifies this;
- ensures z == y;
+ ensures z == y
{
z := y;
}
method InitFromReference(q: InitCalls)
- requires q != null && 15 <= q.z;
+ requires q != null && 15 <= q.z
modifies this;
- ensures p == q;
+ ensures p == q
{
p := q;
}
@@ -264,35 +265,35 @@ class InitCalls {
// --------------- some tests with quantifiers and ranges ----------------------
method QuantifierRange0<T>(a: seq<T>, x: T, y: T, N: int)
- requires 0 <= N && N <= |a|;
- requires forall k | 0 <= k && k < N :: a[k] != x;
- requires exists k | 0 <= k && k < N :: a[k] == y;
- ensures forall k :: 0 <= k && k < N ==> a[k] != x; // same as the precondition, but using ==> instead of |
- ensures exists k :: 0 <= k && k < N && a[k] == y; // same as the precondition, but using && instead of |
+ requires 0 <= N <= |a|
+ requires forall k | 0 <= k < N :: a[k] != x
+ requires exists k | 0 <= k < N :: a[k] == y
+ ensures forall k :: 0 <= k < N ==> a[k] != x; // same as the precondition, but using ==> instead of |
+ ensures exists k :: 0 <= k < N && a[k] == y; // same as the precondition, but using && instead of |
{
assert x != y;
}
method QuantifierRange1<T>(a: seq<T>, x: T, y: T, N: int)
- requires 0 <= N && N <= |a|;
- requires forall k :: 0 <= k && k < N ==> a[k] != x;
- requires exists k :: 0 <= k && k < N && a[k] == y;
- ensures forall k | 0 <= k && k < N :: a[k] != x; // same as the precondition, but using | instead of ==>
- ensures exists k | 0 <= k && k < N :: a[k] == y; // same as the precondition, but using | instead of &&
+ requires 0 <= N <= |a|
+ requires forall k :: 0 <= k < N ==> a[k] != x
+ requires exists k :: 0 <= k < N && a[k] == y
+ ensures forall k | 0 <= k < N :: a[k] != x; // same as the precondition, but using | instead of ==>
+ ensures exists k | 0 <= k < N :: a[k] == y; // same as the precondition, but using | instead of &&
{
assert x != y;
}
method QuantifierRange2<T(==)>(a: seq<T>, x: T, y: T, N: int)
- requires 0 <= N && N <= |a|;
- requires exists k | 0 <= k && k < N :: a[k] == y;
- ensures forall k | 0 <= k && k < N :: a[k] == y; // error
+ requires 0 <= N <= |a|
+ requires exists k | 0 <= k < N :: a[k] == y
+ ensures forall k | 0 <= k < N :: a[k] == y; // error
{
assert N != 0;
if (N == 1) {
- assert forall k | a[if 0 <= k && k < N then k else 0] != y :: k < 0 || N <= k; // in this case, the precondition holds trivially
+ assert forall k {:nowarn} | a[if 0 <= k < N then k else 0] != y :: k < 0 || N <= k; // in this case, the precondition holds trivially
}
- if (forall k | 0 <= k && k < N :: a[k] == x) {
+ if (forall k | 0 <= k < N :: a[k] == x) {
assert x == y;
}
}
@@ -300,8 +301,8 @@ method QuantifierRange2<T(==)>(a: seq<T>, x: T, y: T, N: int)
// ----------------------- tests that involve sequences of boxes --------
ghost method M(zeros: seq<bool>, Z: bool)
- requires 1 <= |zeros| && Z == false;
- requires forall k :: 0 <= k && k < |zeros| ==> zeros[k] == Z;
+ requires 1 <= |zeros| && Z == false
+ requires forall k :: 0 <= k < |zeros| ==> zeros[k] == Z
{
var x := [Z];
assert zeros[0..1] == [Z];
@@ -311,7 +312,7 @@ class SomeType
{
var x: int;
method DoIt(stack: seq<SomeType>)
- requires null !in stack;
+ requires null !in stack
modifies stack;
{
forall n | n in stack {
@@ -331,7 +332,8 @@ method TestSequences0()
assert 1 !in s;
} else {
assert 2 in s;
- assert exists n :: n in s && -3 <= n && n < 2;
+ assert 0 in s;
+ assert exists n :: n in s && -3 <= n < 2;
}
assert 7 in s; // error
}
@@ -397,7 +399,7 @@ class Test {
function F(b: bool): int
// The if-then-else in the following line was once translated incorrectly,
// incorrectly causing the postcondition to verify
- ensures if b then F(b) == 5 else F(b) == 6;
+ ensures if b then F(b) == 5 else F(b) == 6
{
5
}
@@ -428,10 +430,10 @@ class AttributeTests {
}
method testAttributes0() returns (r: AttributeTests)
- ensures {:boolAttr true} true;
- ensures {:boolAttr false} true;
- ensures {:intAttr 0} true;
- ensures {:intAttr 1} true;
+ ensures {:boolAttr true} true
+ ensures {:boolAttr false} true
+ ensures {:intAttr 0} true
+ ensures {:intAttr 1} true
modifies {:boolAttr true} this`f;
modifies {:boolAttr false} this`f;
modifies {:intAttr 0} this`f;
@@ -539,7 +541,7 @@ method TestNotNot()
// ----------------------- Assign-such-that statements -------
method AssignSuchThat0(a: int, b: int) returns (x: int, y: int)
- ensures x == a && y == b;
+ ensures x == a && y == b
{
if (*) {
x, y :| a <= x < a + 1 && b + a <= y + a && y <= b;
@@ -633,7 +635,7 @@ method AssignSuchThat9() returns (q: QuiteFinite)
function method LetSuchThat_P(x: int): bool
method LetSuchThat0(ghost g: int)
- requires LetSuchThat_P(g);
+ requires LetSuchThat_P(g)
{
var t :| LetSuchThat_P(t); // assign-such-that statement
ghost var u := var q :| LetSuchThat_P(q); q + 1; // let-such-that expression
@@ -708,10 +710,10 @@ class GT {
{
if (*) {
P0();
- assert forall x: GT :: x != null ==> !fresh(x); // error: method P2 may have allocated stuff
+ assert forall x: GT {:nowarn} :: x != null ==> !fresh(x); // error: method P2 may have allocated stuff
} else {
P1();
- assert forall x: GT :: x != null ==> !fresh(x); // fine, because the ghost method does not allocate anything
+ assert forall x: GT {:nowarn} :: x != null ==> !fresh(x); // fine, because the ghost method does not allocate anything
}
}
}
@@ -775,20 +777,20 @@ module GenericPick {
var x :| x in s; x
}
function SeqPick3<U>(s: seq<U>): U
- requires exists i :: 0 <= i < |s|
+ requires exists i {:nowarn} :: 0 <= i < |s|
{
EquivalentWaysOfSayingSequenceIsNonempty(s); // I wish this wasn't needed; see comment near Seq#Length axioms in DafnyPrelude.bpl
var x :| x in s; x
}
function SeqPick4<U>(s: seq<U>): U
- requires exists i :: 0 <= i < |s|
+ requires exists i {:nowarn} :: 0 <= i < |s|
{
var i :| 0 <= i < |s|; s[i]
}
lemma EquivalentWaysOfSayingSequenceIsNonempty<U>(s: seq<U>)
requires s != []
|| |s| != 0
- || exists i :: 0 <= i < |s|
+ || exists i {:nowarn} :: 0 <= i < |s|
ensures exists x :: x in s
{
assert s[0] in s;
diff --git a/Test/dafny0/SmallTests.dfy.expect b/Test/dafny0/SmallTests.dfy.expect
index 5f766cd6..746e978a 100644
--- a/Test/dafny0/SmallTests.dfy.expect
+++ b/Test/dafny0/SmallTests.dfy.expect
@@ -1,43 +1,42 @@
-SmallTests.dfy(33,11): Error: index out of range
+SmallTests.dfy(507,4): Warning: /!\ No trigger covering all quantified variables found.
+SmallTests.dfy(34,10): Error: index out of range
Execution trace:
(0,0): anon0
-SmallTests.dfy(64,36): Error: possible division by zero
+SmallTests.dfy(65,35): Error: possible division by zero
Execution trace:
(0,0): anon0
- (0,0): anon12_Then
-SmallTests.dfy(65,51): Error: possible division by zero
+ (0,0): anon13_Then
+SmallTests.dfy(66,50): Error: possible division by zero
Execution trace:
(0,0): anon0
- (0,0): anon12_Else
- (0,0): anon3
(0,0): anon13_Else
-SmallTests.dfy(66,22): Error: target object may be null
+ (0,0): anon14_Else
+SmallTests.dfy(67,21): Error: target object may be null
Execution trace:
(0,0): anon0
- (0,0): anon12_Then
- (0,0): anon3
(0,0): anon13_Then
- (0,0): anon6
-SmallTests.dfy(85,24): Error: target object may be null
+ (0,0): anon14_Then
+ (0,0): anon15_Then
+SmallTests.dfy(86,23): Error: target object may be null
Execution trace:
(0,0): anon0
- SmallTests.dfy(84,5): anon8_LoopHead
+ SmallTests.dfy(85,5): anon8_LoopHead
(0,0): anon8_LoopBody
(0,0): anon9_Then
-SmallTests.dfy(119,6): Error: call may violate context's modifies clause
+SmallTests.dfy(120,5): Error: call may violate context's modifies clause
Execution trace:
(0,0): anon0
(0,0): anon4_Else
(0,0): anon3
-SmallTests.dfy(132,10): Error: call may violate context's modifies clause
+SmallTests.dfy(133,9): Error: call may violate context's modifies clause
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-SmallTests.dfy(134,10): Error: call may violate context's modifies clause
+SmallTests.dfy(135,9): Error: call may violate context's modifies clause
Execution trace:
(0,0): anon0
(0,0): anon3_Else
-SmallTests.dfy(174,9): Error: assignment may update an object field not in the enclosing context's modifies clause
+SmallTests.dfy(175,8): Error: assignment may update an object field not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
(0,0): anon22_Else
@@ -46,23 +45,23 @@ Execution trace:
(0,0): anon28_Then
(0,0): anon29_Then
(0,0): anon19
-SmallTests.dfy(198,14): Error: assertion violation
+SmallTests.dfy(199,13): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon9_Then
-SmallTests.dfy(205,14): Error: assertion violation
+SmallTests.dfy(206,13): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon9_Else
(0,0): anon3
(0,0): anon10_Then
-SmallTests.dfy(207,14): Error: assertion violation
+SmallTests.dfy(208,13): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon9_Else
(0,0): anon3
(0,0): anon10_Else
-SmallTests.dfy(212,14): Error: assertion violation
+SmallTests.dfy(213,13): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon9_Else
@@ -70,7 +69,7 @@ Execution trace:
(0,0): anon10_Then
(0,0): anon6
(0,0): anon11_Then
-SmallTests.dfy(214,14): Error: assertion violation
+SmallTests.dfy(215,13): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon9_Else
@@ -78,37 +77,38 @@ Execution trace:
(0,0): anon10_Then
(0,0): anon6
(0,0): anon11_Else
-SmallTests.dfy(260,24): Error BP5002: A precondition for this call might not hold.
-SmallTests.dfy(238,30): Related location: This is the precondition that might not hold.
+SmallTests.dfy(261,23): Error BP5002: A precondition for this call might not hold.
+SmallTests.dfy(239,29): Related location: This is the precondition that might not hold.
Execution trace:
(0,0): anon0
- SmallTests.dfy(255,19): anon3_Else
+ SmallTests.dfy(256,19): anon3_Else
(0,0): anon2
-SmallTests.dfy(365,12): Error: assertion violation
+SmallTests.dfy(367,11): Error: assertion violation
Execution trace:
(0,0): anon0
-SmallTests.dfy(375,12): Error: assertion violation
+SmallTests.dfy(377,11): Error: assertion violation
Execution trace:
(0,0): anon0
-SmallTests.dfy(385,6): Error: cannot prove termination; try supplying a decreases clause
+SmallTests.dfy(387,5): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-SmallTests.dfy(690,14): Error: assertion violation
+ (0,0): anon4_Else
+SmallTests.dfy(692,13): Error: assertion violation
Execution trace:
(0,0): anon0
- SmallTests.dfy(687,5): anon7_LoopHead
+ SmallTests.dfy(689,5): anon7_LoopHead
(0,0): anon7_LoopBody
- SmallTests.dfy(687,5): anon8_Else
+ SmallTests.dfy(689,5): anon8_Else
(0,0): anon9_Then
-SmallTests.dfy(711,14): Error: assertion violation
+SmallTests.dfy(713,13): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon7_Then
(0,0): anon8_Then
(0,0): anon3
-SmallTests.dfy(295,3): Error BP5003: A postcondition might not hold on this return path.
-SmallTests.dfy(289,11): Related location: This is the postcondition that might not hold.
+SmallTests.dfy(296,2): Error BP5003: A postcondition might not hold on this return path.
+SmallTests.dfy(290,10): Related location: This is the postcondition that might not hold.
+SmallTests.dfy(290,40): Related location
Execution trace:
(0,0): anon0
(0,0): anon18_Else
@@ -116,29 +116,29 @@ Execution trace:
(0,0): anon24_Then
(0,0): anon15
(0,0): anon25_Else
-SmallTests.dfy(336,12): Error: assertion violation
+SmallTests.dfy(338,11): Error: assertion violation
Execution trace:
(0,0): anon0
- (0,0): anon8_Then
- (0,0): anon7
-SmallTests.dfy(343,10): Error: assertion violation
+ (0,0): anon7_Then
+ (0,0): anon6
+SmallTests.dfy(345,9): Error: assertion violation
Execution trace:
(0,0): anon0
-SmallTests.dfy(353,4): Error: cannot prove termination; try supplying a decreases clause
+SmallTests.dfy(355,3): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-SmallTests.dfy(397,10): Error BP5003: A postcondition might not hold on this return path.
-SmallTests.dfy(400,41): Related location: This is the postcondition that might not hold.
+ (0,0): anon4_Else
+SmallTests.dfy(399,9): Error BP5003: A postcondition might not hold on this return path.
+SmallTests.dfy(402,40): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
- (0,0): anon6_Else
-SmallTests.dfy(561,12): Error: assertion violation
+ (0,0): anon7_Else
+SmallTests.dfy(563,11): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Then
(0,0): anon2
-SmallTests.dfy(575,20): Error: left-hand sides 0 and 1 may refer to the same location
+SmallTests.dfy(577,19): Error: left-hand sides 0 and 1 may refer to the same location
Execution trace:
(0,0): anon0
(0,0): anon27_Then
@@ -150,11 +150,11 @@ Execution trace:
(0,0): anon31_Then
(0,0): anon32_Then
(0,0): anon12
-SmallTests.dfy(577,15): Error: left-hand sides 1 and 2 may refer to the same location
+SmallTests.dfy(579,14): Error: left-hand sides 1 and 2 may refer to the same location
Execution trace:
(0,0): anon0
(0,0): anon27_Then
- SmallTests.dfy(570,18): anon28_Else
+ SmallTests.dfy(572,18): anon28_Else
(0,0): anon4
(0,0): anon29_Else
(0,0): anon30_Then
@@ -165,16 +165,16 @@ Execution trace:
(0,0): anon37_Then
(0,0): anon22
(0,0): anon38_Then
-SmallTests.dfy(584,25): Error: target object may be null
+SmallTests.dfy(586,24): Error: target object may be null
Execution trace:
(0,0): anon0
-SmallTests.dfy(597,10): Error: assertion violation
+SmallTests.dfy(599,9): Error: assertion violation
Execution trace:
(0,0): anon0
-SmallTests.dfy(621,5): Error: cannot establish the existence of LHS values that satisfy the such-that predicate
+SmallTests.dfy(623,4): Error: cannot establish the existence of LHS values that satisfy the such-that predicate
Execution trace:
(0,0): anon0
-SmallTests.dfy(644,23): Error: assertion violation
+SmallTests.dfy(646,22): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon8_Then
@@ -182,20 +182,21 @@ Execution trace:
(0,0): anon4
(0,0): anon10_Then
(0,0): anon7
-SmallTests.dfy(658,10): Error: cannot establish the existence of LHS values that satisfy the such-that predicate
+SmallTests.dfy(660,9): Error: cannot establish the existence of LHS values that satisfy the such-that predicate
Execution trace:
(0,0): anon0
(0,0): anon5_Then
(0,0): anon6_Then
(0,0): anon3
-SmallTests.dfy(660,10): Error: cannot establish the existence of LHS values that satisfy the such-that predicate
+SmallTests.dfy(662,9): Error: cannot establish the existence of LHS values that satisfy the such-that predicate
Execution trace:
(0,0): anon0
(0,0): anon5_Else
-SmallTests.dfy(673,9): Error: cannot establish the existence of LHS values that satisfy the such-that predicate
+SmallTests.dfy(675,8): Error: cannot establish the existence of LHS values that satisfy the such-that predicate
Execution trace:
(0,0): anon0
Dafny program verifier finished with 104 verified, 35 errors
+SmallTests.dfy.tmp.dprint.dfy(369,4): Warning: /!\ No trigger covering all quantified variables found.
Dafny program verifier finished with 0 verified, 0 errors
diff --git a/Test/dafny0/SplitExpr.dfy.expect b/Test/dafny0/SplitExpr.dfy.expect
index b7ef524f..29dd6eda 100644
--- a/Test/dafny0/SplitExpr.dfy.expect
+++ b/Test/dafny0/SplitExpr.dfy.expect
@@ -1,5 +1,5 @@
-SplitExpr.dfy(92,15): Error: loop invariant violation
-SplitExpr.dfy(86,44): Related location
+SplitExpr.dfy(92,14): Error: loop invariant violation
+SplitExpr.dfy(86,43): Related location
Execution trace:
SplitExpr.dfy(91,3): anon7_LoopHead
diff --git a/Test/dafny0/StatementExpressions.dfy.expect b/Test/dafny0/StatementExpressions.dfy.expect
index 313c8884..936a3954 100644
--- a/Test/dafny0/StatementExpressions.dfy.expect
+++ b/Test/dafny0/StatementExpressions.dfy.expect
@@ -1,22 +1,22 @@
-StatementExpressions.dfy(55,12): Error: cannot prove termination; try supplying a decreases clause
+StatementExpressions.dfy(55,11): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
(0,0): anon6_Then
(0,0): anon8_Then
-StatementExpressions.dfy(59,14): Error: assertion violation
+StatementExpressions.dfy(59,13): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon6_Then
StatementExpressions.dfy(53,7): anon8_Else
-StatementExpressions.dfy(77,6): Error: possible division by zero
+StatementExpressions.dfy(77,5): Error: possible division by zero
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-StatementExpressions.dfy(88,5): Error: value assigned to a nat must be non-negative
+ (0,0): anon4_Else
+StatementExpressions.dfy(88,4): Error: value assigned to a nat must be non-negative
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-StatementExpressions.dfy(98,18): Error: cannot prove termination; try supplying a decreases clause
+ (0,0): anon4_Else
+StatementExpressions.dfy(98,17): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
(0,0): anon6_Then
diff --git a/Test/dafny0/Superposition.dfy.expect b/Test/dafny0/Superposition.dfy.expect
index 4b8e354f..04ec2f7d 100644
--- a/Test/dafny0/Superposition.dfy.expect
+++ b/Test/dafny0/Superposition.dfy.expect
@@ -10,19 +10,19 @@ Verifying CheckWellformed$$_0_M0.C.P ...
Verifying CheckWellformed$$_0_M0.C.Q ...
[5 proof obligations] error
-Superposition.dfy(27,15): Error BP5003: A postcondition might not hold on this return path.
-Superposition.dfy(28,26): Related location: This is the postcondition that might not hold.
+Superposition.dfy(27,14): Error BP5003: A postcondition might not hold on this return path.
+Superposition.dfy(28,25): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
+ (0,0): anon7_Else
Verifying CheckWellformed$$_0_M0.C.R ...
[5 proof obligations] error
-Superposition.dfy(33,15): Error BP5003: A postcondition might not hold on this return path.
-Superposition.dfy(34,26): Related location: This is the postcondition that might not hold.
+Superposition.dfy(33,14): Error BP5003: A postcondition might not hold on this return path.
+Superposition.dfy(34,25): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
+ (0,0): anon7_Else
Verifying CheckWellformed$$_1_M1.C.M ...
[0 proof obligations] verified
@@ -32,13 +32,13 @@ Verifying Impl$$_1_M1.C.M ...
Verifying CheckWellformed$$_1_M1.C.P ...
[2 proof obligations] error
-Superposition.dfy(50,25): Error BP5003: A postcondition might not hold on this return path.
-Superposition.dfy[M1](22,26): Related location: This is the postcondition that might not hold.
+Superposition.dfy(50,24): Error BP5003: A postcondition might not hold on this return path.
+Superposition.dfy[M1](22,25): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
- (0,0): anon7_Else
- (0,0): anon9_Then
- (0,0): anon6
+ (0,0): anon9_Else
+ (0,0): anon11_Then
+ (0,0): anon8
Verifying CheckWellformed$$_1_M1.C.Q ...
[0 proof obligations] verified
diff --git a/Test/dafny0/Termination.dfy.expect b/Test/dafny0/Termination.dfy.expect
index 98aa0cd8..69cb360d 100644
--- a/Test/dafny0/Termination.dfy.expect
+++ b/Test/dafny0/Termination.dfy.expect
@@ -1,20 +1,20 @@
-Termination.dfy[TerminationRefinement1](441,6): Error: failure to decrease termination measure
+Termination.dfy[TerminationRefinement1](441,5): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
-Termination.dfy(361,47): Error: failure to decrease termination measure
+Termination.dfy(361,46): Error: failure to decrease termination measure
Execution trace:
(0,0): anon0
- (0,0): anon7_Else
- (0,0): anon8_Then
(0,0): anon9_Else
-Termination.dfy(108,3): Error: cannot prove termination; try supplying a decreases clause for the loop
+ (0,0): anon10_Then
+ (0,0): anon11_Else
+Termination.dfy(108,2): Error: cannot prove termination; try supplying a decreases clause for the loop
Execution trace:
(0,0): anon0
Termination.dfy(108,3): anon6_LoopHead
(0,0): anon6_LoopBody
Termination.dfy(108,3): anon7_Else
Termination.dfy(108,3): anon8_Else
-Termination.dfy(116,3): Error: cannot prove termination; try supplying a decreases clause for the loop
+Termination.dfy(116,2): Error: cannot prove termination; try supplying a decreases clause for the loop
Execution trace:
(0,0): anon0
Termination.dfy(116,3): anon8_LoopHead
@@ -23,7 +23,7 @@ Execution trace:
(0,0): anon10_Then
(0,0): anon5
Termination.dfy(116,3): anon11_Else
-Termination.dfy(125,3): Error: decreases expression might not decrease
+Termination.dfy(125,2): Error: decreases expression might not decrease
Execution trace:
(0,0): anon0
Termination.dfy(125,3): anon8_LoopHead
@@ -32,7 +32,7 @@ Execution trace:
(0,0): anon10_Then
(0,0): anon5
Termination.dfy(125,3): anon11_Else
-Termination.dfy(126,17): Error: decreases expression must be bounded below by 0 at end of loop iteration
+Termination.dfy(126,16): Error: decreases expression must be bounded below by 0 at end of loop iteration
Execution trace:
(0,0): anon0
Termination.dfy(125,3): anon8_LoopHead
@@ -41,13 +41,13 @@ Execution trace:
(0,0): anon10_Then
(0,0): anon5
Termination.dfy(125,3): anon11_Else
-Termination.dfy(255,35): Error: cannot prove termination; try supplying a decreases clause
+Termination.dfy(255,34): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon6_Else
- (0,0): anon7_Else
- (0,0): anon8_Then
-Termination.dfy(296,3): Error: decreases expression might not decrease
+ (0,0): anon8_Else
+ (0,0): anon9_Else
+ (0,0): anon10_Then
+Termination.dfy(296,2): Error: decreases expression might not decrease
Execution trace:
Termination.dfy(296,3): anon9_LoopHead
(0,0): anon9_LoopBody
diff --git a/Test/dafny0/Trait/TraitBasix.dfy.expect b/Test/dafny0/Trait/TraitBasix.dfy.expect
index 69af0dc5..dbb11c21 100644
--- a/Test/dafny0/Trait/TraitBasix.dfy.expect
+++ b/Test/dafny0/Trait/TraitBasix.dfy.expect
@@ -1,4 +1,4 @@
-TraitBasix.dfy(91,24): Error: Undeclared top-level type or type parameter: IX (did you forget to qualify a name?)
+TraitBasix.dfy(91,24): Error: Undeclared top-level type or type parameter: IX (did you forget to qualify a name or declare a module import 'opened?')
TraitBasix.dfy(77,8): Error: field 'x' is inherited from trait 'I2' and is not allowed to be re-declared
TraitBasix.dfy(70,8): Error: class 'I0Child' does not implement trait method 'I2.Customizable'
TraitBasix.dfy(80,8): Error: class 'I0Child2' does not implement trait function 'I2.F'
diff --git a/Test/dafny0/Trait/TraitExample.dfy b/Test/dafny0/Trait/TraitExample.dfy
index be38bfe5..9474c7ba 100644
--- a/Test/dafny0/Trait/TraitExample.dfy
+++ b/Test/dafny0/Trait/TraitExample.dfy
@@ -4,79 +4,148 @@
trait Automobile {
ghost var Repr: set<object>
predicate Valid()
- reads this //, Repr
+ reads this, Repr
ensures Valid() ==> this in Repr
function method Brand(): string
var position: int
method Drive()
requires Valid()
- modifies this // Repr
+ modifies Repr
+ ensures Valid() && fresh(Repr - old(Repr))
ensures old(position) <= position
}
+class Fiat extends Automobile {
+ predicate Valid()
+ reads this, Repr
+ ensures Valid() ==> this in Repr
+ {
+ this in Repr && null !in Repr && position <= 100
+ }
+ constructor (pos: int)
+ requires pos <= 100
+ modifies this
+ ensures Valid() && fresh(Repr - {this}) && position == pos
+ {
+ position, Repr := pos, {this};
+ }
+ function method Brand(): string {
+ "Fiat"
+ }
+ method Drive()
+ requires Valid()
+ modifies Repr
+ ensures Valid() && fresh(Repr - old(Repr))
+ ensures old(position) <= position
+ {
+ position := if position < 97 then position + 3 else 100;
+ }
+}
+
class Volvo extends Automobile {
+ var odometer: Odometer
predicate Valid()
- reads this //, Repr
+ reads this, Repr
ensures Valid() ==> this in Repr
{
- this in Repr
+ this in Repr && null !in Repr && odometer in Repr &&
+ position % 10 == 0 && // position is always a multiple of 10
+ odometer.value == position
}
- constructor()
+ constructor ()
modifies this
- ensures Valid()
+ ensures Valid() && fresh(Repr - {this})
{
- Repr := {this};
+ position, Repr := 0, {this};
+ odometer := new Odometer();
+ Repr := Repr + {odometer};
}
function method Brand(): string {
"Volvo"
}
method Drive()
-// requires Valid()
- modifies this // Repr
- ensures old(position) <= position
+ requires Valid()
+ modifies Repr
+ ensures Valid() && fresh(Repr - old(Repr))
+ ensures old(position) < position // always promises to make a move
{
position := position + 10;
+ odometer.Advance(10);
}
}
-class Fiat extends Automobile {
+class Odometer {
+ var value: int
+ constructor ()
+ modifies this
+ ensures value == 0
+ {
+ value := 0;
+ }
+ method Advance(d: int)
+ requires 0 <= d
+ modifies this
+ ensures value == old(value) + d
+ {
+ value := value + d;
+ }
+}
+
+class Catacar extends Automobile {
+ var f: Fiat
+ var v: Volvo
predicate Valid()
- reads this // , Repr
+ reads this, Repr
ensures Valid() ==> this in Repr
{
- this in Repr
+ this in Repr && null !in Repr &&
+ f in Repr && this !in f.Repr && f.Repr <= Repr && f.Valid() &&
+ v in Repr && this !in v.Repr && v.Repr <= Repr && v.Valid() &&
+ f.Repr !! v.Repr &&
+ position == f.position + v.position
}
- constructor()
+ constructor ()
modifies this
- ensures Valid()
+ ensures Valid() && fresh(Repr - {this})
{
Repr := {this};
+ f := new Fiat(0); Repr := Repr + f.Repr;
+ v := new Volvo(); Repr := Repr + v.Repr;
+ position := v.position;
}
function method Brand(): string {
- "Fiat"
+ "Catacar"
}
method Drive()
-// requires Valid()
- modifies this // Repr
+ requires Valid()
+ modifies Repr
+ ensures Valid() && fresh(Repr - old(Repr))
ensures old(position) <= position
{
- position := position + 3;
+ f := new Fiat(f.position);
+ f.Drive(); v.Drive();
+ Repr := Repr + v.Repr + f.Repr;
+ position := f.position + v.position;
}
}
method Main() {
var auto: Automobile;
+ auto := new Fiat(0);
+ WorkIt(auto);
auto := new Volvo();
WorkIt(auto);
- auto := new Fiat();
+ auto := new Catacar();
WorkIt(auto);
}
method WorkIt(auto: Automobile)
requires auto != null && auto.Valid()
- modifies auto // auto.Repr
+ modifies auto.Repr
{
auto.Drive();
+ auto.Drive();
+ assert old(auto.position) <= auto.position;
print auto.Brand(), ": ", auto.position, "\n";
- auto.position := 0;
+ auto.position := 18; // note, this may destroy the automobile's consistency condition (given by Valid)
}
diff --git a/Test/dafny0/Trait/TraitExample.dfy.expect b/Test/dafny0/Trait/TraitExample.dfy.expect
index 4fc71fb5..337b8f2f 100644
--- a/Test/dafny0/Trait/TraitExample.dfy.expect
+++ b/Test/dafny0/Trait/TraitExample.dfy.expect
@@ -1,7 +1,8 @@
-Dafny program verifier finished with 25 verified, 0 errors
+Dafny program verifier finished with 38 verified, 0 errors
Program compiled successfully
Running...
-Volvo: 10
-Fiat: 3
+Fiat: 6
+Volvo: 20
+Catacar: 26
diff --git a/Test/dafny0/Trait/TraitUsingParentMembers.dfy.expect b/Test/dafny0/Trait/TraitUsingParentMembers.dfy.expect
index 9960c1d9..1517dee4 100644
--- a/Test/dafny0/Trait/TraitUsingParentMembers.dfy.expect
+++ b/Test/dafny0/Trait/TraitUsingParentMembers.dfy.expect
@@ -1,4 +1,4 @@
-TraitUsingParentMembers.dfy(10,8): Error: assignment may update an array element not in the enclosing context's modifies clause
+TraitUsingParentMembers.dfy(10,7): Error: assignment may update an array element not in the enclosing context's modifies clause
Execution trace:
(0,0): anon0
(0,0): anon5_Then
diff --git a/Test/dafny0/Trait/TraitsDecreases.dfy b/Test/dafny0/Trait/TraitsDecreases.dfy
index 53ce28be..8ab3672a 100644
--- a/Test/dafny0/Trait/TraitsDecreases.dfy
+++ b/Test/dafny0/Trait/TraitsDecreases.dfy
@@ -106,3 +106,49 @@ class CC extends TT {
decreases *
{ }
}
+
+
+// The following module contains various regression tests
+module More {
+ trait A0 {
+ predicate P() decreases 5
+ }
+ class B0 extends A0 {
+ predicate P() // error: rank is not lower
+ }
+
+ trait A1 {
+ predicate P() decreases 5
+ }
+ class B1 extends A1 {
+ predicate P() reads this // error: rank is not lower
+ }
+
+ trait A2 {
+ predicate P(x: int)
+ }
+ class B2 extends A2 {
+ predicate P(x: int) reads this // error: rank is not lower
+ }
+
+ trait A3 {
+ predicate P() reads this
+ }
+ class B3 extends A3 {
+ predicate P() // error: rank is not lower
+ }
+
+ trait A4 {
+ predicate P(x: int) decreases 5
+ }
+ class B4 extends A4 {
+ predicate P(x: int) // error: rank is not lower
+ }
+
+ trait A5 {
+ method M(x: int) decreases 5
+ }
+ class B5 extends A5 {
+ method M(x: int) // error: rank is not lower
+ }
+}
diff --git a/Test/dafny0/Trait/TraitsDecreases.dfy.expect b/Test/dafny0/Trait/TraitsDecreases.dfy.expect
index 6c76f9a8..7d646bd1 100644
--- a/Test/dafny0/Trait/TraitsDecreases.dfy.expect
+++ b/Test/dafny0/Trait/TraitsDecreases.dfy.expect
@@ -1,17 +1,35 @@
-TraitsDecreases.dfy(57,10): Error: method's decreases clause must be below or equal to that in the trait
+TraitsDecreases.dfy(117,14): Error: predicate's decreases clause must be below or equal to that in the trait
Execution trace:
(0,0): anon0
-TraitsDecreases.dfy(69,10): Error: method's decreases clause must be below or equal to that in the trait
+TraitsDecreases.dfy(124,14): Error: predicate's decreases clause must be below or equal to that in the trait
Execution trace:
(0,0): anon0
-TraitsDecreases.dfy(72,10): Error: method's decreases clause must be below or equal to that in the trait
+TraitsDecreases.dfy(131,14): Error: predicate's decreases clause must be below or equal to that in the trait
Execution trace:
(0,0): anon0
-TraitsDecreases.dfy(78,10): Error: method's decreases clause must be below or equal to that in the trait
+TraitsDecreases.dfy(138,14): Error: predicate's decreases clause must be below or equal to that in the trait
Execution trace:
(0,0): anon0
-TraitsDecreases.dfy(88,10): Error: method's decreases clause must be below or equal to that in the trait
+TraitsDecreases.dfy(145,14): Error: predicate's decreases clause must be below or equal to that in the trait
+Execution trace:
+ (0,0): anon0
+TraitsDecreases.dfy(152,11): Error: method's decreases clause must be below or equal to that in the trait
+Execution trace:
+ (0,0): anon0
+TraitsDecreases.dfy(57,9): Error: method's decreases clause must be below or equal to that in the trait
+Execution trace:
+ (0,0): anon0
+TraitsDecreases.dfy(69,9): Error: method's decreases clause must be below or equal to that in the trait
+Execution trace:
+ (0,0): anon0
+TraitsDecreases.dfy(72,9): Error: method's decreases clause must be below or equal to that in the trait
+Execution trace:
+ (0,0): anon0
+TraitsDecreases.dfy(78,9): Error: method's decreases clause must be below or equal to that in the trait
+Execution trace:
+ (0,0): anon0
+TraitsDecreases.dfy(88,9): Error: method's decreases clause must be below or equal to that in the trait
Execution trace:
(0,0): anon0
-Dafny program verifier finished with 63 verified, 5 errors
+Dafny program verifier finished with 75 verified, 11 errors
diff --git a/Test/dafny0/TriggerInPredicate.dfy b/Test/dafny0/TriggerInPredicate.dfy
new file mode 100644
index 00000000..b9c372dc
--- /dev/null
+++ b/Test/dafny0/TriggerInPredicate.dfy
@@ -0,0 +1,19 @@
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+predicate A(x: bool, y: bool) { x }
+
+predicate B(x: bool, z: bool) { forall y {:trigger A(x, y)} :: A(x, y) && z }
+
+// Inlining is disabled here to prevent pollution of the trigger in B
+method C() requires B(true || false, true) {}
+
+// Inlining should work fine here
+method C'() requires B(true, true) {}
+
+// Inlining should work fine here
+method C''() requires B(true, true && false) {}
+
+// Local Variables:
+// dafny-prover-local-args: ("/autoTriggers:1")
+// End:
diff --git a/Test/dafny0/TriggerInPredicate.dfy.expect b/Test/dafny0/TriggerInPredicate.dfy.expect
new file mode 100644
index 00000000..b3d4ff34
--- /dev/null
+++ b/Test/dafny0/TriggerInPredicate.dfy.expect
@@ -0,0 +1,7 @@
+TriggerInPredicate.dfy(6,32): Info: Not generating triggers for "A(x, y)".
+TriggerInPredicate.dfy(6,32): Info: Not generating triggers for "z".
+TriggerInPredicate.dfy(9,20): Info: Some instances of this call cannot safely be inlined.
+TriggerInPredicate.dfy(9,20): Info: Some instances of this call cannot safely be inlined.
+TriggerInPredicate.dfy(9,20): Info: Some instances of this call cannot safely be inlined.
+
+Dafny program verifier finished with 8 verified, 0 errors
diff --git a/Test/dafny0/Tuples.dfy.expect b/Test/dafny0/Tuples.dfy.expect
index 13c706d3..9b5f3a83 100644
--- a/Test/dafny0/Tuples.dfy.expect
+++ b/Test/dafny0/Tuples.dfy.expect
@@ -1,7 +1,7 @@
-Tuples.dfy(22,19): Error: assertion violation
+Tuples.dfy(22,18): Error: assertion violation
Execution trace:
(0,0): anon0
-Tuples.dfy(24,21): Error: possible division by zero
+Tuples.dfy(24,20): Error: possible division by zero
Execution trace:
(0,0): anon0
diff --git a/Test/dafny0/TypeAntecedents.dfy.expect b/Test/dafny0/TypeAntecedents.dfy.expect
index d6eb08e4..2e2f606d 100644
--- a/Test/dafny0/TypeAntecedents.dfy.expect
+++ b/Test/dafny0/TypeAntecedents.dfy.expect
@@ -1,8 +1,8 @@
-TypeAntecedents.dfy(35,13): Error: assertion violation
+TypeAntecedents.dfy(35,12): Error: assertion violation
Execution trace:
(0,0): anon0
-TypeAntecedents.dfy(58,1): Error BP5003: A postcondition might not hold on this return path.
-TypeAntecedents.dfy(57,15): Related location: This is the postcondition that might not hold.
+TypeAntecedents.dfy(58,0): Error BP5003: A postcondition might not hold on this return path.
+TypeAntecedents.dfy(57,14): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
(0,0): anon25_Then
@@ -16,7 +16,7 @@ Execution trace:
(0,0): anon34_Then
(0,0): anon35_Then
(0,0): anon24
-TypeAntecedents.dfy(66,16): Error: assertion violation
+TypeAntecedents.dfy(66,15): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon25_Else
diff --git a/Test/dafny0/TypeParameters.dfy.expect b/Test/dafny0/TypeParameters.dfy.expect
index 3d00e89a..aca0694d 100644
--- a/Test/dafny0/TypeParameters.dfy.expect
+++ b/Test/dafny0/TypeParameters.dfy.expect
@@ -1,43 +1,43 @@
-TypeParameters.dfy(47,22): Error: assertion violation
+TypeParameters.dfy(47,21): Error: assertion violation
Execution trace:
(0,0): anon0
-TypeParameters.dfy(69,27): Error: assertion violation
+TypeParameters.dfy(69,26): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon3_Then
(0,0): anon2
-TypeParameters.dfy(156,12): Error: assertion violation
-TypeParameters.dfy(156,28): Related location
+TypeParameters.dfy(156,11): Error: assertion violation
+TypeParameters.dfy(156,27): Related location
Execution trace:
(0,0): anon0
(0,0): anon20_Then
TypeParameters.dfy(156,32): anon21_Else
(0,0): anon5
-TypeParameters.dfy(158,12): Error: assertion violation
-TypeParameters.dfy(158,33): Related location
+TypeParameters.dfy(158,11): Error: assertion violation
+TypeParameters.dfy(158,32): Related location
Execution trace:
(0,0): anon0
(0,0): anon23_Then
TypeParameters.dfy(158,37): anon24_Else
(0,0): anon11
-TypeParameters.dfy(160,12): Error: assertion violation
-TypeParameters.dfy(160,20): Related location
+TypeParameters.dfy(160,11): Error: assertion violation
+TypeParameters.dfy(160,19): Related location
Execution trace:
(0,0): anon0
(0,0): anon25_Then
-TypeParameters.dfy(162,12): Error: assertion violation
-TypeParameters.dfy(147,5): Related location
-TypeParameters.dfy(162,21): Related location
+TypeParameters.dfy(162,11): Error: assertion violation
+TypeParameters.dfy(147,4): Related location
+TypeParameters.dfy(162,20): Related location
Execution trace:
(0,0): anon0
(0,0): anon26_Then
-TypeParameters.dfy(164,12): Error: assertion violation
-TypeParameters.dfy(149,8): Related location
+TypeParameters.dfy(164,11): Error: assertion violation
+TypeParameters.dfy(149,7): Related location
Execution trace:
(0,0): anon0
(0,0): anon27_Then
-TypeParameters.dfy(178,15): Error BP5005: This loop invariant might not be maintained by the loop.
-TypeParameters.dfy(178,38): Related location
+TypeParameters.dfy(178,14): Error BP5005: This loop invariant might not be maintained by the loop.
+TypeParameters.dfy(178,37): Related location
Execution trace:
(0,0): anon0
TypeParameters.dfy(171,3): anon16_LoopHead
diff --git a/Test/dafny0/TypeTests.dfy b/Test/dafny0/TypeTests.dfy
index b44f4d68..a9d473f6 100644
--- a/Test/dafny0/TypeTests.dfy
+++ b/Test/dafny0/TypeTests.dfy
@@ -39,7 +39,7 @@ datatype ReverseOrder_TheCounterpart<T> =
// ---------------------
-class ArrayTests {
+module ArrayTests {
ghost method G(a: array<int>)
requires a != null && 10 <= a.Length;
modifies a;
@@ -167,31 +167,33 @@ module Expl_Module {
// --------------------- more ghost tests, for assign-such-that statements
-method M()
-{
- ghost var b: bool;
- ghost var k: int, l: int;
- var m: int;
-
- k :| k < 10;
- k, m :| 0 <= k < m; // error: LHS has non-ghost and RHS has ghost
- m :| m < 10;
-
- // Because of the ghost guard, these 'if' statements are ghost contexts, so only
- // assignments to ghosts are allowed.
- if (b) {
- k :| k < 10; // should be allowed
- k, l :| 0 <= k < l; // ditto
- }
- if (b) {
- m :| m < 10; // error: not allowed in ghost context
- k, m :| 0 <= k < m; // error: not allowed in ghost context
+module MoreGhostTests {
+ method M()
+ {
+ ghost var b: bool;
+ ghost var k: int, l: int;
+ var m: int;
+
+ k :| k < 10;
+ k, m :| 0 <= k < m; // error: LHS has non-ghost and RHS has ghost
+ m :| m < 10;
+
+ // Because of the ghost guard, these 'if' statements are ghost contexts, so only
+ // assignments to ghosts are allowed.
+ if (b) {
+ k :| k < 10; // should be allowed
+ k, l :| 0 <= k < l; // ditto
+ }
+ if (b) {
+ m :| m < 10; // error: not allowed in ghost context
+ k, m :| 0 <= k < m; // error: not allowed in ghost context
+ }
}
-}
-ghost method GhostM() returns (x: int)
-{
- x :| true; // no problem (but there once was a problem with this case, where an error was generated for no reason)
+ ghost method GhostM() returns (x: int)
+ {
+ x :| true; // no problem (but there once was a problem with this case, where an error was generated for no reason)
+ }
}
// ------------------ cycles that could arise from proxy assignments ---------
diff --git a/Test/dafny0/TypeTests.dfy.expect b/Test/dafny0/TypeTests.dfy.expect
index 500b1af9..de0bfbed 100644
--- a/Test/dafny0/TypeTests.dfy.expect
+++ b/Test/dafny0/TypeTests.dfy.expect
@@ -1,6 +1,10 @@
-TypeTests.dfy(205,15): Error: incorrect type of datatype constructor argument (found ? -> ?, expected ? -> Dt<?>)
-TypeTests.dfy(211,15): Error: incorrect type of datatype constructor argument (found ? -> ?, expected ? -> Dt<?>)
-TypeTests.dfy(218,6): Error: RHS (of type set<?>) not assignable to LHS (of type ?)
+TypeTests.dfy(47,9): Error: Assignment to array element is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+TypeTests.dfy(178,7): Error: non-ghost variable cannot be assigned a value that depends on a ghost
+TypeTests.dfy(188,6): Error: cannot assign to non-ghost variable in a ghost context
+TypeTests.dfy(189,9): Error: cannot assign to non-ghost variable in a ghost context
+TypeTests.dfy(207,15): Error: incorrect type of datatype constructor argument (found ? -> ?, expected ? -> Dt<?>)
+TypeTests.dfy(213,15): Error: incorrect type of datatype constructor argument (found ? -> ?, expected ? -> Dt<?>)
+TypeTests.dfy(220,6): Error: RHS (of type set<?>) not assignable to LHS (of type ?)
TypeTests.dfy(7,17): Error: type mismatch for argument 0 (function expects C, got D)
TypeTests.dfy(7,20): Error: type mismatch for argument 1 (function expects D, got C)
TypeTests.dfy(8,15): Error: type mismatch for argument 0 (function expects C, got int)
@@ -8,7 +12,6 @@ TypeTests.dfy(8,18): Error: type mismatch for argument 1 (function expects D, go
TypeTests.dfy(14,16): Error: incorrect type of method in-parameter 0 (expected int, got bool)
TypeTests.dfy(15,12): Error: incorrect type of method out-parameter 0 (expected int, got C)
TypeTests.dfy(15,12): Error: incorrect type of method out-parameter 1 (expected C, got int)
-TypeTests.dfy(47,9): Error: Assignment to array element is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
TypeTests.dfy(56,6): Error: Duplicate local-variable name: z
TypeTests.dfy(58,6): Error: Duplicate local-variable name: x
TypeTests.dfy(61,8): Error: Duplicate local-variable name: x
@@ -56,8 +59,5 @@ TypeTests.dfy(151,13): Error: sorry, cannot instantiate type parameter with a su
TypeTests.dfy(152,2): Error: sorry, cannot instantiate type parameter with a subrange type
TypeTests.dfy(153,16): Error: sorry, cannot instantiate type parameter with a subrange type
TypeTests.dfy(154,14): Error: sorry, cannot instantiate type parameter with a subrange type
-TypeTests.dfy(177,15): Error: ghost variables are allowed only in specification contexts
-TypeTests.dfy(187,4): Error: cannot assign to non-ghost variable in a ghost context
-TypeTests.dfy(188,7): Error: cannot assign to non-ghost variable in a ghost context
TypeTests.dfy(21,9): Error: because of cyclic dependencies among constructor argument types, no instances of datatype 'NeverendingList' can be constructed
62 resolution/type errors detected in TypeTests.dfy
diff --git a/Test/dafny0/UnfoldingPerformance.dfy b/Test/dafny0/UnfoldingPerformance.dfy
new file mode 100644
index 00000000..3eed689a
--- /dev/null
+++ b/Test/dafny0/UnfoldingPerformance.dfy
@@ -0,0 +1,61 @@
+// RUN: %dafny /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+class C {
+ var x: nat
+ function method IgnoreFuel(): nat
+ reads this
+ {
+ x
+ }
+}
+
+function method Fib(n: int): int
+{
+ if n < 2 then n else Fib(n-2) + Fib(n-1)
+}
+
+method Test0() {
+ var c := new C;
+ var f := Fib(c.IgnoreFuel());
+ // with the bug, the following wwould take a long time before it reports an error
+ // after the bug fix, this still fails, but quickly
+ assert 0 <= f;
+}
+
+method Test1() {
+ var c := new C;
+ var f := Fib(c.x);
+ // the following assert will also fail, but quickly
+ assert 0 <= f;
+}
+
+method Test2() {
+ var c := new C;
+ c.x := 10;
+ var f := Fib(c.IgnoreFuel());
+ assert 0 <= f; // passes
+}
+
+method Test3() {
+ var c := new C;
+ c.x := 10;
+ var f := Fib(c.x);
+ assert 0 <= f; // passes
+}
+
+method Test4() {
+ var c := new C;
+ c.x := 10;
+ var f := Fib(c.x - 2);
+ assert 0 <= f; // fails
+}
+
+method Test5(x: int)
+ requires 9 <= x - 1 && x + 1 <= 11
+{
+ var c := new C;
+ c.x := x;
+ var f := Fib(c.x);
+ assert 0 <= f; // succeeds?
+}
diff --git a/Test/dafny0/UnfoldingPerformance.dfy.expect b/Test/dafny0/UnfoldingPerformance.dfy.expect
new file mode 100644
index 00000000..220fecc5
--- /dev/null
+++ b/Test/dafny0/UnfoldingPerformance.dfy.expect
@@ -0,0 +1,11 @@
+UnfoldingPerformance.dfy(23,11): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+UnfoldingPerformance.dfy(30,11): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+UnfoldingPerformance.dfy(51,11): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 11 verified, 3 errors
diff --git a/Test/dafny0/UserSpecifiedTypeParameters.dfy.expect b/Test/dafny0/UserSpecifiedTypeParameters.dfy.expect
index 2504fbfb..347252aa 100644
--- a/Test/dafny0/UserSpecifiedTypeParameters.dfy.expect
+++ b/Test/dafny0/UserSpecifiedTypeParameters.dfy.expect
@@ -1,8 +1,8 @@
UserSpecifiedTypeParameters.dfy(27,12): Error: the type of this variable is underspecified
UserSpecifiedTypeParameters.dfy(27,26): Error: type variable 'T' in the function call to 'H' could not be determined
UserSpecifiedTypeParameters.dfy(27,26): Error: type variable 'U' in the function call to 'H' could not be determined
-UserSpecifiedTypeParameters.dfy(48,22): Error: Undeclared top-level type or type parameter: b (did you forget to qualify a name?)
-UserSpecifiedTypeParameters.dfy(48,26): Error: Undeclared top-level type or type parameter: c (did you forget to qualify a name?)
+UserSpecifiedTypeParameters.dfy(48,22): Error: Undeclared top-level type or type parameter: b (did you forget to qualify a name or declare a module import 'opened?')
+UserSpecifiedTypeParameters.dfy(48,26): Error: Undeclared top-level type or type parameter: c (did you forget to qualify a name or declare a module import 'opened?')
UserSpecifiedTypeParameters.dfy(48,18): Error: variable 'a' does not take any type parameters
UserSpecifiedTypeParameters.dfy(48,30): Error: non-function expression (of type int) is called with parameters
UserSpecifiedTypeParameters.dfy(48,16): Error: wrong number of arguments to function application (function 'F' expects 2, got 1)
diff --git a/Test/dafny0/columns.dfy b/Test/dafny0/columns.dfy
new file mode 100644
index 00000000..72c9ab81
--- /dev/null
+++ b/Test/dafny0/columns.dfy
@@ -0,0 +1,12 @@
+// RUN: %dafny "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// Dafny counts columns from 0, but Boogie from one, so for a while there were small bugs with that.
+
+predicate P(x: int)
+
+static method A(x:int) requires x > 0 { // error os 's'
+ assert (forall y: int :: P(y)); // error on '('
+ assert x != 1; // error on '!'
+ assert x in {}; // error on 'i'
+}
diff --git a/Test/dafny0/columns.dfy.expect b/Test/dafny0/columns.dfy.expect
new file mode 100644
index 00000000..0a99be69
--- /dev/null
+++ b/Test/dafny0/columns.dfy.expect
@@ -0,0 +1,12 @@
+columns.dfy(8,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+columns.dfy(9,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+columns.dfy(10,11): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+columns.dfy(11,11): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 2 verified, 3 errors
diff --git a/Test/dafny0/fun-with-slices.dfy b/Test/dafny0/fun-with-slices.dfy
new file mode 100644
index 00000000..3d8da242
--- /dev/null
+++ b/Test/dafny0/fun-with-slices.dfy
@@ -0,0 +1,19 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This test was contributed by Bryan. It has shown some instabilities in the past.
+
+method seqIntoArray<A>(s: seq<A>, a: array<A>, index: nat)
+ requires a != null
+ requires index + |s| <= a.Length
+ modifies a
+ ensures a[..] == old(a[0..index]) + s + old(a[index + |s|..]) {
+ var i := index;
+
+ while i < index + |s|
+ invariant index <= i <= index + |s| <= a.Length
+ invariant a[..] == old(a[0..index]) + s[0..(i-index)] + old(a[i..]) {
+ a[i] := s[i - index];
+ i := i + 1;
+ }
+}
diff --git a/Test/dafny0/fun-with-slices.dfy.expect b/Test/dafny0/fun-with-slices.dfy.expect
new file mode 100644
index 00000000..069e7767
--- /dev/null
+++ b/Test/dafny0/fun-with-slices.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 2 verified, 0 errors
diff --git a/Test/dafny0/one-message-per-failed-precondition.dfy b/Test/dafny0/one-message-per-failed-precondition.dfy
new file mode 100644
index 00000000..ef4f5bd6
--- /dev/null
+++ b/Test/dafny0/one-message-per-failed-precondition.dfy
@@ -0,0 +1,20 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// When a function call violates two preconditions at the same time, it causes
+// two errors to be reported for the same token
+
+method A(x: int)
+ requires x > 0
+ requires x < 0
+{}
+
+method B(x: int) {
+ A(x);
+}
+
+function fA(x: int): int
+ requires x > 0
+ requires x < 0 { 1 }
+
+function fB(x: int): int { fA(x) }
diff --git a/Test/dafny0/one-message-per-failed-precondition.dfy.expect b/Test/dafny0/one-message-per-failed-precondition.dfy.expect
new file mode 100644
index 00000000..0a76965e
--- /dev/null
+++ b/Test/dafny0/one-message-per-failed-precondition.dfy.expect
@@ -0,0 +1,20 @@
+one-message-per-failed-precondition.dfy(13,3): Error BP5002: A precondition for this call might not hold.
+one-message-per-failed-precondition.dfy(9,13): Related location: This is the precondition that might not hold.
+Execution trace:
+ (0,0): anon0
+one-message-per-failed-precondition.dfy(13,3): Error BP5002: A precondition for this call might not hold.
+one-message-per-failed-precondition.dfy(8,13): Related location: This is the precondition that might not hold.
+Execution trace:
+ (0,0): anon0
+one-message-per-failed-precondition.dfy(20,27): Error: possible violation of function precondition
+one-message-per-failed-precondition.dfy(18,13): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon4_Else
+one-message-per-failed-precondition.dfy(20,27): Error: possible violation of function precondition
+one-message-per-failed-precondition.dfy(17,13): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon4_Else
+
+Dafny program verifier finished with 4 verified, 4 errors
diff --git a/Test/dafny0/snapshots/Snapshots0.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots0.v0.dfy
index 73db9f9c..73db9f9c 100644
--- a/Test/dafny0/snapshots/Snapshots0.v0.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots0.v0.dfy
diff --git a/Test/dafny0/snapshots/Snapshots0.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots0.v1.dfy
index db9fc01a..db9fc01a 100644
--- a/Test/dafny0/snapshots/Snapshots0.v1.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots0.v1.dfy
diff --git a/Test/dafny0/snapshots/Snapshots1.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots1.v0.dfy
index 34d066c3..34d066c3 100644
--- a/Test/dafny0/snapshots/Snapshots1.v0.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots1.v0.dfy
diff --git a/Test/dafny0/snapshots/Snapshots1.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots1.v1.dfy
index 184ac65d..184ac65d 100644
--- a/Test/dafny0/snapshots/Snapshots1.v1.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots1.v1.dfy
diff --git a/Test/dafny0/snapshots/Snapshots2.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots2.v0.dfy
index 727e177d..727e177d 100644
--- a/Test/dafny0/snapshots/Snapshots2.v0.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots2.v0.dfy
diff --git a/Test/dafny0/snapshots/Snapshots2.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots2.v1.dfy
index 02a91b52..02a91b52 100644
--- a/Test/dafny0/snapshots/Snapshots2.v1.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots2.v1.dfy
diff --git a/Test/dafny0/snapshots/Snapshots3.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots3.v0.dfy
index 72607412..72607412 100644
--- a/Test/dafny0/snapshots/Snapshots3.v0.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots3.v0.dfy
diff --git a/Test/dafny0/snapshots/Snapshots3.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots3.v1.dfy
index 3b186318..3b186318 100644
--- a/Test/dafny0/snapshots/Snapshots3.v1.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots3.v1.dfy
diff --git a/Test/dafny0/snapshots/Snapshots4.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots4.v0.dfy
index beaadfeb..beaadfeb 100644
--- a/Test/dafny0/snapshots/Snapshots4.v0.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots4.v0.dfy
diff --git a/Test/dafny0/snapshots/Snapshots4.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots4.v1.dfy
index cf9ae753..cf9ae753 100644
--- a/Test/dafny0/snapshots/Snapshots4.v1.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots4.v1.dfy
diff --git a/Test/dafny0/snapshots/Snapshots5.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots5.v0.dfy
index b81c1a2b..b81c1a2b 100644
--- a/Test/dafny0/snapshots/Snapshots5.v0.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots5.v0.dfy
diff --git a/Test/dafny0/snapshots/Snapshots5.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots5.v1.dfy
index 05dbced0..7b207d74 100644
--- a/Test/dafny0/snapshots/Snapshots5.v1.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots5.v1.dfy
@@ -17,9 +17,9 @@ method M()
}
else
{
- assert (exists b: bool :: b || !b) || 4 != 4;
+ assert (exists b: bool :: true) || 4 != 4;
}
- assert (exists b: bool :: b || !b) || 5 != 5;
+ assert (exists b: bool :: true) || 5 != 5;
}
diff --git a/Test/dafny0/snapshots/Snapshots6.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots6.v0.dfy
index c3742f4b..c3742f4b 100644
--- a/Test/dafny0/snapshots/Snapshots6.v0.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots6.v0.dfy
diff --git a/Test/dafny0/snapshots/Snapshots6.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots6.v1.dfy
index aeb520cb..aeb520cb 100644
--- a/Test/dafny0/snapshots/Snapshots6.v1.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots6.v1.dfy
diff --git a/Test/dafny0/snapshots/Snapshots7.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots7.v0.dfy
index 27c7da5f..27c7da5f 100644
--- a/Test/dafny0/snapshots/Snapshots7.v0.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots7.v0.dfy
diff --git a/Test/dafny0/snapshots/Snapshots7.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots7.v1.dfy
index b45dfe78..b45dfe78 100644
--- a/Test/dafny0/snapshots/Snapshots7.v1.dfy
+++ b/Test/dafny0/snapshots/Inputs/Snapshots7.v1.dfy
diff --git a/Test/dafny0/snapshots/Inputs/Snapshots8.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots8.v0.dfy
new file mode 100644
index 00000000..97fcfccb
--- /dev/null
+++ b/Test/dafny0/snapshots/Inputs/Snapshots8.v0.dfy
@@ -0,0 +1,29 @@
+method M(x: int)
+{ assert x < 20 || 10 <= x; // always true
+ assert x < 10; // error
+ Other(x); // error: precondition violation
+}
+
+method Other(y: int)
+ requires 0 <= y
+{
+}
+
+method Posty() returns (z: int)
+ ensures 2 <= z // error: postcondition violation
+{
+ var t := 20;
+ if t < z {
+ } else { // the postcondition violation occurs on this 'else' branch
+ }
+}
+
+method NoChangeWhazzoeva(u: int)
+{
+ assert u != 53; // error
+}
+
+method NoChangeAndCorrect()
+{
+ assert true;
+}
diff --git a/Test/dafny0/snapshots/Inputs/Snapshots8.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots8.v1.dfy
new file mode 100644
index 00000000..8d8b215b
--- /dev/null
+++ b/Test/dafny0/snapshots/Inputs/Snapshots8.v1.dfy
@@ -0,0 +1,33 @@
+method M(x: int)
+{
+assert x < 20 || 10 <= x; // always true
+
+ assert x < 10; // error
+ Other(x); // error: precondition violation
+ assert x == 7; // error: this is a new error in v1
+}
+
+
+ method Other(y: int)
+ requires 0 <= y
+ {
+ }
+
+
+
+method Posty() returns (z: int)
+ ensures 2 <= z // error: postcondition violation
+{
+ var t := 20;
+ if t < z {
+ assert true; // this is a new assert
+ } else { // the postcondition violation occurs on this 'else' branch
+ }
+}
+
+ method NoChangeWhazzoeva(u: int)
+ {
+ assert u != 53; // error
+ }
+
+method NoChangeAndCorrect() { assert true; }
diff --git a/Test/dafny0/snapshots/Snapshots0.run.dfy b/Test/dafny0/snapshots/Snapshots0.run.dfy
new file mode 100644
index 00000000..5e016c12
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots0.run.dfy
@@ -0,0 +1,2 @@
+// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots0.dfy" > "%t"
+// RUN: %diff "%s.expect" "%t"
diff --git a/Test/dafny0/snapshots/Snapshots0.run.dfy.expect b/Test/dafny0/snapshots/Snapshots0.run.dfy.expect
new file mode 100644
index 00000000..bf7388cf
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots0.run.dfy.expect
@@ -0,0 +1,25 @@
+Processing command (at Snapshots0.v0.dfy(3,6)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
+ >>> DoNothingToAssert
+Processing command (at Snapshots0.v0.dfy(4,10)) assert false;
+ >>> DoNothingToAssert
+
+Dafny program verifier finished with 3 verified, 0 errors
+Processing implementation CheckWellformed$$_module.__default.bar (at Snapshots0.v1.dfy(7,8)):
+ >>> added axiom: ##extracted_function##1() == (0 == $ModuleContextHeight && 0 == $FunctionContextHeight)
+ >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##1();
+Processing call to procedure IntraModuleCall$$_module.__default.bar in implementation Impl$$_module.__default.foo (at Snapshots0.v1.dfy(3,6)):
+ >>> added axiom: (forall call0old#AT#$Heap: Heap, $Heap: Heap :: {:weight 30} { ##extracted_function##2(call0old#AT#$Heap, $Heap) } ##extracted_function##2(call0old#AT#$Heap, $Heap) == (true && Lit(false) && (forall<alpha> $o: ref, $f: Field alpha :: { read($Heap, $o, $f) } $o != null && read(call0old#AT#$Heap, $o, alloc) ==> read($Heap, $o, $f) == read(call0old#AT#$Heap, $o, $f)) && $HeapSucc(call0old#AT#$Heap, $Heap)))
+ >>> added after: a##cached##0 := a##cached##0 && ##extracted_function##2(call0old#AT#$Heap, $Heap);
+Processing command (at <unknown location>) a##cached##0 := a##cached##0 && ##extracted_function##1();
+ >>> AssumeNegationOfAssumptionVariable
+Processing command (at Snapshots0.v1.dfy(3,6)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
+ >>> MarkAsFullyVerified
+Processing command (at <unknown location>) a##cached##0 := a##cached##0 && ##extracted_function##2(call0old#AT#$Heap, $Heap);
+ >>> AssumeNegationOfAssumptionVariable
+Processing command (at Snapshots0.v1.dfy(4,10)) assert false;
+ >>> MarkAsPartiallyVerified
+Snapshots0.v1.dfy(4,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 2 verified, 1 error
diff --git a/Test/dafny0/snapshots/Snapshots1.run.dfy b/Test/dafny0/snapshots/Snapshots1.run.dfy
new file mode 100644
index 00000000..1907f4a0
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots1.run.dfy
@@ -0,0 +1,2 @@
+// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots1.dfy" > "%t"
+// RUN: %diff "%s.expect" "%t"
diff --git a/Test/dafny0/snapshots/Snapshots1.run.dfy.expect b/Test/dafny0/snapshots/Snapshots1.run.dfy.expect
new file mode 100644
index 00000000..1b5c8d24
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots1.run.dfy.expect
@@ -0,0 +1,21 @@
+Processing command (at Snapshots1.v0.dfy(3,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
+ >>> DoNothingToAssert
+Processing command (at Snapshots1.v0.dfy(4,10)) assert false;
+ >>> DoNothingToAssert
+Processing command (at Snapshots1.v0.dfy(12,3)) assert true;
+ >>> DoNothingToAssert
+
+Dafny program verifier finished with 4 verified, 0 errors
+Processing call to procedure IntraModuleCall$$_module.__default.N in implementation Impl$$_module.__default.M (at Snapshots1.v1.dfy(3,4)):
+ >>> added after: a##cached##0 := a##cached##0 && false;
+Processing command (at Snapshots1.v1.dfy(12,3)) assert true;
+ >>> MarkAsFullyVerified
+Processing command (at Snapshots1.v1.dfy(3,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
+ >>> MarkAsFullyVerified
+Processing command (at Snapshots1.v1.dfy(4,10)) assert false;
+ >>> DoNothingToAssert
+Snapshots1.v1.dfy(4,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 3 verified, 1 error
diff --git a/Test/dafny0/snapshots/Snapshots2.run.dfy b/Test/dafny0/snapshots/Snapshots2.run.dfy
new file mode 100644
index 00000000..71f3e18a
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots2.run.dfy
@@ -0,0 +1,2 @@
+// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots2.dfy" > "%t"
+// RUN: %diff "%s.expect" "%t"
diff --git a/Test/dafny0/snapshots/Snapshots2.run.dfy.expect b/Test/dafny0/snapshots/Snapshots2.run.dfy.expect
new file mode 100644
index 00000000..949ecec9
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots2.run.dfy.expect
@@ -0,0 +1,41 @@
+Processing command (at Snapshots2.v0.dfy(3,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
+ >>> DoNothingToAssert
+Processing command (at Snapshots2.v0.dfy(4,10)) assert false;
+ >>> DoNothingToAssert
+Processing command (at Snapshots2.v0.dfy(11,11)) assert true;
+ >>> DoNothingToAssert
+Processing command (at Snapshots2.v0.dfy(11,15)) assert _module.__default.P($LS($LS($LZ)), $Heap) <==> _module.__default.Q($LS($LS($LZ)), $Heap);
+ >>> DoNothingToAssert
+Processing command (at Snapshots2.v0.dfy(14,11)) assert true;
+ >>> DoNothingToAssert
+Processing command (at Snapshots2.v0.dfy(14,15)) assert _module.__default.Q($LS($LS($LZ)), $Heap) <==> Lit(_module.__default.R($Heap));
+ >>> DoNothingToAssert
+Processing command (at Snapshots2.v0.dfy(18,3)) assert true;
+ >>> DoNothingToAssert
+
+Dafny program verifier finished with 6 verified, 0 errors
+Processing call to procedure IntraModuleCall$$_module.__default.N in implementation Impl$$_module.__default.M (at Snapshots2.v1.dfy(3,4)):
+ >>> added after: a##cached##0 := a##cached##0 && false;
+Processing implementation CheckWellformed$$_module.__default.P (at Snapshots2.v1.dfy(10,11)):
+ >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && false;
+Processing implementation CheckWellformed$$_module.__default.Q (at Snapshots2.v1.dfy(13,11)):
+ >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && false;
+Processing command (at Snapshots2.v1.dfy(18,3)) assert true;
+ >>> MarkAsFullyVerified
+Processing command (at Snapshots2.v1.dfy(3,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
+ >>> MarkAsFullyVerified
+Processing command (at Snapshots2.v1.dfy(4,10)) assert false;
+ >>> DoNothingToAssert
+Snapshots2.v1.dfy(4,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Processing command (at Snapshots2.v1.dfy(11,11)) assert true;
+ >>> DoNothingToAssert
+Processing command (at Snapshots2.v1.dfy(11,15)) assert _module.__default.P($LS($LS($LZ)), $Heap) <==> _module.__default.Q($LS($LS($LZ)), $Heap);
+ >>> DoNothingToAssert
+Processing command (at Snapshots2.v1.dfy(14,11)) assert true;
+ >>> DoNothingToAssert
+Processing command (at Snapshots2.v1.dfy(14,15)) assert _module.__default.Q($LS($LS($LZ)), $Heap) <==> Lit(_module.__default.R($Heap));
+ >>> DoNothingToAssert
+
+Dafny program verifier finished with 5 verified, 1 error
diff --git a/Test/dafny0/snapshots/Snapshots3.run.dfy b/Test/dafny0/snapshots/Snapshots3.run.dfy
new file mode 100644
index 00000000..40dd1012
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots3.run.dfy
@@ -0,0 +1,2 @@
+// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots3.dfy" > "%t"
+// RUN: %diff "%s.expect" "%t"
diff --git a/Test/dafny0/snapshots/Snapshots3.run.dfy.expect b/Test/dafny0/snapshots/Snapshots3.run.dfy.expect
new file mode 100644
index 00000000..a7f05a68
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots3.run.dfy.expect
@@ -0,0 +1,18 @@
+Processing command (at Snapshots3.v0.dfy(9,14)) assert 0 != 0;
+ >>> DoNothingToAssert
+Snapshots3.v0.dfy(9,13): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon3_Else
+
+Dafny program verifier finished with 1 verified, 1 error
+Processing command (at Snapshots3.v1.dfy(5,12)) assert true;
+ >>> DoNothingToAssert
+Processing command (at Snapshots3.v1.dfy(9,14)) assert 0 != 0;
+ >>> RecycleError
+Snapshots3.v0.dfy(9,13): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon3_Else
+
+Dafny program verifier finished with 1 verified, 1 error
diff --git a/Test/dafny0/snapshots/Snapshots4.run.dfy b/Test/dafny0/snapshots/Snapshots4.run.dfy
new file mode 100644
index 00000000..803403cf
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots4.run.dfy
@@ -0,0 +1,2 @@
+// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots4.dfy" > "%t"
+// RUN: %diff "%s.expect" "%t"
diff --git a/Test/dafny0/snapshots/Snapshots4.run.dfy.expect b/Test/dafny0/snapshots/Snapshots4.run.dfy.expect
new file mode 100644
index 00000000..e0f07849
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots4.run.dfy.expect
@@ -0,0 +1,20 @@
+Processing command (at Snapshots4.v0.dfy(9,14)) assert LitInt(0) == LitInt(0);
+ >>> DoNothingToAssert
+
+Dafny program verifier finished with 2 verified, 0 errors
+Processing command (at Snapshots4.v1.dfy(5,14)) assert 1 != 1;
+ >>> DoNothingToAssert
+Processing command (at Snapshots4.v1.dfy(9,14)) assert LitInt(0) == LitInt(0);
+ >>> MarkAsFullyVerified
+Processing command (at Snapshots4.v1.dfy(10,14)) assert 2 != 2;
+ >>> DoNothingToAssert
+Snapshots4.v1.dfy(5,13): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon3_Then
+Snapshots4.v1.dfy(10,13): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon3_Else
+
+Dafny program verifier finished with 1 verified, 2 errors
diff --git a/Test/dafny0/snapshots/Snapshots5.run.dfy b/Test/dafny0/snapshots/Snapshots5.run.dfy
new file mode 100644
index 00000000..096df53c
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots5.run.dfy
@@ -0,0 +1,2 @@
+// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots5.dfy" /autoTriggers:1 > "%t"
+// RUN: %diff "%s.expect" "%t"
diff --git a/Test/dafny0/snapshots/Snapshots5.run.dfy.expect b/Test/dafny0/snapshots/Snapshots5.run.dfy.expect
new file mode 100644
index 00000000..8cc44882
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots5.run.dfy.expect
@@ -0,0 +1,35 @@
+Snapshots5.v0.dfy(10,12): Warning: /!\ No terms found to trigger on.
+Snapshots5.v0.dfy(13,10): Warning: /!\ No terms found to trigger on.
+Snapshots5.v0.dfy(20,12): Warning: /!\ No terms found to trigger on.
+Snapshots5.v0.dfy(26,11): Warning: /!\ No terms found to trigger on.
+Processing command (at Snapshots5.v0.dfy(3,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
+ >>> DoNothingToAssert
+Processing command (at Snapshots5.v0.dfy(10,40)) assert (forall b#1: bool :: true ==> b#1 || !b#1) || 0 != 0;
+ >>> DoNothingToAssert
+Processing command (at Snapshots5.v0.dfy(12,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
+ >>> DoNothingToAssert
+Processing command (at Snapshots5.v0.dfy(13,38)) assert (forall b#3: bool :: true ==> b#3 || !b#3) || 3 != 3;
+ >>> DoNothingToAssert
+Processing command (at Snapshots5.v0.dfy(20,40)) assert (forall b#5: bool :: true ==> b#5 || !b#5) || 1 != 1;
+ >>> DoNothingToAssert
+
+Dafny program verifier finished with 3 verified, 0 errors
+Snapshots5.v1.dfy(10,12): Warning: /!\ No terms found to trigger on.
+Snapshots5.v1.dfy(13,10): Warning: /!\ No terms found to trigger on.
+Snapshots5.v1.dfy(20,12): Warning: /!\ No terms found to trigger on.
+Snapshots5.v1.dfy(22,10): Warning: /!\ No terms found to trigger on.
+Snapshots5.v1.dfy(27,11): Warning: /!\ No terms found to trigger on.
+Processing command (at Snapshots5.v1.dfy(3,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
+ >>> MarkAsFullyVerified
+Processing command (at Snapshots5.v1.dfy(10,40)) assert (forall b#1: bool :: true ==> b#1 || !b#1) || 0 != 0;
+ >>> MarkAsFullyVerified
+Processing command (at Snapshots5.v1.dfy(12,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
+ >>> MarkAsFullyVerified
+Processing command (at Snapshots5.v1.dfy(13,38)) assert (forall b#3: bool :: true ==> b#3 || !b#3) || 3 != 3;
+ >>> MarkAsFullyVerified
+Processing command (at Snapshots5.v1.dfy(20,37)) assert (exists b#5: bool :: Lit(true)) || 4 != 4;
+ >>> DoNothingToAssert
+Processing command (at Snapshots5.v1.dfy(22,35)) assert (exists b#7: bool :: Lit(true)) || 5 != 5;
+ >>> DoNothingToAssert
+
+Dafny program verifier finished with 3 verified, 0 errors
diff --git a/Test/dafny0/snapshots/Snapshots6.run.dfy b/Test/dafny0/snapshots/Snapshots6.run.dfy
new file mode 100644
index 00000000..8f958cb9
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots6.run.dfy
@@ -0,0 +1,2 @@
+// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots6.dfy" > "%t"
+// RUN: %diff "%s.expect" "%t"
diff --git a/Test/dafny0/snapshots/Snapshots6.run.dfy.expect b/Test/dafny0/snapshots/Snapshots6.run.dfy.expect
new file mode 100644
index 00000000..cdb942bb
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots6.run.dfy.expect
@@ -0,0 +1,11 @@
+Processing command (at Snapshots6.v0.dfy(20,14)) assert false;
+ >>> DoNothingToAssert
+
+Dafny program verifier finished with 4 verified, 0 errors
+Processing command (at Snapshots6.v1.dfy(20,14)) assert false;
+ >>> DoNothingToAssert
+Snapshots6.v1.dfy(20,13): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 3 verified, 1 error
diff --git a/Test/dafny0/snapshots/Snapshots7.run.dfy b/Test/dafny0/snapshots/Snapshots7.run.dfy
new file mode 100644
index 00000000..c84c41d2
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots7.run.dfy
@@ -0,0 +1,2 @@
+// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots7.dfy" > "%t"
+// RUN: %diff "%s.expect" "%t"
diff --git a/Test/dafny0/snapshots/Snapshots7.run.dfy.expect b/Test/dafny0/snapshots/Snapshots7.run.dfy.expect
new file mode 100644
index 00000000..a08b32c6
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots7.run.dfy.expect
@@ -0,0 +1,31 @@
+Processing command (at Snapshots7.v0.dfy(19,14)) assert false;
+ >>> DoNothingToAssert
+
+Dafny program verifier finished with 4 verified, 0 errors
+Processing implementation CheckWellformed$$_0_M0.C.Foo (at Snapshots7.v1.dfy(5,12)):
+ >>> added axiom: ##extracted_function##1() == (0 == $ModuleContextHeight && 0 == $FunctionContextHeight)
+ >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##1();
+Processing implementation Impl$$_0_M0.C.Foo (at Snapshots7.v1.dfy(5,12)):
+ >>> added axiom: ##extracted_function##2() == (0 == $ModuleContextHeight && 0 == $FunctionContextHeight && Lit(false))
+ >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##2();
+Processing implementation CheckWellformed$$_1_M1.C.Foo (at Snapshots7.v1.dfy[M1](5,12)):
+ >>> added axiom: ##extracted_function##3() == (1 == $ModuleContextHeight && 0 == $FunctionContextHeight)
+ >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##3();
+Processing implementation Impl$$_1_M1.C.Foo (at Snapshots7.v1.dfy[M1](5,12)):
+ >>> added axiom: ##extracted_function##4() == (1 == $ModuleContextHeight && 0 == $FunctionContextHeight && Lit(false))
+ >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##4();
+Processing command (at <unknown location>) a##cached##0 := a##cached##0 && ##extracted_function##1();
+ >>> AssumeNegationOfAssumptionVariable
+Processing command (at <unknown location>) a##cached##0 := a##cached##0 && ##extracted_function##2();
+ >>> AssumeNegationOfAssumptionVariable
+Processing command (at <unknown location>) a##cached##0 := a##cached##0 && ##extracted_function##3();
+ >>> AssumeNegationOfAssumptionVariable
+Processing command (at <unknown location>) a##cached##0 := a##cached##0 && ##extracted_function##4();
+ >>> AssumeNegationOfAssumptionVariable
+Processing command (at Snapshots7.v1.dfy(19,14)) assert false;
+ >>> MarkAsPartiallyVerified
+Snapshots7.v1.dfy(19,13): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 3 verified, 1 error
diff --git a/Test/dafny0/snapshots/Snapshots8.run.dfy b/Test/dafny0/snapshots/Snapshots8.run.dfy
new file mode 100644
index 00000000..00d20f91
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots8.run.dfy
@@ -0,0 +1,2 @@
+// RUN: %dafny /compile:0 /verifySnapshots:3 /traceCaching:1 /errorTrace:0 "%S/Inputs/Snapshots8.dfy" > "%t"
+// RUN: %diff "%s.expect" "%t"
diff --git a/Test/dafny0/snapshots/Snapshots8.run.dfy.expect b/Test/dafny0/snapshots/Snapshots8.run.dfy.expect
new file mode 100644
index 00000000..e1cbdbe0
--- /dev/null
+++ b/Test/dafny0/snapshots/Snapshots8.run.dfy.expect
@@ -0,0 +1,55 @@
+Processing command (at Snapshots8.v0.dfy(2,37)) assert x#0 < 20 || LitInt(10) <= x#0;
+ >>> DoNothingToAssert
+Processing command (at Snapshots8.v0.dfy(3,12)) assert x#0 < 10;
+ >>> DoNothingToAssert
+Processing command (at Snapshots8.v0.dfy(4,9)) assert true;
+ >>> DoNothingToAssert
+Processing command (at Snapshots8.v0.dfy(4,8)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
+ >>> DoNothingToAssert
+Processing command (at Snapshots8.v0.dfy(4,8)) assert LitInt(0) <= call0formal#AT#y#0;
+ >>> DoNothingToAssert
+Snapshots8.v0.dfy(3,11): Error: assertion violation
+Snapshots8.v0.dfy(4,7): Error BP5002: A precondition for this call might not hold.
+Snapshots8.v0.dfy(8,13): Related location: This is the precondition that might not hold.
+Processing command (at Snapshots8.v0.dfy(15,12)) assert true;
+ >>> DoNothingToAssert
+Processing command (at Snapshots8.v0.dfy(13,13)) assert LitInt(2) <= z#0;
+ >>> DoNothingToAssert
+Snapshots8.v0.dfy(17,9): Error BP5003: A postcondition might not hold on this return path.
+Snapshots8.v0.dfy(13,12): Related location: This is the postcondition that might not hold.
+Processing command (at Snapshots8.v0.dfy(23,12)) assert u#0 != 53;
+ >>> DoNothingToAssert
+Snapshots8.v0.dfy(23,11): Error: assertion violation
+Processing command (at Snapshots8.v0.dfy(28,10)) assert true;
+ >>> DoNothingToAssert
+
+Dafny program verifier finished with 7 verified, 4 errors
+Processing command (at Snapshots8.v1.dfy(30,17)) assert u#0 != 53;
+ >>> RecycleError
+Snapshots8.v1.dfy(30,16): Error: assertion violation
+Processing command (at Snapshots8.v1.dfy(3,15)) assert x#0 < 20 || LitInt(10) <= x#0;
+ >>> MarkAsFullyVerified
+Processing command (at Snapshots8.v1.dfy(5,17)) assert x#0 < 10;
+ >>> RecycleError
+Processing command (at Snapshots8.v1.dfy(6,9)) assert true;
+ >>> MarkAsFullyVerified
+Processing command (at Snapshots8.v1.dfy(6,8)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
+ >>> MarkAsFullyVerified
+Processing command (at Snapshots8.v1.dfy(6,8)) assert LitInt(0) <= call0formal#AT#y#0;
+ >>> RecycleError
+Processing command (at Snapshots8.v1.dfy(7,12)) assert x#0 == LitInt(7);
+ >>> DoNothingToAssert
+Snapshots8.v1.dfy(5,16): Error: assertion violation
+Snapshots8.v1.dfy(6,7): Error BP5002: A precondition for this call might not hold.
+Snapshots8.v1.dfy(12,20): Related location: This is the precondition that might not hold.
+Snapshots8.v1.dfy(7,11): Error: assertion violation
+Processing command (at Snapshots8.v1.dfy(21,12)) assert true;
+ >>> MarkAsFullyVerified
+Processing command (at Snapshots8.v1.dfy(23,12)) assert true;
+ >>> DoNothingToAssert
+Processing command (at Snapshots8.v1.dfy(19,13)) assert LitInt(2) <= z#0;
+ >>> DoNothingToAssert
+Snapshots8.v1.dfy(24,9): Error BP5003: A postcondition might not hold on this return path.
+Snapshots8.v1.dfy(19,12): Related location: This is the postcondition that might not hold.
+
+Dafny program verifier finished with 7 verified, 5 errors
diff --git a/Test/dafny0/snapshots/lit.local.cfg b/Test/dafny0/snapshots/lit.local.cfg
deleted file mode 100644
index 07cb869f..00000000
--- a/Test/dafny0/snapshots/lit.local.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# This test is unusual in that we don't use the .bpl files
-# directly on the command line. So instead we'll invoke
-# files in this directory with extension '.snapshot'. There
-# will only be one for now
-config.suffixes = ['.snapshot']
diff --git a/Test/dafny0/snapshots/runtest.snapshot b/Test/dafny0/snapshots/runtest.snapshot
deleted file mode 100644
index 62ccabb3..00000000
--- a/Test/dafny0/snapshots/runtest.snapshot
+++ /dev/null
@@ -1,2 +0,0 @@
-// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 /verifySeparately Snapshots0.dfy Snapshots1.dfy Snapshots2.dfy Snapshots3.dfy Snapshots4.dfy Snapshots5.dfy Snapshots6.dfy Snapshots7.dfy > "%t"
-// RUN: %diff "%s.expect" "%t"
diff --git a/Test/dafny0/snapshots/runtest.snapshot.expect b/Test/dafny0/snapshots/runtest.snapshot.expect
deleted file mode 100644
index 8ad86f3b..00000000
--- a/Test/dafny0/snapshots/runtest.snapshot.expect
+++ /dev/null
@@ -1,180 +0,0 @@
-
--------------------- Snapshots0.dfy --------------------
-Processing command (at Snapshots0.v0.dfy(3,6)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
- >>> DoNothingToAssert
-Processing command (at Snapshots0.v0.dfy(4,10)) assert Lit(false);
- >>> DoNothingToAssert
-
-Dafny program verifier finished with 3 verified, 0 errors
-Processing call to procedure IntraModuleCall$$_module.__default.bar in implementation Impl$$_module.__default.foo (at Snapshots0.v1.dfy(3,6)):
- >>> added axiom: (forall call0old#AT#$Heap: Heap, $Heap: Heap :: {:weight 30} { ##extracted_function##1(call0old#AT#$Heap, $Heap) } ##extracted_function##1(call0old#AT#$Heap, $Heap) == (true && Lit(false) && (forall<alpha> $o: ref, $f: Field alpha :: { read($Heap, $o, $f) } $o != null && read(call0old#AT#$Heap, $o, alloc) ==> read($Heap, $o, $f) == read(call0old#AT#$Heap, $o, $f)) && $HeapSucc(call0old#AT#$Heap, $Heap)))
- >>> added after: a##post##0 := a##post##0 && ##extracted_function##1(call0old#AT#$Heap, $Heap);
-Processing command (at Snapshots0.v1.dfy(3,6)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
- >>> MarkAsFullyVerified
-Processing command (at <unknown location>) a##post##0 := a##post##0 && ##extracted_function##1(call0old#AT#$Heap, $Heap);
- >>> AssumeNegationOfAssumptionVariable
-Processing command (at Snapshots0.v1.dfy(4,10)) assert Lit(false);
- >>> MarkAsPartiallyVerified
-Snapshots0.v1.dfy(4,10): Error: assertion violation
-Execution trace:
- (0,0): anon0
-
-Dafny program verifier finished with 2 verified, 1 error
-
--------------------- Snapshots1.dfy --------------------
-Processing command (at Snapshots1.v0.dfy(3,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
- >>> DoNothingToAssert
-Processing command (at Snapshots1.v0.dfy(4,10)) assert Lit(false);
- >>> DoNothingToAssert
-Processing command (at Snapshots1.v0.dfy(12,3)) assert true;
- >>> DoNothingToAssert
-
-Dafny program verifier finished with 4 verified, 0 errors
-Processing call to procedure IntraModuleCall$$_module.__default.N in implementation Impl$$_module.__default.M (at Snapshots1.v1.dfy(3,4)):
- >>> added after: a##post##0 := a##post##0 && false;
-Processing command (at Snapshots1.v1.dfy(12,3)) assert true;
- >>> MarkAsFullyVerified
-Processing command (at Snapshots1.v1.dfy(3,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
- >>> MarkAsFullyVerified
-Processing command (at Snapshots1.v1.dfy(4,10)) assert Lit(false);
- >>> DoNothingToAssert
-Snapshots1.v1.dfy(4,10): Error: assertion violation
-Execution trace:
- (0,0): anon0
-
-Dafny program verifier finished with 3 verified, 1 error
-
--------------------- Snapshots2.dfy --------------------
-Processing command (at Snapshots2.v0.dfy(3,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
- >>> DoNothingToAssert
-Processing command (at Snapshots2.v0.dfy(4,10)) assert Lit(false);
- >>> DoNothingToAssert
-Processing command (at Snapshots2.v0.dfy(11,11)) assert true;
- >>> DoNothingToAssert
-Processing command (at Snapshots2.v0.dfy(11,15)) assert _module.__default.P($LS($LS($LZ)), $Heap) <==> _module.__default.Q($LS($LS($LZ)), $Heap);
- >>> DoNothingToAssert
-Processing command (at Snapshots2.v0.dfy(14,11)) assert true;
- >>> DoNothingToAssert
-Processing command (at Snapshots2.v0.dfy(14,15)) assert _module.__default.Q($LS($LS($LZ)), $Heap) <==> Lit(_module.__default.R($Heap));
- >>> DoNothingToAssert
-Processing command (at Snapshots2.v0.dfy(18,3)) assert true;
- >>> DoNothingToAssert
-
-Dafny program verifier finished with 6 verified, 0 errors
-Processing call to procedure IntraModuleCall$$_module.__default.N in implementation Impl$$_module.__default.M (at Snapshots2.v1.dfy(3,4)):
- >>> added after: a##post##0 := a##post##0 && false;
-Processing command (at Snapshots2.v1.dfy(18,3)) assert true;
- >>> MarkAsFullyVerified
-Processing command (at Snapshots2.v1.dfy(3,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
- >>> MarkAsFullyVerified
-Processing command (at Snapshots2.v1.dfy(4,10)) assert Lit(false);
- >>> DoNothingToAssert
-Snapshots2.v1.dfy(4,10): Error: assertion violation
-Execution trace:
- (0,0): anon0
-Processing command (at Snapshots2.v1.dfy(11,11)) assert true;
- >>> MarkAsFullyVerified
-Processing command (at Snapshots2.v1.dfy(11,15)) assert _module.__default.P($LS($LS($LZ)), $Heap) <==> _module.__default.Q($LS($LS($LZ)), $Heap);
- >>> DoNothingToAssert
-Processing command (at Snapshots2.v1.dfy(14,11)) assert true;
- >>> MarkAsFullyVerified
-Processing command (at Snapshots2.v1.dfy(14,15)) assert _module.__default.Q($LS($LS($LZ)), $Heap) <==> Lit(_module.__default.R($Heap));
- >>> DoNothingToAssert
-
-Dafny program verifier finished with 5 verified, 1 error
-
--------------------- Snapshots3.dfy --------------------
-Processing command (at Snapshots3.v0.dfy(9,14)) assert Lit(0 != 0);
- >>> DoNothingToAssert
-Snapshots3.v0.dfy(9,14): Error: assertion violation
-Execution trace:
- (0,0): anon0
- (0,0): anon3_Else
-
-Dafny program verifier finished with 1 verified, 1 error
-Processing command (at Snapshots3.v1.dfy(5,12)) assert Lit(true);
- >>> DoNothingToAssert
-Processing command (at Snapshots3.v1.dfy(9,14)) assert Lit(0 != 0);
- >>> RecycleError
-Snapshots3.v0.dfy(9,14): Error: assertion violation
-Execution trace:
- (0,0): anon0
- (0,0): anon3_Else
-
-Dafny program verifier finished with 1 verified, 1 error
-
--------------------- Snapshots4.dfy --------------------
-Processing command (at Snapshots4.v0.dfy(9,14)) assert LitInt(0) == LitInt(0);
- >>> DoNothingToAssert
-
-Dafny program verifier finished with 2 verified, 0 errors
-Processing command (at Snapshots4.v1.dfy(5,14)) assert Lit(1 != 1);
- >>> DoNothingToAssert
-Processing command (at Snapshots4.v1.dfy(9,14)) assert LitInt(0) == LitInt(0);
- >>> MarkAsFullyVerified
-Processing command (at Snapshots4.v1.dfy(10,14)) assert Lit(2 != 2);
- >>> DoNothingToAssert
-Snapshots4.v1.dfy(5,14): Error: assertion violation
-Execution trace:
- (0,0): anon0
- (0,0): anon3_Then
-Snapshots4.v1.dfy(10,14): Error: assertion violation
-Execution trace:
- (0,0): anon0
- (0,0): anon3_Else
-
-Dafny program verifier finished with 1 verified, 2 errors
-
--------------------- Snapshots5.dfy --------------------
-Processing command (at Snapshots5.v0.dfy(3,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
- >>> DoNothingToAssert
-Processing command (at Snapshots5.v0.dfy(10,40)) assert (forall b#1: bool :: true ==> b#1 || !b#1) || 0 != 0;
- >>> DoNothingToAssert
-Processing command (at Snapshots5.v0.dfy(12,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
- >>> DoNothingToAssert
-Processing command (at Snapshots5.v0.dfy(13,38)) assert (forall b#3: bool :: true ==> b#3 || !b#3) || 3 != 3;
- >>> DoNothingToAssert
-Processing command (at Snapshots5.v0.dfy(20,40)) assert (forall b#5: bool :: true ==> b#5 || !b#5) || 1 != 1;
- >>> DoNothingToAssert
-
-Dafny program verifier finished with 3 verified, 0 errors
-Processing command (at Snapshots5.v1.dfy(3,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
- >>> MarkAsFullyVerified
-Processing command (at Snapshots5.v1.dfy(10,40)) assert (forall b#1: bool :: true ==> b#1 || !b#1) || 0 != 0;
- >>> MarkAsFullyVerified
-Processing command (at Snapshots5.v1.dfy(12,4)) assert (forall<alpha> $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]);
- >>> MarkAsFullyVerified
-Processing command (at Snapshots5.v1.dfy(13,38)) assert (forall b#3: bool :: true ==> b#3 || !b#3) || 3 != 3;
- >>> MarkAsFullyVerified
-Processing command (at Snapshots5.v1.dfy(20,40)) assert (exists b#5: bool :: b#5 || !b#5) || 4 != 4;
- >>> DoNothingToAssert
-Processing command (at Snapshots5.v1.dfy(22,38)) assert (exists b#7: bool :: b#7 || !b#7) || 5 != 5;
- >>> DoNothingToAssert
-
-Dafny program verifier finished with 3 verified, 0 errors
-
--------------------- Snapshots6.dfy --------------------
-Processing command (at Snapshots6.v0.dfy(20,14)) assert Lit(false);
- >>> DoNothingToAssert
-
-Dafny program verifier finished with 4 verified, 0 errors
-Processing command (at Snapshots6.v1.dfy(20,14)) assert Lit(false);
- >>> DoNothingToAssert
-Snapshots6.v1.dfy(20,14): Error: assertion violation
-Execution trace:
- (0,0): anon0
-
-Dafny program verifier finished with 3 verified, 1 error
-
--------------------- Snapshots7.dfy --------------------
-Processing command (at Snapshots7.v0.dfy(19,14)) assert Lit(false);
- >>> DoNothingToAssert
-
-Dafny program verifier finished with 4 verified, 0 errors
-Processing command (at Snapshots7.v1.dfy(19,14)) assert Lit(false);
- >>> DoNothingToAssert
-Snapshots7.v1.dfy(19,14): Error: assertion violation
-Execution trace:
- (0,0): anon0
-
-Dafny program verifier finished with 3 verified, 1 error
diff --git a/Test/dafny1/BDD.dfy b/Test/dafny1/BDD.dfy
index 252164db..59dc3092 100644
--- a/Test/dafny1/BDD.dfy
+++ b/Test/dafny1/BDD.dfy
@@ -55,6 +55,7 @@ module SimpleBDD
node := if s[n-i] then node.t else node.f;
i := i - 1;
}
+ assert s[n-i..] == [];
b := node.b;
}
}
diff --git a/Test/dafny1/ExtensibleArrayAuto.dfy b/Test/dafny1/ExtensibleArrayAuto.dfy
index b2e5ecc4..01afdafd 100644
--- a/Test/dafny1/ExtensibleArrayAuto.dfy
+++ b/Test/dafny1/ExtensibleArrayAuto.dfy
@@ -2,12 +2,12 @@
// RUN: %diff "%s.expect" "%t"
class {:autocontracts} ExtensibleArray<T> {
- ghost var Contents: seq<T>;
+ ghost var Contents: seq<T>
- var elements: array<T>;
- var more: ExtensibleArray<array<T>>;
- var length: int;
- var M: int; // shorthand for: if more == null then 0 else 256 * |more.Contents|
+ var elements: array<T>
+ var more: ExtensibleArray<array<T>>
+ var length: int
+ var M: int // shorthand for: if more == null then 0 else 256 * |more.Contents|
predicate Valid()
{
@@ -35,7 +35,7 @@ class {:autocontracts} ExtensibleArray<T> {
}
constructor Init()
- ensures Contents == [];
+ ensures Contents == []
{
elements := new T[256];
more := null;
@@ -46,11 +46,11 @@ class {:autocontracts} ExtensibleArray<T> {
}
method Get(i: int) returns (t: T)
- requires 0 <= i < |Contents|;
- ensures t == Contents[i];
- decreases Repr;
+ requires 0 <= i < |Contents|
+ ensures t == Contents[i]
+ decreases Repr
{
- if (M <= i) {
+ if M <= i {
t := elements[i - M];
} else {
var arr := more.Get(i / 256);
@@ -59,10 +59,10 @@ class {:autocontracts} ExtensibleArray<T> {
}
method Set(i: int, t: T)
- requires 0 <= i < |Contents|;
- ensures Contents == old(Contents)[i := t];
+ requires 0 <= i < |Contents|
+ ensures Contents == old(Contents)[i := t]
{
- if (M <= i) {
+ if M <= i {
elements[i - M] := t;
} else {
var arr := more.Get(i / 256);
@@ -72,14 +72,14 @@ class {:autocontracts} ExtensibleArray<T> {
}
method Append(t: T)
- ensures Contents == old(Contents) + [t];
- decreases |Contents|;
+ ensures Contents == old(Contents) + [t]
+ decreases |Contents|
{
- if (length == 0 || length % 256 != 0) {
+ if length == 0 || length % 256 != 0 {
// there is room in "elements"
elements[length - M] := t;
} else {
- if (more == null) {
+ if more == null {
more := new ExtensibleArray<array<T>>.Init();
Repr := Repr + {more} + more.Repr;
}
@@ -99,7 +99,7 @@ class {:autocontracts} ExtensibleArray<T> {
method Main() {
var a := new ExtensibleArray<int>.Init();
var n := 0;
- while (n < 256*256+600)
+ while n < 256*256+600
invariant a.Valid() && fresh(a.Repr);
invariant |a.Contents| == n;
{
diff --git a/Test/dafny1/FindZero.dfy b/Test/dafny1/FindZero.dfy
index f0eb6a60..374555b0 100644
--- a/Test/dafny1/FindZero.dfy
+++ b/Test/dafny1/FindZero.dfy
@@ -3,7 +3,7 @@
method FindZero(a: array<int>) returns (r: int)
requires a != null && forall i :: 0 <= i < a.Length ==> 0 <= a[i];
- requires forall i :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1];
+ requires forall i {:nowarn} :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1];
ensures 0 <= r ==> r < a.Length && a[r] == 0;
ensures r < 0 ==> forall i :: 0 <= i < a.Length ==> a[i] != 0;
{
@@ -18,9 +18,9 @@ method FindZero(a: array<int>) returns (r: int)
r := -1;
}
-ghost method Lemma(a: array<int>, k: int, m: int)
+lemma Lemma(a: array<int>, k: int, m: int)
requires a != null && forall i :: 0 <= i < a.Length ==> 0 <= a[i];
- requires forall i :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1];
+ requires forall i {:nowarn} :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1];
requires 0 <= k;
requires k < a.Length ==> m <= a[k];
ensures forall i :: k <= i < k+m && i < a.Length ==> a[i] != 0;
@@ -36,7 +36,7 @@ ghost method Lemma(a: array<int>, k: int, m: int)
method FindZero_GhostLoop(a: array<int>) returns (r: int)
requires a != null && forall i :: 0 <= i < a.Length ==> 0 <= a[i];
- requires forall i :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1];
+ requires forall i {:nowarn} :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1];
ensures 0 <= r ==> r < a.Length && a[r] == 0;
ensures r < 0 ==> forall i :: 0 <= i < a.Length ==> a[i] != 0;
{
@@ -63,7 +63,7 @@ method FindZero_GhostLoop(a: array<int>) returns (r: int)
method FindZero_Assert(a: array<int>) returns (r: int)
requires a != null && forall i :: 0 <= i < a.Length ==> 0 <= a[i];
- requires forall i :: 0 <= i-1 && i < a.Length ==> a[i-1]-1 <= a[i];
+ requires forall i {:nowarn} :: 0 <= i-1 && i < a.Length ==> a[i-1]-1 <= a[i];
ensures 0 <= r ==> r < a.Length && a[r] == 0;
ensures r < 0 ==> forall i :: 0 <= i < a.Length ==> a[i] != 0;
{
diff --git a/Test/dafny1/Induction.dfy b/Test/dafny1/Induction.dfy
index 28171896..e2cd4ade 100644
--- a/Test/dafny1/Induction.dfy
+++ b/Test/dafny1/Induction.dfy
@@ -22,7 +22,7 @@ class IntegerInduction {
// Here is one proof. It uses a lemma, which is proved separately.
- ghost method Theorem0(n: int)
+ lemma Theorem0(n: int)
requires 0 <= n;
ensures SumOfCubes(n) == Gauss(n) * Gauss(n);
{
@@ -32,7 +32,7 @@ class IntegerInduction {
}
}
- ghost method Lemma(n: int)
+ lemma Lemma(n: int)
requires 0 <= n;
ensures 2 * Gauss(n) == n*(n+1);
{
@@ -42,7 +42,7 @@ class IntegerInduction {
// Here is another proof. It states the lemma as part of the theorem, and
// thus proves the two together.
- ghost method Theorem1(n: int)
+ lemma Theorem1(n: int)
requires 0 <= n;
ensures SumOfCubes(n) == Gauss(n) * Gauss(n);
ensures 2 * Gauss(n) == n*(n+1);
@@ -52,24 +52,24 @@ class IntegerInduction {
}
}
- ghost method DoItAllInOneGo()
- ensures (forall n :: 0 <= n ==>
+ lemma DoItAllInOneGo()
+ ensures (forall n {:split false} :: 0 <= n ==> // WISH reenable quantifier splitting here. This will only work once we generate induction hypotheses at the Dafny level.
SumOfCubes(n) == Gauss(n) * Gauss(n) &&
2 * Gauss(n) == n*(n+1));
{
}
- // The following two ghost methods are the same as the previous two, but
+ // The following two lemmas are the same as the previous two, but
// here no body is given--and the proof still goes through (thanks to
// Dafny's ghost-method induction tactic).
- ghost method Lemma_Auto(n: int)
+ lemma Lemma_Auto(n: int)
requires 0 <= n;
ensures 2 * Gauss(n) == n*(n+1);
{
}
- ghost method Theorem1_Auto(n: int)
+ lemma Theorem1_Auto(n: int)
requires 0 <= n;
ensures SumOfCubes(n) == Gauss(n) * Gauss(n);
ensures 2 * Gauss(n) == n*(n+1);
@@ -79,7 +79,7 @@ class IntegerInduction {
// Here is another proof. It makes use of Dafny's induction heuristics to
// prove the lemma.
- ghost method Theorem2(n: int)
+ lemma Theorem2(n: int)
requires 0 <= n;
ensures SumOfCubes(n) == Gauss(n) * Gauss(n);
{
@@ -90,7 +90,7 @@ class IntegerInduction {
}
}
- ghost method M(n: int)
+ lemma M(n: int)
requires 0 <= n;
{
assume (forall k :: 0 <= k && k < n ==> 2 * Gauss(k) == k*(k+1)); // manually assume the induction hypothesis
@@ -99,7 +99,7 @@ class IntegerInduction {
// Another way to prove the lemma is to supply a postcondition on the Gauss function
- ghost method Theorem3(n: int)
+ lemma Theorem3(n: int)
requires 0 <= n;
ensures SumOfCubes(n) == GaussWithPost(n) * GaussWithPost(n);
{
@@ -117,14 +117,14 @@ class IntegerInduction {
// Finally, with the postcondition of GaussWithPost, one can prove the entire theorem by induction
- ghost method Theorem4()
+ lemma Theorem4()
ensures (forall n :: 0 <= n ==>
SumOfCubes(n) == GaussWithPost(n) * GaussWithPost(n));
{
// look ma, no hints!
}
- ghost method Theorem5(n: int)
+ lemma Theorem5(n: int)
requires 0 <= n;
ensures SumOfCubes(n) == GaussWithPost(n) * GaussWithPost(n);
{
@@ -148,11 +148,11 @@ class IntegerInduction {
// Proving the "<==" case is simple; it's the "==>" case that requires induction.
// The example uses an attribute that requests induction on just "j". However, the proof also
// goes through by applying induction on both bound variables.
- function method IsSorted(s: seq<int>): bool
- ensures IsSorted(s) ==> (forall i,j {:induction j} :: 0 <= i && i < j && j < |s| ==> s[i] <= s[j]);
+ function method IsSorted(s: seq<int>): bool //WISH remove autotriggers false
+ ensures IsSorted(s) ==> (forall i,j {:induction j} {:autotriggers false} :: 0 <= i < j < |s| ==> s[i] <= s[j]);
ensures (forall i,j :: 0 <= i && i < j && j < |s| ==> s[i] <= s[j]) ==> IsSorted(s);
{
- (forall i :: 1 <= i && i < |s| ==> s[i-1] <= s[i])
+ (forall i {:nowarn} :: 1 <= i && i < |s| ==> s[i-1] <= s[i])
}
}
diff --git a/Test/dafny1/MoreInduction.dfy b/Test/dafny1/MoreInduction.dfy
index 41adcf50..2b5187a4 100644
--- a/Test/dafny1/MoreInduction.dfy
+++ b/Test/dafny1/MoreInduction.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
datatype List<X> = Nil | Cons(Node<X>, List<X>)
@@ -42,13 +42,13 @@ function ToSeq<X>(list: List<X>): seq<X>
case Nary(nn) => ToSeq(nn) + ToSeq(rest)
}
-ghost method Theorem<X>(list: List<X>)
+lemma Theorem<X>(list: List<X>)
ensures ToSeq(list) == ToSeq(FlattenMain(list));
{
Lemma(list, Nil);
}
-ghost method Lemma<X>(list: List<X>, ext: List<X>)
+lemma Lemma<X>(list: List<X>, ext: List<X>)
requires IsFlat(ext);
ensures ToSeq(list) + ToSeq(ext) == ToSeq(Flatten(list, ext));
{
@@ -73,27 +73,27 @@ function NegFac(n: int): int
if -1 <= n then -1 else - NegFac(n+1) * n
}
-ghost method LemmaAll()
+lemma LemmaAll()
ensures forall n :: NegFac(n) <= -1; // error: induction heuristic does not give a useful well-founded order, and thus this fails to verify
{
}
-ghost method LemmaOne(n: int)
+lemma LemmaOne(n: int)
ensures NegFac(n) <= -1; // error: induction heuristic does not give a useful well-founded order, and thus this fails to verify
{
}
-ghost method LemmaAll_Neg()
- ensures forall n :: NegFac(-n) <= -1; // error: fails to verify because of the minus in the trigger
+lemma LemmaAll_Neg() //FIXME I don't understand the comment below; what trigger?
+ ensures forall n {:nowarn} :: NegFac(-n) <= -1; // error: fails to verify because of the minus in the trigger
{
}
-ghost method LemmaOne_Neg(n: int)
+lemma LemmaOne_Neg(n: int) //FIXME What trigger?
ensures NegFac(-n) <= -1; // error: fails to verify because of the minus in the trigger
{
}
-ghost method LemmaOneWithDecreases(n: int)
+lemma LemmaOneWithDecreases(n: int)
ensures NegFac(n) <= -1; // here, the programmer gives a good well-founded order, so this verifies
decreases -n;
{
diff --git a/Test/dafny1/MoreInduction.dfy.expect b/Test/dafny1/MoreInduction.dfy.expect
index c8785e56..5de0ace6 100644
--- a/Test/dafny1/MoreInduction.dfy.expect
+++ b/Test/dafny1/MoreInduction.dfy.expect
@@ -1,17 +1,17 @@
-MoreInduction.dfy(78,1): Error BP5003: A postcondition might not hold on this return path.
-MoreInduction.dfy(77,11): Related location: This is the postcondition that might not hold.
+MoreInduction.dfy(78,0): Error BP5003: A postcondition might not hold on this return path.
+MoreInduction.dfy(77,10): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-MoreInduction.dfy(83,1): Error BP5003: A postcondition might not hold on this return path.
-MoreInduction.dfy(82,21): Related location: This is the postcondition that might not hold.
+MoreInduction.dfy(83,0): Error BP5003: A postcondition might not hold on this return path.
+MoreInduction.dfy(82,20): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-MoreInduction.dfy(88,1): Error BP5003: A postcondition might not hold on this return path.
-MoreInduction.dfy(87,11): Related location: This is the postcondition that might not hold.
+MoreInduction.dfy(88,0): Error BP5003: A postcondition might not hold on this return path.
+MoreInduction.dfy(87,10): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
-MoreInduction.dfy(93,1): Error BP5003: A postcondition might not hold on this return path.
-MoreInduction.dfy(92,22): Related location: This is the postcondition that might not hold.
+MoreInduction.dfy(93,0): Error BP5003: A postcondition might not hold on this return path.
+MoreInduction.dfy(92,21): Related location: This is the postcondition that might not hold.
Execution trace:
(0,0): anon0
diff --git a/Test/dafny1/PriorityQueue.dfy b/Test/dafny1/PriorityQueue.dfy
index 94223cba..3d2a5d78 100644
--- a/Test/dafny1/PriorityQueue.dfy
+++ b/Test/dafny1/PriorityQueue.dfy
@@ -12,7 +12,7 @@ class PriorityQueue {
reads this, Repr;
{
MostlyValid() &&
- (forall j :: 2 <= j && j <= n ==> a[j/2] <= a[j])
+ (forall j {:nowarn} :: 2 <= j && j <= n ==> a[j/2] <= a[j])
}
predicate MostlyValid()
@@ -50,8 +50,8 @@ class PriorityQueue {
method SiftUp(k: int)
requires 1 <= k && k <= n;
requires MostlyValid();
- requires (forall j :: 2 <= j && j <= n && j != k ==> a[j/2] <= a[j]);
- requires (forall j :: 1 <= j && j <= n ==> j/2 != k); // k is a leaf
+ requires (forall j {:nowarn} :: 2 <= j && j <= n && j != k ==> a[j/2] <= a[j]);
+ requires (forall j {:nowarn} :: 1 <= j && j <= n ==> j/2 != k); // k is a leaf
modifies a;
ensures Valid();
{
@@ -59,8 +59,8 @@ class PriorityQueue {
assert MostlyValid();
while (1 < i)
invariant i <= k && MostlyValid();
- invariant (forall j :: 2 <= j && j <= n && j != i ==> a[j/2] <= a[j]);
- invariant (forall j :: 1 <= j/2/2 && j/2 == i && j <= n ==> a[j/2/2] <= a[j]);
+ invariant (forall j {:nowarn} :: 2 <= j && j <= n && j != i ==> a[j/2] <= a[j]);
+ invariant (forall j {:nowarn} :: 1 <= j/2/2 && j/2 == i && j <= n ==> a[j/2/2] <= a[j]);
{
if (a[i/2] <= a[i]) {
return;
@@ -85,8 +85,8 @@ class PriorityQueue {
method SiftDown(k: int)
requires 1 <= k;
requires MostlyValid();
- requires (forall j :: 2 <= j && j <= n && j/2 != k ==> a[j/2] <= a[j]);
- requires (forall j :: 2 <= j && j <= n && 1 <= j/2/2 && j/2/2 != k ==> a[j/2/2] <= a[j]);
+ requires (forall j {:nowarn} :: 2 <= j && j <= n && j/2 != k ==> a[j/2] <= a[j]);
+ requires (forall j {:nowarn} :: 2 <= j && j <= n && 1 <= j/2/2 && j/2/2 != k ==> a[j/2/2] <= a[j]);
// Alternatively, the line above can be expressed as:
// requires (forall j :: 1 <= k/2 && j/2 == k && j <= n ==> a[j/2/2] <= a[j]);
modifies a;
@@ -95,8 +95,8 @@ class PriorityQueue {
var i := k;
while (2*i <= n) // while i is not a leaf
invariant 1 <= i && MostlyValid();
- invariant (forall j :: 2 <= j && j <= n && j/2 != i ==> a[j/2] <= a[j]);
- invariant (forall j :: 2 <= j && j <= n && 1 <= j/2/2 && j/2/2 != i ==> a[j/2/2] <= a[j]);
+ invariant (forall j {:nowarn} :: 2 <= j && j <= n && j/2 != i ==> a[j/2] <= a[j]);
+ invariant (forall j {:nowarn} :: 2 <= j && j <= n && 1 <= j/2/2 && j/2/2 != i ==> a[j/2/2] <= a[j]);
{
var smallestChild;
if (2*i + 1 <= n && a[2*i + 1] < a[2*i]) {
@@ -127,7 +127,7 @@ class PriorityQueue_Alternative {
reads this, Repr;
{
MostlyValid() &&
- (forall j :: 2 <= j && j <= n ==> a[j/2] <= a[j])
+ (forall j {:nowarn} :: 2 <= j && j <= n ==> a[j/2] <= a[j])
}
predicate MostlyValid()
@@ -164,7 +164,7 @@ class PriorityQueue_Alternative {
method SiftUp()
requires MostlyValid();
- requires (forall j :: 2 <= j && j <= n && j != n ==> a[j/2] <= a[j]);
+ requires (forall j {:nowarn} :: 2 <= j && j <= n && j != n ==> a[j/2] <= a[j]);
modifies a;
ensures Valid();
{
@@ -172,8 +172,8 @@ class PriorityQueue_Alternative {
assert MostlyValid();
while (1 < i)
invariant i <= n && MostlyValid();
- invariant (forall j :: 2 <= j && j <= n && j != i ==> a[j/2] <= a[j]);
- invariant (forall j :: 1 <= j/2/2 && j/2 == i && j <= n ==> a[j/2/2] <= a[j]);
+ invariant (forall j {:nowarn} :: 2 <= j && j <= n && j != i ==> a[j/2] <= a[j]);
+ invariant (forall j {:nowarn} :: 1 <= j/2/2 && j/2 == i && j <= n ==> a[j/2/2] <= a[j]);
{
if (a[i/2] <= a[i]) {
return;
@@ -197,15 +197,15 @@ class PriorityQueue_Alternative {
method SiftDown()
requires MostlyValid();
- requires (forall j :: 4 <= j && j <= n ==> a[j/2] <= a[j]);
+ requires (forall j {:nowarn} :: 4 <= j && j <= n ==> a[j/2] <= a[j]);
modifies a;
ensures Valid();
{
var i := 1;
while (2*i <= n) // while i is not a leaf
invariant 1 <= i && MostlyValid();
- invariant (forall j :: 2 <= j && j <= n && j/2 != i ==> a[j/2] <= a[j]);
- invariant (forall j :: 1 <= j/2/2 && j/2 == i && j <= n ==> a[j/2/2] <= a[j]);
+ invariant (forall j {:nowarn} :: 2 <= j && j <= n && j/2 != i ==> a[j/2] <= a[j]);
+ invariant (forall j {:nowarn} :: 1 <= j/2/2 && j/2 == i && j <= n ==> a[j/2/2] <= a[j]);
{
var smallestChild;
if (2*i + 1 <= n && a[2*i + 1] < a[2*i]) {
diff --git a/Test/dafny1/Rippling.dfy b/Test/dafny1/Rippling.dfy
index 55701a93..d888a5cc 100644
--- a/Test/dafny1/Rippling.dfy
+++ b/Test/dafny1/Rippling.dfy
@@ -300,244 +300,244 @@ function AlwaysTrueFunction(): FunctionValue
// The theorems to be proved
// -----------------------------------------------------------------------------------
-ghost method P1()
+lemma P1()
ensures forall n, xs :: concat(take(n, xs), drop(n, xs)) == xs;
{
}
-ghost method P2()
+lemma P2()
ensures forall n, xs, ys :: add(count(n, xs), count(n, ys)) == count(n, concat(xs, ys));
{
}
-ghost method P3()
+lemma P3()
ensures forall n, xs, ys :: leq(count(n, xs), count(n, concat(xs, ys))) == True;
{
}
-ghost method P4()
+lemma P4()
ensures forall n, xs :: add(Suc(Zero), count(n, xs)) == count(n, Cons(n, xs));
{
}
-ghost method P5()
+lemma P5()
ensures forall n, xs, x ::
add(Suc(Zero), count(n, xs)) == count(n, Cons(x, xs))
==> n == x;
{
}
-ghost method P6()
+lemma P6()
ensures forall m, n :: minus(n, add(n, m)) == Zero;
{
}
-ghost method P7()
+lemma P7()
ensures forall m, n :: minus(add(n, m), n) == m;
{
}
-ghost method P8()
+lemma P8()
ensures forall k, m, n :: minus(add(k, m), add(k, n)) == minus(m, n);
{
}
-ghost method P9()
+lemma P9()
ensures forall i, j, k :: minus(minus(i, j), k) == minus(i, add(j, k));
{
}
-ghost method P10()
+lemma P10()
ensures forall m :: minus(m, m) == Zero;
{
}
-ghost method P11()
+lemma P11()
ensures forall xs :: drop(Zero, xs) == xs;
{
}
-ghost method P12()
+lemma P12()
ensures forall n, xs, f :: drop(n, apply(f, xs)) == apply(f, drop(n, xs));
{
}
-ghost method P13()
+lemma P13()
ensures forall n, x, xs :: drop(Suc(n), Cons(x, xs)) == drop(n, xs);
{
}
-ghost method P14()
+lemma P14()
ensures forall xs, ys, p :: filter(p, concat(xs, ys)) == concat(filter(p, xs), filter(p, ys));
{
}
-ghost method P15()
+lemma P15()
ensures forall x, xs :: len(ins(x, xs)) == Suc(len(xs));
{
}
-ghost method P16()
+lemma P16()
ensures forall x, xs :: xs == Nil ==> last(Cons(x, xs)) == x;
{
}
-ghost method P17()
+lemma P17()
ensures forall n :: leq(n, Zero) == True <==> n == Zero;
{
}
-ghost method P18()
+lemma P18()
ensures forall i, m :: less(i, Suc(add(i, m))) == True;
{
}
-ghost method P19()
+lemma P19()
ensures forall n, xs :: len(drop(n, xs)) == minus(len(xs), n);
{
}
-ghost method P20()
+lemma P20()
ensures forall xs :: len(sort(xs)) == len(xs);
{
// the proof of this theorem requires a lemma about "insort"
assert forall x, xs :: len(insort(x, xs)) == Suc(len(xs));
}
-ghost method P21()
+lemma P21()
ensures forall n, m :: leq(n, add(n, m)) == True;
{
}
-ghost method P22()
+lemma P22()
ensures forall a, b, c :: max(max(a, b), c) == max(a, max(b, c));
{
}
-ghost method P23()
+lemma P23()
ensures forall a, b :: max(a, b) == max(b, a);
{
}
-ghost method P24()
+lemma P24()
ensures forall a, b :: max(a, b) == a <==> leq(b, a) == True;
{
}
-ghost method P25()
+lemma P25()
ensures forall a, b :: max(a, b) == b <==> leq(a, b) == True;
{
}
-ghost method P26()
+lemma P26()
ensures forall x, xs, ys :: mem(x, xs) == True ==> mem(x, concat(xs, ys)) == True;
{
}
-ghost method P27()
+lemma P27()
ensures forall x, xs, ys :: mem(x, ys) == True ==> mem(x, concat(xs, ys)) == True;
{
}
-ghost method P28()
+lemma P28()
ensures forall x, xs :: mem(x, concat(xs, Cons(x, Nil))) == True;
{
}
-ghost method P29()
+lemma P29()
ensures forall x, xs :: mem(x, ins1(x, xs)) == True;
{
}
-ghost method P30()
+lemma P30()
ensures forall x, xs :: mem(x, ins(x, xs)) == True;
{
}
-ghost method P31()
+lemma P31()
ensures forall a, b, c :: min(min(a, b), c) == min(a, min(b, c));
{
}
-ghost method P32()
+lemma P32()
ensures forall a, b :: min(a, b) == min(b, a);
{
}
-ghost method P33()
+lemma P33()
ensures forall a, b :: min(a, b) == a <==> leq(a, b) == True;
{
}
-ghost method P34()
+lemma P34()
ensures forall a, b :: min(a, b) == b <==> leq(b, a) == True;
{
}
-ghost method P35()
+lemma P35()
ensures forall xs :: dropWhileAlways(AlwaysFalseFunction(), xs) == xs;
{
}
-ghost method P36()
+lemma P36()
ensures forall xs :: takeWhileAlways(AlwaysTrueFunction(), xs) == xs;
{
}
-ghost method P37()
+lemma P37()
ensures forall x, xs :: not(mem(x, delete(x, xs))) == True;
{
}
-ghost method P38()
+lemma P38()
ensures forall n, xs :: count(n, concat(xs, Cons(n, Nil))) == Suc(count(n, xs));
{
}
-ghost method P39()
+lemma P39()
ensures forall n, x, xs ::
add(count(n, Cons(x, Nil)), count(n, xs)) == count(n, Cons(x, xs));
{
}
-ghost method P40()
+lemma P40()
ensures forall xs :: take(Zero, xs) == Nil;
{
}
-ghost method P41()
+lemma P41()
ensures forall n, xs, f :: take(n, apply(f, xs)) == apply(f, take(n, xs));
{
}
-ghost method P42()
+lemma P42()
ensures forall n, x, xs :: take(Suc(n), Cons(x, xs)) == Cons(x, take(n, xs));
{
}
-ghost method P43(p: FunctionValue)
+lemma P43(p: FunctionValue)
ensures forall xs :: concat(takeWhileAlways(p, xs), dropWhileAlways(p, xs)) == xs;
{
}
-ghost method P44()
+lemma P44()
ensures forall x, xs, ys :: zip(Cons(x, xs), ys) == zipConcat(x, xs, ys);
{
}
-ghost method P45()
+lemma P45()
ensures forall x, xs, y, ys ::
zip(Cons(x, xs), Cons(y, ys)) ==
PCons(Pair.Pair(x, y), zip(xs, ys));
{
}
-ghost method P46()
+lemma P46()
ensures forall ys :: zip(Nil, ys) == PNil;
{
}
-ghost method P47()
+lemma P47()
ensures forall a :: height(mirror(a)) == height(a);
{
// proving this theorem requires a previously proved lemma:
@@ -546,20 +546,20 @@ ghost method P47()
// ...
-ghost method P54()
+lemma P54()
ensures forall m, n :: minus(add(m, n), n) == m;
{
// the proof of this theorem follows from two lemmas:
- assert forall m, n :: minus(add(n, m), n) == m;
+ assert forall m, n {:autotriggers false} :: minus(add(n, m), n) == m; // FIXME: Why does Autotriggers false make things verify?
assert forall m, n :: add(m, n) == add(n, m);
}
-ghost method P65()
+lemma P65()
ensures forall i, m :: less(i, Suc(add(m, i))) == True;
{
if (*) {
// the proof of this theorem follows from two lemmas:
- assert forall i, m :: less(i, Suc(add(i, m))) == True;
+ assert forall i, m {:autotriggers false} :: less(i, Suc(add(i, m))) == True; // FIXME: Why does Autotriggers false make things verify?
assert forall m, n :: add(m, n) == add(n, m);
} else {
// a different way to prove it uses the following lemma:
@@ -567,12 +567,12 @@ ghost method P65()
}
}
-ghost method P67()
+lemma P67()
ensures forall m, n :: leq(n, add(m, n)) == True;
{
if (*) {
// the proof of this theorem follows from two lemmas:
- assert forall m, n :: leq(n, add(n, m)) == True;
+ assert forall m, n {:autotriggers false} :: leq(n, add(n, m)) == True; // FIXME: Why does Autotriggers false make things verify?
assert forall m, n :: add(m, n) == add(n, m);
} else {
// a different way to prove it uses the following lemma:
@@ -583,19 +583,19 @@ ghost method P67()
// ---------
// Here is a alternate way of writing down the proof obligations:
-ghost method P1_alt(n: Nat, xs: List)
+lemma P1_alt(n: Nat, xs: List)
ensures concat(take(n, xs), drop(n, xs)) == xs;
{
}
-ghost method P2_alt(n: Nat, xs: List, ys: List)
+lemma P2_alt(n: Nat, xs: List, ys: List)
ensures add(count(n, xs), count(n, ys)) == count(n, (concat(xs, ys)));
{
}
// ---------
-ghost method Lemma_RevConcat(xs: List, ys: List)
+lemma Lemma_RevConcat(xs: List, ys: List)
ensures reverse(concat(xs, ys)) == concat(reverse(ys), reverse(xs));
{
match (xs) {
@@ -606,7 +606,7 @@ ghost method Lemma_RevConcat(xs: List, ys: List)
}
}
-ghost method Theorem(xs: List)
+lemma Theorem(xs: List)
ensures reverse(reverse(xs)) == xs;
{
match (xs) {
diff --git a/Test/dafny1/SchorrWaite-stages.dfy b/Test/dafny1/SchorrWaite-stages.dfy
index 0eaed68c..a6e5e3aa 100644
--- a/Test/dafny1/SchorrWaite-stages.dfy
+++ b/Test/dafny1/SchorrWaite-stages.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// Schorr-Waite algorithms, written and verified in Dafny.
diff --git a/Test/dafny1/SchorrWaite.dfy b/Test/dafny1/SchorrWaite.dfy
index b29a6829..b0877f9f 100644
--- a/Test/dafny1/SchorrWaite.dfy
+++ b/Test/dafny1/SchorrWaite.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// Rustan Leino
@@ -180,7 +180,7 @@ class Main {
ensures forall n :: n in S && n.marked ==>
forall ch :: ch in n.children && ch != null ==> ch.marked
// every marked node was reachable from 'root' in the pre-state:
- ensures forall n :: n in S && n.marked ==> old(Reachable(root, n, S))
+ ensures forall n {:autotriggers false} :: n in S && n.marked ==> old(Reachable(root, n, S))
// the structure of the graph has not changed:
ensures forall n :: n in S ==>
n.childrenVisited == old(n.childrenVisited) &&
@@ -207,7 +207,7 @@ class Main {
forall j :: 0 <= j < n.childrenVisited ==>
n.children[j] == null || n.children[j].marked
invariant forall n :: n in stackNodes ==> n.childrenVisited < |n.children|
- invariant forall n :: n in S && n.marked && n !in stackNodes && n != t ==>
+ invariant forall n {:autotriggers false} :: n in S && n.marked && n !in stackNodes && n != t ==>
forall ch :: ch in n.children && ch != null ==> ch.marked
invariant forall n :: n in S && n !in stackNodes && n != t ==>
n.childrenVisited == old(n.childrenVisited)
@@ -219,7 +219,7 @@ class Main {
// every marked node is reachable:
invariant !fresh(path); // needed to show 'path' worthy as argument to old(Reachable(...))
invariant old(ReachableVia(root, path, t, S));
- invariant forall n, pth :: n in S && n.marked && pth == n.pathFromRoot ==> !fresh(pth)
+ invariant forall n, pth {:nowarn} :: n in S && n.marked && pth == n.pathFromRoot ==> !fresh(pth)
invariant forall n, pth :: n in S && n.marked && pth == n.pathFromRoot ==>
old(ReachableVia(root, pth, n, S))
invariant forall n :: n in S && n.marked ==> old(Reachable(root, n, S))
diff --git a/Test/dafny1/Substitution.dfy b/Test/dafny1/Substitution.dfy
index da64d004..b9c83aff 100644
--- a/Test/dafny1/Substitution.dfy
+++ b/Test/dafny1/Substitution.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
datatype List = Nil | Cons(Expr, List)
diff --git a/Test/dafny1/UltraFilter.dfy b/Test/dafny1/UltraFilter.dfy
index a32e6e0b..7ac4e749 100644
--- a/Test/dafny1/UltraFilter.dfy
+++ b/Test/dafny1/UltraFilter.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// ultra filter
diff --git a/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy b/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy
index c752bd38..f691384c 100644
--- a/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy
+++ b/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy
@@ -117,6 +117,11 @@ class Tree {
Repr := lft.Repr + {this} + rgt.Repr;
}
+ lemma exists_intro<T>(P: T -> bool, x: T)
+ requires P.requires(x)
+ requires P(x)
+ ensures exists y :: P.requires(y) && P(y) { }
+
method ComputeMax() returns (mx: int)
requires Valid() && !IsEmpty();
ensures forall x :: x in Contents ==> x <= mx;
@@ -124,13 +129,17 @@ class Tree {
decreases Repr;
{
mx := value;
+
if (!left.IsEmpty()) {
var m := left.ComputeMax();
mx := if mx < m then m else mx;
}
+
if (!right.IsEmpty()) {
var m := right.ComputeMax();
mx := if mx < m then m else mx;
}
+
+ exists_intro(x reads this => x in Contents && x == mx, mx);
}
}
diff --git a/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy.expect b/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy.expect
index 42fd56a5..c87e2af2 100644
--- a/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy.expect
+++ b/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy.expect
@@ -1,2 +1,2 @@
-Dafny program verifier finished with 8 verified, 0 errors
+Dafny program verifier finished with 10 verified, 0 errors
diff --git a/Test/dafny2/COST-verif-comp-2011-3-TwoDuplicates.dfy b/Test/dafny2/COST-verif-comp-2011-3-TwoDuplicates.dfy
index 72a22cfd..4c702674 100644
--- a/Test/dafny2/COST-verif-comp-2011-3-TwoDuplicates.dfy
+++ b/Test/dafny2/COST-verif-comp-2011-3-TwoDuplicates.dfy
@@ -93,8 +93,8 @@ method Search(a: array<int>) returns (p: int, q: int)
invariant forall j :: 0 <= j < d.Length ==>
(d[j] == -1 && forall k :: 0 <= k < i ==> a[k] != j) ||
(0 <= d[j] < i && a[d[j]] == j);
- invariant p == q ==> IsDuplicate(a, p);
- invariant forall k :: 0 <= k < i && IsPrefixDuplicate(a, i, a[k]) ==> p == q == a[k];
+ invariant p == q ==> IsDuplicate(a, p); //WISH remove the trigger on the next line
+ invariant forall k {:trigger old(a[k])} :: 0 <= k < i && IsPrefixDuplicate(a, i, a[k]) ==> p == q == a[k];
decreases a.Length - i;
{
var k := d[a[i]];
diff --git a/Test/dafny2/COST-verif-comp-2011-4-FloydCycleDetect.dfy b/Test/dafny2/COST-verif-comp-2011-4-FloydCycleDetect.dfy
index 2aa14db7..72250f99 100644
--- a/Test/dafny2/COST-verif-comp-2011-4-FloydCycleDetect.dfy
+++ b/Test/dafny2/COST-verif-comp-2011-4-FloydCycleDetect.dfy
@@ -164,7 +164,7 @@ class Node {
invariant 0 <= t < h && Nexxxt(t, S) == tortoise && Nexxxt(h, S) == hare;
// What follows of the invariant is for proving termination:
invariant h == 1 + 2*t && t <= A + B;
- invariant forall k :: 0 <= k < t ==> Nexxxt(k, S) != Nexxxt(1+2*k, S);
+ invariant forall k {:nowarn} :: 0 <= k < t ==> Nexxxt(k, S) != Nexxxt(1+2*k, S);
decreases A + B - t;
{
if hare == null || hare.next == null {
@@ -225,7 +225,7 @@ class Node {
requires 0 <= a && 1 <= b;
requires forall k,l :: 0 <= k < l < a ==> Nexxxt(k, S) != Nexxxt(l, S);
requires Nexxxt(a, S) == null || Nexxxt(a, S).Nexxxt(b, S) == Nexxxt(a, S);
- ensures exists T :: 0 <= T < a+b && Nexxxt(T, S) == Nexxxt(1+2*T, S);
+ ensures exists T {:nowarn} :: 0 <= T < a+b && Nexxxt(T, S) == Nexxxt(1+2*T, S);
{
if Nexxxt(a, S) == null {
Lemma_NullIsTerminal(1+2*a, S);
diff --git a/Test/dafny2/Calculations.dfy b/Test/dafny2/Calculations.dfy
index 8af0afe9..3870490f 100644
--- a/Test/dafny2/Calculations.dfy
+++ b/Test/dafny2/Calculations.dfy
@@ -41,12 +41,12 @@ function qreverse(l: List): List
// Here are two lemmas about the List functions.
-ghost method Lemma_ConcatNil(xs : List)
+lemma Lemma_ConcatNil(xs : List)
ensures concat(xs, Nil) == xs;
{
}
-ghost method Lemma_RevCatCommute(xs : List)
+lemma Lemma_RevCatCommute(xs : List)
ensures forall ys, zs :: revacc(xs, concat(ys, zs)) == concat(revacc(xs, ys), zs);
{
}
@@ -55,7 +55,7 @@ ghost method Lemma_RevCatCommute(xs : List)
// is given in a calculational style. The proof is not minimal--some lines can be omitted
// and Dafny will still fill in the details.
-ghost method Theorem_QReverseIsCorrect_Calc(l: List)
+lemma Theorem_QReverseIsCorrect_Calc(l: List)
ensures qreverse(l) == reverse(l);
{
calc {
@@ -69,7 +69,7 @@ ghost method Theorem_QReverseIsCorrect_Calc(l: List)
}
}
-ghost method Lemma_Revacc_calc(xs: List, ys: List)
+lemma Lemma_Revacc_calc(xs: List, ys: List)
ensures revacc(xs, ys) == concat(reverse(xs), ys);
{
match (xs) {
@@ -93,7 +93,7 @@ ghost method Lemma_Revacc_calc(xs: List, ys: List)
// Here is a version of the same proof, as it was constructed before Dafny's "calc" construct.
-ghost method Theorem_QReverseIsCorrect(l: List)
+lemma Theorem_QReverseIsCorrect(l: List)
ensures qreverse(l) == reverse(l);
{
assert qreverse(l)
@@ -105,7 +105,7 @@ ghost method Theorem_QReverseIsCorrect(l: List)
Lemma_ConcatNil(reverse(l));
}
-ghost method Lemma_Revacc(xs: List, ys: List)
+lemma Lemma_Revacc(xs: List, ys: List)
ensures revacc(xs, ys) == concat(reverse(xs), ys);
{
match (xs) {
@@ -140,7 +140,7 @@ function Fib(n: nat): nat
if n < 2 then n else Fib(n - 2) + Fib(n - 1)
}
-ghost method Lemma_Fib()
+lemma Lemma_Fib()
ensures Fib(5) < 6;
{
calc {
@@ -160,11 +160,11 @@ ghost method Lemma_Fib()
/* List length */
// Here are some proofs that show the use of nested calculations.
-ghost method Lemma_Concat_Length(xs: List, ys: List)
+lemma Lemma_Concat_Length(xs: List, ys: List)
ensures length(concat(xs, ys)) == length(xs) + length(ys);
{}
-ghost method Lemma_Reverse_Length(xs: List)
+lemma Lemma_Reverse_Length(xs: List)
ensures length(xs) == length(reverse(xs));
{
match (xs) {
@@ -193,7 +193,7 @@ ghost method Lemma_Reverse_Length(xs: List)
}
}
-ghost method Window(xs: List, ys: List)
+lemma Window(xs: List, ys: List)
ensures length(xs) == length(ys) ==> length(reverse(xs)) == length(reverse(ys));
{
calc {
@@ -221,11 +221,11 @@ function ith<a>(xs: List, i: nat): a
case Cons(x, xrest) => if i == 0 then x else ith(xrest, i - 1)
}
-ghost method lemma_zero_length(xs: List)
+lemma lemma_zero_length(xs: List)
ensures length(xs) == 0 <==> xs.Nil?;
{}
-ghost method lemma_extensionality(xs: List, ys: List)
+lemma lemma_extensionality(xs: List, ys: List)
requires length(xs) == length(ys); // (0)
requires forall i: nat | i < length(xs) :: ith(xs, i) == ith(ys, i); // (1)
ensures xs == ys;
diff --git a/Test/dafny2/MajorityVote.dfy b/Test/dafny2/MajorityVote.dfy
index 51e5b968..f1c3b485 100644
--- a/Test/dafny2/MajorityVote.dfy
+++ b/Test/dafny2/MajorityVote.dfy
@@ -165,7 +165,7 @@ method SearchForWinner<Candidate(==)>(a: seq<Candidate>, ghost hasWinner: bool,
// Here are two lemmas about Count that are used in the methods above.
-ghost method Lemma_Split<T>(a: seq<T>, s: int, t: int, u: int, x: T)
+lemma Lemma_Split<T>(a: seq<T>, s: int, t: int, u: int, x: T)
requires 0 <= s <= t <= u <= |a|;
ensures Count(a, s, t, x) + Count(a, t, u, x) == Count(a, s, u, x);
{
@@ -178,7 +178,7 @@ ghost method Lemma_Split<T>(a: seq<T>, s: int, t: int, u: int, x: T)
*/
}
-ghost method Lemma_Unique<T>(a: seq<T>, s: int, t: int, x: T, y: T)
+lemma Lemma_Unique<T>(a: seq<T>, s: int, t: int, x: T, y: T)
requires 0 <= s <= t <= |a|;
ensures x != y ==> Count(a, s, t, x) + Count(a, s, t, y) <= t - s;
{
diff --git a/Test/dafny2/SnapshotableTrees.dfy b/Test/dafny2/SnapshotableTrees.dfy
index 2bdfb83b..033c5db0 100644
--- a/Test/dafny2/SnapshotableTrees.dfy
+++ b/Test/dafny2/SnapshotableTrees.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:2 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:2 /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// Rustan Leino, September 2011.
diff --git a/Test/dafny2/SnapshotableTrees.dfy.expect b/Test/dafny2/SnapshotableTrees.dfy.expect
index 849b9e38..68b4ff73 100644
--- a/Test/dafny2/SnapshotableTrees.dfy.expect
+++ b/Test/dafny2/SnapshotableTrees.dfy.expect
@@ -1,8 +1,15 @@
-SnapshotableTrees.dfy(68,24): Error BP5002: A precondition for this call might not hold.
-SnapshotableTrees.dfy(648,16): Related location: This is the precondition that might not hold.
+SnapshotableTrees.dfy(68,23): Error BP5002: A precondition for this call might not hold.
+SnapshotableTrees.dfy(648,15): Related location: This is the precondition that might not hold.
+SnapshotableTrees.dfy(545,15): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon3_Then
+SnapshotableTrees.dfy(68,23): Error BP5002: A precondition for this call might not hold.
+SnapshotableTrees.dfy(648,15): Related location: This is the precondition that might not hold.
+SnapshotableTrees.dfy(548,18): Related location
Execution trace:
(0,0): anon0
(0,0): anon3_Then
-Dafny program verifier finished with 65 verified, 1 error
+Dafny program verifier finished with 65 verified, 2 errors
Compiled assembly into SnapshotableTrees.exe
diff --git a/Test/dafny3/CachedContainer.dfy.expect b/Test/dafny3/CachedContainer.dfy.expect
index c6c90498..0185aacd 100644
--- a/Test/dafny3/CachedContainer.dfy.expect
+++ b/Test/dafny3/CachedContainer.dfy.expect
@@ -1,2 +1,3 @@
+CachedContainer.dfy(120,25): Warning: "import A as B" has been deprecated; in the new syntax, it is "import A:B"
Dafny program verifier finished with 47 verified, 0 errors
diff --git a/Test/dafny3/CalcExample.dfy b/Test/dafny3/CalcExample.dfy
index 2782d049..b9d3260b 100644
--- a/Test/dafny3/CalcExample.dfy
+++ b/Test/dafny3/CalcExample.dfy
@@ -3,14 +3,14 @@
function f(x: int, y: int): int
-ghost method Associativity(x: int, y: int, z: int)
+lemma Associativity(x: int, y: int, z: int)
ensures f(x, f(y, z)) == f(f(x, y), z);
-ghost method Monotonicity(y: int, z: int)
+lemma Monotonicity(y: int, z: int)
requires y <= z;
ensures forall x :: f(x, y) <= f(x, z);
-ghost method DiagonalIdentity(x: int)
+lemma DiagonalIdentity(x: int)
ensures f(x, x) == x;
method M(a: int, b: int, c: int, x: int)
diff --git a/Test/dafny3/Dijkstra.dfy b/Test/dafny3/Dijkstra.dfy
index 1fbd9b7c..56719180 100644
--- a/Test/dafny3/Dijkstra.dfy
+++ b/Test/dafny3/Dijkstra.dfy
@@ -3,7 +3,7 @@
// Example taken from:
// Edsger W. Dijkstra: Heuristics for a Calculational Proof. Inf. Process. Lett. (IPL) 53(3):141-143 (1995)
-// Transcribed into Dafny by Valentin Wüstholz and Nadia Polikarpova.
+// Transcribed into Dafny by Valentin Wüstholz and Nadia Polikarpova.
// f is an arbitrary function on the natural numbers
function f(n: nat) : nat
@@ -56,7 +56,7 @@ lemma lemma_ping(j: nat, n: nat)
}
}
-// The other directorion: f(n) <= n
+// The other direction: f(n) <= n
lemma lemma_pong(n: nat)
requires P()
ensures f(n) <= n
diff --git a/Test/dafny3/Filter.dfy b/Test/dafny3/Filter.dfy
index 24c8e94e..7473a580 100644
--- a/Test/dafny3/Filter.dfy
+++ b/Test/dafny3/Filter.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
codatatype Stream<T> = Cons(head: T, tail: Stream)
@@ -35,7 +35,7 @@ lemma Lemma_TailSubStreamK(s: Stream, u: Stream, k: nat) // this lemma could ha
{
if k != 0 {
Lemma_InTail(s.head, u);
- Lemma_TailSubStreamK(s.tail, u, k-1);
+ //Lemma_TailSubStreamK(s.tail, u, k-1);
}
}
lemma Lemma_InSubStream<T>(x: T, s: Stream<T>, u: Stream<T>)
@@ -53,104 +53,119 @@ lemma Lemma_InSubStream<T>(x: T, s: Stream<T>, u: Stream<T>)
}
}
-type PredicateHandle
-predicate P<T>(x: T, h: PredicateHandle)
+type Predicate<T> = T -> bool
-copredicate AllP(s: Stream, h: PredicateHandle)
+predicate Total<T,U>(f: T -> U)
+ reads f.reads
{
- P(s.head, h) && AllP(s.tail, h)
+ forall t :: f.reads(t) == {} && f.requires(t)
}
-lemma Lemma_InAllP<T>(x: T, s: Stream<T>, h: PredicateHandle)
- requires In(x, s) && AllP(s, h);
- ensures P(x, h);
+
+copredicate AllP(s: Stream, P: Predicate)
+ requires Total(P)
+{
+ P(s.head) && AllP(s.tail, P)
+}
+lemma Lemma_InAllP<T>(x: T, s: Stream<T>, P: Predicate)
+ requires Total(P)
+ requires In(x, s) && AllP(s, P)
+ ensures P(x)
{
var n :| 0 <= n && Tail(s, n).head == x;
var t := s;
while n != 0
- invariant 0 <= n;
- invariant Tail(t, n).head == x;
- invariant AllP(t, h);
+ invariant 0 <= n
+ invariant Tail(t, n).head == x
+ invariant AllP(t, P)
{
t, n := t.tail, n - 1;
}
}
-predicate IsAnother(s: Stream, h: PredicateHandle)
+predicate IsAnother(s: Stream, P: Predicate)
+ requires Total(P)
{
- exists n :: 0 <= n && P(Tail(s, n).head, h)
+ exists n :: 0 <= n && P(Tail(s, n).head)
}
-copredicate AlwaysAnother(s: Stream, h: PredicateHandle)
+copredicate AlwaysAnother(s: Stream, P: Predicate)
+ requires Total(P)
{
- IsAnother(s, h) && AlwaysAnother(s.tail, h)
+ IsAnother(s, P) && AlwaysAnother(s.tail, P)
}
-colemma Lemma_AllImpliesAlwaysAnother(s: Stream, h: PredicateHandle)
- requires AllP(s, h);
- ensures AlwaysAnother(s, h);
+colemma Lemma_AllImpliesAlwaysAnother(s: Stream, P: Predicate)
+ requires Total(P)
+ requires AllP(s, P)
+ ensures AlwaysAnother(s, P)
{
assert Tail(s, 0) == s;
}
-function Next(s: Stream, h: PredicateHandle): nat
- requires AlwaysAnother(s, h);
- ensures P(Tail(s, Next(s, h)).head, h);
- ensures forall i :: 0 <= i < Next(s, h) ==> !P(Tail(s, i).head, h);
+function Next(s: Stream, P: Predicate): nat
+ requires Total(P)
+ requires AlwaysAnother(s, P)
+ ensures P(Tail(s, Next(s, P)).head)
+ ensures forall i :: 0 <= i < Next(s, P) ==> !P(Tail(s, i).head)
{
- var n :| 0 <= n && P(Tail(s, n).head, h);
- NextMinimizer(s, h, n)
+ var n :| 0 <= n && P(Tail(s, n).head);
+ NextMinimizer(s, P, n)
}
// the following is an auxiliary function of the definition of Next
-function NextMinimizer(s: Stream, h: PredicateHandle, n: nat): nat
- requires P(Tail(s, n).head, h);
- ensures P(Tail(s, NextMinimizer(s, h, n)).head, h);
- ensures forall i :: 0 <= i < NextMinimizer(s, h, n) ==> !P(Tail(s, i).head, h);
+function NextMinimizer(s: Stream, P: Predicate, n: nat): nat
+ requires Total(P)
+ requires P(Tail(s, n).head)
+ ensures P(Tail(s, NextMinimizer(s, P, n)).head)
+ ensures forall i :: 0 <= i < NextMinimizer(s, P, n) ==> !P(Tail(s, i).head)
{
- if forall i :: 0 <= i < n ==> !P(Tail(s, i).head, h) then
+ if forall i :: 0 <= i < n ==> !P(Tail(s, i).head) then
n
else
- var k :| 0 <= k < n && P(Tail(s, k).head, h);
- NextMinimizer(s, h, k)
+ var k :| 0 <= k < n && P(Tail(s, k).head);
+ NextMinimizer(s, P, k)
}
-function Filter(s: Stream, h: PredicateHandle): Stream
- requires AlwaysAnother(s, h);
- decreases Next(s, h);
+function Filter(s: Stream, P: Predicate): Stream
+ requires Total(P)
+ requires AlwaysAnother(s, P)
+ decreases Next(s, P)
{
- if P(s.head, h) then
- Cons(s.head, Filter(s.tail, h))
+ if P(s.head) then
+ Cons(s.head, Filter(s.tail, P))
else
- Filter(s.tail, h)
+ Filter(s.tail, P)
}
// properties about Filter
-colemma Filter_AlwaysAnother(s: Stream, h: PredicateHandle)
- requires AlwaysAnother(s, h);
- ensures AllP(Filter(s, h), h);
- decreases Next(s, h);
+colemma Filter_AlwaysAnother(s: Stream, P: Predicate)
+ requires Total(P)
+ requires AlwaysAnother(s, P)
+ ensures AllP(Filter(s, P), P)
+ decreases Next(s, P)
{
- if P(s.head, h) {
- Filter_AlwaysAnother(s.tail, h);
+ if P(s.head) {
+ Filter_AlwaysAnother(s.tail, P);
} else {
- Filter_AlwaysAnother#[_k](s.tail, h);
+ Filter_AlwaysAnother#[_k](s.tail, P);
}
}
-colemma Filter_IsSubStream(s: Stream, h: PredicateHandle)
- requires AlwaysAnother(s, h);
- ensures IsSubStream(Filter(s, h), s);
- decreases Next(s, h);
+colemma Filter_IsSubStream(s: Stream, P: Predicate)
+ requires Total(P)
+ requires AlwaysAnother(s, P)
+ ensures IsSubStream(Filter(s, P), s)
+ decreases Next(s, P)
{
- if P(s.head, h) {
+ if P(s.head) {
// To prove IsSubStream#[_k](Filter(s, h), s), we prove the two conjuncts from the definition
calc {
true;
- == { Filter_IsSubStream(s.tail, h); } // induction hypothesis
- IsSubStream#[_k-1](Filter(s.tail, h), s.tail);
+ == { Filter_IsSubStream(s.tail, P); } // induction hypothesis
+ IsSubStream(Filter(s.tail, P), s.tail);
== // { assert Filter(s.tail, h) == Filter(s, h).tail; }
- IsSubStream#[_k-1](Filter(s, h).tail, s.tail);
- ==> { Lemma_TailSubStreamK(Filter(s, h).tail, s, _k-1); }
- IsSubStream#[_k-1](Filter(s, h).tail, s);
+ IsSubStream(Filter(s, P).tail, s.tail);
+ ==> { Lemma_TailSubStreamK(Filter(s, P).tail, s, _k-1); }
+ IsSubStream(Filter(s, P).tail, s);
}
calc {
- In(Filter(s, h).head, s);
- == { assert Filter(s, h) == Cons(s.head, Filter(s.tail, h)); }
+ In(Filter(s, P).head, s);
+ == { assert Filter(s, P) == Cons(s.head, Filter(s.tail, P)); }
In(s.head, s);
== { assert Tail(s, 0) == s;
assert exists n :: 0 <= n && Tail(s, n).head == s.head;
@@ -158,105 +173,114 @@ colemma Filter_IsSubStream(s: Stream, h: PredicateHandle)
true;
}
} else {
- Lemma_TailSubStreamK(Filter(s.tail, h), s, _k);
+ Lemma_TailSubStreamK(Filter(s.tail, P), s, _k);
}
}
// The following says nothing about the order of the elements in the stream
-lemma Theorem_Filter<T>(s: Stream<T>, h: PredicateHandle)
- requires AlwaysAnother(s, h);
- ensures forall x :: In(x, Filter(s, h)) <==> In(x, s) && P(x, h);
+lemma Theorem_Filter<T>(s: Stream<T>, P: Predicate)
+ requires Total(P)
+ requires AlwaysAnother(s, P)
+ ensures forall x :: In(x, Filter(s, P)) <==> In(x, s) && P(x)
{
forall x
- ensures In(x, Filter(s, h)) <==> In(x, s) && P(x, h);
+ ensures In(x, Filter(s, P)) <==> In(x, s) && P(x)
{
- if In(x, Filter(s, h)) {
- FS_Ping(s, h, x);
+ if In(x, Filter(s, P)) {
+ FS_Ping(s, P, x);
}
- if In(x, s) && P(x, h) {
+ if In(x, s) && P(x) {
var k :| 0 <= k && Tail(s, k).head == x;
- FS_Pong(s, h, x, k);
+ FS_Pong(s, P, x, k);
}
}
}
-lemma FS_Ping<T>(s: Stream<T>, h: PredicateHandle, x: T)
- requires AlwaysAnother(s, h) && In(x, Filter(s, h));
- ensures In(x, s) && P(x, h);
+lemma FS_Ping<T>(s: Stream<T>, P: Predicate, x: T)
+ requires Total(P)
+ requires AlwaysAnother(s, P) && In(x, Filter(s, P));
+ ensures In(x, s) && P(x);
{
- Filter_IsSubStream(s, h);
- Lemma_InSubStream(x, Filter(s, h), s);
+ Filter_IsSubStream(s, P);
+ Lemma_InSubStream(x, Filter(s, P), s);
- Filter_AlwaysAnother(s, h);
- assert AllP(Filter(s, h), h);
- Lemma_InAllP(x, Filter(s, h), h);
+ Filter_AlwaysAnother(s, P);
+ assert AllP(Filter(s, P), P);
+ Lemma_InAllP(x, Filter(s, P), P);
}
-lemma FS_Pong<T>(s: Stream<T>, h: PredicateHandle, x: T, k: nat)
- requires AlwaysAnother(s, h) && In(x, s) && P(x, h);
- requires Tail(s, k).head == x;
- ensures In(x, Filter(s, h));
- decreases k;
+lemma FS_Pong<T>(s: Stream<T>, P: Predicate, x: T, k: nat)
+ requires Total(P)
+ requires AlwaysAnother(s, P) && In(x, s) && P(x)
+ requires Tail(s, k).head == x
+ ensures In(x, Filter(s, P))
+ decreases k
{
- var fs := Filter(s, h);
+ var fs := Filter(s, P);
if s.head == x {
assert Tail(fs, 0) == fs;
- } else if P(s.head, h) {
- assert fs == Cons(s.head, Filter(s.tail, h)); // reminder of where we are
+ } else if P(s.head) {
+ assert fs == Cons(s.head, Filter(s.tail, P)); // reminder of where we are
calc {
true;
- == { FS_Pong(s.tail, h, x, k-1); }
- In(x, Filter(s.tail, h));
+ //== { FS_Pong(s.tail, h, x, k-1); }
+ In(x, Filter(s.tail, P));
==> { assert fs.head != x; Lemma_InTail(x, fs); }
In(x, fs);
}
} else {
- assert fs == Filter(s.tail, h); // reminder of where we are
- FS_Pong(s.tail, h, x, k-1);
+ //assert fs == Filter(s.tail, h); // reminder of where we are
+ //FS_Pong(s.tail, h, x, k-1);
}
}
// ----- orderings ------
-function Ord<T>(x: T, ord: PredicateHandle): int
+type Ord<T> = T -> int
-copredicate Increasing(s: Stream, ord: PredicateHandle)
+copredicate Increasing(s: Stream, ord: Ord)
+ requires Total(ord)
{
- Ord(s.head, ord) < Ord(s.tail.head, ord) && Increasing(s.tail, ord)
+ ord(s.head) < ord(s.tail.head) && Increasing(s.tail, ord)
}
-copredicate IncrFrom(s: Stream, low: int, ord: PredicateHandle)
+copredicate IncrFrom(s: Stream, low: int, ord: Ord)
+ requires Total(ord)
{
- low <= Ord(s.head, ord) && IncrFrom(s.tail, Ord(s.head, ord) + 1, ord)
+ low <= ord(s.head) && IncrFrom(s.tail, ord(s.head) + 1, ord)
}
-colemma Lemma_Incr0(s: Stream, low: int, ord: PredicateHandle)
- requires IncrFrom(s, low, ord);
- ensures Increasing(s, ord);
+colemma Lemma_Incr0(s: Stream, low: int, ord: Ord)
+ requires Total(ord)
+ requires IncrFrom(s, low, ord)
+ ensures Increasing(s, ord)
{
}
-colemma Lemma_Incr1(s: Stream, ord: PredicateHandle)
+colemma Lemma_Incr1(s: Stream, ord: Ord)
+ requires Total(ord)
requires Increasing(s, ord);
- ensures IncrFrom(s, Ord(s.head, ord), ord);
+ ensures IncrFrom(s, ord(s.head), ord);
{
Lemma_Incr1(s.tail, ord);
}
-lemma Theorem_FilterPreservesOrdering(s: Stream, h: PredicateHandle, ord: PredicateHandle)
- requires Increasing(s, ord) && AlwaysAnother(s, h);
- ensures Increasing(Filter(s, h), ord);
+lemma Theorem_FilterPreservesOrdering(s: Stream, P: Predicate, ord: Ord)
+ requires Total(P) && Total(ord)
+ requires Increasing(s, ord) && AlwaysAnother(s, P)
+ ensures Increasing(Filter(s, P), ord)
{
Lemma_Incr1(s, ord);
- Lemma_FilterPreservesIncrFrom(s, h, Ord(s.head, ord), ord);
- Lemma_Incr0(Filter(s, h), Ord(s.head, ord), ord);
+ Lemma_FilterPreservesIncrFrom(s, P, ord(s.head), ord);
+ Lemma_Incr0(Filter(s, P), ord(s.head), ord);
}
-colemma Lemma_FilterPreservesIncrFrom(s: Stream, h: PredicateHandle, low: int, ord: PredicateHandle)
- requires IncrFrom(s, low, ord) && AlwaysAnother(s, h) && low <= Ord(s.head, ord);
- ensures IncrFrom(Filter(s, h), low, ord);
- decreases Next(s, h);
+colemma Lemma_FilterPreservesIncrFrom(s: Stream, P: Predicate, low: int, ord: Ord)
+ requires Total(P) && Total(ord)
+ requires IncrFrom(s, low, ord) && AlwaysAnother(s, P) && low <= ord(s.head)
+ ensures IncrFrom(Filter(s, P), low, ord)
+ decreases Next(s, P)
{
- if P(s.head, h) {
- Lemma_FilterPreservesIncrFrom(s.tail, h, Ord(s.head, ord)+1, ord);
+ if P(s.head) {
+ Lemma_FilterPreservesIncrFrom(s.tail, P, ord(s.head)+1, ord);
} else {
- Lemma_FilterPreservesIncrFrom#[_k](s.tail, h, low, ord);
+ Lemma_FilterPreservesIncrFrom#[_k](s.tail, P, low, ord);
}
}
diff --git a/Test/dafny3/Filter.dfy.expect b/Test/dafny3/Filter.dfy.expect
index 91aa9b47..6ba9b9bc 100644
--- a/Test/dafny3/Filter.dfy.expect
+++ b/Test/dafny3/Filter.dfy.expect
@@ -1,2 +1,2 @@
-Dafny program verifier finished with 43 verified, 0 errors
+Dafny program verifier finished with 42 verified, 0 errors
diff --git a/Test/dafny3/GenericSort.dfy b/Test/dafny3/GenericSort.dfy
index 7ea038be..7555817c 100644
--- a/Test/dafny3/GenericSort.dfy
+++ b/Test/dafny3/GenericSort.dfy
@@ -7,25 +7,25 @@ abstract module TotalOrder {
// Three properties of total orders. Here, they are given as unproved
// lemmas, that is, as axioms.
lemma Antisymmetry(a: T, b: T)
- requires Leq(a, b) && Leq(b, a);
- ensures a == b;
+ requires Leq(a, b) && Leq(b, a)
+ ensures a == b
lemma Transitivity(a: T, b: T, c: T)
- requires Leq(a, b) && Leq(b, c);
- ensures Leq(a, c);
+ requires Leq(a, b) && Leq(b, c)
+ ensures Leq(a, c)
lemma Totality(a: T, b: T)
- ensures Leq(a, b) || Leq(b, a);
+ ensures Leq(a, b) || Leq(b, a)
}
abstract module Sort {
- import O as TotalOrder // let O denote some module that has the members of TotalOrder
+ import O : TotalOrder // let O denote some module that has the members of TotalOrder
predicate Sorted(a: array<O.T>, low: int, high: int)
- requires a != null && 0 <= low <= high <= a.Length;
- reads a;
+ requires a != null && 0 <= low <= high <= a.Length
+ reads a
// The body of the predicate is hidden outside the module, but the postcondition is
// "exported" (that is, visible, known) outside the module. Here, we choose the
// export the following property:
- ensures Sorted(a, low, high) ==> forall i, j :: low <= i < j < high ==> O.Leq(a[i], a[j]);
+ ensures Sorted(a, low, high) ==> forall i, j :: low <= i < j < high ==> O.Leq(a[i], a[j])
{
forall i, j :: low <= i < j < high ==> O.Leq(a[i], a[j])
}
@@ -33,18 +33,18 @@ abstract module Sort {
// In the insertion sort routine below, it's more convenient to keep track of only that
// neighboring elements of the array are sorted...
predicate NeighborSorted(a: array<O.T>, low: int, high: int)
- requires a != null && 0 <= low <= high <= a.Length;
- reads a;
+ requires a != null && 0 <= low <= high <= a.Length
+ reads a
{
- forall i :: low < i < high ==> O.Leq(a[i-1], a[i])
+ forall i {:nowarn} :: low < i < high ==> O.Leq(a[i-1], a[i])
}
// ...but we show that property to imply all pairs to be sorted. The proof of this
// lemma uses the transitivity property.
lemma NeighborSorted_implies_Sorted(a: array<O.T>, low: int, high: int)
- requires a != null && 0 <= low <= high <= a.Length;
- requires NeighborSorted(a, low, high);
- ensures Sorted(a, low, high);
- decreases high - low;
+ requires a != null && 0 <= low <= high <= a.Length
+ requires NeighborSorted(a, low, high)
+ ensures Sorted(a, low, high)
+ decreases high - low
{
if low != high {
NeighborSorted_implies_Sorted(a, low+1, high);
@@ -57,25 +57,25 @@ abstract module Sort {
// Standard insertion sort method
method InsertionSort(a: array<O.T>)
- requires a != null;
- modifies a;
- ensures Sorted(a, 0, a.Length);
- ensures multiset(a[..]) == old(multiset(a[..]));
+ requires a != null
+ modifies a
+ ensures Sorted(a, 0, a.Length)
+ ensures multiset(a[..]) == old(multiset(a[..]))
{
if a.Length == 0 { return; }
var i := 1;
while i < a.Length
- invariant 0 < i <= a.Length;
- invariant NeighborSorted(a, 0, i);
- invariant multiset(a[..]) == old(multiset(a[..]));
+ invariant 0 < i <= a.Length
+ invariant NeighborSorted(a, 0, i)
+ invariant multiset(a[..]) == old(multiset(a[..]))
{
var j := i;
while 0 < j && !O.Leq(a[j-1], a[j])
- invariant 0 <= j <= i;
- invariant NeighborSorted(a, 0, j);
- invariant NeighborSorted(a, j, i+1);
- invariant 0 < j < i ==> O.Leq(a[j-1], a[j+1]);
- invariant multiset(a[..]) == old(multiset(a[..]));
+ invariant 0 <= j <= i
+ invariant NeighborSorted(a, 0, j)
+ invariant NeighborSorted(a, j, i+1)
+ invariant 0 < j < i ==> O.Leq(a[j-1], a[j+1])
+ invariant multiset(a[..]) == old(multiset(a[..]))
{
// The proof of correctness uses the totality property here.
// It implies that if two elements are not previously in
@@ -97,7 +97,7 @@ module IntOrder refines TotalOrder {
datatype T = Int(i: int)
// Define the ordering on these integers
predicate method Leq ...
- ensures Leq(a, b) ==> a.i <= b.i;
+ ensures Leq(a, b) ==> a.i <= b.i
{
a.i <= b.i
}
@@ -156,7 +156,7 @@ module intOrder refines TotalOrder {
type T = int
// Define the ordering on these integers
predicate method Leq ...
- ensures Leq(a, b) ==> a <= b;
+ ensures Leq(a, b) ==> a <= b
{
a <= b
}
diff --git a/Test/dafny3/InductionVsCoinduction.dfy b/Test/dafny3/InductionVsCoinduction.dfy
index 89fa6cc8..0074b742 100644
--- a/Test/dafny3/InductionVsCoinduction.dfy
+++ b/Test/dafny3/InductionVsCoinduction.dfy
@@ -80,7 +80,7 @@ lemma SAppendIsAssociative(a:Stream, b:Stream, c:Stream)
{
forall k:nat { SAppendIsAssociativeK(k, a, b, c); }
// assert for clarity only, postcondition follows directly from it
- assert (forall k:nat :: SAppend(SAppend(a, b), c) ==#[k] SAppend(a, SAppend(b, c)));
+ assert (forall k:nat {:autotriggers false} :: SAppend(SAppend(a, b), c) ==#[k] SAppend(a, SAppend(b, c))); //FIXME: Should Dafny generate a trigger here? If so then which one?
}
// Equivalent proof using the colemma syntax.
diff --git a/Test/dafny3/InfiniteTrees.dfy b/Test/dafny3/InfiniteTrees.dfy
index 516a9e4e..85f73bc3 100644
--- a/Test/dafny3/InfiniteTrees.dfy
+++ b/Test/dafny3/InfiniteTrees.dfy
@@ -286,7 +286,7 @@ lemma Theorem1_Lemma(t: Tree, n: nat, p: Stream<int>)
LowerThan(ch, n);
==> // def. LowerThan
LowerThan(ch.head.children, n-1);
- ==> { Theorem1_Lemma(ch.head, n-1, tail); }
+ ==> //{ Theorem1_Lemma(ch.head, n-1, tail); }
!IsNeverEndingStream(tail);
==> // def. IsNeverEndingStream
!IsNeverEndingStream(p);
@@ -374,30 +374,30 @@ lemma Proposition3b()
}
}
lemma Proposition3b_Lemma(t: Tree, h: nat, p: Stream<int>)
- requires LowerThan(t.children, h) && ValidPath(t, p);
- ensures !IsNeverEndingStream(p);
- decreases h;
+ requires LowerThan(t.children, h) && ValidPath(t, p)
+ ensures !IsNeverEndingStream(p)
+ decreases h
{
match p {
case Nil =>
case Cons(index, tail) =>
// From the definition of ValidPath(t, p), we get the following:
var ch := Tail(t.children, index);
- assert ch.Cons? && ValidPath(ch.head, tail);
+ // assert ch.Cons? && ValidPath(ch.head, tail);
// From the definition of LowerThan(t.children, h), we get the following:
match t.children {
case Nil =>
ValidPath_Lemma(p);
assert false; // absurd case
case Cons(_, _) =>
- assert 1 <= h;
+ // assert 1 <= h;
LowerThan_Lemma(t.children, index, h);
- assert LowerThan(ch, h);
+ // assert LowerThan(ch, h);
}
// Putting these together, by ch.Cons? and the definition of LowerThan(ch, h), we get:
- assert LowerThan(ch.head.children, h-1);
+ // assert LowerThan(ch.head.children, h-1);
// And now we can invoke the induction hypothesis:
- Proposition3b_Lemma(ch.head, h-1, tail);
+ // Proposition3b_Lemma(ch.head, h-1, tail);
}
}
@@ -627,30 +627,10 @@ colemma Path_Lemma2'(p: Stream<int>)
}
}
colemma Path_Lemma2''(p: Stream<int>, n: nat, tail: Stream<int>)
- requires IsNeverEndingStream(p) && p.tail == tail;
- ensures InfinitePath'(S2N'(n, tail));
+ requires IsNeverEndingStream(p) && p.tail == tail
+ ensures InfinitePath'(S2N'(n, tail))
{
- if n <= 0 {
- calc {
- InfinitePath'#[_k](S2N'(n, tail));
- // def. S2N'
- InfinitePath'#[_k](Zero(S2N(tail)));
- // def. InfinitePath'
- InfinitePath#[_k-1](S2N(tail));
- { Path_Lemma2'(tail); }
- true;
- }
- } else {
- calc {
- InfinitePath'#[_k](S2N'(n, tail));
- // def. S2N'
- InfinitePath'#[_k](Succ(S2N'(n-1, tail)));
- // def. InfinitePath'
- InfinitePath'#[_k-1](S2N'(n-1, tail));
- { Path_Lemma2''(p, n-1, tail); }
- true;
- }
- }
+ Path_Lemma2'(tail);
}
lemma Path_Lemma3(r: CoOption<Number>)
ensures InfinitePath(r) ==> IsNeverEndingStream(N2S(r));
diff --git a/Test/dafny3/SimpleInduction.dfy b/Test/dafny3/SimpleInduction.dfy
index 83ea6d14..8cf937e1 100644
--- a/Test/dafny3/SimpleInduction.dfy
+++ b/Test/dafny3/SimpleInduction.dfy
@@ -13,7 +13,7 @@ function Fib(n: nat): nat
decreases n;
{ if n < 2 then n else Fib(n-2) + Fib(n-1) }
-ghost method FibLemma(n: nat)
+lemma FibLemma(n: nat)
ensures Fib(n) % 2 == 0 <==> n % 3 == 0;
decreases n;
{
@@ -30,7 +30,7 @@ ghost method FibLemma(n: nat)
satisfying 0 <= k < n, and in the second example, to all non-negative n.
*/
-ghost method FibLemma_Alternative(n: nat)
+lemma FibLemma_Alternative(n: nat)
ensures Fib(n) % 2 == 0 <==> n % 3 == 0;
{
forall k | 0 <= k < n {
@@ -38,7 +38,7 @@ ghost method FibLemma_Alternative(n: nat)
}
}
-ghost method FibLemma_All()
+lemma FibLemma_All()
ensures forall n :: 0 <= n ==> (Fib(n) % 2 == 0 <==> n % 3 == 0);
{
forall n | 0 <= n {
@@ -48,8 +48,8 @@ ghost method FibLemma_All()
/*
A standard inductive definition of a generic List type and a function Append
- that concatenates two lists. The ghost method states the lemma that Append
- is associative, and its recursive body gives the inductive proof.
+ that concatenates two lists. The lemma states that Append is associative,
+ and its recursive body gives the inductive proof.
We omitted the explicit declaration and uses of the List type parameter in
the signature of the method, since in simple cases like this, Dafny is able
@@ -68,7 +68,7 @@ function Append<T>(xs: List<T>, ys: List<T>): List<T>
// The {:induction false} attribute disables automatic induction tactic,
// so we can make the proof explicit.
-ghost method {:induction false} AppendIsAssociative(xs: List, ys: List, zs: List)
+lemma {:induction false} AppendIsAssociative(xs: List, ys: List, zs: List)
ensures Append(Append(xs, ys), zs) == Append(xs, Append(ys, zs));
decreases xs;
{
@@ -81,7 +81,7 @@ ghost method {:induction false} AppendIsAssociative(xs: List, ys: List, zs: List
// Here the proof is fully automatic - the body of the method is empty,
// yet still verifies.
-ghost method AppendIsAssociative_Auto(xs: List, ys: List, zs: List)
+lemma AppendIsAssociative_Auto(xs: List, ys: List, zs: List)
ensures Append(Append(xs, ys), zs) == Append(xs, Append(ys, zs));
{
}
diff --git a/Test/dafny4/ACL2-extractor.dfy b/Test/dafny4/ACL2-extractor.dfy
index 8fe98531..bd2c9d83 100644
--- a/Test/dafny4/ACL2-extractor.dfy
+++ b/Test/dafny4/ACL2-extractor.dfy
@@ -116,9 +116,9 @@ lemma RevLength(xs: List)
// you can prove two lists equal by proving their elements equal
lemma EqualElementsMakeEqualLists(xs: List, ys: List)
- requires length(xs) == length(ys);
- requires forall i :: 0 <= i < length(xs) ==> nth(i, xs) == nth(i, ys);
- ensures xs == ys;
+ requires length(xs) == length(ys)
+ requires forall i :: 0 <= i < length(xs) ==> nth(i, xs) == nth(i, ys)
+ ensures xs == ys
{
match xs {
case Nil =>
@@ -132,7 +132,7 @@ lemma EqualElementsMakeEqualLists(xs: List, ys: List)
nth(i+1, xs) == nth(i+1, ys);
}
}
- EqualElementsMakeEqualLists(xs.tail, ys.tail);
+ // EqualElementsMakeEqualLists(xs.tail, ys.tail);
}
}
diff --git a/Test/dafny4/Ackermann.dfy b/Test/dafny4/Ackermann.dfy
new file mode 100644
index 00000000..7ab686e0
--- /dev/null
+++ b/Test/dafny4/Ackermann.dfy
@@ -0,0 +1,235 @@
+// RUN: %dafny /compile:3 /rprint:"%t.rprint" /autoTriggers:1 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// Rustan Leino, 8 Sep 2015.
+// This file proves that the Ackermann function is monotonic in each argument
+
+method Main() {
+ // Note, don't try much larger numbers unless you're prepared to wait!
+ print "Ack(3, 4) = ", Ack(3, 4), "\n";
+}
+
+// Here is the definition of the Ackermann function:
+function method Ack(m: nat, n: nat): nat
+{
+ if m == 0 then
+ n + 1
+ else if n == 0 then
+ Ack(m - 1, 1)
+ else
+ Ack(m - 1, Ack(m, n - 1))
+}
+
+// And here is the theorem that proves the desired result:
+lemma AckermannIsMonotonic(m: nat, n: nat, m': nat, n': nat)
+ requires m <= m' && n <= n'
+ ensures Ack(m, n) <= Ack(m', n')
+{
+ // This is the proof. It calls two lemmas, stated and proved below.
+ MonotonicN(m, n, n');
+ MonotonicM(m, n, m');
+}
+
+// --------------------------------------------------------
+// What follows are the supporting lemmas and their proofs.
+// --------------------------------------------------------
+
+// Proving that Ackermann is monotonic in its second argument is easy.
+lemma MonotonicN(m: nat, n: nat, n': nat)
+ requires n <= n'
+ ensures Ack(m, n) <= Ack(m, n')
+{
+ // In fact, Dafny completes this proof automatically.
+}
+
+// To prove the other direction, we consider an alternative formulation
+// of the Ackermann function, namely a curried form:
+function CurriedAckermann(m: int, n: int): int
+{
+ A(m)(n)
+}
+
+function A(m: int): int -> int
+{
+ if m <= 0 then
+ n => n + 1
+ else
+ n => Iter(A(m-1), n)
+}
+
+function Iter(f: int -> int, n: int): int
+ requires IsTotal(f)
+ decreases n
+{
+ if n <= 0 then
+ f(1)
+ else
+ f(Iter(f, n-1))
+}
+
+// In the 3-part definition above, function Iter needs to know that it can
+// apply its function parameter "f". Therefore, Iter's precondition says that
+// "f" must be total. We define totality of "f" by saying that its precondition
+// holds for any n and that it never reads the heap:
+predicate IsTotal(f: int -> int)
+ reads f.reads
+{
+ forall n :: f.requires(n) && f.reads(n) == {}
+}
+
+// Now, we can prove certain things about CurriedAckermann. The first thing we
+// prove is that it gives the same result as the Ack function above.
+
+lemma CurriedIsTheSame(m: nat, n: nat)
+ ensures CurriedAckermann(m, n) == Ack(m, n)
+{
+ // The proof considers 3 cases, following the 3 cases in the definition of Ack
+ if m == 0 {
+ // trivial
+ } else if n == 0 {
+ // trivial
+ } else {
+ // we help Dafny out with the following lemma:
+ assert A(m)(n) == A(m-1)(Iter(A(m-1), n-1));
+ }
+}
+
+// Monotonicity in the first argument of Ackermann now follows from the fact that,
+// for m <= m', A(m) is a function that is always below A(m')
+lemma MonotonicM(m: nat, n: nat, m': nat)
+ requires m <= m'
+ ensures Ack(m, n) <= Ack(m', n)
+{
+ CurriedIsTheSame(m, n);
+ CurriedIsTheSame(m', n);
+ ABelow(m, m');
+}
+
+// We must now prove ABelow. To do that, we will prove some properties of A and of
+// Iter. Let us define the three properties we will reason about.
+
+// The first property is a relation on functions. It is the property that above was referred
+// to as "f is a function that is always below g". The lemma ABelow(m, m') used above establishes
+// FunctionBelow(A(m), A(m')).
+predicate FunctionBelow(f: int -> int, g: int -> int)
+ requires IsTotal(f) && IsTotal(g)
+{
+ forall n :: f(n) <= g(n)
+}
+
+// The next property says that a function is monotonic.
+predicate FunctionMonotonic(f: int -> int)
+ requires IsTotal(f)
+{
+ forall n,n' :: n <= n' ==> f(n) <= f(n')
+}
+
+// The third property says that a function's return value is strictly greater than its argument.
+predicate Expanding(f: int -> int)
+ requires IsTotal(f)
+{
+ forall n :: n < f(n)
+}
+
+// We will prove that A(_) satisfies the properties FunctionBelow, FunctionMonotonic, and
+// FunctionExpanding. But first we prove three properties of Iter.
+
+// Provided that "f" is monotonic and expanding, Iter(f, _) is monotonic.
+lemma IterIsMonotonicN(f: int -> int, n: int, n': int)
+ requires IsTotal(f) && Expanding(f) && FunctionMonotonic(f) && n <= n'
+ ensures Iter(f, n) <= Iter(f, n')
+{
+ // This proof is a simple induction over n' and Dafny completes the proof automatically.
+}
+
+// Next, we prove that Iter(_, n) is monotonic. That is, given functions "f" and "g"
+// such that FunctionBelow(f, g), we have Iter(f, n) <= Iter(g, n). The lemma requires
+// "f" to be monotonic, but we don't have to state the same property for g.
+lemma IterIsMonotonicF(f: int -> int, g: int -> int, n: int)
+ requires IsTotal(f) && IsTotal(g) && FunctionMonotonic(f) && FunctionBelow(f, g)
+ ensures Iter(f, n) <= Iter(g, n)
+{
+ // This proof is a simple induction over n and Dafny completes the proof automatically.
+}
+
+// Finally, we shows that for any expanding function "f", Iter(f, _) is also expanding.
+lemma IterExpanding(f: int -> int, n: int)
+ requires IsTotal(f) && Expanding(f)
+ ensures n < Iter(f, n)
+{
+ // Here, too, the proof is a simple induction of n and Dafny completes it automatically.
+}
+
+// We complete the proof by showing that A(_) has the three properties we need.
+
+// Of the three properties we prove about A(_), we start by showing that A(m) returns a
+// function that is expanding.
+lemma AExp(m: int)
+ ensures Expanding(A(m))
+{
+ if m <= 0 {
+ // trivial
+ } else {
+ // This branch of the proof follows from the fact that Iter(A(m-1), _) is expanding.
+ forall n {
+ // The following lemma requires A(m-1) to be expanding, which is something that
+ // following from our induction hypothesis.
+ IterExpanding(A(m-1), n);
+ }
+ }
+}
+
+// Next, we show that A(m) returns a monotonic function.
+lemma Am(m: int)
+ ensures FunctionMonotonic(A(m))
+{
+ if m <= 0 {
+ // trivial
+ } else {
+ // We make use of the fact that A(m-1) is expanding:
+ AExp(m-1);
+ // and the fact that Iter(A(m-1), _) is monotonic:
+ forall n,n' | n <= n' {
+ // The following lemma requires A(m-1) to be expanding and monotonic.
+ // The former comes from the invocation of AExp(m-1) above, and the
+ // latter comes from our induction hypothesis.
+ IterIsMonotonicN(A(m-1), n, n');
+ }
+ }
+}
+
+// Our final property about A, which is the lemma we had used above to prove
+// that Ackermann is monotonic in its first argument, is stated and proved here:
+lemma ABelow(m: int, m': int)
+ requires m <= m'
+ ensures FunctionBelow(A(m), A(m'))
+{
+ if m' <= 0 {
+ // trivial
+ } else if m <= 0 {
+ forall n {
+ calc {
+ A(m)(n);
+ ==
+ n + 1;
+ <= { AExp(m'-1); IterExpanding(A(m'-1), n); }
+ Iter(A(m'-1), n);
+ ==
+ A(m')(n);
+ }
+ }
+ } else {
+ forall n {
+ calc {
+ A(m)(n);
+ ==
+ Iter(A(m-1), n);
+ <= { Am(m-1); ABelow(m-1, m'-1);
+ IterIsMonotonicF(A(m-1), A(m'-1), n); }
+ Iter(A(m'-1), n);
+ ==
+ A(m')(n);
+ }
+ }
+ }
+}
diff --git a/Test/dafny4/Ackermann.dfy.expect b/Test/dafny4/Ackermann.dfy.expect
new file mode 100644
index 00000000..158f2a48
--- /dev/null
+++ b/Test/dafny4/Ackermann.dfy.expect
@@ -0,0 +1,6 @@
+
+Dafny program verifier finished with 30 verified, 0 errors
+Program compiled successfully
+Running...
+
+Ack(3, 4) = 125
diff --git a/Test/dafny4/BinarySearch.dfy.expect b/Test/dafny4/BinarySearch.dfy.expect
index 944f677a..a9f834b7 100644
--- a/Test/dafny4/BinarySearch.dfy.expect
+++ b/Test/dafny4/BinarySearch.dfy.expect
@@ -1,4 +1,4 @@
-BinarySearch.dfy(44,20): Error: result of operation might violate newtype constraint
+BinarySearch.dfy(44,19): Error: result of operation might violate newtype constraint
Execution trace:
(0,0): anon0
BinarySearch.dfy(40,3): anon18_LoopHead
diff --git a/Test/dafny4/Bug100.dfy b/Test/dafny4/Bug100.dfy
new file mode 100644
index 00000000..0c971e82
--- /dev/null
+++ b/Test/dafny4/Bug100.dfy
@@ -0,0 +1,23 @@
+// RUN: %dafny /compile:0 /autoTriggers:1 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+lemma lemma_ensures(x:int, RefineInt:int->int)
+ requires forall y :: RefineInt.requires(y);
+ ensures forall y :: RefineInt(y) + x == RefineInt(x) + y;
+
+function Identity(z:int) : int
+
+lemma test()
+{
+ var v,w:int;
+ lemma_ensures(w, Identity);
+ //var RefineInt := Identity;
+ //assert RefineInt(v) == Identity(v);
+ assert Identity(v) + w == Identity(w) + v;
+}
+
+
+
+
+
+
diff --git a/Test/dafny4/Bug100.dfy.expect b/Test/dafny4/Bug100.dfy.expect
new file mode 100644
index 00000000..73ba063c
--- /dev/null
+++ b/Test/dafny4/Bug100.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 4 verified, 0 errors
diff --git a/Test/dafny4/Bug101.dfy b/Test/dafny4/Bug101.dfy
new file mode 100644
index 00000000..878ed57a
--- /dev/null
+++ b/Test/dafny4/Bug101.dfy
@@ -0,0 +1,19 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+predicate P(i:int) { true }
+
+lemma Tester()
+{
+// forall i ensures false ==> P(i) {}
+ forall i ensures P(i) <== false {}
+ assert forall i :: P(i) ==> false;
+ assert P(0);
+ assert false;
+}
+
+
+
+
+
+
diff --git a/Test/dafny4/Bug101.dfy.expect b/Test/dafny4/Bug101.dfy.expect
new file mode 100644
index 00000000..a4e5f4b3
--- /dev/null
+++ b/Test/dafny4/Bug101.dfy.expect
@@ -0,0 +1,8 @@
+Bug101.dfy(10,11): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon6_Else
+ (0,0): anon7_Then
+ (0,0): anon5
+
+Dafny program verifier finished with 2 verified, 1 error
diff --git a/Test/dafny4/Bug103.dfy b/Test/dafny4/Bug103.dfy
new file mode 100644
index 00000000..559a361c
--- /dev/null
+++ b/Test/dafny4/Bug103.dfy
@@ -0,0 +1,20 @@
+// RUN: %dafny /compile:0 /autoTriggers:1 /print:"%t.print" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+predicate IsLessThanSuccesor(i:int)
+{
+ i < i + 1
+}
+
+lemma LemmaWithoutTriggerOnForallStatement()
+{
+ forall i
+ ensures IsLessThanSuccesor(i);
+ {
+ }
+}
+
+
+
+
+
diff --git a/Test/dafny4/Bug103.dfy.expect b/Test/dafny4/Bug103.dfy.expect
new file mode 100644
index 00000000..52595bf9
--- /dev/null
+++ b/Test/dafny4/Bug103.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 3 verified, 0 errors
diff --git a/Test/dafny4/Bug104.dfy b/Test/dafny4/Bug104.dfy
new file mode 100644
index 00000000..ffabbb92
--- /dev/null
+++ b/Test/dafny4/Bug104.dfy
@@ -0,0 +1,12 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+datatype PartRealPartGhost = PartRealPartGhost(x:int, ghost y:int)
+
+method UpdateField()
+{
+ var v := PartRealPartGhost(3, 4);
+ ghost var g := 5;
+ v := v[y := g];
+ v := v.(y := g);
+} \ No newline at end of file
diff --git a/Test/dafny4/Bug104.dfy.expect b/Test/dafny4/Bug104.dfy.expect
new file mode 100644
index 00000000..1cc43cbf
--- /dev/null
+++ b/Test/dafny4/Bug104.dfy.expect
@@ -0,0 +1,3 @@
+Bug104.dfy(10,7): Warning: datatype update syntax D[f := E] is deprecated; the new syntax is D.(f := E)
+
+Dafny program verifier finished with 2 verified, 0 errors
diff --git a/Test/dafny4/Bug107.dfy b/Test/dafny4/Bug107.dfy
new file mode 100644
index 00000000..56965d92
--- /dev/null
+++ b/Test/dafny4/Bug107.dfy
@@ -0,0 +1,16 @@
+// RUN: %dafny /compile:3 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+method Main()
+{
+ var f := Inc;
+ print(f(4));
+}
+
+function method Inc(x: int): int
+{
+ x + 2
+}
+
+
+
diff --git a/Test/dafny4/Bug107.dfy.expect b/Test/dafny4/Bug107.dfy.expect
new file mode 100644
index 00000000..eaa3aa54
--- /dev/null
+++ b/Test/dafny4/Bug107.dfy.expect
@@ -0,0 +1,6 @@
+
+Dafny program verifier finished with 3 verified, 0 errors
+Program compiled successfully
+Running...
+
+6 \ No newline at end of file
diff --git a/Test/dafny4/Bug108.dfy b/Test/dafny4/Bug108.dfy
new file mode 100644
index 00000000..17cb9f12
--- /dev/null
+++ b/Test/dafny4/Bug108.dfy
@@ -0,0 +1,11 @@
+// RUN: %dafny /compile:3 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+method Main() {
+ var A := map[0 := 1];
+ var B := map x | x in (set y | y in A) :: A[x];
+ print A, "\n";
+ print B, "\n";
+}
+
+
diff --git a/Test/dafny4/Bug108.dfy.expect b/Test/dafny4/Bug108.dfy.expect
new file mode 100644
index 00000000..94e65ba2
--- /dev/null
+++ b/Test/dafny4/Bug108.dfy.expect
@@ -0,0 +1,7 @@
+
+Dafny program verifier finished with 2 verified, 0 errors
+Program compiled successfully
+Running...
+
+map[0 := 1]
+map[0 := 1]
diff --git a/Test/dafny4/Bug110.dfy b/Test/dafny4/Bug110.dfy
new file mode 100644
index 00000000..aa808669
--- /dev/null
+++ b/Test/dafny4/Bug110.dfy
@@ -0,0 +1,31 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+module Io {
+ predicate AdvanceTime(oldTime:int) { oldTime > 2 }
+ class Time
+ {
+ static method GetTime()
+ ensures AdvanceTime(1);
+ }
+
+ function MaxPacketSize() : int { 65507 }
+
+ class UdpClient
+ {
+ method Receive()
+ ensures AdvanceTime(3);
+
+ method Send() returns(ok:bool)
+ requires 0 <= MaxPacketSize();
+ }
+}
+
+abstract module Host {
+ import opened Io // Doesn't work.
+ //import Io // Works
+}
+
+abstract module Main {
+ import H : Host
+}
diff --git a/Test/dafny4/Bug110.dfy.expect b/Test/dafny4/Bug110.dfy.expect
new file mode 100644
index 00000000..42fd56a5
--- /dev/null
+++ b/Test/dafny4/Bug110.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 8 verified, 0 errors
diff --git a/Test/dafny4/Bug111.dfy b/Test/dafny4/Bug111.dfy
new file mode 100644
index 00000000..730d44d3
--- /dev/null
+++ b/Test/dafny4/Bug111.dfy
@@ -0,0 +1,18 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+datatype A = A(i:int)
+datatype B = B1(a1:A) | B2(a2:A)
+
+function f(b:B):int
+{
+ match b
+ {
+ case B1(A(i)) => i
+ case B2(A(j)) => j
+ }
+}
+
+
+
+
diff --git a/Test/dafny4/Bug111.dfy.expect b/Test/dafny4/Bug111.dfy.expect
new file mode 100644
index 00000000..c0c48e2b
--- /dev/null
+++ b/Test/dafny4/Bug111.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 1 verified, 0 errors
diff --git a/Test/dafny4/Bug113.dfy b/Test/dafny4/Bug113.dfy
new file mode 100644
index 00000000..8f5ddf9f
--- /dev/null
+++ b/Test/dafny4/Bug113.dfy
@@ -0,0 +1,10 @@
+// RUN: %dafny /compile:3 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+datatype D = D(q:int, r:int, s:int, t:int)
+
+method Main()
+{
+ print D(10, 20, 30, 40);
+ print "\n";
+} \ No newline at end of file
diff --git a/Test/dafny4/Bug113.dfy.expect b/Test/dafny4/Bug113.dfy.expect
new file mode 100644
index 00000000..c4be010e
--- /dev/null
+++ b/Test/dafny4/Bug113.dfy.expect
@@ -0,0 +1,6 @@
+
+Dafny program verifier finished with 2 verified, 0 errors
+Program compiled successfully
+Running...
+
+D.D(10, 20, 30, 40)
diff --git a/Test/dafny4/Bug114.dfy b/Test/dafny4/Bug114.dfy
new file mode 100644
index 00000000..1c0f0109
--- /dev/null
+++ b/Test/dafny4/Bug114.dfy
@@ -0,0 +1,10 @@
+// RUN: %dafny /compile:0 /autoTriggers:1 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+function f1(d:int):map<int,int>
+function f2(y:int, d:int):int
+
+method M(m:map<int,int>, d:int, x2:int)
+{
+ assert forall d :: f1(d) == (map x | x in m :: f2(x, d));
+} \ No newline at end of file
diff --git a/Test/dafny4/Bug114.dfy.expect b/Test/dafny4/Bug114.dfy.expect
new file mode 100644
index 00000000..8e671f90
--- /dev/null
+++ b/Test/dafny4/Bug114.dfy.expect
@@ -0,0 +1,7 @@
+Bug114.dfy(9,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon3_Then
+ (0,0): anon2
+
+Dafny program verifier finished with 3 verified, 1 error
diff --git a/Test/dafny4/Bug116.dfy b/Test/dafny4/Bug116.dfy
new file mode 100644
index 00000000..9fd30597
--- /dev/null
+++ b/Test/dafny4/Bug116.dfy
@@ -0,0 +1,15 @@
+// RUN: %dafny "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+datatype struct = S // this is ok
+
+method Main()
+{
+ var s := S; // this line generates illegal C# code
+ print s;
+}
+
+
+
+
+
diff --git a/Test/dafny4/Bug116.dfy.expect b/Test/dafny4/Bug116.dfy.expect
new file mode 100644
index 00000000..b0cf7300
--- /dev/null
+++ b/Test/dafny4/Bug116.dfy.expect
@@ -0,0 +1,3 @@
+
+Dafny program verifier finished with 2 verified, 0 errors
+Compiled assembly into Bug116.exe
diff --git a/Test/dafny4/Bug117.dfy b/Test/dafny4/Bug117.dfy
new file mode 100644
index 00000000..418746cb
--- /dev/null
+++ b/Test/dafny4/Bug117.dfy
@@ -0,0 +1,37 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+abstract module AbstractModule1
+{
+ type AbstractType1
+}
+
+abstract module AbstractModule2
+{
+ import opened AM1 : AbstractModule1
+
+ datatype AbstractType2 = AbstractType2(x:AbstractType1)
+}
+
+abstract module AbstractModule3
+{
+ import AM1 : AbstractModule1
+
+ datatype AbstractType2 = AbstractType2(x:AM1.AbstractType1)
+}
+
+module ConcreteModule1
+{
+ type AbstractType1 = int
+}
+
+module ConcreteModule2 refines AbstractModule2
+{
+ import AM1 = ConcreteModule1 // error: must be declared "opened" to match AbstratctModule2
+}
+
+module ConcreteModule3 refines AbstractModule3
+{
+ import opened AM1 = ConcreteModule1 // error: can't be declared "opened" to match AbstractModule3
+}
+
diff --git a/Test/dafny4/Bug117.dfy.expect b/Test/dafny4/Bug117.dfy.expect
new file mode 100644
index 00000000..0c5a5445
--- /dev/null
+++ b/Test/dafny4/Bug117.dfy.expect
@@ -0,0 +1,3 @@
+Bug117.dfy(28,7): Error: AM1 in ConcreteModule2 must be imported with "opened" to match the corresponding import in its refinement base AbstractModule2.
+Bug117.dfy(33,7): Error: AM1 in ConcreteModule3 cannot be imported with "opened" because it does not match the corresponding import in the refinement base AbstractModule3
+2 resolution/type errors detected in Bug117.dfy
diff --git a/Test/dafny4/Bug118.dfy b/Test/dafny4/Bug118.dfy
new file mode 100644
index 00000000..1e2dddeb
--- /dev/null
+++ b/Test/dafny4/Bug118.dfy
@@ -0,0 +1,12 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+class Foo {
+ ghost var Repr: set<object>
+}
+
+function SeqRepr(s:seq<Foo>) : set<object>
+ reads set b | b in s
+{
+ set o,b | b in s && b != null && o in b.Repr :: o // Works if you say "set b,o | ..."
+} \ No newline at end of file
diff --git a/Test/dafny4/Bug118.dfy.expect b/Test/dafny4/Bug118.dfy.expect
new file mode 100644
index 00000000..c0c48e2b
--- /dev/null
+++ b/Test/dafny4/Bug118.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 1 verified, 0 errors
diff --git a/Test/dafny4/Bug120.dfy b/Test/dafny4/Bug120.dfy
new file mode 100644
index 00000000..73553f2a
--- /dev/null
+++ b/Test/dafny4/Bug120.dfy
@@ -0,0 +1,11 @@
+// RUN: %dafny /compile:0 /noNLarith "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+function G(n: nat): nat
+{
+ if n == 0 then 5 else G(n-1)
+}
+
+method Test() {
+ assert G(10) == 5;
+} \ No newline at end of file
diff --git a/Test/dafny4/Bug120.dfy.expect b/Test/dafny4/Bug120.dfy.expect
new file mode 100644
index 00000000..52595bf9
--- /dev/null
+++ b/Test/dafny4/Bug120.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 3 verified, 0 errors
diff --git a/Test/dafny4/Bug121.dfy b/Test/dafny4/Bug121.dfy
new file mode 100644
index 00000000..13798fa8
--- /dev/null
+++ b/Test/dafny4/Bug121.dfy
@@ -0,0 +1,18 @@
+// RUN: %dafny /compile:0 /autoTriggers:1 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+method Try (a:int, b:int, c:int)
+{
+ forall
+ ensures a * c == a * c;
+ ensures b * c == b * c;
+ {
+ }
+}
+
+
+
+
+
+
+
diff --git a/Test/dafny4/Bug121.dfy.expect b/Test/dafny4/Bug121.dfy.expect
new file mode 100644
index 00000000..069e7767
--- /dev/null
+++ b/Test/dafny4/Bug121.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 2 verified, 0 errors
diff --git a/Test/dafny4/Bug122.dfy b/Test/dafny4/Bug122.dfy
new file mode 100644
index 00000000..adbee30d
--- /dev/null
+++ b/Test/dafny4/Bug122.dfy
@@ -0,0 +1,17 @@
+// RUN: %dafny /compile:0 /autoTriggers:1 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+method Try (a:int)
+{
+ forall
+ ensures a == a;
+ {
+ }
+}
+
+
+
+
+
+
+
diff --git a/Test/dafny4/Bug122.dfy.expect b/Test/dafny4/Bug122.dfy.expect
new file mode 100644
index 00000000..069e7767
--- /dev/null
+++ b/Test/dafny4/Bug122.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 2 verified, 0 errors
diff --git a/Test/dafny4/Bug124.dfy b/Test/dafny4/Bug124.dfy
new file mode 100644
index 00000000..60f26a00
--- /dev/null
+++ b/Test/dafny4/Bug124.dfy
@@ -0,0 +1,14 @@
+// RUN: %dafny /compile:0 /autoTriggers:1 /noNLarith "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+function power(n:nat, e:nat) : int
+
+lemma lemma_power()
+ ensures forall n:nat, e:nat :: 0 <= n * e && power(n, e) == 5;
+{
+ forall n:nat, e:nat
+ ensures 0 <= n * e && power(n, e) == 5;
+ {
+ assume false;
+ }
+} \ No newline at end of file
diff --git a/Test/dafny4/Bug124.dfy.expect b/Test/dafny4/Bug124.dfy.expect
new file mode 100644
index 00000000..52595bf9
--- /dev/null
+++ b/Test/dafny4/Bug124.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 3 verified, 0 errors
diff --git a/Test/dafny4/Bug125.dfy b/Test/dafny4/Bug125.dfy
new file mode 100644
index 00000000..5dbdea6f
--- /dev/null
+++ b/Test/dafny4/Bug125.dfy
@@ -0,0 +1,58 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+abstract module AbstractModuleA
+{
+ type T
+}
+
+abstract module AbstractModuleB
+{
+ import opened AMA : AbstractModuleA
+
+ method Foo(t:T)
+}
+
+abstract module AbstractModuleC refines AbstractModuleB
+{
+ import opened AMA2 : AbstractModuleA
+}
+
+module LibA {
+ class G {
+ static function f(x:int) : bool {
+ x >= 10
+ }
+ }
+
+ function g() : bool {
+ true
+ }
+}
+
+module LibB {
+ class G {
+ static function f(x:int) : bool {
+ x < 10
+ }
+ }
+
+ function g() : bool {
+ false
+ }
+}
+
+module R {
+ import opened LibA
+}
+
+module S refines R {
+ import opened LibB
+ method m() {
+ assert g(); // should be LibA.g
+ }
+
+ method m1() {
+ assert G.f(20); // should be LibA.G.f
+ }
+}
diff --git a/Test/dafny4/Bug125.dfy.expect b/Test/dafny4/Bug125.dfy.expect
new file mode 100644
index 00000000..c87e2af2
--- /dev/null
+++ b/Test/dafny4/Bug125.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 10 verified, 0 errors
diff --git a/Test/dafny4/Bug128.dfy b/Test/dafny4/Bug128.dfy
new file mode 100644
index 00000000..b7220335
--- /dev/null
+++ b/Test/dafny4/Bug128.dfy
@@ -0,0 +1,13 @@
+// RUN: %dafny /noNLarith /z3opt:pi.warnings=true /proverWarnings:1 /compile:0 /autoTriggers:1 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+function GetIndexInSequence<T>(s:seq<T>, x:T) : int
+ requires x in s;
+ ensures 0 <= GetIndexInSequence(s, x) < |s|;
+ ensures s[GetIndexInSequence(s, x)] == x; {
+ var i :| 0 <= i < |s| && s[i] == x;
+ i
+ }
+
+
+
diff --git a/Test/dafny4/Bug128.dfy.expect b/Test/dafny4/Bug128.dfy.expect
new file mode 100644
index 00000000..c0c48e2b
--- /dev/null
+++ b/Test/dafny4/Bug128.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 1 verified, 0 errors
diff --git a/Test/dafny4/Bug129.dfy b/Test/dafny4/Bug129.dfy
new file mode 100644
index 00000000..f6cf0fe8
--- /dev/null
+++ b/Test/dafny4/Bug129.dfy
@@ -0,0 +1,12 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+datatype Maybe<T> = Some(v:T) | None
+datatype B = B(b:Maybe<B>)
+
+datatype List<T> = Nil | Cons(T, List<T>)
+datatype Tree<T> = Nodes(children: List<Tree<T>>)
+
+
+
+
diff --git a/Test/dafny4/Bug129.dfy.expect b/Test/dafny4/Bug129.dfy.expect
new file mode 100644
index 00000000..a1c1f7b9
--- /dev/null
+++ b/Test/dafny4/Bug129.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 0 verified, 0 errors
diff --git a/Test/dafny4/Bug131.dfy b/Test/dafny4/Bug131.dfy
new file mode 100644
index 00000000..5d01cf59
--- /dev/null
+++ b/Test/dafny4/Bug131.dfy
@@ -0,0 +1,11 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+class Cell {
+ method Test(c: Cell) {
+ assert c.F();
+ }
+}
+
+predicate F()
+
diff --git a/Test/dafny4/Bug131.dfy.expect b/Test/dafny4/Bug131.dfy.expect
new file mode 100644
index 00000000..b4c98652
--- /dev/null
+++ b/Test/dafny4/Bug131.dfy.expect
@@ -0,0 +1,2 @@
+Bug131.dfy(6,13): Error: member F does not exist in class Cell
+1 resolution/type errors detected in Bug131.dfy
diff --git a/Test/dafny4/Bug132.dfy b/Test/dafny4/Bug132.dfy
new file mode 100644
index 00000000..b2a26876
--- /dev/null
+++ b/Test/dafny4/Bug132.dfy
@@ -0,0 +1,45 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+class Cell {
+ var data: int
+
+ predicate P()
+ reads this
+ { data < 0 }
+
+ predicate Q(e: Cell)
+ reads this, e
+ { e != null ==> e.data == data }
+
+ method Test() {
+ ghost var b;
+ var c := new Cell;
+ if {
+ // In the current state, everything is allowed:
+ case true => b := this.P();
+ case true => b := c.P();
+ case true => b := this.Q(this);
+ case true => b := this.Q(c);
+ case true => b := c.Q(this);
+ case true => b := c.Q(c);
+
+ // 'this' was allocated already in the 'old' state, so all of these are fine:
+ case true => b := old(this.P());
+ case true => b := old(P()); // same as previous line, of course
+ case true => b := old(this.Q(this));
+
+ // 'c' is freshly allocaed in this method, so it cannot be used inside 'old'
+ case true => b := old(c.P()); // error: receiver argument must be allocated in the state in which the function is invoked
+ case true => b := old(c.Q(this)); // error: receiver argument must be allocated in the state in which the function is invoked
+
+ // The same rule should apply if 'c' is a non-receiver argument
+ case true => b := old(this.Q(c)); // BOGUS: this should also generate an error
+
+ // In the following, 'c' is used as both of the two arguments. It's not allowed as either argument. However, since the error
+ // about the receiver masks the error about the other parameter, only one error (about the receiver) should be reported.
+ case true => b := old(c.Q(c)); // BOGUS: this should generate an error about the receiver
+ }
+ }
+}
+
diff --git a/Test/dafny4/Bug132.dfy.expect b/Test/dafny4/Bug132.dfy.expect
new file mode 100644
index 00000000..0452f42d
--- /dev/null
+++ b/Test/dafny4/Bug132.dfy.expect
@@ -0,0 +1,18 @@
+Bug132.dfy(33,29): Error: receiver argument must be allocated in the state in which the function is invoked
+Execution trace:
+ (0,0): anon0
+ (0,0): anon24_Then
+Bug132.dfy(34,29): Error: receiver argument must be allocated in the state in which the function is invoked
+Execution trace:
+ (0,0): anon0
+ (0,0): anon25_Then
+Bug132.dfy(37,36): Error: argument must be allocated in the state in which the function is invoked
+Execution trace:
+ (0,0): anon0
+ (0,0): anon26_Then
+Bug132.dfy(41,29): Error: receiver argument must be allocated in the state in which the function is invoked
+Execution trace:
+ (0,0): anon0
+ (0,0): anon27_Then
+
+Dafny program verifier finished with 3 verified, 4 errors
diff --git a/Test/dafny4/Bug133.dfy b/Test/dafny4/Bug133.dfy
new file mode 100644
index 00000000..f7da133e
--- /dev/null
+++ b/Test/dafny4/Bug133.dfy
@@ -0,0 +1,18 @@
+// RUN: %dafny /noNLarith /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+module Math__div_def_i {
+ function my_div_pos(x:int, d:int) : int
+ requires d > 0;
+ decreases if x < 0 then (d - x) else x;
+ {
+ if x < 0 then
+ -1 + my_div_pos(x+d, d)
+ else if x < d then
+ 0
+ else
+ 1 + my_div_pos(x-d, d)
+ }
+}
+
+
diff --git a/Test/dafny4/Bug133.dfy.expect b/Test/dafny4/Bug133.dfy.expect
new file mode 100644
index 00000000..c0c48e2b
--- /dev/null
+++ b/Test/dafny4/Bug133.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 1 verified, 0 errors
diff --git a/Test/dafny4/Bug134.dfy b/Test/dafny4/Bug134.dfy
new file mode 100644
index 00000000..fff6c36a
--- /dev/null
+++ b/Test/dafny4/Bug134.dfy
@@ -0,0 +1,23 @@
+// RUN: %dafny /compile:0 /ironDafny "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+module NativeTypes {
+ newtype{:nativeType "ushort"} uint16 = i:int | 0 <= i < 0x10000
+}
+
+abstract module AbstractModuleA
+{
+ import opened NativeTypes
+ datatype T = T(i:uint16)
+}
+
+abstract module AbstractModuleB
+{
+ import opened A as AbstractModuleA
+}
+
+abstract module AbstractModuleC
+{
+ import opened B as AbstractModuleB
+}
+
diff --git a/Test/dafny4/Bug134.dfy.expect b/Test/dafny4/Bug134.dfy.expect
new file mode 100644
index 00000000..d5d7ae53
--- /dev/null
+++ b/Test/dafny4/Bug134.dfy.expect
@@ -0,0 +1,4 @@
+Bug134.dfy(16,20): Warning: "import A as B" has been deprecated; in the new syntax, it is "import A:B"
+Bug134.dfy(21,20): Warning: "import A as B" has been deprecated; in the new syntax, it is "import A:B"
+
+Dafny program verifier finished with 4 verified, 0 errors
diff --git a/Test/dafny4/Bug136.dfy b/Test/dafny4/Bug136.dfy
new file mode 100644
index 00000000..97c9e389
--- /dev/null
+++ b/Test/dafny4/Bug136.dfy
@@ -0,0 +1,12 @@
+// RUN: %dafny /compile:0 /print:"%t.print" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+method test()
+{
+ assume false;
+ assert true;
+}
+
+
+
+
diff --git a/Test/dafny4/Bug136.dfy.expect b/Test/dafny4/Bug136.dfy.expect
new file mode 100644
index 00000000..069e7767
--- /dev/null
+++ b/Test/dafny4/Bug136.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 2 verified, 0 errors
diff --git a/Test/dafny4/Bug138.dfy b/Test/dafny4/Bug138.dfy
new file mode 100644
index 00000000..db0e54ef
--- /dev/null
+++ b/Test/dafny4/Bug138.dfy
@@ -0,0 +1,22 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+datatype List = Nil | Cons(int, List)
+
+method R(xs: List)
+{
+ match xs
+ case Nil() => // currently produces a parsing error, but shouldn't
+ case Cons(x, Nil()) => // currently allowed
+ case Cons(x, Cons(y, tail)) =>
+}
+
+function F(xs: List) : int
+{
+ match xs
+ case Nil() => 0 // currently produces a parsing error, but shouldn't
+ case Cons(x, Nil()) => 1 // currently allowed
+ case Cons(x, Cons(y, tail)) => 2
+}
+
+
diff --git a/Test/dafny4/Bug138.dfy.expect b/Test/dafny4/Bug138.dfy.expect
new file mode 100644
index 00000000..52595bf9
--- /dev/null
+++ b/Test/dafny4/Bug138.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 3 verified, 0 errors
diff --git a/Test/dafny4/Bug139.dfy b/Test/dafny4/Bug139.dfy
new file mode 100644
index 00000000..bd4ded73
--- /dev/null
+++ b/Test/dafny4/Bug139.dfy
@@ -0,0 +1,25 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+datatype List = Nil | Cons(int, List)
+
+method R(xs: List)
+{
+ var a: int;
+ var b: int;
+ match xs
+ case Nil =>
+ case Cons(a, Nil()) => // this 'a' is allowed
+ case Cons(x, Cons(b, tail)) => // this 'b' (which is in a nested position) generates an error
+}
+
+function F(xs: List): int
+{
+ var a := 4;
+ var b := 7;
+ match xs
+ case Nil => 0
+ case Cons(a, Nil()) => 1
+ case Cons(x, Cons(b, tail)) => 2
+}
+
diff --git a/Test/dafny4/Bug139.dfy.expect b/Test/dafny4/Bug139.dfy.expect
new file mode 100644
index 00000000..52595bf9
--- /dev/null
+++ b/Test/dafny4/Bug139.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 3 verified, 0 errors
diff --git a/Test/dafny4/Bug140.dfy b/Test/dafny4/Bug140.dfy
new file mode 100644
index 00000000..9a85e36c
--- /dev/null
+++ b/Test/dafny4/Bug140.dfy
@@ -0,0 +1,67 @@
+// RUN: %dafny /compile:3 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+class Node<T> {
+ ghost var List: seq<T>;
+ ghost var Repr: set<Node<T>>;
+
+ var data: T;
+ var next: Node<T>;
+
+ predicate Valid()
+ reads this, Repr
+ {
+ this in Repr && null !in Repr &&
+ (next == null ==> List == [data]) &&
+ (next != null ==>
+ next in Repr && next.Repr <= Repr &&
+ this !in next.Repr &&
+ List == [data] + next.List &&
+ next.Valid())
+ }
+
+ constructor (d: T)
+ modifies this
+ ensures Valid() && fresh(Repr - {this})
+ ensures List == [d]
+ {
+ data, next := d, null;
+ List, Repr := [d], {this};
+ }
+
+ constructor InitAsPredecessor(d: T, succ: Node<T>)
+ requires succ != null && succ.Valid() && this !in succ.Repr;
+ modifies this;
+ ensures Valid() && fresh(Repr - {this} - succ.Repr);
+ ensures List == [d] + succ.List;
+ {
+ data, next := d, succ;
+ List := [d] + succ.List;
+ Repr := {this} + succ.Repr;
+ }
+
+ method Prepend(d: T) returns (r: Node<T>)
+ requires Valid()
+ ensures r != null && r.Valid() && fresh(r.Repr - old(Repr))
+ ensures r.List == [d] + List
+ {
+ r := new Node.InitAsPredecessor(d, this);
+ }
+
+ method Print()
+ requires Valid()
+ decreases |List|
+ {
+ print data;
+ if (next != null) {
+ next.Print();
+ }
+ }
+}
+
+method Main()
+{
+ var l2 := new Node(2);
+ var l1 := l2.Prepend(1);
+ l1.Print();
+}
diff --git a/Test/dafny4/Bug140.dfy.expect b/Test/dafny4/Bug140.dfy.expect
new file mode 100644
index 00000000..00c7d129
--- /dev/null
+++ b/Test/dafny4/Bug140.dfy.expect
@@ -0,0 +1,6 @@
+
+Dafny program verifier finished with 11 verified, 0 errors
+Program compiled successfully
+Running...
+
+12 \ No newline at end of file
diff --git a/Test/dafny4/Bug148.dfy b/Test/dafny4/Bug148.dfy
new file mode 100644
index 00000000..b7a08952
--- /dev/null
+++ b/Test/dafny4/Bug148.dfy
@@ -0,0 +1,25 @@
+// RUN: %dafny /compile:3 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+method Main()
+{
+ var zero : real := 0.0;
+ var three : real := 3.0;
+ var fifteen : real := 15.0;
+ var negone : real := -1.0;
+ var negthree : real := -3.0;
+
+ print zero <= fifteen, "\n"; // true
+ print fifteen <= zero, "\n"; // false
+ print negone <= zero, "\n"; // true
+ print zero <= negone, "\n"; // false
+ print negone <= fifteen, "\n"; // true
+ print fifteen <= negone, "\n"; // false
+
+ print zero >= fifteen, "\n"; // false
+ print fifteen >= zero, "\n"; // true
+ print negone >= zero, "\n"; // false
+ print zero >= negone, "\n"; // true
+ print negone >= fifteen, "\n"; // false
+ print fifteen >= negone, "\n"; // true
+}
diff --git a/Test/dafny4/Bug148.dfy.expect b/Test/dafny4/Bug148.dfy.expect
new file mode 100644
index 00000000..7acfb169
--- /dev/null
+++ b/Test/dafny4/Bug148.dfy.expect
@@ -0,0 +1,17 @@
+
+Dafny program verifier finished with 2 verified, 0 errors
+Program compiled successfully
+Running...
+
+True
+False
+True
+False
+True
+False
+False
+True
+False
+True
+False
+True
diff --git a/Test/dafny4/Bug49.dfy b/Test/dafny4/Bug49.dfy
new file mode 100644
index 00000000..887938d6
--- /dev/null
+++ b/Test/dafny4/Bug49.dfy
@@ -0,0 +1,74 @@
+// RUN: %dafny /compile:3 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+method Main()
+{
+ print apply(i => i + 1, 5), "\n";
+ print mapply(map[5 := 6], 5), "\n";
+ var f;
+ print five(f), "\n";
+}
+
+// -----
+// test that the definition axiom for function "apply" is available
+
+function method apply(f:int->int, a:int): int
+ reads f.reads
+ requires f.requires(a)
+{
+ f(a)
+}
+
+lemma TestPost()
+ ensures apply(i => i + 1, 5) == 6
+{
+}
+
+lemma M() {
+ assert apply(i => i + 1, 5) == 6;
+}
+
+lemma TestPre()
+ requires apply(i => i + 1, 5) == 6
+{
+}
+
+lemma TestPreCaller()
+{
+ TestPre();
+}
+
+// -----
+// test that the above thing for arrows also works for maps
+
+function method mapply(m: map<int,int>, a:int): int
+ requires a in m
+{
+ m[a]
+}
+
+lemma TestMPost()
+ ensures mapply(map[5 := 6], 5) == 6
+{
+}
+
+lemma N() {
+ assert mapply(map[5 := 6], 5) == 6;
+}
+
+// -----
+// test that g's result is known to be $Is'ed and $IsAlloc'ed
+
+function method five(f:int->int): int { 5 }
+
+lemma P() {
+ var f := i => i + 1;
+ assert five(f) == 5;
+}
+
+lemma Q(g: real->int->int)
+ requires g.requires(0.0)
+{
+ var f := g(0.0);
+ assert five(f) == 5;
+}
diff --git a/Test/dafny4/Bug49.dfy.expect b/Test/dafny4/Bug49.dfy.expect
new file mode 100644
index 00000000..653dda07
--- /dev/null
+++ b/Test/dafny4/Bug49.dfy.expect
@@ -0,0 +1,8 @@
+
+Dafny program verifier finished with 21 verified, 0 errors
+Program compiled successfully
+Running...
+
+6
+6
+5
diff --git a/Test/dafny4/Bug60.dfy b/Test/dafny4/Bug60.dfy
index 5340ad6b..c433451c 100644
--- a/Test/dafny4/Bug60.dfy
+++ b/Test/dafny4/Bug60.dfy
@@ -9,5 +9,5 @@ method Main()
print (s, m), "\n";
print (|s|, |m|), "\n";
print(set s | s in m), "\n";
- print (forall x :: x in (map [1:=10, 2:=20]) ==> x > 0), "\n";
-} \ No newline at end of file
+ print (forall x {:nowarn} :: x in (map [1:=10, 2:=20]) ==> x > 0), "\n";
+}
diff --git a/Test/dafny4/Bug63.dfy b/Test/dafny4/Bug63.dfy
index 86aad232..39cbae1b 100644
--- a/Test/dafny4/Bug63.dfy
+++ b/Test/dafny4/Bug63.dfy
@@ -8,6 +8,6 @@ method M()
method Client()
{
- assume forall o: object :: o != null ==> false;
+ assume forall o: object {:nowarn} :: o != null ==> false;
M();
-} \ No newline at end of file
+}
diff --git a/Test/dafny4/Bug73.dfy.expect b/Test/dafny4/Bug73.dfy.expect
index 6cf5c156..8beaa18c 100644
--- a/Test/dafny4/Bug73.dfy.expect
+++ b/Test/dafny4/Bug73.dfy.expect
@@ -1,9 +1,9 @@
-Bug73.dfy(7,14): Error: assertion violation
+Bug73.dfy(7,13): Error: assertion violation
Execution trace:
(0,0): anon0
Bug73.dfy(7,19): anon3_Else
(0,0): anon2
-Bug73.dfy(13,14): Error: assertion violation
+Bug73.dfy(13,13): Error: assertion violation
Execution trace:
(0,0): anon0
Bug73.dfy(13,20): anon3_Else
diff --git a/Test/dafny4/Bug75.dfy b/Test/dafny4/Bug75.dfy
new file mode 100644
index 00000000..37d35f77
--- /dev/null
+++ b/Test/dafny4/Bug75.dfy
@@ -0,0 +1,50 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+predicate R1(x:int, y:int) { x > 0 ==> R2(x - 1) }
+predicate R2(x:int) { exists y :: R1(x, y) }
+
+lemma L1(x:int)
+{
+ assume R2(x);
+ assert exists y :: R1(x, y); // FAILS
+}
+
+lemma L2(x:int)
+ requires R2(x); // Oddly, adding this requires fixes the problem
+{
+ assume R2(x);
+ assert exists y :: R1(x, y); // SUCCEEDS
+}
+
+// this predicate says that the first "n" elements of "s"
+
+// are in strictly increasing order
+
+predicate method Increasing(s: seq<int>, n: nat)
+ requires n <= |s|
+{
+ n < 2 ||
+ (s[n-2] < s[n-1] && Increasing(s, n-1))
+}
+
+method Extend(s: seq<int>, n: nat) returns (n': nat)
+ requires n < |s|
+ requires forall i :: 0 <= i < n ==> Increasing(s, i)
+ ensures n <= n' <= |s|
+ ensures forall j :: 0 <= j < n' ==> Increasing(s, j)
+{
+ if 2 <= n && s[n-2] < s[n-1] {
+ n' := n + 1;
+ } else {
+ n' := n;
+ }
+}
+
+function pred(i:int):int { i - 1 }
+predicate f(a:int, s:int) { (a <= 0 || (exists s0 :: f(pred(a), s0))) }
+
+lemma Fuel1(a:int, s:int)
+{
+ assert f(a, s) <==> (a <= 0 || (exists s0 :: f(pred(a), s0))); // FAILS
+}
diff --git a/Test/dafny4/Bug75.dfy.expect b/Test/dafny4/Bug75.dfy.expect
new file mode 100644
index 00000000..aeb37948
--- /dev/null
+++ b/Test/dafny4/Bug75.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 13 verified, 0 errors
diff --git a/Test/dafny4/Bug79.dfy b/Test/dafny4/Bug79.dfy
new file mode 100644
index 00000000..49f2421b
--- /dev/null
+++ b/Test/dafny4/Bug79.dfy
@@ -0,0 +1,10 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+function foo(s:int) : (int, int)
+
+function bar(s:int) : bool
+{
+ var (x, rest) := foo(s);
+ x > 0
+} \ No newline at end of file
diff --git a/Test/dafny4/Bug79.dfy.expect b/Test/dafny4/Bug79.dfy.expect
new file mode 100644
index 00000000..069e7767
--- /dev/null
+++ b/Test/dafny4/Bug79.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 2 verified, 0 errors
diff --git a/Test/dafny4/Bug81.dfy b/Test/dafny4/Bug81.dfy
new file mode 100644
index 00000000..1992d666
--- /dev/null
+++ b/Test/dafny4/Bug81.dfy
@@ -0,0 +1,9 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+function {:opaque} RefineSeqToSeq<T,U>(s:seq<T>, refine_func:T->U) : seq<U>
+ reads refine_func.reads;
+{
+ if |s| == 0 then []
+ else RefineSeqToSeq(s[1..], refine_func)
+}
diff --git a/Test/dafny4/Bug81.dfy.expect b/Test/dafny4/Bug81.dfy.expect
new file mode 100644
index 00000000..069e7767
--- /dev/null
+++ b/Test/dafny4/Bug81.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 2 verified, 0 errors
diff --git a/Test/dafny4/Bug82.dfy b/Test/dafny4/Bug82.dfy
new file mode 100644
index 00000000..bc7ff321
--- /dev/null
+++ b/Test/dafny4/Bug82.dfy
@@ -0,0 +1,11 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+function {:opaque} Reverse(id:int) : int
+
+function RefineToMap(ReverseKey:int->int) : bool
+
+function RefineToMapOfSeqNums() : bool
+{
+ RefineToMap(Reverse)
+}
diff --git a/Test/dafny4/Bug82.dfy.expect b/Test/dafny4/Bug82.dfy.expect
new file mode 100644
index 00000000..73ba063c
--- /dev/null
+++ b/Test/dafny4/Bug82.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 4 verified, 0 errors
diff --git a/Test/dafny4/Bug88.dfy b/Test/dafny4/Bug88.dfy
new file mode 100644
index 00000000..cab2524a
--- /dev/null
+++ b/Test/dafny4/Bug88.dfy
@@ -0,0 +1,18 @@
+// RUN: %dafny "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+lemma T(a: int) returns (b: int)
+ ensures a == b
+{
+ calc {
+ a;
+ }
+}
+
+lemma A(i: int)
+ ensures false
+{
+ if * {
+ } else {
+ }
+} \ No newline at end of file
diff --git a/Test/dafny4/Bug88.dfy.expect b/Test/dafny4/Bug88.dfy.expect
new file mode 100644
index 00000000..3bd22329
--- /dev/null
+++ b/Test/dafny4/Bug88.dfy.expect
@@ -0,0 +1,11 @@
+Bug88.dfy(6,0): Error BP5003: A postcondition might not hold on this return path.
+Bug88.dfy(5,12): Related location: This is the postcondition that might not hold.
+Execution trace:
+ (0,0): anon0
+ Bug88.dfy(7,3): anon2_Else
+Bug88.dfy(14,0): Error BP5003: A postcondition might not hold on this return path.
+Bug88.dfy(13,10): Related location: This is the postcondition that might not hold.
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 2 verified, 2 errors
diff --git a/Test/dafny4/Bug89.dfy b/Test/dafny4/Bug89.dfy
new file mode 100644
index 00000000..12aec5f4
--- /dev/null
+++ b/Test/dafny4/Bug89.dfy
@@ -0,0 +1,15 @@
+// RUN: %dafny /compile:3 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+method F() returns(x:int)
+ ensures x == 6;
+{
+ x := 5;
+ x := (var y := 1; y + x);
+}
+
+method Main()
+{
+ var x := F();
+ print x;
+} \ No newline at end of file
diff --git a/Test/dafny4/Bug89.dfy.expect b/Test/dafny4/Bug89.dfy.expect
new file mode 100644
index 00000000..5221a5d1
--- /dev/null
+++ b/Test/dafny4/Bug89.dfy.expect
@@ -0,0 +1,6 @@
+
+Dafny program verifier finished with 4 verified, 0 errors
+Program compiled successfully
+Running...
+
+6 \ No newline at end of file
diff --git a/Test/dafny4/Bug91.dfy b/Test/dafny4/Bug91.dfy
new file mode 100644
index 00000000..53e5d5b2
--- /dev/null
+++ b/Test/dafny4/Bug91.dfy
@@ -0,0 +1,40 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+type SendState = map<int, seq<int>>
+
+function UnAckedMessages(s:SendState) : set<int>
+{
+ set m,dst | dst in s && m in s[dst] :: m
+}
+
+function UnAckedMessagesForDst(s:SendState, dst:int) : set<int>
+ requires dst in s;
+{
+ set m | m in s[dst] :: m
+}
+
+function UnAckedMessages3(s:SendState) : set<int>
+{
+ set m,dst | dst in s && m in UnAckedMessagesForDst(s, dst) :: m
+}
+
+function SeqToSet<T>(s:seq<T>) : set<T>
+{
+ set i | i in s
+}
+
+function UnAckedMessages4(s:SendState) : set<int>
+{
+ set m,dst | dst in s && m in SeqToSet(s[dst]) :: m
+}
+
+function UnAckedLists(s:SendState) : set<seq<int>>
+{
+ set dst | dst in s :: s[dst]
+}
+
+function UnAckedMessages5(s:SendState) : set<int>
+{
+ set m, list | list in UnAckedLists(s) && m in list :: m
+} \ No newline at end of file
diff --git a/Test/dafny4/Bug91.dfy.expect b/Test/dafny4/Bug91.dfy.expect
new file mode 100644
index 00000000..76f19e0d
--- /dev/null
+++ b/Test/dafny4/Bug91.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 7 verified, 0 errors
diff --git a/Test/dafny4/Bug93.dfy b/Test/dafny4/Bug93.dfy
new file mode 100644
index 00000000..5a1dd27f
--- /dev/null
+++ b/Test/dafny4/Bug93.dfy
@@ -0,0 +1,37 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+module Fuel {
+ function FunctionA(x:int) : int
+ {
+ x + 2
+ }
+
+ function FunctionB(y:int) : int
+ {
+ FunctionA(y - 2)
+ }
+
+ method {:fuel FunctionA,0,0} MethodX(z:int)
+ {
+ assert FunctionB(z) == z; // error: Cannot see the body of FunctionA
+ }
+}
+
+module Opaque {
+ function {:opaque} FunctionA(x:int) : int
+ {
+ x + 2
+ }
+
+ function FunctionB(y:int) : int
+ {
+ FunctionA(y - 2)
+ }
+
+ method MethodX(z:int)
+ {
+ assert FunctionB(z) == z;
+ }
+}
+
diff --git a/Test/dafny4/Bug93.dfy.expect b/Test/dafny4/Bug93.dfy.expect
new file mode 100644
index 00000000..d0baf877
--- /dev/null
+++ b/Test/dafny4/Bug93.dfy.expect
@@ -0,0 +1,8 @@
+Bug93.dfy(17,28): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Bug93.dfy(34,28): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 7 verified, 2 errors
diff --git a/Test/dafny4/Bug94.dfy b/Test/dafny4/Bug94.dfy
new file mode 100644
index 00000000..2f437785
--- /dev/null
+++ b/Test/dafny4/Bug94.dfy
@@ -0,0 +1,35 @@
+// RUN: %dafny /compile:3 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+function foo() : (int, int)
+{
+ (5, 10)
+}
+
+function bar() : int
+{
+ var (x, y) := foo();
+ x + y
+}
+
+lemma test()
+{
+ var (x, y) := foo();
+}
+
+function method foo2() : (int,int)
+{
+ (5, 10)
+}
+
+method test2()
+{
+ var (x, y) := foo2();
+}
+
+method Main()
+{
+ var (x, y) := foo2();
+ assert (x+y == 15);
+ print(x+y);
+}
diff --git a/Test/dafny4/Bug94.dfy.expect b/Test/dafny4/Bug94.dfy.expect
new file mode 100644
index 00000000..6b337d5a
--- /dev/null
+++ b/Test/dafny4/Bug94.dfy.expect
@@ -0,0 +1,6 @@
+
+Dafny program verifier finished with 9 verified, 0 errors
+Program compiled successfully
+Running...
+
+15 \ No newline at end of file
diff --git a/Test/dafny4/Bug99.dfy b/Test/dafny4/Bug99.dfy
new file mode 100644
index 00000000..3f95ce9f
--- /dev/null
+++ b/Test/dafny4/Bug99.dfy
@@ -0,0 +1,11 @@
+// RUN: %dafny /autoTriggers:1 /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+predicate P(e:int, p:int) { true }
+predicate Q(i:int, t:int)
+
+lemma Tester(x:int)
+{
+ assert forall i :: Q(i, x) ==> (forall p {:trigger P(i, p)} :: P(i, p));
+
+}
diff --git a/Test/dafny4/Bug99.dfy.expect b/Test/dafny4/Bug99.dfy.expect
new file mode 100644
index 00000000..73ba063c
--- /dev/null
+++ b/Test/dafny4/Bug99.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 4 verified, 0 errors
diff --git a/Test/dafny4/Circ.dfy b/Test/dafny4/Circ.dfy
index e7609195..d110c05c 100644
--- a/Test/dafny4/Circ.dfy
+++ b/Test/dafny4/Circ.dfy
@@ -16,6 +16,7 @@ function zip(a: Stream, b: Stream): Stream { Cons(a.head, zip(b, a.tail)) }
colemma BlinkZipProperty()
ensures zip(zeros(), ones()) == blink();
{
+ BlinkZipProperty();
}
// ----- Thue-Morse sequence -----
@@ -75,6 +76,7 @@ colemma FProperty(s: Stream<Bit>)
// def. zip
Cons(s.head, Cons(not(s).head, zip(s.tail, not(s).tail)));
}
+ FProperty(s.tail);
}
// The fix-point theorem now follows easily.
diff --git a/Test/dafny4/CoqArt-InsertionSort.dfy b/Test/dafny4/CoqArt-InsertionSort.dfy
index efd01537..99e0f0b1 100644
--- a/Test/dafny4/CoqArt-InsertionSort.dfy
+++ b/Test/dafny4/CoqArt-InsertionSort.dfy
@@ -151,6 +151,7 @@ lemma existence_proof(l: List<int>)
{
match l {
case Nil =>
+ assert sorted(Nil);
case Cons(x, m) =>
existence_proof(m);
var m' :| equiv(m, m') && sorted(m');
diff --git a/Test/dafny4/FlyingRobots.dfy b/Test/dafny4/FlyingRobots.dfy
new file mode 100644
index 00000000..c80d310d
--- /dev/null
+++ b/Test/dafny4/FlyingRobots.dfy
@@ -0,0 +1,285 @@
+// RUN: %dafny /compile:3 /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// The flying robots examples from an F* tutorial. It demonstrates how to specify
+// mutable data structures in the heap.
+
+class Cell {
+ var val:int
+ constructor (v:int)
+ modifies this
+ ensures val == v
+ {
+ val := v;
+ }
+}
+
+class Point {
+ ghost var Value: (int, int, int)
+
+ ghost var Repr: set<object>
+ predicate Valid()
+ reads this, Repr
+ {
+ this in Repr && null !in Repr &&
+ {x,y,z} <= Repr &&
+ x != y && y != z && z != x &&
+ Value == (x.val, y.val, z.val)
+ }
+
+ var x:Cell, y:Cell, z:Cell
+
+ constructor (a:int, b:int, c:int)
+ modifies this
+ ensures Valid() && fresh(Repr - {this})
+ ensures Value == (a, b, c)
+ {
+ x := new Cell(a);
+ y := new Cell(b);
+ z := new Cell(c);
+ Repr := {this};
+ Repr := Repr + {x, y, z};
+ Value := (a, b, c);
+ }
+
+ method Mutate(a:int, b:int, c:int)
+ requires Valid()
+ modifies Repr
+ ensures Valid() && fresh(Repr - old(Repr))
+ ensures Value == (a, b, c)
+ {
+ x.val, y.val, z.val := a, b, c;
+ Value := (a, b, c);
+ }
+}
+
+class Arm {
+ ghost var Value: (int, int)
+
+ ghost var Repr: set<object>
+ predicate Valid()
+ reads this, Repr
+ {
+ this in Repr && null !in Repr &&
+ {polar, azim} <= Repr &&
+ polar != azim &&
+ Value == (polar.val, azim.val)
+ }
+
+ var polar:Cell
+ var azim:Cell
+
+ constructor (polar_in:int, azim_in:int)
+ modifies this
+ ensures Valid() && fresh(Repr - {this})
+ ensures Value == (polar_in, azim_in)
+ {
+ polar := new Cell(polar_in);
+ azim := new Cell(azim_in);
+ Repr := {this};
+ Repr := Repr + {polar, azim};
+ Value := (polar_in, azim_in);
+ }
+
+ method Mutate(polar_in:int, azim_in:int)
+ requires Valid()
+ modifies Repr
+ ensures Valid() && fresh(Repr - old(Repr))
+ ensures Value == (polar_in, azim_in)
+ {
+ polar.val, azim.val := polar_in, azim_in;
+ Value := (polar_in, azim_in);
+ }
+}
+
+class Bot {
+ ghost var Repr: set<object>
+ predicate {:opaque} Valid()
+ reads this, Repr
+ ensures Valid() ==> this in Repr && null !in Repr
+ {
+ this in Repr && null !in Repr &&
+ pos in Repr && {left, right} <= Repr &&
+ left != right &&
+ pos.Repr <= Repr && left.Repr <= Repr && right.Repr <= Repr &&
+ pos.Repr !! left.Repr !! right.Repr &&
+ pos.Valid() && left.Valid() && right.Valid()
+ }
+
+ var pos:Point
+ var left:Arm
+ var right:Arm
+
+ constructor ()
+ modifies this
+ ensures Valid() && fresh(Repr - {this})
+ {
+ pos := new Point(0, 0, 0);
+ left := new Arm(0, 0);
+ right := new Arm(0, 0);
+ Repr := {this};
+ Repr := Repr + pos.Repr + left.Repr + right.Repr;
+ reveal_Valid();
+ }
+
+ predicate flying()
+ requires (reveal_Valid(); Valid())
+ reads Repr
+ {
+ pos.z.val > 0
+ }
+
+ predicate arms_up()
+ requires (reveal_Valid(); Valid())
+ reads Repr
+ {
+ left.polar.val == right.polar.val == 0
+ }
+
+ predicate robot_inv()
+ requires (reveal_Valid(); Valid())
+ reads Repr
+ {
+ flying() ==> arms_up()
+ }
+
+ method Fly()
+ requires Valid()
+ modifies Repr
+ ensures Valid() && fresh(Repr - old(Repr))
+ ensures robot_inv() && flying()
+ {
+ reveal_Valid();
+ left.polar.val, right.polar.val := 0, 0;
+ pos.z.val := 100;
+ right.azim.val := 17;
+ pos.Value := (pos.Value.0, pos.Value.1, 100);
+ left.Value, right.Value := (0, left.Value.1), (0, 17);
+ reveal_Valid();
+ }
+}
+
+// This method tests that Fly operates independently on disjoint robots
+method FlyRobots(b0:Bot, b1:Bot)
+ requires b0 != null && b0.Valid()
+ requires b1 != null && b1.Valid()
+ requires b0 != b1 ==> b0.Repr !! b1.Repr
+ modifies b0.Repr, b1.Repr
+ ensures b0.Valid() && fresh(b0.Repr - old(b0.Repr))
+ ensures b1.Valid() && fresh(b1.Repr - old(b1.Repr))
+ ensures b0 != b1 ==> b0.Repr !! b1.Repr
+ ensures b0.robot_inv() && b1.robot_inv()
+ ensures b0.flying() && b1.flying()
+{
+ b0.Fly();
+ b1.Fly();
+}
+
+// ----- robot armies ----------
+
+// The union of .Repr for the robots in "bots"
+function ArmyRepr(bots:seq<Bot>) : set<object>
+ reads set b | b in bots
+{
+ set b,o | b in bots && b != null && o in b.Repr :: o
+}
+
+// An army is a sequence of disjoint, valid robots
+predicate ValidArmy(bots:seq<Bot>)
+ reads set b | b in bots
+ reads ArmyRepr(bots)
+{
+ (forall i :: 0 <= i < |bots| ==> bots[i] != null && bots[i].Valid())
+ && (forall i,j :: 0 <= i < j < |bots| ==> bots[i].Repr !! bots[j].Repr)
+}
+
+method FlyRobotArmy(bots:seq<Bot>)
+ requires ValidArmy(bots)
+ modifies ArmyRepr(bots)
+ ensures ValidArmy(bots) && fresh(ArmyRepr(bots) - old(ArmyRepr(bots)))
+ ensures forall b :: b in bots ==> b.Valid() && b.robot_inv() && b.flying()
+{
+ if * {
+ // fly recursively
+ FlyRobotArmy_Recursively(bots);
+ } else {
+ // fly iteratively
+ var n := 0;
+ while n < |bots|
+ invariant 0 <= n <= |bots|
+ invariant ValidArmy(bots)
+ invariant forall j :: 0 <= j < n ==> bots[j].Valid() && bots[j].robot_inv() && bots[j].flying()
+ invariant forall i :: 0 <= i < |bots| ==> fresh(bots[i].Repr - old(bots[i].Repr))
+ {
+ FlyOne(bots, n);
+ n := n + 1;
+ }
+ }
+}
+
+method FlyRobotArmy_Recursively(bots:seq<Bot>)
+ requires ValidArmy(bots)
+ modifies ArmyRepr(bots)
+ ensures ValidArmy(bots)
+ ensures forall i :: 0 <= i < |bots| ==> fresh(bots[i].Repr - old(bots[i].Repr))
+ ensures forall b :: b in bots ==> b.robot_inv() && b.flying()
+{
+ if bots != [] {
+ FlyOne(bots, 0);
+ FlyRobotArmy_Recursively(bots[1..]);
+ }
+}
+
+// This method is intended to be called in each loop iteration of FlyRobotArmy
+method FlyOne(bots:seq<Bot>, n:int)
+ requires 0 <= n < |bots|
+ requires forall j :: 0 <= j < |bots| ==> bots[j] != null && bots[j].Valid()
+ requires forall i,j :: 0 <= i < j < |bots| ==> bots[i].Repr !! bots[j].Repr
+ requires forall j :: 0 <= j < n ==> bots[j].robot_inv() && bots[j].flying()
+ modifies bots[n].Repr
+ ensures forall j :: 0 <= j < |bots| ==> bots[j].Valid()
+ ensures fresh(bots[n].Repr - old(bots[n].Repr))
+ ensures bots[n].robot_inv() && bots[n].flying()
+ ensures forall j :: 0 <= j < |bots| && j != n ==> bots[j].Repr == old(bots[j].Repr)
+ ensures forall j :: 0 <= j < n ==> bots[j].robot_inv() && bots[j].flying()
+{
+ bots[n].Fly();
+}
+
+// This method makes sure FlyRobotArmy is callable and callable again
+method FormArmy(b0:Bot, b1:Bot, b2:Bot)
+ requires null !in {b0, b1, b2}
+ requires b0.Valid() && b1.Valid() && b2.Valid()
+ requires b0.Repr !! b1.Repr !! b2.Repr
+ modifies b0.Repr, b1.Repr, b2.Repr
+ ensures b0.Valid() && b1.Valid() && b2.Valid()
+ ensures b0.Repr !! b1.Repr !! b2.Repr
+ ensures fresh(b0.Repr + b1.Repr + b2.Repr - old(b0.Repr + b1.Repr + b2.Repr))
+{
+ var army := [b0, b1, b2];
+ ArmyRepr3(army);
+ FlyRobotArmy(army);
+ FlyRobotArmy(army); // do it again
+ ArmyRepr3(army);
+}
+
+lemma ArmyRepr3(army:seq<Bot>)
+ requires null !in army && |army| == 3
+ ensures ArmyRepr(army) == army[0].Repr + army[1].Repr + army[2].Repr
+{
+}
+
+// ----- Make sure everything is callable ----------
+
+method Main()
+{
+ var b0 := new Bot();
+ var b1 := new Bot();
+ FlyRobots(b0, b1);
+ FlyRobots(b0, b1);
+ FlyRobots(b1, b0);
+
+ var b2 := new Bot();
+ FormArmy(b0, b1, b2);
+ FormArmy(b2, b0, b1);
+}
diff --git a/Test/dafny4/FlyingRobots.dfy.expect b/Test/dafny4/FlyingRobots.dfy.expect
new file mode 100644
index 00000000..99cdc3cb
--- /dev/null
+++ b/Test/dafny4/FlyingRobots.dfy.expect
@@ -0,0 +1,5 @@
+
+Dafny program verifier finished with 37 verified, 0 errors
+Program compiled successfully
+Running...
+
diff --git a/Test/dafny4/Fstar-QuickSort.dfy b/Test/dafny4/Fstar-QuickSort.dfy
index 4c5bc09b..d895ccf4 100644
--- a/Test/dafny4/Fstar-QuickSort.dfy
+++ b/Test/dafny4/Fstar-QuickSort.dfy
@@ -6,8 +6,6 @@
// Dafny needs help with a couple of lemmas in places where F* does not need them.
// Comments below show differences between the F* and Dafny versions.
-datatype Pair<T, U> = P(T, U)
-
datatype List<T> = Nil | Cons(T, List)
function length(list: List): nat // for termination proof
@@ -26,7 +24,7 @@ function In(x: int, list: List<int>): nat
}
predicate SortedRange(m: int, n: int, list: List<int>)
- decreases list; // for termination proof
+ decreases list // for termination proof
{
match list
case Nil => m <= n
@@ -34,45 +32,45 @@ predicate SortedRange(m: int, n: int, list: List<int>)
}
function append(n0: int, n1: int, n2: int, n3: int, i: List<int>, j: List<int>): List<int>
- requires n0 <= n1 <= n2 <= n3;
- requires SortedRange(n0, n1, i) && SortedRange(n2, n3, j);
- ensures SortedRange(n0, n3, append(n0, n1, n2, n3, i, j));
- ensures forall x :: In(x, append(n0, n1, n2, n3, i, j)) == In(x, i) + In(x, j);
- decreases i; // for termination proof
+ requires n0 <= n1 <= n2 <= n3
+ requires SortedRange(n0, n1, i) && SortedRange(n2, n3, j)
+ ensures SortedRange(n0, n3, append(n0, n1, n2, n3, i, j))
+ ensures forall x :: In(x, append(n0, n1, n2, n3, i, j)) == In(x, i) + In(x, j)
+ decreases i // for termination proof
{
match i
case Nil => j
case Cons(hd, tl) => Cons(hd, append(hd, n1, n2, n3, tl, j))
}
-function partition(x: int, l: List<int>): Pair<List<int>, List<int>>
- ensures var P(lo, hi) := partition(x, l);
+function partition(x: int, l: List<int>): (List<int>, List<int>)
+ ensures var (lo, hi) := partition(x, l);
(forall y :: In(y, lo) == if y <= x then In(y, l) else 0) &&
(forall y :: In(y, hi) == if x < y then In(y, l) else 0) &&
- length(l) == length(lo) + length(hi); // for termination proof
+ length(l) == length(lo) + length(hi) // for termination proof
{
match l
- case Nil => P(Nil, Nil)
+ case Nil => (Nil, Nil)
case Cons(hd, tl) =>
- var P(lo, hi) := partition(x, tl);
+ var (lo, hi) := partition(x, tl);
if hd <= x then
- P(Cons(hd, lo), hi)
+ (Cons(hd, lo), hi)
else
- P(lo, Cons(hd, hi))
+ (lo, Cons(hd, hi))
}
function sort(min: int, max: int, i: List<int>): List<int>
- requires min <= max;
- requires forall x :: In(x, i) != 0 ==> min <= x <= max;
- ensures SortedRange(min, max, sort(min, max, i));
- ensures forall x :: In(x, i) == In(x, sort(min, max, i));
- decreases length(i); // for termination proof
+ requires min <= max
+ requires forall x :: In(x, i) != 0 ==> min <= x <= max
+ ensures SortedRange(min, max, sort(min, max, i))
+ ensures forall x :: In(x, i) == In(x, sort(min, max, i))
+ decreases length(i) // for termination proof
{
match i
case Nil => Nil
case Cons(hd, tl) =>
assert In(hd, i) != 0; // this proof line not needed in F*
- var P(lo, hi) := partition(hd, tl);
+ var (lo, hi) := partition(hd, tl);
assert forall y :: In(y, lo) <= In(y, i); // this proof line not needed in F*
var i' := sort(min, hd, lo);
var j' := sort(hd, max, hi);
diff --git a/Test/dafny4/GHC-MergeSort.dfy b/Test/dafny4/GHC-MergeSort.dfy
index e06773eb..24903d87 100644
--- a/Test/dafny4/GHC-MergeSort.dfy
+++ b/Test/dafny4/GHC-MergeSort.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// Rustan Leino
@@ -412,11 +412,8 @@ lemma sorted_replaceSuffix(xs: List<G>, ys: List<G>, zs: List<G>)
match xs {
case Nil =>
case Cons(c, xs') =>
- forall a,b | a in multiset_of(xs') && b in multiset_of(Cons(c, zs))
- ensures Below(a, b);
- {
- sorted_reverse(xs', Cons(c, ys));
- }
+ sorted_reverse(xs, ys);
+ sorted_reverse(xs', Cons(c, ys));
sorted_replaceSuffix(xs', Cons(c, ys), Cons(c, zs));
}
}
diff --git a/Test/dafny4/KozenSilva.dfy b/Test/dafny4/KozenSilva.dfy
index af0cdc71..ef49b10f 100644
--- a/Test/dafny4/KozenSilva.dfy
+++ b/Test/dafny4/KozenSilva.dfy
@@ -25,8 +25,8 @@ copredicate LexLess(s: Stream<int>, t: Stream<int>)
// A co-lemma is used to establish the truth of a co-predicate.
colemma Theorem1_LexLess_Is_Transitive(s: Stream<int>, t: Stream<int>, u: Stream<int>)
- requires LexLess(s, t) && LexLess(t, u);
- ensures LexLess(s, u);
+ requires LexLess(s, t) && LexLess(t, u)
+ ensures LexLess(s, u)
{
// Here is the proof, which is actually a body of code. It lends itself to a
// simple, intuitive co-inductive reading. For a theorem this simple, this simple
@@ -38,6 +38,14 @@ colemma Theorem1_LexLess_Is_Transitive(s: Stream<int>, t: Stream<int>, u: Stream
}
}
+// Actually, Dafny can do the proof of the previous lemma completely automatically. Here it is:
+colemma Theorem1_LexLess_Is_Transitive_Automatic(s: Stream<int>, t: Stream<int>, u: Stream<int>)
+ requires LexLess(s, t) && LexLess(t, u)
+ ensures LexLess(s, u)
+{
+ // no manual proof needed, so the body of the co-lemma is empty
+}
+
// The following predicate captures the (inductively defined) negation of (the
// co-inductively defined) LexLess above.
predicate NotLexLess(s: Stream<int>, t: Stream<int>)
@@ -51,7 +59,7 @@ predicate NotLexLess'(k: nat, s: Stream<int>, t: Stream<int>)
}
lemma EquivalenceTheorem(s: Stream<int>, t: Stream<int>)
- ensures LexLess(s, t) <==> !NotLexLess(s, t);
+ ensures LexLess(s, t) <==> !NotLexLess(s, t)
{
if !NotLexLess(s, t) {
EquivalenceTheorem0(s, t);
@@ -61,8 +69,8 @@ lemma EquivalenceTheorem(s: Stream<int>, t: Stream<int>)
}
}
colemma EquivalenceTheorem0(s: Stream<int>, t: Stream<int>)
- requires !NotLexLess(s, t);
- ensures LexLess(s, t);
+ requires !NotLexLess(s, t)
+ ensures LexLess(s, t)
{
// Here, more needs to be said about the way Dafny handles co-lemmas.
// The way a co-lemma establishes a co-predicate is to prove, by induction,
@@ -74,14 +82,14 @@ colemma EquivalenceTheorem0(s: Stream<int>, t: Stream<int>)
// indicates a finite unrolling of a co-inductive predicate. In particular,
// LexLess#[k] refers to k unrollings of LexLess.
lemma EquivalenceTheorem0_Lemma(k: nat, s: Stream<int>, t: Stream<int>)
- requires !NotLexLess'(k, s, t);
- ensures LexLess#[k](s, t);
+ requires !NotLexLess'(k, s, t)
+ ensures LexLess#[k](s, t)
{
// This simple inductive proof is done completely automatically by Dafny.
}
lemma EquivalenceTheorem1(s: Stream<int>, t: Stream<int>)
- requires LexLess(s, t);
- ensures !NotLexLess(s, t);
+ requires LexLess(s, t)
+ ensures !NotLexLess(s, t)
{
// The forall statement in Dafny is used, here, as universal introduction:
// what EquivalenceTheorem1_Lemma establishes for one k, the forall
@@ -91,22 +99,22 @@ lemma EquivalenceTheorem1(s: Stream<int>, t: Stream<int>)
}
}
lemma EquivalenceTheorem1_Lemma(k: nat, s: Stream<int>, t: Stream<int>)
- requires LexLess(s, t);
- ensures !NotLexLess'(k, s, t);
+ requires LexLess(s, t)
+ ensures !NotLexLess'(k, s, t)
{
}
lemma Theorem1_Alt(s: Stream<int>, t: Stream<int>, u: Stream<int>)
- requires NotLexLess(s, u);
- ensures NotLexLess(s, t) || NotLexLess(t, u);
+ requires NotLexLess(s, u)
+ ensures NotLexLess(s, t) || NotLexLess(t, u)
{
forall k: nat | NotLexLess'(k, s, u) {
Theorem1_Alt_Lemma(k, s, t, u);
}
}
lemma Theorem1_Alt_Lemma(k: nat, s: Stream<int>, t: Stream<int>, u: Stream<int>)
- requires NotLexLess'(k, s, u);
- ensures NotLexLess'(k, s, t) || NotLexLess'(k, t, u);
+ requires NotLexLess'(k, s, u)
+ ensures NotLexLess'(k, s, t) || NotLexLess'(k, t, u)
{
}
@@ -116,8 +124,8 @@ function PointwiseAdd(s: Stream<int>, t: Stream<int>): Stream<int>
}
colemma Theorem2_Pointwise_Addition_Is_Monotone(s: Stream<int>, t: Stream<int>, u: Stream<int>, v: Stream<int>)
- requires LexLess(s, t) && LexLess(u, v);
- ensures LexLess(PointwiseAdd(s, u), PointwiseAdd(t, v));
+ requires LexLess(s, t) && LexLess(u, v)
+ ensures LexLess(PointwiseAdd(s, u), PointwiseAdd(t, v))
{
// The co-lemma will establish the co-inductive predicate by establishing
// all finite unrollings thereof. Each finite unrolling is proved by
@@ -148,15 +156,9 @@ copredicate Subtype(a: RecType, b: RecType)
}
colemma Theorem3_Subtype_Is_Transitive(a: RecType, b: RecType, c: RecType)
- requires Subtype(a, b) && Subtype(b, c);
- ensures Subtype(a, c);
-{
- if a == Bottom || c == Top {
- // done
- } else {
- Theorem3_Subtype_Is_Transitive(c.dom, b.dom, a.dom);
- Theorem3_Subtype_Is_Transitive(a.ran, b.ran, c.ran);
- }
+ requires Subtype(a, b) && Subtype(b, c)
+ ensures Subtype(a, c)
+{
}
// --------------------------------------------------------------------------
@@ -184,16 +186,16 @@ copredicate ValBelow(u: Val, v: Val)
}
colemma Theorem4a_ClEnvBelow_Is_Transitive(c: ClEnv, d: ClEnv, e: ClEnv)
- requires ClEnvBelow(c, d) && ClEnvBelow(d, e);
- ensures ClEnvBelow(c, e);
+ requires ClEnvBelow(c, d) && ClEnvBelow(d, e)
+ ensures ClEnvBelow(c, e)
{
forall y | y in c.m {
Theorem4b_ValBelow_Is_Transitive#[_k-1](c.m[y], d.m[y], e.m[y]);
}
}
colemma Theorem4b_ValBelow_Is_Transitive(u: Val, v: Val, w: Val)
- requires ValBelow(u, v) && ValBelow(v, w);
- ensures ValBelow(u, w);
+ requires ValBelow(u, v) && ValBelow(v, w)
+ ensures ValBelow(u, w)
{
if u.ValCl? {
Theorem4a_ClEnvBelow_Is_Transitive(u.cl.env, v.cl.env, w.cl.env);
@@ -209,7 +211,7 @@ predicate IsCapsule(cap: Capsule)
}
function ClosureConversion(cap: Capsule): Cl
- requires IsCapsule(cap);
+ requires IsCapsule(cap)
{
Closure(cap.e.abs, ClosureConvertedMap(cap.s))
// In the Kozen and Silva paper, there are more conditions, having to do with free variables,
@@ -229,8 +231,8 @@ predicate CapsuleEnvironmentBelow(s: map<Var, ConstOrAbs>, t: map<Var, ConstOrAb
}
colemma Theorem5_ClosureConversion_Is_Monotone(s: map<Var, ConstOrAbs>, t: map<Var, ConstOrAbs>)
- requires CapsuleEnvironmentBelow(s, t);
- ensures ClEnvBelow(ClosureConvertedMap(s), ClosureConvertedMap(t));
+ requires CapsuleEnvironmentBelow(s, t)
+ ensures ClEnvBelow(ClosureConvertedMap(s), ClosureConvertedMap(t))
{
}
@@ -251,8 +253,8 @@ copredicate Bisim(s: Stream, t: Stream)
}
colemma Theorem6_Bisim_Is_Symmetric(s: Stream, t: Stream)
- requires Bisim(s, t);
- ensures Bisim(t, s);
+ requires Bisim(s, t)
+ ensures Bisim(t, s)
{
// proof is automatic
}
@@ -270,8 +272,8 @@ function merge(s: Stream, t: Stream): Stream
// In general, the termination argument needs to be supplied explicitly in terms
// of a metric, rank, variant function, or whatever you want to call it--a
// "decreases" clause in Dafny. Dafny provides some help in making up "decreases"
-// clauses, and in this case it automatically adds "decreases 0;" to SplitLeft
-// and "decreases 1;" to SplitRight. With these "decreases" clauses, the
+// clauses, and in this case it automatically adds "decreases 0" to SplitLeft
+// and "decreases 1" to SplitRight. With these "decreases" clauses, the
// termination check of SplitRight's call to SplitLeft will simply be "0 < 1",
// which is trivial to check.
function SplitLeft(s: Stream): Stream
@@ -284,7 +286,7 @@ function SplitRight(s: Stream): Stream
}
colemma Theorem7_Merge_Is_Left_Inverse_Of_Split_Bisim(s: Stream)
- ensures Bisim(merge(SplitLeft(s), SplitRight(s)), s);
+ ensures Bisim(merge(SplitLeft(s), SplitRight(s)), s)
{
var LHS := merge(SplitLeft(s), SplitRight(s));
// The construct that follows is a "calc" statement. It gives a way to write an
@@ -318,7 +320,7 @@ colemma Theorem7_Merge_Is_Left_Inverse_Of_Split_Bisim(s: Stream)
}
colemma Theorem7_Merge_Is_Left_Inverse_Of_Split_Equal(s: Stream)
- ensures merge(SplitLeft(s), SplitRight(s)) == s;
+ ensures merge(SplitLeft(s), SplitRight(s)) == s
{
// The proof of this co-lemma is actually done completely automatically (so the
// body of this co-lemma can be empty). However, just to show what the calculations
diff --git a/Test/dafny4/KozenSilva.dfy.expect b/Test/dafny4/KozenSilva.dfy.expect
index c6c90498..90432af3 100644
--- a/Test/dafny4/KozenSilva.dfy.expect
+++ b/Test/dafny4/KozenSilva.dfy.expect
@@ -1,2 +1,2 @@
-Dafny program verifier finished with 47 verified, 0 errors
+Dafny program verifier finished with 49 verified, 0 errors
diff --git a/Test/dafny4/LargeConstants.dfy b/Test/dafny4/LargeConstants.dfy
new file mode 100644
index 00000000..18435c30
--- /dev/null
+++ b/Test/dafny4/LargeConstants.dfy
@@ -0,0 +1,14 @@
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+lemma largeIsLarge()
+ ensures 0x8000000000000000 > 0 {
+}
+
+lemma SmallIsSmall()
+ ensures -0x8000000000000000 < 0 {
+}
+
+lemma ShouldCancelOut()
+ ensures -0x8000000000000000 + 0x8000000000000000 == 0 {
+}
diff --git a/Test/dafny4/LargeConstants.dfy.expect b/Test/dafny4/LargeConstants.dfy.expect
new file mode 100644
index 00000000..4ef2de53
--- /dev/null
+++ b/Test/dafny4/LargeConstants.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 6 verified, 0 errors
diff --git a/Test/dafny4/Leq.dfy b/Test/dafny4/Leq.dfy
new file mode 100644
index 00000000..0491dd00
--- /dev/null
+++ b/Test/dafny4/Leq.dfy
@@ -0,0 +1,174 @@
+// RUN: %dafny /rprint:"%t.rprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// Rustan Leino, 22 Sep 2015.
+// This file considers two definitions of Leq on naturals+infinity. One
+// definition uses the least fixpoint, the other the greatest fixpoint.
+
+// Nat represents natural numbers extended with infinity
+codatatype Nat = Z | S(pred: Nat)
+
+function Num(n: nat): Nat
+{
+ if n == 0 then Z else S(Num(n-1))
+}
+
+predicate IsFinite(a: Nat)
+{
+ exists m:nat :: a == Num(m)
+}
+
+copredicate IsInfinity(a: Nat)
+{
+ a.S? && IsInfinity(a.pred)
+}
+
+lemma NatCases(a: Nat)
+ ensures IsFinite(a) || IsInfinity(a)
+{
+ if IsFinite(a) {
+ } else {
+ NatCasesAux(a);
+ }
+}
+colemma NatCasesAux(a: Nat)
+ requires !IsFinite(a)
+ ensures IsInfinity(a)
+{
+ assert a != Num(0);
+ if IsFinite(a.pred) {
+ // going for a contradiction
+ var m:nat :| a.pred == Num(m);
+ assert a == Num(m+1);
+ assert false; // the case is absurd
+ }
+ NatCasesAux(a.pred);
+}
+
+// ----------- inductive semantics (more precisely, a least-fixpoint definition of Leq)
+
+inductive predicate Leq(a: Nat, b: Nat)
+{
+ a == Z ||
+ (a.S? && b.S? && Leq(a.pred, b.pred))
+}
+
+lemma LeqTheorem(a: Nat, b: Nat)
+ ensures Leq(a, b) <==>
+ exists m:nat :: a == Num(m) &&
+ (IsInfinity(b) || exists n:nat :: b == Num(n) && m <= n)
+{
+ if exists m:nat,n:nat :: a == Num(m) && b == Num(n) && m <= n {
+ var m:nat,n:nat :| a == Num(m) && b == Num(n) && m <= n;
+ Leq0_finite(m, n);
+ }
+ if (exists m:nat :: a == Num(m)) && IsInfinity(b) {
+ var m:nat :| a == Num(m);
+ Leq0_infinite(m, b);
+ }
+ if Leq(a, b) {
+ var k:nat :| Leq#[k](a, b);
+ var m, n := Leq1(k, a, b);
+ }
+}
+
+lemma Leq0_finite(m: nat, n: nat)
+ requires m <= n
+ ensures Leq(Num(m), Num(n))
+{
+ // proof is automatic
+}
+
+lemma Leq0_infinite(m: nat, b: Nat)
+ requires IsInfinity(b)
+ ensures Leq(Num(m), b)
+{
+ // proof is automatic
+}
+
+lemma Leq1(k: nat, a: Nat, b: Nat) returns (m: nat, n: nat)
+ requires Leq#[k](a, b)
+ ensures a == Num(m)
+ ensures IsInfinity(b) || (b == Num(n) && m <= n)
+{
+ if a == Z {
+ m := 0;
+ NatCases(b);
+ if !IsInfinity(b) {
+ n :| b == Num(n);
+ }
+ } else {
+ assert a.S? && b.S? && Leq(a.pred, b.pred);
+ m,n := Leq1(k-1, a.pred, b.pred);
+ m, n := m + 1, n + 1;
+ }
+}
+
+// ----------- co-inductive semantics (more precisely, a greatest-fixpoint definition of Leq)
+
+copredicate CoLeq(a: Nat, b: Nat)
+{
+ a == Z ||
+ (a.S? && b.S? && CoLeq(a.pred, b.pred))
+}
+
+lemma CoLeqTheorem(a: Nat, b: Nat)
+ ensures CoLeq(a, b) <==>
+ IsInfinity(b) ||
+ exists m:nat,n:nat :: a == Num(m) && b == Num(n) && m <= n
+{
+ if IsInfinity(b) {
+ CoLeq0_infinite(a, b);
+ }
+ if exists m:nat,n:nat :: a == Num(m) && b == Num(n) && m <= n {
+ var m:nat,n:nat :| a == Num(m) && b == Num(n) && m <= n;
+ CoLeq0_finite(m, n);
+ }
+ if CoLeq(a, b) {
+ CoLeq1(a, b);
+ }
+}
+
+lemma CoLeq0_finite(m: nat, n: nat)
+ requires m <= n
+ ensures CoLeq(Num(m), Num(n))
+{
+ // proof is automatic
+}
+
+colemma CoLeq0_infinite(a: Nat, b: Nat)
+ requires IsInfinity(b)
+ ensures CoLeq(a, b)
+{
+ // proof is automatic
+}
+
+lemma CoLeq1(a: Nat, b: Nat)
+ requires CoLeq(a, b)
+ ensures IsInfinity(b) || exists m:nat,n:nat :: a == Num(m) && b == Num(n) && m <= n
+{
+ var m,n := CoLeq1'(a, b);
+}
+
+lemma CoLeq1'(a: Nat, b: Nat) returns (m: nat, n: nat)
+ requires CoLeq(a, b)
+ ensures IsInfinity(b) || (a == Num(m) && b == Num(n) && m <= n)
+{
+ if !IsInfinity(b) {
+ NatCases(b);
+ n :| b == Num(n);
+ m := CoLeq1Aux(a, n);
+ }
+}
+
+lemma CoLeq1Aux(a: Nat, n: nat) returns (m: nat)
+ requires CoLeq(a, Num(n))
+ ensures a == Num(m) && m <= n
+{
+ if a == Z {
+ m := 0;
+ } else {
+ m := CoLeq1Aux(a.pred, n-1);
+ m := m + 1;
+ }
+}
diff --git a/Test/dafny4/Leq.dfy.expect b/Test/dafny4/Leq.dfy.expect
new file mode 100644
index 00000000..754c1e55
--- /dev/null
+++ b/Test/dafny4/Leq.dfy.expect
@@ -0,0 +1,3 @@
+
+Dafny program verifier finished with 29 verified, 0 errors
+Compiled assembly into Leq.dll
diff --git a/Test/dafny4/McCarthy91.dfy b/Test/dafny4/McCarthy91.dfy
new file mode 100644
index 00000000..2e599f0d
--- /dev/null
+++ b/Test/dafny4/McCarthy91.dfy
@@ -0,0 +1,86 @@
+// RUN: %dafny /compile:3 /rprint:"%t.rprint" /autoTriggers:1 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+// The usual recursive method for computing McCarthy's 91 function
+
+method Main() {
+ var s := [3, 99, 100, 101, 1013];
+
+ var n := 0;
+ while n < |s| {
+ var m := M(s[n]);
+ print "M(", s[n], ") = ", m, "\n";
+ n := n + 1;
+ }
+
+ n := 0;
+ while n < |s| {
+ print "mc91(", s[n], ") = ", mc91(s[n]), "\n";
+ n := n + 1;
+ }
+
+ n := 0;
+ while n < |s| {
+ var m := Mc91(s[n]);
+ print "Mc91(", s[n], ") = ", m, "\n";
+ n := n + 1;
+ }
+
+ n := 0;
+ while n < 5 {
+ var m := iter(n, mc91, 40);
+ print "iter(", n, ", mc91, 40) = ", m, "\n";
+ n := n + 1;
+ }
+}
+
+method M(n: int) returns (r: int)
+ ensures r == if n <= 100 then 91 else n - 10
+ decreases 100 - n
+{
+ if n <= 100 {
+ r := M(n + 11);
+ r := M(r);
+ } else {
+ r := n - 10;
+ }
+}
+
+// Same as above, but as a function
+
+function method mc91(n: int): int
+ ensures n <= 100 ==> mc91(n) == 91
+ decreases 100 - n
+{
+ if n <= 100 then
+ mc91(mc91(n + 11))
+ else
+ n - 10
+}
+
+// Iterating a function f e times starting from n
+
+function method iter(e: nat, f: int -> int, n: int): int
+ requires forall x :: f.requires(x) && f.reads(x) == {}
+{
+ if e == 0 then n else iter(e-1, f, f(n))
+}
+
+// Iterative version of McCarthy's 91 function, following in lockstep
+// what the recursive version would do
+
+method Mc91(n0: int) returns (r: int)
+ ensures r == mc91(n0)
+{
+ var e, n := 1, n0;
+ while e > 0
+ invariant iter(e, mc91, n) == mc91(n0)
+ decreases 100 - n + 10 * e, e
+ {
+ if n <= 100 {
+ e, n := e+1, n+11;
+ } else {
+ e, n := e-1, n-10;
+ }
+ }
+ return n;
+}
diff --git a/Test/dafny4/McCarthy91.dfy.expect b/Test/dafny4/McCarthy91.dfy.expect
new file mode 100644
index 00000000..bbc91c35
--- /dev/null
+++ b/Test/dafny4/McCarthy91.dfy.expect
@@ -0,0 +1,25 @@
+
+Dafny program verifier finished with 8 verified, 0 errors
+Program compiled successfully
+Running...
+
+M(3) = 91
+M(99) = 91
+M(100) = 91
+M(101) = 91
+M(1013) = 1003
+mc91(3) = 91
+mc91(99) = 91
+mc91(100) = 91
+mc91(101) = 91
+mc91(1013) = 1003
+Mc91(3) = 91
+Mc91(99) = 91
+Mc91(100) = 91
+Mc91(101) = 91
+Mc91(1013) = 1003
+iter(0, mc91, 40) = 40
+iter(1, mc91, 40) = 91
+iter(2, mc91, 40) = 91
+iter(3, mc91, 40) = 91
+iter(4, mc91, 40) = 91
diff --git a/Test/dafny4/MonadicLaws.dfy b/Test/dafny4/MonadicLaws.dfy
new file mode 100644
index 00000000..b668ffb8
--- /dev/null
+++ b/Test/dafny4/MonadicLaws.dfy
@@ -0,0 +1,100 @@
+// RUN: %dafny /compile:0 /rprint:"%t.rprint" /autoTriggers:1 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// Monadic Laws
+// Niki Vazou and Rustan Leino
+// 28 March 2016
+
+datatype List<T> = Nil | Cons(head: T, tail: List)
+
+predicate Total<T,U>(p: T -> U)
+ reads p.reads
+{
+ forall x :: p.reads(x) == {} && p.requires(x)
+}
+
+function append(xs: List, ys: List): List
+{
+ match xs
+ case Nil => ys
+ case Cons(x, xs') => Cons(x, append(xs', ys))
+}
+
+lemma AppendNil(xs: List)
+ ensures append(xs, Nil) == xs
+{
+}
+
+lemma AppendAssoc(xs: List, ys: List, zs: List)
+ ensures append(append(xs, ys), zs) == append(xs, append(ys, zs));
+{
+}
+
+function Return<T>(a: T): List
+{
+ Cons(a, Nil)
+}
+
+function Bind<T,U>(xs: List<T>, f: T -> List<U>): List<U>
+ requires Total(f)
+{
+ match xs
+ case Nil => Nil
+ case Cons(x, xs') => append(f(x), Bind(xs', f))
+}
+
+lemma LeftIdentity<T>(a: T, f: T -> List)
+ requires Total(f)
+ ensures Bind(Return(a), f) == f(a)
+{
+ AppendNil(f(a));
+}
+
+lemma RightIdentity<T>(m: List)
+ ensures Bind(m, Return) == m
+{
+ match m
+ case Nil =>
+ assert Bind<T,T>(Nil, Return) == Nil;
+ case Cons(x, m') =>
+ calc {
+ Bind(Cons(x, m'), Return);
+ append(Return(x), Bind(m', Return));
+ Cons(x, Bind(m', Return));
+ }
+}
+
+lemma Associativity<T>(m: List, f: T -> List, g: T -> List)
+ requires Total(f) && Total(g)
+ ensures Bind(Bind(m, f), g) == Bind(m, x => Bind(f(x), g))
+{
+ match m
+ case Nil =>
+ assert Bind(m, x => Bind(f(x), g)) == Nil;
+ case Cons(x, xs) =>
+ match f(x)
+ case Nil =>
+ calc {
+ Bind(xs, y => Bind(f(y), g));
+ Bind(Cons(x, xs), y => Bind(f(y), g));
+ }
+ case Cons(y, ys) =>
+ calc {
+ append(g(y), Bind(append(ys, Bind(xs, f)), g));
+ { BindOverAppend(ys, Bind(xs, f), g); }
+ append(g(y), append(Bind(ys, g), Bind(Bind(xs, f), g)));
+ { AppendAssoc(g(y), Bind(ys, g), Bind(Bind(xs, f), g)); }
+ append(append(g(y), Bind(ys, g)), Bind(Bind(xs, f), g));
+ Bind(Cons(x, xs), z => Bind(f(z), g));
+ }
+}
+
+lemma BindOverAppend<T>(xs: List, ys: List, g: T -> List)
+ requires Total(g)
+ ensures Bind(append(xs, ys), g) == append(Bind(xs, g), Bind(ys, g))
+{
+ match xs
+ case Nil =>
+ case Cons(x, xs') =>
+ AppendAssoc(g(x), Bind(xs', g), Bind(ys, g));
+}
diff --git a/Test/dafny4/MonadicLaws.dfy.expect b/Test/dafny4/MonadicLaws.dfy.expect
new file mode 100644
index 00000000..d903c7c5
--- /dev/null
+++ b/Test/dafny4/MonadicLaws.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 16 verified, 0 errors
diff --git a/Test/dafny4/NipkowKlein-chapter3.dfy b/Test/dafny4/NipkowKlein-chapter3.dfy
index 6572359a..3de6a5fc 100644
--- a/Test/dafny4/NipkowKlein-chapter3.dfy
+++ b/Test/dafny4/NipkowKlein-chapter3.dfy
@@ -18,7 +18,7 @@ function append(xs: List, ys: List): List
// ----- arithmetic expressions -----
type vname = string // variable names
-datatype aexp = N(n: int) | V(x: vname) | Plus(0: aexp, 1: aexp) // arithmetic expressions
+datatype aexp = N(n: int) | V(vname) | Plus(aexp, aexp) // arithmetic expressions
type val = int
type state = vname -> val
@@ -131,9 +131,15 @@ lemma AsimpCorrect(a: aexp, s: state)
forall a' | a' < a { AsimpCorrect(a', s); }
}
+// The following lemma is not in the Nipkow and Klein book, but it's a fun one to prove.
+lemma ASimplInvolutive(a: aexp)
+ ensures asimp(asimp(a)) == asimp(a)
+{
+}
+
// ----- boolean expressions -----
-datatype bexp = Bc(v: bool) | Not(op: bexp) | And(0: bexp, 1: bexp) | Less(a0: aexp, a1: aexp)
+datatype bexp = Bc(v: bool) | Not(bexp) | And(bexp, bexp) | Less(aexp, aexp)
function bval(b: bexp, s: state): bool
reads s.reads
@@ -189,9 +195,12 @@ lemma BsimpCorrect(b: bexp, s: state)
ensures bval(bsimp(b), s) == bval(b, s)
{
/* Here is one proof, which uses the induction hypothesis any anything smaller than b and also invokes
- the lemma AsimpCorrect on anything smaller than b.
+ the lemma AsimpCorrect on every arithmetic expression.
forall b' | b' < b { BsimpCorrect(b', s); }
- forall a' | a' < b { AsimpCorrect(a', s); }
+ forall a { AsimpCorrect(a, s); }
+ Yet another possibility is to mark the lemma with {:induction b} and to use the following line in
+ the body:
+ forall a { AsimpCorrect(a, s); }
*/
// Here is another proof, which makes explicit the uses of the induction hypothesis and the other lemma.
match b
diff --git a/Test/dafny4/NipkowKlein-chapter3.dfy.expect b/Test/dafny4/NipkowKlein-chapter3.dfy.expect
index ab18d98e..bb45fee9 100644
--- a/Test/dafny4/NipkowKlein-chapter3.dfy.expect
+++ b/Test/dafny4/NipkowKlein-chapter3.dfy.expect
@@ -1,2 +1,2 @@
-Dafny program verifier finished with 28 verified, 0 errors
+Dafny program verifier finished with 30 verified, 0 errors
diff --git a/Test/dafny4/NipkowKlein-chapter7.dfy b/Test/dafny4/NipkowKlein-chapter7.dfy
index 33be9dd6..0c089895 100644
--- a/Test/dafny4/NipkowKlein-chapter7.dfy
+++ b/Test/dafny4/NipkowKlein-chapter7.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /rprint:"%t.rprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /rprint:"%t.rprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// This file is a Dafny encoding of chapter 7 from "Concrete Semantics: With Isabelle/HOL" by
@@ -10,25 +10,19 @@ datatype List<T> = Nil | Cons(head: T, tail: List<T>)
type vname = string // variable names
type val = int
-type state = imap<vname, val>
-predicate Total(s: state)
-{
- forall x :: x in s
-}
+type state = map<vname, val>
datatype aexp = N(n: int) | V(x: vname) | Plus(0: aexp, 1: aexp) // arithmetic expressions
function aval(a: aexp, s: state): val
- requires Total(s)
{
match a
case N(n) => n
- case V(x) => s[x]
+ case V(x) => if x in s then s[x] else 0
case Plus(a0, a1) => aval(a0,s ) + aval(a1, s)
}
datatype bexp = Bc(v: bool) | Not(op: bexp) | And(0: bexp, 1: bexp) | Less(a0: aexp, a1: aexp)
function bval(b: bexp, s: state): bool
- requires Total(s)
{
match b
case Bc(v) => v
@@ -44,7 +38,6 @@ datatype com = SKIP | Assign(vname, aexp) | Seq(com, com) | If(bexp, com, com) |
// ----- Big-step semantics -----
inductive predicate big_step(c: com, s: state, t: state)
- requires Total(s)
{
match c
case SKIP =>
@@ -53,7 +46,6 @@ inductive predicate big_step(c: com, s: state, t: state)
t == s[x := aval(a, s)]
case Seq(c0, c1) =>
exists s' ::
- Total(s') &&
big_step(c0, s, s') &&
big_step(c1, s', t)
case If(b, thn, els) =>
@@ -61,13 +53,11 @@ inductive predicate big_step(c: com, s: state, t: state)
case While(b, body) =>
(!bval(b, s) && s == t) ||
(bval(b, s) && exists s' ::
- Total(s') &&
big_step(body, s, s') &&
big_step(While(b, body), s', t))
}
lemma Example1(s: state, t: state)
- requires Total(s)
requires t == s["x" := 5]["y" := 5]
ensures big_step(Seq(Assign("x", N(5)), Assign("y", V("x"))), s, t)
{
@@ -83,29 +73,13 @@ lemma Example1(s: state, t: state)
}
lemma SemiAssociativity(c0: com, c1: com, c2: com, s: state, t: state)
- requires Total(s)
ensures big_step(Seq(Seq(c0, c1), c2), s, t) == big_step(Seq(c0, Seq(c1, c2)), s, t)
{
- calc {
- big_step(Seq(Seq(c0, c1), c2), s, t);
- // def. big_step
- exists s'' :: Total(s'') && big_step(Seq(c0, c1), s, s'') && big_step(c2, s'', t);
- // def. big_step
- exists s'' :: Total(s'') && (exists s' :: Total(s') && big_step(c0, s, s') && big_step(c1, s', s'')) && big_step(c2, s'', t);
- // logic
- exists s', s'' :: Total(s') && Total(s'') && big_step(c0, s, s') && big_step(c1, s', s'') && big_step(c2, s'', t);
- // logic
- exists s' :: Total(s') && big_step(c0, s, s') && exists s'' :: Total(s'') && big_step(c1, s', s'') && big_step(c2, s'', t);
- // def. big_step
- exists s' :: Total(s') && big_step(c0, s, s') && big_step(Seq(c1, c2), s', t);
- // def. big_step
- big_step(Seq(c0, Seq(c1, c2)), s, t);
- }
}
predicate equiv_c(c: com, c': com)
{
- forall s,t :: Total(s) ==> big_step(c, s, t) == big_step(c', s, t)
+ forall s,t :: big_step(c, s, t) == big_step(c', s, t)
}
lemma lemma_7_3(b: bexp, c: com)
@@ -122,7 +96,7 @@ lemma lemma_7_5(b: bexp, c: com, c': com)
requires equiv_c(c, c')
ensures equiv_c(While(b, c), While(b, c'))
{
- forall s,t | Total(s)
+ forall s,t
ensures big_step(While(b, c), s, t) == big_step(While(b, c'), s, t)
{
if big_step(While(b, c), s, t) {
@@ -135,15 +109,9 @@ lemma lemma_7_5(b: bexp, c: com, c': com)
}
inductive lemma lemma_7_6(b: bexp, c: com, c': com, s: state, t: state)
- requires Total(s) && big_step(While(b, c), s, t) && equiv_c(c, c')
+ requires big_step(While(b, c), s, t) && equiv_c(c, c')
ensures big_step(While(b, c'), s, t)
{
- if !bval(b, s) {
- // trivial
- } else {
- var s' :| Total(s') && big_step#[_k-1](c, s, s') && big_step#[_k-1](While(b, c), s', t);
- lemma_7_6(b, c, c', s', t); // induction hypothesis
- }
}
// equiv_c is an equivalence relation
@@ -161,36 +129,15 @@ lemma equiv_c_transitive(c: com, c': com, c'': com)
}
inductive lemma IMP_is_deterministic(c: com, s: state, t: state, t': state)
- requires Total(s) && big_step(c, s, t) && big_step(c, s, t')
+ requires big_step(c, s, t) && big_step(c, s, t')
ensures t == t'
{
- match c
- case SKIP =>
- // trivial
- case Assign(x, a) =>
- // trivial
- case Seq(c0, c1) =>
- var s' :| Total(s') && big_step#[_k-1](c0, s, s') && big_step#[_k-1](c1, s', t);
- var s'' :| Total(s'') && big_step#[_k-1](c0, s, s'') && big_step#[_k-1](c1, s'', t');
- IMP_is_deterministic(c0, s, s', s'');
- IMP_is_deterministic(c1, s', t, t');
- case If(b, thn, els) =>
- IMP_is_deterministic(if bval(b, s) then thn else els, s, t, t');
- case While(b, body) =>
- if !bval(b, s) {
- // trivial
- } else {
- var s' :| Total(s') && big_step#[_k-1](body, s, s') && big_step#[_k-1](While(b, body), s', t);
- var s'' :| Total(s'') && big_step#[_k-1](body, s, s'') && big_step#[_k-1](While(b, body), s'', t');
- IMP_is_deterministic(body, s, s', s'');
- IMP_is_deterministic(While(b, body), s', t, t');
- }
+ // Dafny totally rocks!
}
// ----- Small-step semantics -----
inductive predicate small_step(c: com, s: state, c': com, s': state)
- requires Total(s)
{
match c
case SKIP => false
@@ -206,7 +153,6 @@ inductive predicate small_step(c: com, s: state, c': com, s': state)
}
inductive lemma SmallStep_is_deterministic(cs: (com, state), cs': (com, state), cs'': (com, state))
- requires Total(cs.1)
requires small_step(cs.0, cs.1, cs'.0, cs'.1)
requires small_step(cs.0, cs.1, cs''.0, cs''.1)
ensures cs' == cs''
@@ -216,100 +162,59 @@ inductive lemma SmallStep_is_deterministic(cs: (com, state), cs': (com, state),
case Seq(c0, c1) =>
if c0 == SKIP {
} else {
- var c0' :| cs'.0 == Seq(c0', c1) && small_step#[_k-1](c0, cs.1, c0', cs'.1);
- var c0'' :| cs''.0 == Seq(c0'', c1) && small_step#[_k-1](c0, cs.1, c0'', cs''.1);
+ var c0' :| cs'.0 == Seq(c0', c1) && small_step(c0, cs.1, c0', cs'.1);
+ var c0'' :| cs''.0 == Seq(c0'', c1) && small_step(c0, cs.1, c0'', cs''.1);
SmallStep_is_deterministic((c0, cs.1), (c0', cs'.1), (c0'', cs''.1));
}
case If(b, thn, els) =>
case While(b, body) =>
}
-inductive lemma small_step_ends_in_Total_state(c: com, s: state, c': com, s': state)
- requires Total(s) && small_step(c, s, c', s')
- ensures Total(s')
-{
- match c
- case Assign(x, a) =>
- case Seq(c0, c1) =>
- if c0 != SKIP {
- var c0' :| c' == Seq(c0', c1) && small_step(c0, s, c0', s');
- small_step_ends_in_Total_state(c0, s, c0', s');
- }
- case If(b, thn, els) =>
- case While(b, body) =>
-}
-
inductive predicate small_step_star(c: com, s: state, c': com, s': state)
- requires Total(s)
{
(c == c' && s == s') ||
exists c'', s'' ::
- small_step(c, s, c'', s'') &&
- (small_step_ends_in_Total_state(c, s, c'', s''); small_step_star(c'', s'', c', s'))
-}
-
-inductive lemma small_step_star_ends_in_Total_state(c: com, s: state, c': com, s': state)
- requires Total(s) && small_step_star(c, s, c', s')
- ensures Total(s')
-{
- if c == c' && s == s' {
- } else {
- var c'', s'' :| small_step(c, s, c'', s'') &&
- (small_step_ends_in_Total_state(c, s, c'', s''); small_step_star#[_k-1](c'', s'', c', s'));
- small_step_star_ends_in_Total_state(c'', s'', c', s');
- }
+ small_step(c, s, c'', s'') && small_step_star(c'', s'', c', s')
}
lemma star_transitive(c0: com, s0: state, c1: com, s1: state, c2: com, s2: state)
- requires Total(s0) && Total(s1)
requires small_step_star(c0, s0, c1, s1) && small_step_star(c1, s1, c2, s2)
ensures small_step_star(c0, s0, c2, s2)
{
star_transitive_aux(c0, s0, c1, s1, c2, s2);
}
inductive lemma star_transitive_aux(c0: com, s0: state, c1: com, s1: state, c2: com, s2: state)
- requires Total(s0) && Total(s1)
requires small_step_star(c0, s0, c1, s1)
ensures small_step_star(c1, s1, c2, s2) ==> small_step_star(c0, s0, c2, s2)
{
- if c0 == c1 && s0 == s1 {
- } else {
- var c', s' :|
- small_step(c0, s0, c', s') &&
- (small_step_ends_in_Total_state(c0, s0, c', s'); small_step_star#[_k-1](c', s', c1, s1));
- star_transitive_aux(c', s', c1, s1, c2, s2);
- }
}
// The big-step semantics can be simulated by some number of small steps
inductive lemma BigStep_implies_SmallStepStar(c: com, s: state, t: state)
- requires Total(s) && big_step(c, s, t)
+ requires big_step(c, s, t)
ensures small_step_star(c, s, SKIP, t)
{
match c
case SKIP =>
// trivial
case Assign(x, a) =>
- assert t == s[x := aval(a, s)];
- assert small_step(c, s, SKIP, t);
assert small_step_star(SKIP, t, SKIP, t);
case Seq(c0, c1) =>
- var s' :| Total(s') && big_step#[_k-1](c0, s, s') && big_step#[_k-1](c1, s', t);
+ var s' :| big_step(c0, s, s') && big_step(c1, s', t);
calc <== {
small_step_star(c, s, SKIP, t);
{ star_transitive(Seq(c0, c1), s, Seq(SKIP, c1), s', SKIP, t); }
small_step_star(Seq(c0, c1), s, Seq(SKIP, c1), s') && small_step_star(Seq(SKIP, c1), s', SKIP, t);
{ lemma_7_13(c0, s, SKIP, s', c1); }
small_step_star(c0, s, SKIP, s') && small_step_star(Seq(SKIP, c1), s', SKIP, t);
- { BigStep_implies_SmallStepStar(c0, s, s'); }
+ //{ BigStep_implies_SmallStepStar(c0, s, s'); }
small_step_star(Seq(SKIP, c1), s', SKIP, t);
{ assert small_step(Seq(SKIP, c1), s', c1, s'); }
small_step_star(c1, s', SKIP, t);
- { BigStep_implies_SmallStepStar(c1, s', t); }
+ //{ BigStep_implies_SmallStepStar(c1, s', t); }
true;
}
case If(b, thn, els) =>
- BigStep_implies_SmallStepStar(if bval(b, s) then thn else els, s, t);
case While(b, body) =>
if !bval(b, s) && s == t {
calc <== {
@@ -321,7 +226,7 @@ inductive lemma BigStep_implies_SmallStepStar(c: com, s: state, t: state)
true;
}
} else {
- var s' :| Total(s') && big_step#[_k-1](body, s, s') && big_step#[_k-1](While(b, body), s', t);
+ var s' :| big_step(body, s, s') && big_step(While(b, body), s', t);
calc <== {
small_step_star(c, s, SKIP, t);
{ assert small_step(c, s, If(b, Seq(body, While(b, body)), SKIP), s); }
@@ -332,41 +237,40 @@ inductive lemma BigStep_implies_SmallStepStar(c: com, s: state, t: state)
small_step_star(Seq(body, While(b, body)), s, Seq(SKIP, While(b, body)), s') && small_step_star(Seq(SKIP, While(b, body)), s', SKIP, t);
{ lemma_7_13(body, s, SKIP, s', While(b, body)); }
small_step_star(body, s, SKIP, s') && small_step_star(Seq(SKIP, While(b, body)), s', SKIP, t);
- { BigStep_implies_SmallStepStar(body, s, s'); }
+ //{ BigStep_implies_SmallStepStar(body, s, s'); }
small_step_star(Seq(SKIP, While(b, body)), s', SKIP, t);
{ assert small_step(Seq(SKIP, While(b, body)), s', While(b, body), s'); }
small_step_star(While(b, body), s', SKIP, t);
- { BigStep_implies_SmallStepStar(While(b, body), s', t); }
+ //{ BigStep_implies_SmallStepStar(While(b, body), s', t); }
true;
}
}
}
inductive lemma lemma_7_13(c0: com, s0: state, c: com, t: state, c1: com)
- requires Total(s0) && small_step_star(c0, s0, c, t)
+ requires small_step_star(c0, s0, c, t)
ensures small_step_star(Seq(c0, c1), s0, Seq(c, c1), t)
{
if c0 == c && s0 == t {
} else {
- var c', s' :| small_step(c0, s0, c', s') && (small_step_ends_in_Total_state(c0, s0, c', s'); small_step_star#[_k-1](c', s', c, t));
+ var c', s' :| small_step(c0, s0, c', s') && small_step_star(c', s', c, t);
lemma_7_13(c', s', c, t, c1);
}
}
inductive lemma SmallStepStar_implies_BigStep(c: com, s: state, t: state)
- requires Total(s) && small_step_star(c, s, SKIP, t)
+ requires small_step_star(c, s, SKIP, t)
ensures big_step(c, s, t)
{
if c == SKIP && s == t {
} else {
- var c', s' :| small_step(c, s, c', s') && (small_step_ends_in_Total_state(c, s, c', s'); small_step_star#[_k-1](c', s', SKIP, t));
- SmallStepStar_implies_BigStep(c', s', t);
+ var c', s' :| small_step(c, s, c', s') && small_step_star(c', s', SKIP, t);
SmallStep_plus_BigStep(c, s, c', s', t);
}
}
inductive lemma SmallStep_plus_BigStep(c: com, s: state, c': com, s': state, t: state)
- requires Total(s) && Total(s') && small_step(c, s, c', s')
+ requires small_step(c, s, c', s')
ensures big_step(c', s', t) ==> big_step(c, s, t)
{
match c
@@ -376,9 +280,7 @@ inductive lemma SmallStep_plus_BigStep(c: com, s: state, c': com, s': state, t:
} else {
var c0' :| c' == Seq(c0', c1) && small_step(c0, s, c0', s');
if big_step(c', s', t) {
- var k: nat :| big_step#[k](Seq(c0', c1), s', t);
- var s'' :| Total(s'') && big_step(c0', s', s'') && big_step(c1, s'', t);
- SmallStep_plus_BigStep(c0, s, c0', s', s'');
+ var s'' :| big_step(c0', s', s'') && big_step(c1, s'', t);
}
}
case If(b, thn, els) =>
@@ -391,7 +293,6 @@ inductive lemma SmallStep_plus_BigStep(c: com, s: state, c': com, s': state, t:
// big-step and small-step semantics agree
lemma BigStep_SmallStepStar_Same(c: com, s: state, t: state)
- requires Total(s)
ensures big_step(c, s, t) <==> small_step_star(c, s, SKIP, t)
{
if big_step(c, s, t) {
@@ -403,14 +304,12 @@ lemma BigStep_SmallStepStar_Same(c: com, s: state, t: state)
}
predicate final(c: com, s: state)
- requires Total(s)
{
!exists c',s' :: small_step(c, s, c', s')
}
// lemma 7.17:
lemma final_is_skip(c: com, s: state)
- requires Total(s)
ensures final(c, s) <==> c == SKIP
{
if c == SKIP {
@@ -420,7 +319,7 @@ lemma final_is_skip(c: com, s: state)
}
}
lemma only_skip_has_no_next_state(c: com, s: state) returns (c': com, s': state)
- requires Total(s) && c != SKIP
+ requires c != SKIP
ensures small_step(c, s, c', s')
{
match c
@@ -441,15 +340,12 @@ lemma only_skip_has_no_next_state(c: com, s: state) returns (c': com, s': state)
}
lemma lemma_7_18(c: com, s: state)
- requires Total(s)
ensures (exists t :: big_step(c, s, t)) <==>
- (exists c',s' :: small_step_star(c, s, c', s') &&
- (small_step_star_ends_in_Total_state(c, s, c', s'); final(c', s')))
+ (exists c',s' :: small_step_star(c, s, c', s') && final(c', s'))
{
if exists t :: big_step(c, s, t) {
var t :| big_step(c, s, t);
BigStep_SmallStepStar_Same(c, s, t);
- small_step_star_ends_in_Total_state(c, s, SKIP, t);
calc ==> {
true;
big_step(c, s, t);
@@ -458,11 +354,11 @@ lemma lemma_7_18(c: com, s: state)
small_step_star(c, s, SKIP, t) && final(SKIP, t);
}
}
- if exists c',s' :: small_step_star(c, s, c', s') &&
- (small_step_star_ends_in_Total_state(c, s, c', s'); final(c', s')) {
- var c',s' :| small_step_star(c, s, c', s') &&
- (small_step_star_ends_in_Total_state(c, s, c', s'); final(c', s'));
+ if exists c',s' :: small_step_star(c, s, c', s') && final(c', s') {
+ var c',s' :| small_step_star(c, s, c', s') && final(c', s');
final_is_skip(c', s');
BigStep_SmallStepStar_Same(c, s, s');
}
}
+
+// Autotriggers:0 added as this file relies on proving a property of the form body(f) == f
diff --git a/Test/dafny4/NipkowKlein-chapter7.dfy.expect b/Test/dafny4/NipkowKlein-chapter7.dfy.expect
index e08b3632..90432af3 100644
--- a/Test/dafny4/NipkowKlein-chapter7.dfy.expect
+++ b/Test/dafny4/NipkowKlein-chapter7.dfy.expect
@@ -1,2 +1,2 @@
-Dafny program verifier finished with 54 verified, 0 errors
+Dafny program verifier finished with 49 verified, 0 errors
diff --git a/Test/dafny4/NumberRepresentations.dfy b/Test/dafny4/NumberRepresentations.dfy
index 1ebdf64c..c15f4987 100644
--- a/Test/dafny4/NumberRepresentations.dfy
+++ b/Test/dafny4/NumberRepresentations.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
// We consider a number representation that consists of a sequence of digits. The least
@@ -234,17 +234,16 @@ lemma UniqueRepresentation(a: seq<int>, b: seq<int>, lowDigit: int, base: nat)
}
lemma ZeroIsUnique(a: seq<int>, lowDigit: int, base: nat)
- requires 2 <= base && lowDigit <= 0 < lowDigit + base;
- requires a == trim(a);
- requires IsSkewNumber(a, lowDigit, base);
- requires eval(a, base) == 0;
- ensures a == [];
+ requires 2 <= base && lowDigit <= 0 < lowDigit + base
+ requires a == trim(a)
+ requires IsSkewNumber(a, lowDigit, base)
+ requires eval(a, base) == 0
+ ensures a == []
{
if a != [] {
- assert eval(a, base) == a[0] + base * eval(a[1..], base);
if eval(a[1..], base) == 0 {
TrimProperty(a);
- ZeroIsUnique(a[1..], lowDigit, base);
+ // ZeroIsUnique(a[1..], lowDigit, base);
}
assert false;
}
@@ -293,3 +292,7 @@ lemma MulInverse(x: int, a: int, b: int, y: int)
ensures a == b;
{
}
+
+// Local Variables:
+// dafny-prover-local-args: ("/vcsMaxKeepGoingSplits:5")
+// End:
diff --git a/Test/dafny4/Primes.dfy b/Test/dafny4/Primes.dfy
index 31e3a19b..0c2a64dd 100644
--- a/Test/dafny4/Primes.dfy
+++ b/Test/dafny4/Primes.dfy
@@ -1,9 +1,9 @@
-// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
predicate IsPrime(n: int)
{
- 2 <= n && forall m :: 2 <= m < n ==> n % m != 0
+ 2 <= n && forall m {:nowarn} :: 2 <= m < n ==> n % m != 0 // WISH It would be great to think about the status of modulo as a trigger
}
// The following theorem shows that there is an infinite number of primes
@@ -110,6 +110,13 @@ lemma RemoveFactor(x: int, s: set<int>)
x * y * product(s - {y} - {x});
{ assert s - {y} - {x} == s - {x} - {y}; }
x * y * product(s - {x} - {y});
+ /* FIXME: This annotation wasn't needed before the introduction
+ * of auto-triggers. It's not needed if one adds {:no_trigger}
+ * to the forall y :: y in s ==> y <= x part of PickLargest, but that
+ * boils down to z3 picking $Box(...) as good trigger
+ */
+ // FIXME: the parens shouldn't be needed around (s - {x})
+ { assert y in (s - {x}); }
{ assert y == PickLargest(s - {x}); }
x * product(s - {x});
}
@@ -160,8 +167,8 @@ lemma Composite(c: int) returns (a: int, b: int)
calc {
true;
!IsPrime(c);
- !(2 <= c && forall m :: 2 <= m < c ==> c % m != 0);
- exists m :: 2 <= m < c && c % m == 0;
+ !(2 <= c && forall m {:nowarn} :: 2 <= m < c ==> c % m != 0);
+ exists m {:nowarn} :: 2 <= m < c && c % m == 0;
}
a :| 2 <= a < c && c % a == 0;
b := c / a;
@@ -187,7 +194,7 @@ lemma LargestElementExists(s: set<int>)
var s' := s;
while true
invariant s' != {} && s' <= s;
- invariant forall x,y :: x in s' && y in s - s' ==> y <= x;
+ invariant forall x,y {:nowarn} :: x in s' && y in s - s' ==> y <= x;
decreases s';
{
var x :| x in s'; // pick something
diff --git a/Test/dafny4/Regression0.dfy b/Test/dafny4/Regression0.dfy
new file mode 100644
index 00000000..666d9575
--- /dev/null
+++ b/Test/dafny4/Regression0.dfy
@@ -0,0 +1,13 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This once crashed Dafny
+
+method M() {
+ var s := [1, "2"]; // error: all elements must have the same type
+ if * {
+ assert exists n :: n in s && n != 1; // the type of n is inferred to be int
+ } else {
+ assert "2" in s; // error: since the type of s wasn't determined
+ }
+}
diff --git a/Test/dafny4/Regression0.dfy.expect b/Test/dafny4/Regression0.dfy.expect
new file mode 100644
index 00000000..566b3e3f
--- /dev/null
+++ b/Test/dafny4/Regression0.dfy.expect
@@ -0,0 +1,3 @@
+Regression0.dfy(7,15): Error: All elements of display must be of the same type (got string, but type of previous elements is int)
+Regression0.dfy(11,15): Error: second argument to "in" must be a set, multiset, or sequence with elements of type string, or a map with domain string (instead got ?)
+2 resolution/type errors detected in Regression0.dfy
diff --git a/Test/dafny4/Regression1.dfy b/Test/dafny4/Regression1.dfy
new file mode 100644
index 00000000..ebd8cf6d
--- /dev/null
+++ b/Test/dafny4/Regression1.dfy
@@ -0,0 +1,9 @@
+// RUN: %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+ghost method M() {
+ var x :=
+// In the following line, why are the range and term copied in Substitute?
+// var loo := 100; map y | 0 <= y < 100 :: y+1;
+ var loo := 100; imap y: int | true :: 3;
+}
diff --git a/Test/dafny4/Regression1.dfy.expect b/Test/dafny4/Regression1.dfy.expect
new file mode 100644
index 00000000..069e7767
--- /dev/null
+++ b/Test/dafny4/Regression1.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 2 verified, 0 errors
diff --git a/Test/dafny4/SoftwareFoundations-Basics.dfy.expect b/Test/dafny4/SoftwareFoundations-Basics.dfy.expect
index 0f9eb8d0..f07b068f 100644
--- a/Test/dafny4/SoftwareFoundations-Basics.dfy.expect
+++ b/Test/dafny4/SoftwareFoundations-Basics.dfy.expect
@@ -1,4 +1,4 @@
-SoftwareFoundations-Basics.dfy(41,12): Error: assertion violation
+SoftwareFoundations-Basics.dfy(41,11): Error: assertion violation
Execution trace:
(0,0): anon0
diff --git a/Test/dafny4/UnionFind.dfy b/Test/dafny4/UnionFind.dfy
new file mode 100644
index 00000000..6c4b4cfb
--- /dev/null
+++ b/Test/dafny4/UnionFind.dfy
@@ -0,0 +1,332 @@
+// RUN: %dafny /rprint:"%t.rprint" /autoTriggers:1 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+// Rustan Leino, Nov 2015
+
+// Module M0 gives the high-level specification of the UnionFind data structure
+abstract module M0 {
+ class Element { }
+
+ class {:autocontracts} UnionFind {
+ ghost var M: map<Element, Element>
+ protected predicate Valid()
+ reads this, Repr
+
+ constructor ()
+ ensures M == map[]
+ {
+ Repr, M:= {this}, map[];
+ }
+
+ method New() returns (e: Element)
+ ensures old(e !in M && forall e' :: e' in M ==> M[e'] != e)
+ ensures M == old(M)[e := e]
+
+ method Find(e: Element) returns (r: Element)
+ requires e in M
+ ensures M == old(M) && M[e] == r
+
+ method Union(e0: Element, e1: Element) returns (ghost r: Element)
+ requires e0 in M && e1 in M
+ ensures r in old(M) && old(M[r] == M[e0] || M[r] == M[e1])
+ ensures M == map e | e in old(M) :: old(if M[e] == M[e0] || M[e] == M[e1] then r else M[e])
+ }
+}
+
+// Module M1 gives the concrete data structure used in UnionFind, along with the invariant of
+// that data structure. It also implements the New method and, by declaring a new method Join,
+// the Union method.
+abstract module M1 refines M0 {
+ datatype Contents = Root(depth: nat) | Link(next: Element)
+ class Element {
+ var c: Contents
+ }
+
+ type CMap = map<Element, Contents>
+ predicate GoodCMap(C: CMap)
+ {
+ null !in C && forall f :: f in C && C[f].Link? ==> C[f].next in C
+ }
+
+ class UnionFind {
+ protected predicate Valid...
+ {
+ (forall e :: e in M ==> e in Repr && M[e] in M && M[M[e]] == M[e]) &&
+ (forall e :: e in M && e.c.Link? ==> e.c.next in M) &&
+ (forall e :: e in M ==> M[e].c.Root? && Reaches(M[e].c.depth, e, M[e], Collect()))
+ }
+
+ // This function returns a snapshot of the .c fields of the objects in the domain of M
+ function Collect(): CMap
+ requires null !in M && forall f :: f in M && f.c.Link? ==> f.c.next in M
+ reads this, set a | a in M
+ ensures GoodCMap(Collect())
+ {
+ map e | e in M :: e.c
+ }
+ predicate Reaches(d: nat, e: Element, r: Element, C: CMap)
+ requires GoodCMap(C)
+ requires e in C
+ {
+ match C[e]
+ case Root(_) => e == r
+ case Link(next) => d != 0 && Reaches(d-1, next, r, C)
+ }
+ lemma {:autocontracts false} Reaches_Monotonic(d: nat, e: Element, r: Element, C: CMap, C': CMap)
+ requires GoodCMap(C) && GoodCMap(C')
+ requires e in C
+ requires Reaches(d, e, r, C) && forall f :: f in C ==> f in C' && C[f] == C'[f]
+ ensures Reaches(d, e, r, C')
+ {
+ }
+
+ method New...
+ {
+ e := new Element;
+ e.c := Root(0);
+ Repr := Repr + {e};
+ M := M[e := e];
+ assert Reaches(0, e, e, Collect());
+ forall f | f in M
+ ensures M[f].c.Root? && Reaches(M[f].c.depth, f, M[f], Collect())
+ {
+ if f != e {
+ Reaches_Monotonic(M[f].c.depth, f, M[f], old(Collect()), Collect());
+ }
+ }
+ }
+
+ method Union...
+ {
+ var r0 := Find(e0);
+ var r1 := Find(e1);
+ r := Join(r0, r1);
+ }
+
+ method Join(r0: Element, r1: Element) returns (ghost r: Element)
+ requires r0 in M && r1 in M && M[r0] == r0 && M[r1] == r1
+ ensures r == r0 || r == r1
+ ensures M == map e | e in old(M) :: old(if M[e] == r0 || M[e] == r1 then r else M[e])
+ }
+
+ // stupid help lemma to get around boxing
+ lemma MapsEqual<D>(m: map<D, D>, n: map<D, D>)
+ requires forall d :: d in m <==> d in n;
+ requires forall d :: d in m ==> m[d] == n[d]
+ ensures m == n
+ {
+ }
+}
+
+// Module M2 adds the implementation of Find, together with the proofs needed for the verification.
+abstract module M2 refines M1 {
+ class UnionFind {
+ method Find...
+ {
+ r := FindAux(M[e].c.depth, e);
+ }
+
+ lemma NextReachesSame(e: Element)
+ requires e in M && e.c.Link?
+ ensures M[e] == M[e.c.next]
+ {
+ var next := e.c.next;
+ var d0, d1 := M[e].c.depth, M[next].c.depth;
+ assert Reaches(d0 - 1, next, M[e], Collect());
+ assert Reaches(d1, next, M[next], Collect());
+ Reaches_SinkIsFunctionOfStart(d0 - 1, d1, next, M[e], M[next], Collect());
+ }
+
+ lemma {:autocontracts false} Reaches_SinkIsFunctionOfStart(d0: nat, d1: nat, e: Element, r0: Element, r1: Element, C: CMap)
+ requires GoodCMap(C)
+ requires e in C
+ requires Reaches(d0, e, r0, C) && Reaches(d1, e, r1, C)
+ ensures r0 == r1
+ {
+ }
+
+ method FindAux(ghost d: nat, e: Element) returns (r: Element)
+ requires e in M && Reaches(d, e, M[e], Collect())
+ ensures M == old(M) && M[e] == r
+ ensures forall d: nat, e, r :: e in old(Collect()) && Reaches(d, e, r, old(Collect())) ==> Reaches(d, e, r, Collect())
+ {
+ match e.c
+ case Root(_) =>
+ r := e;
+ case Link(next) =>
+ NextReachesSame(e);
+ r := FindAux(d-1, next);
+ ghost var C := Collect(); // take a snapshot of all the .c fields
+ e.c := Link(r);
+ UpdateMaintainsReaches(d, e, r, C, Collect());
+ }
+
+ lemma {:autocontracts false} UpdateMaintainsReaches(td: nat, tt: Element, tm: Element, C: CMap, C': CMap)
+ requires GoodCMap(C)
+ requires tt in C && Reaches(td, tt, tm, C)
+ requires C' == C[tt := Link(tm)] && C'[tt].Link? && tm in C' && C'[tm].Root?
+ requires null !in C' && forall f :: f in C && C'[f].Link? ==> C'[f].next in C
+ ensures forall d: nat, e, r :: e in C && Reaches(d, e, r, C) ==> Reaches(d, e, r, C')
+ {
+ forall d: nat, e, r | e in C && Reaches(d, e, r, C)
+ ensures Reaches(d, e, r, C')
+ {
+ ConstructReach(d, e, r, C, td, tt, tm, C');
+ }
+ }
+
+ lemma {:autocontracts false} ConstructReach(d: nat, e: Element, r: Element, C: CMap, td: nat, tt: Element, tm: Element, C': CMap)
+ requires GoodCMap(C)
+ requires e in C
+ requires Reaches(d, e, r, C)
+ requires tt in C && Reaches(td, tt, tm, C);
+ requires C' == C[tt := Link(tm)] && C'[tt].Link? && tm in C' && C'[tm].Root?
+ requires GoodCMap(C')
+ ensures Reaches(d, e, r, C')
+ {
+ if e == tt {
+ Reaches_SinkIsFunctionOfStart(d, td, e, r, tm, C);
+ } else {
+ match C[e]
+ case Root(_) =>
+ case Link(next) =>
+ ConstructReach(d-1, next, r, C, td, tt, tm, C');
+ }
+ }
+ }
+}
+
+// Finally, module M3 adds the implementation of Join, along with what's required to
+// verify its correctness.
+module M3 refines M2 {
+ class UnionFind {
+ method Join...
+ {
+ if r0 == r1 {
+ r := r0;
+ MapsEqual(M, map e | e in M :: if M[e] == r0 || M[e] == r1 then r else M[e]);
+ return;
+ } else if r0.c.depth < r1.c.depth {
+ r0.c := Link(r1);
+ r := r1;
+ M := map e | e in M :: if M[e] == r0 || M[e] == r1 then r else M[e];
+ forall e | e in Collect()
+ ensures M[e].c.Root? && Reaches(M[e].c.depth, e, M[e], Collect())
+ {
+ JoinMaintainsReaches0(r0, r1, old(Collect()), Collect());
+ assert Reaches(old(M[e].c).depth, e, old(M)[e], old(Collect()));
+ }
+ } else if r1.c.depth < r0.c.depth {
+ r1.c := Link(r0);
+ r := r0;
+ M := map e | e in M :: if M[e] == r0 || M[e] == r1 then r else M[e];
+ forall e | e in Collect()
+ ensures M[e].c.Root? && Reaches(M[e].c.depth, e, M[e], Collect())
+ {
+ JoinMaintainsReaches0(r1, r0, old(Collect()), Collect());
+ assert Reaches(old(M[e].c).depth, e, old(M)[e], old(Collect()));
+ }
+ } else {
+ r0.c := Link(r1);
+ r1.c := Root(r1.c.depth + 1);
+ r := r1;
+ M := map e | e in M :: if M[e] == r0 || M[e] == r1 then r else M[e];
+ forall e | e in Collect()
+ ensures M[e].c.Root? && Reaches(M[e].c.depth, e, M[e], Collect())
+ {
+ JoinMaintainsReaches1(r0, r1, old(Collect()), Collect());
+ assert Reaches(old(M[e].c).depth, e, old(M)[e], old(Collect()));
+ }
+ }
+ }
+
+ lemma {:autocontracts false} JoinMaintainsReaches1(r0: Element, r1: Element, C: CMap, C': CMap)
+ requires GoodCMap(C)
+ requires r0 in C && r1 in C && C[r0].Root? && C[r1].Root? && C[r0].depth == C[r1].depth && r0 != r1
+ requires C' == C[r0 := Link(r1)][r1 := Root(C[r1].depth + 1)]
+ requires GoodCMap(C')
+ ensures forall d: nat, e, r :: e in C && Reaches(d, e, r, C) && r != r0 && r != r1 ==> Reaches(d, e, r, C') // proved automatically by induction
+ ensures forall e :: e in C && Reaches(C[r0].depth, e, r0, C) ==> Reaches(C'[r1].depth, e, r1, C')
+ ensures forall e :: e in C && Reaches(C[r1].depth, e, r1, C) ==> Reaches(C'[r1].depth, e, r1, C')
+ {
+ forall e | e in C && Reaches(C[r0].depth, e, r0, C)
+ ensures Reaches(C'[r1].depth, e, r1, C')
+ {
+ ExtendedReach'(e, C, C[r0].depth, C'[r1].depth, r0, r1, C');
+ }
+ forall e | e in C && Reaches(C[r1].depth, e, r1, C)
+ ensures Reaches(C'[r1].depth, e, r1, C')
+ {
+ ReachUnaffectedByChangeFromRoot'(C[r1].depth, e, r1, C, C[r0].depth, r0, r1, C');
+ }
+ }
+
+ lemma {:autocontracts false} ReachUnaffectedByChangeFromRoot'(d: nat, e: Element, r: Element, C: CMap, td: nat, r0: Element, r1: Element, C': CMap)
+ requires GoodCMap(C)
+ requires e in C && Reaches(d, e, r, C)
+ requires r0 in C && r1 in C && C[r0].Root? && C[r1].Root? && C[r0].depth == C[r1].depth && r0 != r1
+ requires C[r0].Root? && C' == C[r0 := Link(r1)][r1 := Root(C[r1].depth + 1)]
+ requires Reaches(td, r0, r0, C) && r0 != r
+ requires GoodCMap(C')
+ ensures Reaches(d+1, e, r, C')
+ {
+ }
+
+ lemma {:autocontracts false} ExtendedReach'(e: Element, C: CMap, d0: nat, d1: nat, r0: Element, r1: Element, C': CMap)
+ requires GoodCMap(C) && GoodCMap(C')
+ requires r0 in C && r1 in C && C[r0].Root? && C[r1].Root? && C[r0].depth == C[r1].depth && r0 != r1
+ requires C' == C[r0 := Link(r1)][r1 := Root(C[r1].depth + 1)]
+ requires C[r0].Root? && d0 <= C[r0].depth && C[r1].Root? && d1 <= C'[r1].depth && d0 < d1
+ requires e in C && Reaches(d0, e, r0, C)
+ ensures Reaches(d1, e, r1, C')
+ {
+ match C[e]
+ case Root(_) =>
+ case Link(next) =>
+ ExtendedReach'(next, C, d0-1, d1-1, r0, r1, C');
+ }
+
+ lemma {:autocontracts false} JoinMaintainsReaches0(r0: Element, r1: Element, C: CMap, C': CMap)
+ requires GoodCMap(C)
+ requires r0 in C && r1 in C && C[r0].Root? && C[r1].Root? && C[r0].depth < C[r1].depth
+ requires C' == C[r0 := Link(r1)]
+ requires GoodCMap(C')
+ ensures forall d: nat, e, r :: e in C && Reaches(d, e, r, C) && r != r0 ==> Reaches(d, e, r, C')
+ ensures forall e :: e in C && Reaches(C[r0].depth, e, r0, C) ==> Reaches(C[r1].depth, e, r1, C')
+ {
+ forall d: nat, e, r | e in C && Reaches(d, e, r, C) && r != r0
+ ensures Reaches(d, e, r, C')
+ {
+ ReachUnaffectedByChangeFromRoot(d, e, r, C, C[r0].depth, r0, r1, C');
+ }
+ forall e | e in C && Reaches(C[r0].depth, e, r0, C)
+ ensures Reaches(C[r1].depth, e, r1, C')
+ {
+ ExtendedReach(e, C, C[r0].depth, C[r1].depth, r0, r1, C');
+ }
+ }
+
+ lemma {:autocontracts false} ReachUnaffectedByChangeFromRoot(d: nat, e: Element, r: Element, C: CMap, td: nat, tt: Element, tm: Element, C': CMap)
+ requires GoodCMap(C)
+ requires e in C && Reaches(d, e, r, C)
+ requires tt in C && Reaches(td, tt, tt, C) && tt != r
+ requires C[tt].Root? && C' == C[tt := Link(tm)]
+ requires GoodCMap(C')
+ ensures Reaches(d, e, r, C')
+ {
+ }
+
+ lemma {:autocontracts false} ExtendedReach(e: Element, C: CMap, d0: nat, d1: nat, r0: Element, r1: Element, C': CMap)
+ requires GoodCMap(C) && GoodCMap(C')
+ requires r0 in C && r1 in C && r0 != r1
+ requires C' == C[r0 := Link(r1)]
+ requires C[r0].Root? && d0 <= C[r0].depth && C[r1].Root? && d1 <= C[r1].depth && d0 < d1
+ requires e in C && Reaches(d0, e, r0, C)
+ ensures Reaches(d1, e, r1, C')
+ {
+ match C[e]
+ case Root(_) =>
+ case Link(next) =>
+ ExtendedReach(next, C, d0-1, d1-1, r0, r1, C');
+ }
+ }
+}
diff --git a/Test/dafny4/UnionFind.dfy.expect b/Test/dafny4/UnionFind.dfy.expect
new file mode 100644
index 00000000..8c2a9667
--- /dev/null
+++ b/Test/dafny4/UnionFind.dfy.expect
@@ -0,0 +1,3 @@
+
+Dafny program verifier finished with 89 verified, 0 errors
+Compiled assembly into UnionFind.dll
diff --git a/Test/dafny4/set-compr.dfy b/Test/dafny4/set-compr.dfy
index 71a07f3d..d093a924 100644
--- a/Test/dafny4/set-compr.dfy
+++ b/Test/dafny4/set-compr.dfy
@@ -22,7 +22,7 @@ method O() returns (ghost p: set<object>)
method P() returns (p: set<object>)
{
- p := set o: object | true; // not allowed -- not in a ghost context
+ p := set o: object | true; // error: not (easily) compilable
}
ghost method Q() returns (p: set<object>)
@@ -30,26 +30,54 @@ ghost method Q() returns (p: set<object>)
p := set o: object | true; // allowed, since the whole method is ghost
}
-function F(): int
+function F(p: object): int
+ requires p in set o: object | true // error: function is not allowed to depend on allocation state
+ ensures p in set o: object | true // error: ditto (although one could argue that this would be okay)
+ reads set o: object | true // error: same as for 'requires'
+ decreases set o: object | true // error: same as for 'ensures'
+{
+ if p in set o: object | true then // error: function is not allowed to depend on allocation state
+ F(p)
+ else
+ 0
+}
+
+function method G(p: object): int
+ requires p in set o: object | true // error (see F)
+ ensures p in set o: object | true // error (see F)
+ reads set o: object | true // error (see F)
+ decreases set o: object | true // error (see F)
+{
+ if p in set o: object | true then // error (see F)
+ G(p)
+ else
+ 0
+}
+
+method M0() returns (ghost r: int, s: int)
requires null in set o: object | true // allowed
ensures null in set o: object | true // allowed
- reads set o: object | true // allowed
+ modifies set o: object | true // allowed
decreases set o: object | true // allowed
{
- if null in set o: object | true then // allowed -- in a ghost context
- F()
- else
- 0
+ if null in set o: object | true { // this makes the "if" a ghost
+ r := G(null);
+ s := G(null); // error: assignment of non-ghost not allowed inside ghost "if"
+ } else {
+ r := 0;
+ }
}
-function method G(): int
+method M1() returns (ghost r: int, s: int)
requires null in set o: object | true // (X) allowed
ensures null in set o: object | true // (X) allowed
- reads set o: object | true // allowed
+ modifies set o: object | true // allowed
decreases set o: object | true // (X) allowed
{
- if null in set o: object | true then // not allowed, since this is not a ghost context
- G()
- else
- 0
+ if null in set o: object | true { // this makes the "if" a ghost
+ r := G(null);
+ s := G(null); // error: assignment of non-ghost not allowed inside ghost "if"
+ } else {
+ r := 0;
+ }
}
diff --git a/Test/dafny4/set-compr.dfy.expect b/Test/dafny4/set-compr.dfy.expect
index b31c6ac0..b0490a11 100644
--- a/Test/dafny4/set-compr.dfy.expect
+++ b/Test/dafny4/set-compr.dfy.expect
@@ -1,3 +1,14 @@
-set-compr.dfy(25,7): Error: a set comprehension must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'o'
-set-compr.dfy(51,13): Error: a set comprehension must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'o'
-2 resolution/type errors detected in set-compr.dfy
+set-compr.dfy(25,7): Error: set comprehensions in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'o'
+set-compr.dfy(34,16): Error: a set comprehension involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'o'
+set-compr.dfy(35,15): Error: a set comprehension involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'o'
+set-compr.dfy(36,8): Error: a set comprehension involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'o'
+set-compr.dfy(37,12): Error: a set comprehension involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'o'
+set-compr.dfy(39,10): Error: a set comprehension involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'o'
+set-compr.dfy(46,16): Error: a set comprehension involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'o'
+set-compr.dfy(47,15): Error: a set comprehension involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'o'
+set-compr.dfy(48,8): Error: a set comprehension involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'o'
+set-compr.dfy(49,12): Error: a set comprehension involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'o'
+set-compr.dfy(51,10): Error: a set comprehension involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'o'
+set-compr.dfy(65,6): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+set-compr.dfy(79,6): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)
+13 resolution/type errors detected in set-compr.dfy
diff --git a/Test/hofs/Apply.dfy.expect b/Test/hofs/Apply.dfy.expect
index 77d34c4c..0a923143 100644
--- a/Test/hofs/Apply.dfy.expect
+++ b/Test/hofs/Apply.dfy.expect
@@ -1,4 +1,4 @@
-Apply.dfy(27,16): Error: assertion violation
+Apply.dfy(27,15): Error: assertion violation
Execution trace:
(0,0): anon0
Apply.dfy(26,27): anon15_Else
diff --git a/Test/hofs/Classes.dfy b/Test/hofs/Classes.dfy
index 2b892b35..9d8044db 100644
--- a/Test/hofs/Classes.dfy
+++ b/Test/hofs/Classes.dfy
@@ -30,15 +30,14 @@ function B(t : T) : int -> int
}
function J(t : T) : int
- requires t != null;
- requires t.h.reads(0) == {};
- reads t;
- reads if t != null then t.h.reads(0) else {};
+ requires t != null
+ reads t
+ reads t.h.reads(0)
{
if t.h.requires(0) then
B(t)(0)
else
- B(t)(0) // fail
+ B(t)(0) // error: precondition violation
}
method U(t : T)
@@ -48,3 +47,20 @@ method U(t : T)
t.h := x => x;
assert J(t) == 0; // ok
}
+
+class MyClass {
+ var data: int
+ function method F(): int
+ reads this
+ {
+ data
+ }
+ method M(that: MyClass)
+ requires that != null
+ {
+ var fn := that.F; // "that" is captured into the closure
+ var d := fn();
+ assert d == that.data; // yes
+ assert d == this.data; // error: no reason to believe that this would hold
+ }
+}
diff --git a/Test/hofs/Classes.dfy.expect b/Test/hofs/Classes.dfy.expect
index 3c933bae..a5b33522 100644
--- a/Test/hofs/Classes.dfy.expect
+++ b/Test/hofs/Classes.dfy.expect
@@ -1,10 +1,10 @@
-Classes.dfy(41,6): Error: possible violation of function precondition
+Classes.dfy(64,11): Error: assertion violation
Execution trace:
(0,0): anon0
- (0,0): anon11_Then
- (0,0): anon3
- (0,0): anon12_Then
- (0,0): anon13_Else
- (0,0): anon14_Else
+Classes.dfy(40,5): Error: possible violation of function precondition
+Execution trace:
+ (0,0): anon0
+ (0,0): anon7_Else
+ (0,0): anon8_Else
-Dafny program verifier finished with 6 verified, 1 error
+Dafny program verifier finished with 8 verified, 2 errors
diff --git a/Test/hofs/Examples.dfy b/Test/hofs/Examples.dfy
index be2672f5..306d278d 100644
--- a/Test/hofs/Examples.dfy
+++ b/Test/hofs/Examples.dfy
@@ -1,14 +1,14 @@
// RUN: %dafny /print:"%t.print" "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
-function Apply(f: A -> B, x: A): B
+function Apply<A,B>(f: A -> B, x: A): B
reads f.reads(x);
requires f.requires(x);
{
f(x)
}
-function Apply'(f: A -> B) : A -> B
+function Apply'<A,B>(f: A -> B) : A -> B
{
x reads f.reads(x)
requires f.requires(x)
@@ -16,7 +16,7 @@ function Apply'(f: A -> B) : A -> B
}
-function Compose(f: B -> C, g:A -> B): A -> C
+function Compose<A,B,C>(f: B -> C, g:A -> B): A -> C
{
x reads g.reads(x)
reads if g.requires(x) then f.reads(g(x)) else {}
@@ -25,21 +25,21 @@ function Compose(f: B -> C, g:A -> B): A -> C
=> f(g(x))
}
-function W(f : (A,A) -> A): A -> A
+function W<A>(f : (A,A) -> A): A -> A
{
x requires f.requires(x,x)
reads f.reads(x,x)
=> f(x,x)
}
-function Curry(f : (A,B) -> C) : A -> B -> C
+function Curry<A,B,C>(f : (A,B) -> C) : A -> B -> C
{
x => y requires f.requires(x,y)
reads f.reads(x,y)
=> f(x,y)
}
-function Uncurry(f : A -> B -> C) : (A,B) -> C
+function Uncurry<A,B,C>(f : A -> B -> C) : (A,B) -> C
{
(x,y) requires f.requires(x)
requires f(x).requires(y)
@@ -48,7 +48,7 @@ function Uncurry(f : A -> B -> C) : (A,B) -> C
=> f(x)(y)
}
-function S(f : (A,B) -> C, g : A -> B): A -> C
+function S<A,B,C>(f : (A,B) -> C, g : A -> B): A -> C
{
x requires g.requires(x)
requires f.requires(x,g(x))
diff --git a/Test/hofs/Field.dfy.expect b/Test/hofs/Field.dfy.expect
index 9f6998f5..0859d83c 100644
--- a/Test/hofs/Field.dfy.expect
+++ b/Test/hofs/Field.dfy.expect
@@ -1,13 +1,13 @@
-Field.dfy(12,12): Error: possible violation of function precondition
+Field.dfy(12,11): Error: possible violation of function precondition
Execution trace:
(0,0): anon0
-Field.dfy(12,15): Error: assertion violation
+Field.dfy(12,14): Error: assertion violation
Execution trace:
(0,0): anon0
-Field.dfy(21,12): Error: possible violation of function precondition
+Field.dfy(21,11): Error: possible violation of function precondition
Execution trace:
(0,0): anon0
-Field.dfy(21,14): Error: assertion violation
+Field.dfy(21,13): Error: assertion violation
Execution trace:
(0,0): anon0
diff --git a/Test/hofs/FnRef.dfy.expect b/Test/hofs/FnRef.dfy.expect
index 0f6f2aa9..e665c830 100644
--- a/Test/hofs/FnRef.dfy.expect
+++ b/Test/hofs/FnRef.dfy.expect
@@ -1,19 +1,19 @@
-FnRef.dfy(17,45): Error: possible violation of function precondition
+FnRef.dfy(17,44): Error: possible violation of function precondition
Execution trace:
(0,0): anon0
FnRef.dfy(15,12): anon5_Else
(0,0): anon6_Then
-FnRef.dfy(32,8): Error: possible violation of function precondition
+FnRef.dfy(32,7): Error: possible violation of function precondition
Execution trace:
(0,0): anon0
FnRef.dfy(26,12): anon9_Else
FnRef.dfy(28,8): anon10_Else
-FnRef.dfy(46,12): Error: assertion violation
+FnRef.dfy(46,11): Error: assertion violation
Execution trace:
(0,0): anon0
FnRef.dfy(43,12): anon7_Else
(0,0): anon9_Then
-FnRef.dfy(65,14): Error: assertion violation
+FnRef.dfy(65,13): Error: assertion violation
Execution trace:
(0,0): anon0
FnRef.dfy(56,12): anon8_Else
diff --git a/Test/hofs/Fold.dfy b/Test/hofs/Fold.dfy
index 6ca2d3b1..9bcd9e02 100644
--- a/Test/hofs/Fold.dfy
+++ b/Test/hofs/Fold.dfy
@@ -13,7 +13,7 @@ function method Eval(e : Expr): int
case Lit(i) => i
}
-function method Fold(xs : List<A>, unit : B, f : (A,B) -> B): B
+function method Fold<A,B>(xs : List<A>, unit : B, f : (A,B) -> B): B
reads f.reads;
requires forall x, y :: x < xs ==> f.requires(x,y);
{
diff --git a/Test/hofs/Frame.dfy.expect b/Test/hofs/Frame.dfy.expect
index 0ee2eadb..9964deb4 100644
--- a/Test/hofs/Frame.dfy.expect
+++ b/Test/hofs/Frame.dfy.expect
@@ -1,35 +1,35 @@
-Frame.dfy(23,16): Error: assertion violation
+Frame.dfy(23,15): Error: assertion violation
Execution trace:
(0,0): anon0
Frame.dfy(19,12): anon5_Else
(0,0): anon6_Then
-Frame.dfy(37,14): Error: assertion violation
+Frame.dfy(37,13): Error: assertion violation
Execution trace:
(0,0): anon0
Frame.dfy(33,12): anon3_Else
-Frame.dfy(63,23): Error: assertion violation
+Frame.dfy(63,22): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon13_Then
Frame.dfy(55,12): anon14_Else
(0,0): anon15_Then
(0,0): anon5
-Frame.dfy(66,19): Error: insufficient reads clause to read array element
+Frame.dfy(66,18): Error: insufficient reads clause to read array element
Execution trace:
(0,0): anon0
(0,0): anon16_Then
(0,0): anon17_Then
-Frame.dfy(68,28): Error: insufficient reads clause to read array element
+Frame.dfy(68,27): Error: insufficient reads clause to read array element
Execution trace:
(0,0): anon0
(0,0): anon16_Else
(0,0): anon18_Then
-Frame.dfy(123,14): Error: possible violation of function precondition
+Frame.dfy(123,13): Error: possible violation of function precondition
Execution trace:
(0,0): anon0
(0,0): anon5_Then
(0,0): anon6_Else
-Frame.dfy(123,19): Error: assertion violation
+Frame.dfy(123,18): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon5_Then
diff --git a/Test/hofs/Lambda.dfy.expect b/Test/hofs/Lambda.dfy.expect
index 4fe8275f..ab57fbe0 100644
--- a/Test/hofs/Lambda.dfy.expect
+++ b/Test/hofs/Lambda.dfy.expect
@@ -1,4 +1,4 @@
-Lambda.dfy(24,12): Error: assertion violation
+Lambda.dfy(24,11): Error: assertion violation
Execution trace:
(0,0): anon0
Lambda.dfy(6,24): anon31_Else
diff --git a/Test/hofs/LambdaParsefail.dfy.expect b/Test/hofs/LambdaParsefail.dfy.expect
index 11deb9b0..a72fc978 100644
--- a/Test/hofs/LambdaParsefail.dfy.expect
+++ b/Test/hofs/LambdaParsefail.dfy.expect
@@ -1,6 +1,6 @@
-LambdaParsefail.dfy(5,19): error: this symbol not expected in VarDeclStatement
-LambdaParsefail.dfy(6,19): error: this symbol not expected in VarDeclStatement
-LambdaParsefail.dfy(7,21): error: this symbol not expected in VarDeclStatement
-LambdaParsefail.dfy(8,15): error: cannot declare identifier beginning with underscore
-LambdaParsefail.dfy(9,17): error: this symbol not expected in VarDeclStatement
+LambdaParsefail.dfy(5,18): Error: this symbol not expected in VarDeclStatement
+LambdaParsefail.dfy(6,18): Error: this symbol not expected in VarDeclStatement
+LambdaParsefail.dfy(7,20): Error: this symbol not expected in VarDeclStatement
+LambdaParsefail.dfy(8,14): Error: cannot declare identifier beginning with underscore
+LambdaParsefail.dfy(9,16): Error: this symbol not expected in VarDeclStatement
5 parse errors detected in LambdaParsefail.dfy
diff --git a/Test/hofs/LambdaParsefail2.dfy.expect b/Test/hofs/LambdaParsefail2.dfy.expect
index 0c9ecb83..1a6a65dc 100644
--- a/Test/hofs/LambdaParsefail2.dfy.expect
+++ b/Test/hofs/LambdaParsefail2.dfy.expect
@@ -1,2 +1,2 @@
-LambdaParsefail2.dfy(6,39): error: invalid LambdaArrow
+LambdaParsefail2.dfy(6,38): Error: invalid LambdaArrow
1 parse errors detected in LambdaParsefail2.dfy
diff --git a/Test/hofs/Monads.dfy b/Test/hofs/Monads.dfy
index 3598d2b3..633dd339 100644
--- a/Test/hofs/Monads.dfy
+++ b/Test/hofs/Monads.dfy
@@ -4,29 +4,29 @@
abstract module Monad {
type M<A>
- function method Return(x: A): M<A>
- function method Bind(m: M<A>, f:A -> M<B>):M<B>
- reads f.reads;
- requires forall a :: f.requires(a);
+ function method Return<A>(x: A): M<A>
+ function method Bind<A,B>(m: M<A>, f:A -> M<B>):M<B>
+ reads f.reads
+ requires forall a :: f.requires(a)
// return x >>= f = f x
- lemma LeftIdentity(x : A, f : A -> M<B>)
- requires forall a :: f.requires(a);
- ensures Bind(Return(x),f) == f(x);
+ lemma LeftIdentity<A,B>(x : A, f : A -> M<B>)
+ requires forall a :: f.requires(a)
+ ensures Bind(Return(x),f) == f(x)
// m >>= return = m
- lemma RightIdentity(m : M<A>)
- ensures Bind(m,Return) == m;
+ lemma RightIdentity<A>(m : M<A>)
+ ensures Bind(m,Return) == m
// (m >>= f) >>= g = m >>= (x => f(x) >>= g)
- lemma Associativity(m : M<A>, f:A -> M<B>, g: B -> M<C>)
- requires forall a :: f.requires(a);
- requires forall b :: g.requires(b);
+ lemma Associativity<A,B,C>(m : M<A>, f:A -> M<B>, g: B -> M<C>)
+ requires forall a :: f.requires(a)
+ requires forall b :: g.requires(b)
ensures Bind(Bind(m,f),g) ==
Bind(m,x reads f.reads(x)
reads g.reads
requires f.requires(x)
- requires forall b :: g.requires(b) => Bind(f(x),g));
+ requires forall b :: g.requires(b) => Bind(f(x),g))
}
module Identity refines Monad {
@@ -101,21 +101,21 @@ module List refines Monad {
function method Return<A>(x: A): M<A>
{ Cons(x,Nil) }
- function method Concat(xs: M<A>, ys: M<A>): M<A>
+ function method Concat<A>(xs: M<A>, ys: M<A>): M<A>
{
match xs
case Nil => ys
case Cons(x,xs) => Cons(x,Concat(xs,ys))
}
- function method Join(xss: M<M<A>>) : M<A>
+ function method Join<A>(xss: M<M<A>>) : M<A>
{
match xss
case Nil => Nil
case Cons(xs,xss) => Concat(xs,Join(xss))
}
- function method Map(xs: M<A>, f: A -> B):M<B>
+ function method Map<A,B>(xs: M<A>, f: A -> B):M<B>
reads f.reads;
requires forall a :: f.requires(a);
{
@@ -170,7 +170,7 @@ module List refines Monad {
ensures Concat(Concat(xs,ys),zs) == Concat(xs,Concat(ys,zs));
{}
- lemma BindMorphism(xs : M<A>, ys: M<A>, f : A -> M<B>)
+ lemma BindMorphism<A,B>(xs : M<A>, ys: M<A>, f : A -> M<B>)
requires forall a :: f.requires(a);
ensures Bind(Concat(xs,ys),f) == Concat(Bind(xs,f),Bind(ys,f));
{
diff --git a/Test/hofs/Naked.dfy b/Test/hofs/Naked.dfy
index fa99377f..d23eb507 100644
--- a/Test/hofs/Naked.dfy
+++ b/Test/hofs/Naked.dfy
@@ -19,17 +19,17 @@ module Functions {
module Requires {
function t(x: nat): nat
- requires !t.requires(x);
+ requires !t.requires(x); // error: use of naked function in its own SCC
{ x }
function g(x: nat): nat
- requires !(g).requires(x);
+ requires !(g).requires(x); // error: use of naked function in its own SCC
{ x }
- function g2(x: int): int { h(x) }
-
+ function D(x: int): int // used so termination errors don't mask other errors
+ function g2(x: int): int decreases D(x) { h(x) } // error: precondition violation
function h(x: int): int
- requires !g2.requires(x);
+ requires !g2.requires(x); // error: use of naked function in its own SCC
{ x }
}
diff --git a/Test/hofs/Naked.dfy.expect b/Test/hofs/Naked.dfy.expect
index 62c035b2..9794478d 100644
--- a/Test/hofs/Naked.dfy.expect
+++ b/Test/hofs/Naked.dfy.expect
@@ -1,50 +1,46 @@
-Naked.dfy(9,16): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
+Naked.dfy(9,15): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
Execution trace:
(0,0): anon0
- (0,0): anon7_Else
- (0,0): anon8_Else
- (0,0): anon9_Then
-Naked.dfy(12,8): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
-Execution trace:
- (0,0): anon0
- (0,0): anon7_Else
- (0,0): anon8_Else
(0,0): anon9_Else
-Naked.dfy(17,53): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
+ (0,0): anon10_Else
+ (0,0): anon11_Then
+Naked.dfy(12,7): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
- (0,0): anon6_Else
-Naked.dfy(22,13): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
+ (0,0): anon9_Else
+ (0,0): anon10_Else
+ (0,0): anon11_Else
+Naked.dfy(17,52): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
Execution trace:
(0,0): anon0
-Naked.dfy(26,14): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
+ (0,0): anon7_Else
+ (0,0): anon8_Else
+Naked.dfy(22,12): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
Execution trace:
(0,0): anon0
-Naked.dfy(29,30): Error: cannot prove termination; try supplying a decreases clause
+Naked.dfy(26,13): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-Naked.dfy(29,30): Error: possible violation of function precondition
-Naked.dfy(32,14): Related location
+Naked.dfy(30,44): Error: possible violation of function precondition
+Naked.dfy(32,13): Related location
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-Naked.dfy(32,15): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
+ (0,0): anon4_Else
+Naked.dfy(32,14): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
Execution trace:
(0,0): anon0
-Naked.dfy(38,9): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
+Naked.dfy(38,8): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
Execution trace:
(0,0): anon0
-Naked.dfy(42,10): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
+Naked.dfy(42,9): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
Execution trace:
(0,0): anon0
-Naked.dfy(45,30): Error: cannot prove termination; try supplying a decreases clause
+Naked.dfy(45,29): Error: cannot prove termination; try supplying a decreases clause
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-Naked.dfy(48,11): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
+ (0,0): anon4_Else
+Naked.dfy(48,10): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
Execution trace:
(0,0): anon0
-Dafny program verifier finished with 1 verified, 12 errors
+Dafny program verifier finished with 2 verified, 11 errors
diff --git a/Test/hofs/OneShot.dfy b/Test/hofs/OneShot.dfy
index 286be898..e920530a 100644
--- a/Test/hofs/OneShot.dfy
+++ b/Test/hofs/OneShot.dfy
@@ -10,16 +10,15 @@ method OneShot() {
var i : Ref<int>;
i := new Ref;
- g := () -> true;
-
+ g := () reads i -> true; // using a (deprecated) one-shot arrow here means "g" acquires
+ // a precondition that says it can only be applied in this heap
assert g();
i.val := i.val + 1; // heap changes
if * {
- assert g(); // should fail
+ assert g(); // error: precondition violation
} else {
- assert !g(); // should fail
+ assert !g(); // error: precondition violation
}
}
-
diff --git a/Test/hofs/OneShot.dfy.expect b/Test/hofs/OneShot.dfy.expect
index 91b931b8..0b4a2bb8 100644
--- a/Test/hofs/OneShot.dfy.expect
+++ b/Test/hofs/OneShot.dfy.expect
@@ -1,16 +1,16 @@
-OneShot.dfy(20,12): Error: possible violation of function precondition
+OneShot.dfy(20,11): Error: possible violation of function precondition
Execution trace:
(0,0): anon0
(0,0): anon5_Then
OneShot.dfy(13,8): anon5_Else
(0,0): anon6_Then
-OneShot.dfy(22,12): Error: assertion violation
+OneShot.dfy(22,11): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon5_Then
OneShot.dfy(13,8): anon5_Else
(0,0): anon6_Else
-OneShot.dfy(22,13): Error: possible violation of function precondition
+OneShot.dfy(22,12): Error: possible violation of function precondition
Execution trace:
(0,0): anon0
(0,0): anon5_Then
diff --git a/Test/hofs/ReadsReads.dfy b/Test/hofs/ReadsReads.dfy
index e11473bd..60ac35f5 100644
--- a/Test/hofs/ReadsReads.dfy
+++ b/Test/hofs/ReadsReads.dfy
@@ -2,58 +2,58 @@
// RUN: %diff "%s.expect" "%t"
module ReadsRequiresReads {
- function MyReadsOk(f : A -> B, a : A) : set<object>
- reads f.reads(a);
+ function MyReadsOk<A,B>(f : A -> B, a : A) : set<object>
+ reads f.reads(a)
{
f.reads(a)
}
- function MyReadsOk2(f : A -> B, a : A) : set<object>
- reads f.reads(a);
+ function MyReadsOk2<A,B>(f : A -> B, a : A) : set<object>
+ reads f.reads(a)
{
(f.reads)(a)
}
- function MyReadsOk3(f : A -> B, a : A) : set<object>
- reads (f.reads)(a);
+ function MyReadsOk3<A,B>(f : A -> B, a : A) : set<object>
+ reads (f.reads)(a)
{
f.reads(a)
}
- function MyReadsOk4(f : A -> B, a : A) : set<object>
- reads (f.reads)(a);
+ function MyReadsOk4<A,B>(f : A -> B, a : A) : set<object>
+ reads (f.reads)(a)
{
(f.reads)(a)
}
- function MyReadsBad(f : A -> B, a : A) : set<object>
+ function MyReadsBad<A,B>(f : A -> B, a : A) : set<object>
{
f.reads(a) // error: MyReadsBad does not have permission to read what f.reads(a) reads
}
- function MyReadsBad2(f : A -> B, a : A) : set<object>
+ function MyReadsBad2<A,B>(f : A -> B, a : A) : set<object>
{
(f.reads)(a) // error: MyReadsBad2 does not have permission to read what f.reads(a) reads
}
- function MyReadsOk'(f : A -> B, a : A, o : object) : bool
- reads f.reads(a);
+ function MyReadsOk'<A,B>(f : A -> B, a : A, o : object) : bool
+ reads f.reads(a)
{
o in f.reads(a)
}
- function MyReadsBad'(f : A -> B, a : A, o : object) : bool
+ function MyReadsBad'<A,B>(f : A -> B, a : A, o : object) : bool
{
o in f.reads(a) // error: MyReadsBad' does not have permission to read what f.reads(a) reads
}
- function MyRequiresOk(f : A -> B, a : A) : bool
- reads f.reads(a);
+ function MyRequiresOk<A,B>(f : A -> B, a : A) : bool
+ reads f.reads(a)
{
f.requires(a)
}
- function MyRequiresBad(f : A -> B, a : A) : bool
+ function MyRequiresBad<A,B>(f : A -> B, a : A) : bool
{
f.requires(a) // error: MyRequiresBad does not have permission to read what f.requires(a) reads
}
@@ -72,11 +72,11 @@ module WhatWeKnowAboutReads {
}
class S {
- var s : S;
+ var s : S
}
function ReadsSomething(s : S):()
- reads s;
+ reads s
{()}
method MaybeSomething() {
@@ -105,29 +105,29 @@ module WhatWeKnowAboutReads {
module ReadsAll {
function A(f: int -> int) : int
- reads set o,x | o in f.reads(x) :: o;
- requires forall x :: f.requires(x);
+ reads set x,o | o in f.reads(x) :: o // note, with "set o,x ..." instead, Dafny complains (this is perhaps less than ideal)
+ requires forall x :: f.requires(x)
{
f(0) + f(1) + f(2)
}
function method B(f: int -> int) : int
- reads set o,x | o in f.reads(x) :: o;
- requires forall x :: f.requires(x);
+ reads set x,o | o in f.reads(x) :: o // note, with "set o,x ..." instead, Dafny complains (this is perhaps less than ideal)
+ requires forall x :: f.requires(x)
{
f(0) + f(1) + f(2)
}
function C(f: int -> int) : int
- reads f.reads;
- requires forall x :: f.requires(x);
+ reads f.reads
+ requires forall x :: f.requires(x)
{
f(0) + f(1) + f(2)
}
function method D(f: int -> int) : int
- reads f.reads;
- requires forall x :: f.requires(x);
+ reads f.reads
+ requires forall x :: f.requires(x)
{
f(0) + f(1) + f(2)
}
diff --git a/Test/hofs/ReadsReads.dfy.expect b/Test/hofs/ReadsReads.dfy.expect
index 73002b73..0a374c44 100644
--- a/Test/hofs/ReadsReads.dfy.expect
+++ b/Test/hofs/ReadsReads.dfy.expect
@@ -1,33 +1,33 @@
-ReadsReads.dfy(31,7): Error: insufficient reads clause to invoke function
+ReadsReads.dfy(31,6): Error: insufficient reads clause to invoke function
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-ReadsReads.dfy(36,5): Error: insufficient reads clause to invoke function
+ (0,0): anon4_Else
+ReadsReads.dfy(36,4): Error: insufficient reads clause to invoke function
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-ReadsReads.dfy(47,12): Error: insufficient reads clause to invoke function
+ (0,0): anon4_Else
+ReadsReads.dfy(47,11): Error: insufficient reads clause to invoke function
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-ReadsReads.dfy(58,7): Error: insufficient reads clause to invoke function
+ (0,0): anon4_Else
+ReadsReads.dfy(58,6): Error: insufficient reads clause to invoke function
Execution trace:
(0,0): anon0
- (0,0): anon3_Else
-ReadsReads.dfy(87,50): Error: assertion violation
+ (0,0): anon4_Else
+ReadsReads.dfy(87,49): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon16_Then
-ReadsReads.dfy(89,29): Error: assertion violation
+ReadsReads.dfy(89,28): Error: assertion violation
Execution trace:
(0,0): anon0
(0,0): anon18_Then
-ReadsReads.dfy(99,37): Error: assertion violation
+ReadsReads.dfy(99,36): Error: assertion violation
Execution trace:
(0,0): anon0
ReadsReads.dfy(96,14): anon15_Else
(0,0): anon19_Then
-ReadsReads.dfy(101,29): Error: assertion violation
+ReadsReads.dfy(101,28): Error: assertion violation
Execution trace:
(0,0): anon0
ReadsReads.dfy(96,14): anon15_Else
diff --git a/Test/hofs/Requires.dfy b/Test/hofs/Requires.dfy
new file mode 100644
index 00000000..68677b3e
--- /dev/null
+++ b/Test/hofs/Requires.dfy
@@ -0,0 +1,82 @@
+// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+method Main()
+{
+ test0(10);
+ test5(11);
+ test6(12);
+ test1();
+ test2();
+}
+
+predicate valid(x:int)
+{
+ x > 0
+}
+
+function ref1(y:int) : int
+ requires valid(y);
+{
+ y - 1
+}
+
+lemma assumption1()
+ ensures forall a, b :: valid(a) && valid(b) && ref1(a) == ref1(b) ==> a == b;
+{
+}
+
+method test0(a: int)
+{
+ if ref1.requires(a) {
+ // the precondition should suffice to let us call the method
+ ghost var b := ref1(a);
+ }
+}
+method test5(a: int)
+{
+ if valid(a) {
+ // valid(a) is the precondition of ref1
+ assert ref1.requires(a);
+ }
+}
+method test6(a: int)
+{
+ if ref1.requires(a) {
+ // the precondition of ref1 is valid(a)
+ assert valid(a);
+ }
+}
+
+method test1()
+{
+ if * {
+ assert forall a, b :: valid(a) && valid(b) && ref1(a) == ref1(b) ==> a == b;
+ } else {
+ assert forall a, b :: ref1.requires(a) && ref1.requires(b) && ref1(a) == ref1(b)
+ ==> a == b;
+ }
+}
+
+function {:opaque} ref2(y:int) : int // Now with an opaque attribute
+ requires valid(y);
+{
+ y - 1
+}
+
+lemma assumption2()
+ ensures forall a, b :: valid(a) && valid(b) && ref2(a) == ref2(b) ==> a == b;
+{
+ reveal_ref2();
+}
+
+method test2()
+{
+ assumption2();
+ if * {
+ assert forall a, b :: valid(a) && valid(b) && ref2(a) == ref2(b) ==> a == b;
+ } else {
+ assert forall a, b :: ref2.requires(a) && ref2.requires(b) && ref2(a) == ref2(b)
+ ==> a == b;
+ }
+}
diff --git a/Test/hofs/Requires.dfy.expect b/Test/hofs/Requires.dfy.expect
new file mode 100644
index 00000000..b9a40d66
--- /dev/null
+++ b/Test/hofs/Requires.dfy.expect
@@ -0,0 +1,5 @@
+
+Dafny program verifier finished with 20 verified, 0 errors
+Program compiled successfully
+Running...
+
diff --git a/Test/hofs/ResolveError.dfy b/Test/hofs/ResolveError.dfy
index 3c0d7cd9..ae838eb3 100644
--- a/Test/hofs/ResolveError.dfy
+++ b/Test/hofs/ResolveError.dfy
@@ -3,9 +3,9 @@
method ResolutionErrors() {
- var x;
- var g5 := x, y => (y, x); // fail at resolution
- var g6 := x, (y => (y, x)); // fail at resolution
+ var x;
+ var g5 := x, y => (y, x); // fail at resolution
+ var g6 := x, (y => (y, x)); // fail at resolution
}
// cannot assign functions
@@ -23,20 +23,20 @@ method Nope3() {
method RequiresFail(f : int -> int)
// ok
- requires f(0) == 0;
- requires f.requires(0);
- requires f.reads(0) == {};
+ requires f(0) == 0
+ requires f.requires(0)
+ requires f.reads(0) == {}
// fail
- requires f(0) == true;
- requires f(1,2) == 0;
- requires f(true) == 0;
- requires f.requires(true);
- requires f.requires(1) == 0;
- requires f.requires(1,2);
- requires f.reads(true) == {};
- requires f.reads(1) == 0;
- requires f.reads(1,2) == {};
+ requires f(0) == true
+ requires f(1,2) == 0
+ requires f(true) == 0
+ requires f.requires(true)
+ requires f.requires(1) == 0
+ requires f.requires(1,2)
+ requires f.reads(true) == {}
+ requires f.reads(1) == 0
+ requires f.reads(1,2) == {}
{
}
@@ -56,7 +56,7 @@ method Bla() {
assert Bool;
}
-method Pli(f : A -> B) requires f != null;
+method Pli<A,B>(f : A -> B) requires f != null
{
var o : object;
assert f != o;
@@ -102,7 +102,7 @@ module AritySituations {
w := V; // error
}
- method P(r: T -> U, x: T) returns (u: U)
+ method P<T,U>(r: T -> U, x: T) returns (u: U)
requires r.requires(x);
{
u := r(x);
diff --git a/Test/hofs/ResolveError.dfy.expect b/Test/hofs/ResolveError.dfy.expect
index c3e0c242..11471ffd 100644
--- a/Test/hofs/ResolveError.dfy.expect
+++ b/Test/hofs/ResolveError.dfy.expect
@@ -2,8 +2,8 @@ ResolveError.dfy(86,6): Error: RHS (of type ((int,bool)) -> real) not assignable
ResolveError.dfy(91,15): Error: incorrect type of method in-parameter 0 (expected ? -> ?, got (int,bool) -> real)
ResolveError.dfy(101,6): Error: RHS (of type (()) -> real) not assignable to LHS (of type () -> real)
ResolveError.dfy(102,6): Error: RHS (of type () -> real) not assignable to LHS (of type (()) -> real)
-ResolveError.dfy(7,11): Error: the number of left-hand sides (1) and right-hand sides (2) must match for a multi-assignment
-ResolveError.dfy(8,11): Error: the number of left-hand sides (1) and right-hand sides (2) must match for a multi-assignment
+ResolveError.dfy(7,9): Error: the number of left-hand sides (1) and right-hand sides (2) must match for a multi-assignment
+ResolveError.dfy(8,9): Error: the number of left-hand sides (1) and right-hand sides (2) must match for a multi-assignment
ResolveError.dfy(21,6): Error: LHS of assignment must denote a mutable field
ResolveError.dfy(31,16): Error: arguments must have the same type (got int and bool)
ResolveError.dfy(32,12): Error: wrong number of arguments to function application (function type 'int -> int' expects 1, got 2)
@@ -17,7 +17,7 @@ ResolveError.dfy(39,18): Error: wrong number of arguments to function applicatio
ResolveError.dfy(46,15): Error: a reads-clause expression must denote an object or a collection of objects (instead got int)
ResolveError.dfy(47,7): Error: Precondition must be boolean (got int)
ResolveError.dfy(56,9): Error: condition is expected to be of type bool, but is () -> bool
-ResolveError.dfy(59,34): Error: arguments must have the same type (got A -> B and ?)
+ResolveError.dfy(59,39): Error: arguments must have the same type (got A -> B and ?)
ResolveError.dfy(62,11): Error: arguments must have the same type (got A -> B and object)
ResolveError.dfy(68,24): Error: unresolved identifier: _
22 resolution/type errors detected in ResolveError.dfy
diff --git a/Test/hofs/Simple.dfy b/Test/hofs/Simple.dfy
index c27fa82c..6d98531e 100644
--- a/Test/hofs/Simple.dfy
+++ b/Test/hofs/Simple.dfy
@@ -50,7 +50,7 @@ method Main() {
}
function method succ(x : int) : int
- requires x > 0;
+ requires x > 0
{
x + 1
}
@@ -74,24 +74,24 @@ method Main3() {
}
-function P(f: A -> B, x : A): B
- reads (f.reads)(x);
- requires (f.requires)(x);
+function P<A,B>(f: A -> B, x : A): B
+ reads (f.reads)(x)
+ requires (f.requires)(x)
{
f(x)
}
-function Q(f: U -> V, x : U): V
- reads P.reads(f,x);
- requires f.requires(x); // would be nice to be able to write P.requires(f,x)
+function Q<U,V>(f: U -> V, x : U): V
+ reads P.reads(f,x)
+ requires f.requires(x) // would be nice to be able to write P.requires(f,x)
{
P(f,x)
}
-function QQ(f: U -> V, x : U): V
- reads ((() => ((()=>f)()).reads)())((()=>x)());
- requires ((() => ((()=>f)()).requires)())((()=>x)());
+function QQ<U,V>(f: U -> V, x : U): V
+ reads ((() => ((()=>f)()).reads)())((()=>x)())
+ requires ((() => ((()=>f)()).requires)())((()=>x)())
{
((() => P)())((()=>f)(),(()=>x)())
}
diff --git a/Test/hofs/Simple.dfy.expect b/Test/hofs/Simple.dfy.expect
index b3c126d5..c0123c80 100644
--- a/Test/hofs/Simple.dfy.expect
+++ b/Test/hofs/Simple.dfy.expect
@@ -1,32 +1,29 @@
-Simple.dfy(14,10): Error: possible division by zero
+Simple.dfy(14,9): Error: possible division by zero
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
- (0,0): anon6_Then
-Simple.dfy(27,10): Error: possible division by zero
+ (0,0): anon6_Else
+ (0,0): anon7_Then
+Simple.dfy(27,9): Error: possible division by zero
Execution trace:
(0,0): anon0
- (0,0): anon5_Else
- (0,0): anon6_Then
-Simple.dfy(37,9): Error: possible violation of function precondition
+ (0,0): anon6_Else
+ (0,0): anon7_Then
+Simple.dfy(37,8): Error: possible violation of function precondition
Execution trace:
(0,0): anon0
Simple.dfy(35,13): anon5_Else
-Simple.dfy(49,9): Error: possible violation of function precondition
+Simple.dfy(49,8): Error: possible violation of function precondition
Execution trace:
(0,0): anon0
(0,0): anon3_Then
(0,0): anon2
-Simple.dfy(61,10): Error: possible violation of function precondition
+Simple.dfy(61,9): Error: possible violation of function precondition
Execution trace:
(0,0): anon0
-Simple.dfy(61,18): Error: assertion violation
-Execution trace:
- (0,0): anon0
-Simple.dfy(73,10): Error: assertion violation
+Simple.dfy(73,9): Error: assertion violation
Execution trace:
(0,0): anon0
Simple.dfy(72,38): anon5_Else
Simple.dfy(73,38): anon6_Else
-Dafny program verifier finished with 14 verified, 7 errors
+Dafny program verifier finished with 14 verified, 6 errors
diff --git a/Test/hofs/TreeMapSimple.dfy b/Test/hofs/TreeMapSimple.dfy
index a853b82c..6b8f1377 100644
--- a/Test/hofs/TreeMapSimple.dfy
+++ b/Test/hofs/TreeMapSimple.dfy
@@ -6,7 +6,7 @@ datatype List<A> = Nil | Cons(head: A,tail: List<A>)
datatype Tree<A> = Branch(val: A,trees: List<Tree<A>>)
function ListData(xs : List) : set
- ensures forall x :: x in ListData(xs) ==> x < xs;
+ ensures forall x :: x in ListData(xs) ==> x < xs
{
match xs
case Nil => {}
@@ -14,32 +14,32 @@ function ListData(xs : List) : set
}
function TreeData(t0 : Tree) : set
- ensures forall t :: t in TreeData(t0) ==> t < t0;
+ ensures forall t :: t in TreeData(t0) ==> t < t0
{
var Branch(x,ts) := t0;
{x} + set t, y | t in ListData(ts) && y in TreeData(t) :: y
}
-function Pre(f : A -> B, s : set<A>) : bool
- reads (set x, y | x in s && y in f.reads(x) :: y);
+function Pre<A,B>(f : A -> B, s : set<A>) : bool
+ reads (set x, y | x in s && y in f.reads(x) :: y)
{
forall x :: x in s ==> f.reads(x) == {} && f.requires(x)
}
-function method Map(xs : List<A>, f : A -> B): List<B>
- reads Pre.reads(f, ListData(xs));
- requires Pre(f, ListData(xs));
- decreases xs;
+function method Map<A,B>(xs : List<A>, f : A -> B): List<B>
+ reads Pre.reads(f, ListData(xs))
+ requires Pre(f, ListData(xs))
+ decreases xs
{
match xs
case Nil => Nil
case Cons(x,xs) => Cons(f(x),Map(xs,f))
}
-function method TMap(t0 : Tree<A>, f : A -> B) : Tree<B>
- reads Pre.reads(f, TreeData(t0));
- requires Pre(f, TreeData(t0));
- decreases t0;
+function method TMap<A,B>(t0 : Tree<A>, f : A -> B) : Tree<B>
+ reads Pre.reads(f, TreeData(t0))
+ requires Pre(f, TreeData(t0))
+ decreases t0
{
var Branch(x,ts) := t0;
Branch(f(x),Map(ts, t requires t in ListData(ts)
diff --git a/Test/hofs/Twice.dfy b/Test/hofs/Twice.dfy
index add7e83c..5d948a58 100644
--- a/Test/hofs/Twice.dfy
+++ b/Test/hofs/Twice.dfy
@@ -1,7 +1,7 @@
// RUN: %dafny /print:"%t.print" "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
-function method Twice(f : A -> A): A -> A
+function method Twice<A>(f : A -> A): A -> A
{
x requires f.requires(x) && f.requires(f(x))
reads f.reads(x) reads if f.requires(x) then f.reads(f(x)) else {}
@@ -29,7 +29,7 @@ method WithReads() {
}
-function method Twice_bad(f : A -> A): A -> A
+function method Twice_bad<A>(f : A -> A): A -> A
{
x requires f.requires(x) && f.requires(f(x))
reads f.reads(x) + f.reads(f(x))
diff --git a/Test/hofs/Twice.dfy.expect b/Test/hofs/Twice.dfy.expect
index 5ba4b47b..0ce2450c 100644
--- a/Test/hofs/Twice.dfy.expect
+++ b/Test/hofs/Twice.dfy.expect
@@ -1,11 +1,11 @@
-Twice.dfy(27,22): Error: assertion violation
+Twice.dfy(27,21): Error: assertion violation
Execution trace:
(0,0): anon0
Twice.dfy(23,12): anon3_Else
-Twice.dfy(35,32): Error: possible violation of function precondition
+Twice.dfy(35,31): Error: possible violation of function precondition
Execution trace:
(0,0): anon0
- (0,0): anon9_Else
- (0,0): anon10_Then
+ (0,0): anon10_Else
+ (0,0): anon11_Then
Dafny program verifier finished with 4 verified, 2 errors
diff --git a/Test/hofs/VectorUpdate.dfy b/Test/hofs/VectorUpdate.dfy
index 96edbe77..6fb25a87 100644
--- a/Test/hofs/VectorUpdate.dfy
+++ b/Test/hofs/VectorUpdate.dfy
@@ -1,28 +1,59 @@
-// RUN: %dafny /compile:3 "%s" > "%t"
+// RUN: %dafny /compile:3 /autoTriggers:1 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
-method VectorUpdate(N: int, a : array<A>, f : (int,A) -> A)
- requires a != null;
- requires N == a.Length;
- requires forall j :: 0 <= j < N ==> f.requires(j,a[j]);
- requires forall j :: 0 <= j < N ==> a !in f.reads(j,a[j]);
- modifies a;
- ensures forall j :: 0 <= j < N ==> a[j] == f(j,old(a[j]));
+// this is a rather verbose version of the VectorUpdate method
+method VectorUpdate<A>(N: int, a : array<A>, f : (int,A) -> A)
+ requires a != null
+ requires N == a.Length
+ requires forall j :: 0 <= j < N ==> f.requires(j,a[j])
+ requires forall j :: 0 <= j < N ==> a !in f.reads(j,a[j])
+ modifies a
+ ensures forall j :: 0 <= j < N ==> a[j] == f(j,old(a[j]))
{
var i := 0;
- while (i < N)
- invariant 0 <= i <= N;
- invariant forall j :: i <= j < N ==> f.requires(j,a[j]);
- invariant forall j :: 0 <= j < N ==> f.requires(j,old(a[j]));
- invariant forall j :: i <= j < N ==> a !in f.reads(j,a[j]);
- invariant forall j :: i <= j < N ==> a[j] == old(a[j]);
- invariant forall j :: 0 <= j < i ==> a[j] == f(j,old(a[j]));
+ while i < N
+ invariant 0 <= i <= N
+ invariant forall j :: i <= j < N ==> f.requires(j,a[j])
+ invariant forall j :: 0 <= j < N ==> f.requires(j,old(a[j]))
+ invariant forall j :: i <= j < N ==> a !in f.reads(j,a[j])
+ invariant forall j :: i <= j < N ==> a[j] == old(a[j])
+ invariant forall j :: 0 <= j < i ==> a[j] == f(j,old(a[j]))
{
a[i] := f(i,a[i]);
i := i + 1;
}
}
+// here's a shorter version of the method above
+method VectorUpdate'<A>(a : array<A>, f : (int,A) -> A)
+ requires a != null
+ requires forall j :: 0 <= j < a.Length ==> a !in f.reads(j,a[j]) && f.requires(j,a[j])
+ modifies a
+ ensures forall j :: 0 <= j < a.Length ==> a[j] == f(j,old(a[j]))
+{
+ var i := 0;
+ while i < a.Length
+ invariant 0 <= i <= a.Length
+ invariant forall j :: i <= j < a.Length ==> a[j] == old(a[j])
+ invariant forall j :: 0 <= j < i ==> a[j] == f(j,old(a[j]))
+ {
+ a[i] := f(i,a[i]);
+ i := i + 1;
+ }
+}
+
+// here's yet another version
+method VectorUpdate''<A>(a : array<A>, f : (int,A) -> A)
+ requires a != null
+ requires forall j :: 0 <= j < a.Length ==> a !in f.reads(j,a[j]) && f.requires(j,a[j])
+ modifies a
+ ensures forall j :: 0 <= j < a.Length ==> a[j] == f(j,old(a[j]))
+{
+ forall i | 0 <= i < a.Length {
+ a[i] := f(i,a[i]);
+ }
+}
+
method Main()
{
var v := new int[10];
@@ -46,11 +77,11 @@ method Main()
}
method PrintArray(a : array<int>)
- requires a != null;
+ requires a != null
{
var i := 0;
- while (i < a.Length) {
- if (i != 0) {
+ while i < a.Length {
+ if i != 0 {
print ", ";
}
print a[i];
diff --git a/Test/hofs/VectorUpdate.dfy.expect b/Test/hofs/VectorUpdate.dfy.expect
index b01ace00..18a7b110 100644
--- a/Test/hofs/VectorUpdate.dfy.expect
+++ b/Test/hofs/VectorUpdate.dfy.expect
@@ -1,5 +1,5 @@
-Dafny program verifier finished with 6 verified, 0 errors
+Dafny program verifier finished with 10 verified, 0 errors
Program compiled successfully
Running...
diff --git a/Test/hofs/WhileLoop.dfy b/Test/hofs/WhileLoop.dfy
index f79562e9..2c91a8cc 100644
--- a/Test/hofs/WhileLoop.dfy
+++ b/Test/hofs/WhileLoop.dfy
@@ -34,14 +34,14 @@ method OneShot(n: int) {
method HeapQuant(n: int) {
var f : int -> int := x => x;
- var i := new Ref<int>;
+ var i := new Ref;
ghost var r := 0;
i.val := 0;
- while (i.val < n)
- invariant forall u {:heapQuantifier} :: f.requires(u);
- invariant forall u {:heapQuantifier} :: f.reads(u) == {};
+ while i.val < n
+ invariant forall u :: f.requires(u);
+ invariant forall u :: f.reads(u) == {};
invariant r == i.val;
- invariant forall u {:heapQuantifier} :: f(u) == u + r;
+ invariant forall u :: f(u) == u + r;
{
i.val, r := i.val + 1, r + 1;
f := x => f(x) + 1;
diff --git a/Test/irondafny0/FIFO.dfy b/Test/irondafny0/FIFO.dfy
new file mode 100644
index 00000000..ded8f567
--- /dev/null
+++ b/Test/irondafny0/FIFO.dfy
@@ -0,0 +1,43 @@
+// RUN: %dafny /ironDafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+include "Queue.dfyi"
+
+module FIFO exclusively refines Queue {
+ type Item = int
+
+ method Init() returns (q: Queue) {
+ q := [];
+ }
+
+ method Push(item: Item, q: Queue) returns (q': Queue) {
+ return q + [item];
+ }
+
+ method Pop(q: Queue) returns (item: Item, q': Queue)
+ ensures item == q[0]
+ {
+ item := q[0];
+ q' := q[1..];
+ }
+}
+
+module MainImpl refines MainSpec {
+ import Q = FIFO
+
+ method Main()
+ {
+ var q := Q.Init();
+ q := Q.Push(0, q);
+ q := Q.Push(1, q);
+ q := Q.Push(2, q);
+
+ var n: int;
+ n, q := Q.Pop(q);
+ print n, "\n";
+ n, q := Q.Pop(q);
+ print n, "\n";
+ n, q := Q.Pop(q);
+ print n, "\n";
+ }
+}
diff --git a/Test/irondafny0/FIFO.dfy.expect b/Test/irondafny0/FIFO.dfy.expect
new file mode 100644
index 00000000..25021947
--- /dev/null
+++ b/Test/irondafny0/FIFO.dfy.expect
@@ -0,0 +1,8 @@
+
+Dafny program verifier finished with 8 verified, 0 errors
+Program compiled successfully
+Running...
+
+0
+1
+2
diff --git a/Test/irondafny0/LIFO.dfy b/Test/irondafny0/LIFO.dfy
new file mode 100644
index 00000000..8c0a08e8
--- /dev/null
+++ b/Test/irondafny0/LIFO.dfy
@@ -0,0 +1,43 @@
+// RUN: %dafny /ironDafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+include "Queue.dfyi"
+
+module LIFO exclusively refines Queue {
+ type Item = int
+
+ method Init() returns (q: Queue) {
+ q := [];
+ }
+
+ method Push(item: Item, q: Queue) returns (q': Queue) {
+ return [item] + q;
+ }
+
+ method Pop(q: Queue) returns (item: Item, q': Queue)
+ ensures item == q[0]
+ {
+ item := q[0];
+ q' := q[1..];
+ }
+}
+
+module MainImpl refines MainSpec {
+ import Q = LIFO
+
+ method Main()
+ {
+ var q := Q.Init();
+ q := Q.Push(0, q);
+ q := Q.Push(1, q);
+ q := Q.Push(2, q);
+
+ var n: int;
+ n, q := Q.Pop(q);
+ print n, "\n";
+ n, q := Q.Pop(q);
+ print n, "\n";
+ n, q := Q.Pop(q);
+ print n, "\n";
+ }
+}
diff --git a/Test/irondafny0/LIFO.dfy.expect b/Test/irondafny0/LIFO.dfy.expect
new file mode 100644
index 00000000..83f90a5b
--- /dev/null
+++ b/Test/irondafny0/LIFO.dfy.expect
@@ -0,0 +1,8 @@
+
+Dafny program verifier finished with 8 verified, 0 errors
+Program compiled successfully
+Running...
+
+2
+1
+0
diff --git a/Test/irondafny0/Queue.dfyi b/Test/irondafny0/Queue.dfyi
new file mode 100644
index 00000000..06f4b29e
--- /dev/null
+++ b/Test/irondafny0/Queue.dfyi
@@ -0,0 +1,22 @@
+// Queue.dfyi
+
+abstract module Queue {
+ type Item
+ type Queue = seq<Item>
+
+ method Init() returns (q: Queue)
+ ensures |q| == 0;
+
+ method Push(item: Item, q: Queue) returns (q': Queue)
+ ensures |q'| == |q| + 1;
+
+ method Pop(q: Queue) returns (item: Item, q': Queue)
+ requires |q| > 0;
+ ensures item in q;
+ ensures |q'| == |q| - 1;
+}
+
+abstract module MainSpec {
+ import Q : Queue
+}
+
diff --git a/Test/irondafny0/inheritreqs0.dfy b/Test/irondafny0/inheritreqs0.dfy
new file mode 100644
index 00000000..a0117da0
--- /dev/null
+++ b/Test/irondafny0/inheritreqs0.dfy
@@ -0,0 +1,22 @@
+// RUN: %dafny /compile:3 /optimize /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+abstract module Spec {
+ method Greet(b: bool)
+ requires b;
+}
+
+module Impl refines Spec {
+ method Greet(b: bool) {
+ print "o hai!\n";
+ }
+
+ method Xyzzy(b: bool)
+ requires b;
+ {}
+
+ method Main() {
+ Greet(false);
+ Xyzzy(false);
+ }
+}
diff --git a/Test/irondafny0/inheritreqs0.dfy.expect b/Test/irondafny0/inheritreqs0.dfy.expect
new file mode 100644
index 00000000..44e33bc0
--- /dev/null
+++ b/Test/irondafny0/inheritreqs0.dfy.expect
@@ -0,0 +1,6 @@
+inheritreqs0.dfy(19,13): Error BP5002: A precondition for this call might not hold.
+inheritreqs0.dfy[Impl](6,17): Related location: This is the precondition that might not hold.
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 6 verified, 1 error
diff --git a/Test/irondafny0/inheritreqs1.dfy b/Test/irondafny0/inheritreqs1.dfy
new file mode 100644
index 00000000..c83d04ac
--- /dev/null
+++ b/Test/irondafny0/inheritreqs1.dfy
@@ -0,0 +1,22 @@
+// RUN: %dafny /compile:3 /optimize /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+abstract module Spec {
+ method Greet(b: bool)
+ requires b;
+}
+
+module Impl refines Spec {
+ method Greet(b: bool) {
+ print "o hai!\n";
+ }
+
+ method Xyzzy(b: bool)
+ requires b;
+ {}
+
+ method Main() {
+ Greet(true);
+ Xyzzy(false);
+ }
+}
diff --git a/Test/irondafny0/inheritreqs1.dfy.expect b/Test/irondafny0/inheritreqs1.dfy.expect
new file mode 100644
index 00000000..a07d179d
--- /dev/null
+++ b/Test/irondafny0/inheritreqs1.dfy.expect
@@ -0,0 +1,6 @@
+inheritreqs1.dfy(20,13): Error BP5002: A precondition for this call might not hold.
+inheritreqs1.dfy(15,17): Related location: This is the precondition that might not hold.
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 6 verified, 1 error
diff --git a/Test/irondafny0/opened_workaround.dfy b/Test/irondafny0/opened_workaround.dfy
new file mode 100644
index 00000000..6d44ccfd
--- /dev/null
+++ b/Test/irondafny0/opened_workaround.dfy
@@ -0,0 +1,21 @@
+// RUN: %dafny /ironDafny /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+module A {
+
+ predicate P()
+
+ class C
+ {
+ static method{:axiom} M()
+ ensures P();
+ }
+}
+
+abstract module B {
+ import opened A
+}
+
+abstract module C {
+ import Bee : B // Works
+}
diff --git a/Test/irondafny0/opened_workaround.dfy.expect b/Test/irondafny0/opened_workaround.dfy.expect
new file mode 100644
index 00000000..0be94b4c
--- /dev/null
+++ b/Test/irondafny0/opened_workaround.dfy.expect
@@ -0,0 +1,3 @@
+
+Dafny program verifier finished with 3 verified, 0 errors
+Compilation error: Function _0_A_Compile._default.P has no body
diff --git a/Test/irondafny0/optimize0.dfy b/Test/irondafny0/optimize0.dfy
new file mode 100644
index 00000000..865d8707
--- /dev/null
+++ b/Test/irondafny0/optimize0.dfy
@@ -0,0 +1,6 @@
+// RUN: %dafny /compile:3 /optimize /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+method Main() {
+ print "o hai!";
+}
diff --git a/Test/irondafny0/optimize0.dfy.expect b/Test/irondafny0/optimize0.dfy.expect
new file mode 100644
index 00000000..6b3e13c5
--- /dev/null
+++ b/Test/irondafny0/optimize0.dfy.expect
@@ -0,0 +1,6 @@
+
+Dafny program verifier finished with 2 verified, 0 errors
+Program compiled successfully
+Running...
+
+o hai! \ No newline at end of file
diff --git a/Test/irondafny0/xrefine0.dfy b/Test/irondafny0/xrefine0.dfy
new file mode 100644
index 00000000..b849111c
--- /dev/null
+++ b/Test/irondafny0/xrefine0.dfy
@@ -0,0 +1,6 @@
+// RUN: %dafny /ironDafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+abstract module Delicious {}
+module Chocolate exclusively refines Delicious {}
+module Strawberry exclusively refines Delicious {}
diff --git a/Test/irondafny0/xrefine0.dfy.expect b/Test/irondafny0/xrefine0.dfy.expect
new file mode 100644
index 00000000..136e06db
--- /dev/null
+++ b/Test/irondafny0/xrefine0.dfy.expect
@@ -0,0 +1,2 @@
+xrefine0.dfy(6,7): Error: no more than one exclusive refinement may exist for a given module.
+1 resolution/type errors detected in xrefine0.dfy
diff --git a/Test/irondafny0/xrefine1.dfy b/Test/irondafny0/xrefine1.dfy
new file mode 100644
index 00000000..1b835649
--- /dev/null
+++ b/Test/irondafny0/xrefine1.dfy
@@ -0,0 +1,77 @@
+// RUN: %dafny /ironDafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+abstract module ProtocolSpec {
+ type ProtoT
+
+ predicate Init(p:ProtoT)
+}
+
+abstract module HostSpec {
+ type HostT
+ import P : ProtocolSpec
+
+ function method foo(h:HostT) : P.ProtoT
+}
+
+module ProtocolImpl exclusively refines ProtocolSpec {
+ type ProtoT = bool
+
+ predicate Init(p:ProtoT) { !p }
+
+ method orange(i:nat) returns (j:nat)
+ {
+ j := i + 1;
+ }
+}
+
+module HostImpl exclusively refines HostSpec {
+ import P = ProtocolImpl
+
+ type HostT = int
+
+ function method foo(h:HostT) : P.ProtoT
+ {
+ h > 0
+ }
+
+ method apple(i:nat) returns (j:nat)
+ {
+ j := i + 1;
+ }
+}
+
+abstract module MainSpec {
+ import HI : HostSpec
+ import PI : ProtocolSpec
+
+ method Test(h1:HI.HostT, h2:HI.HostT)
+ requires HI.foo(h1) == HI.foo(h2);
+ requires PI.Init(HI.foo(h1))
+}
+
+module MainImpl exclusively refines MainSpec {
+ import HI = HostImpl
+ import PI = ProtocolImpl
+
+ method Test(h1:HI.HostT, h2:HI.HostT)
+ {
+ var a := HI.foo(h1);
+ print "HI.foo(h1) => ", a, "\n";
+ var b := HI.foo(h2);
+ print "HI.foo(h2) => ", b, "\n";
+ var i := PI.orange(1);
+ print "PI.orange(1) => ", i, "\n";
+ var j := HI.apple(2);
+ print "PI.apple(2) => ", j, "\n";
+ }
+
+ method Main()
+ {
+ Test(-1, 1);
+ }
+}
+
+
+
+
diff --git a/Test/irondafny0/xrefine1.dfy.expect b/Test/irondafny0/xrefine1.dfy.expect
new file mode 100644
index 00000000..ec946cda
--- /dev/null
+++ b/Test/irondafny0/xrefine1.dfy.expect
@@ -0,0 +1,6 @@
+xrefine1.dfy(71,12): Error BP5002: A precondition for this call might not hold.
+xrefine1.dfy[MainImpl](49,28): Related location: This is the precondition that might not hold.
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 12 verified, 1 error
diff --git a/Test/irondafny0/xrefine2.dfy b/Test/irondafny0/xrefine2.dfy
new file mode 100644
index 00000000..9c33391b
--- /dev/null
+++ b/Test/irondafny0/xrefine2.dfy
@@ -0,0 +1,77 @@
+// RUN: %dafny /ironDafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+abstract module ProtocolSpec {
+ type ProtoT
+
+ predicate Init(p:ProtoT)
+}
+
+abstract module HostSpec {
+ type HostT
+ import P : ProtocolSpec
+
+ function method foo(h:HostT) : P.ProtoT
+}
+
+module ProtocolImpl exclusively refines ProtocolSpec {
+ type ProtoT = bool
+
+ predicate Init(p:ProtoT) { p }
+
+ method orange(i:nat) returns (j:nat)
+ {
+ j := i + 1;
+ }
+}
+
+module HostImpl exclusively refines HostSpec {
+ import P = ProtocolImpl
+
+ type HostT = int
+
+ function method foo(h:HostT) : P.ProtoT
+ {
+ h != 0
+ }
+
+ method apple(i:nat) returns (j:nat)
+ {
+ j := i + 1;
+ }
+}
+
+abstract module MainSpec {
+ import HI : HostSpec
+ import PI : ProtocolSpec
+
+ method Test(h1:HI.HostT, h2:HI.HostT)
+ requires HI.foo(h1) == HI.foo(h2);
+ requires PI.Init(HI.foo(h1))
+}
+
+module MainImpl exclusively refines MainSpec {
+ import HI = HostImpl
+ import PI = ProtocolImpl
+
+ method Test(h1:HI.HostT, h2:HI.HostT)
+ {
+ var a := HI.foo(h1);
+ print "HI.foo(h1) => ", a, "\n";
+ var b := HI.foo(h2);
+ print "HI.foo(h2) => ", b, "\n";
+ var i := PI.orange(1);
+ print "PI.orange(1) => ", i, "\n";
+ var j := HI.apple(2);
+ print "PI.apple(2) => ", j, "\n";
+ }
+
+ method Main()
+ {
+ Test(-1, 1);
+ }
+}
+
+
+
+
diff --git a/Test/irondafny0/xrefine2.dfy.expect b/Test/irondafny0/xrefine2.dfy.expect
new file mode 100644
index 00000000..6d3fecd4
--- /dev/null
+++ b/Test/irondafny0/xrefine2.dfy.expect
@@ -0,0 +1,9 @@
+
+Dafny program verifier finished with 13 verified, 0 errors
+Program compiled successfully
+Running...
+
+HI.foo(h1) => True
+HI.foo(h2) => True
+PI.orange(1) => 2
+PI.apple(2) => 3
diff --git a/Test/irondafny0/xrefine3.dfy b/Test/irondafny0/xrefine3.dfy
new file mode 100644
index 00000000..86dbd957
--- /dev/null
+++ b/Test/irondafny0/xrefine3.dfy
@@ -0,0 +1,72 @@
+// RUN: %dafny /ironDafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+abstract module AlphaSpec {
+ type Alpha
+
+ predicate IsValid(a:Alpha)
+
+ method Init() returns (a:Alpha)
+ ensures IsValid(a);
+}
+
+abstract module BetaSpec {
+ type Beta
+ import A : AlphaSpec
+
+ predicate IsValid(b:Beta)
+
+ method Init(ays:seq<A.Alpha>) returns (b:Beta)
+ requires forall i :: 0 <= i < |ays| ==> A.IsValid(ays[i]);
+ ensures IsValid(b);
+}
+
+module AlphaImpl exclusively refines AlphaSpec {
+ type Alpha = bool
+
+ predicate IsValid(a:Alpha) {
+ a
+ }
+
+ method Init() returns (a:Alpha)
+ ensures IsValid(a);
+ {
+ a := true;
+ }
+}
+
+module BetaImpl exclusively refines BetaSpec {
+ import A = AlphaImpl
+ type Beta = seq<A.Alpha>
+
+ predicate IsValid(b:Beta) {
+ forall i :: 0 <= i < |b| ==> A.IsValid(b[i])
+ }
+
+ method Init(ays:seq<A.Alpha>) returns (b:Beta) {
+ b := ays;
+ }
+}
+
+abstract module MainSpec {
+ import A : AlphaSpec
+ import B : BetaSpec
+
+ method Main()
+ {
+ var a := A.Init();
+ var ays := [a, a];
+ assert forall i :: 0 <= i < |ays| ==> A.IsValid(ays[i]);
+ var b := B.Init(ays);
+ print "o hai!\n";
+ }
+}
+
+module MainImpl exclusively refines MainSpec {
+ import B = BetaImpl
+ import A = AlphaImpl
+}
+
+
+
+
diff --git a/Test/irondafny0/xrefine3.dfy.expect b/Test/irondafny0/xrefine3.dfy.expect
new file mode 100644
index 00000000..1e5a5b4e
--- /dev/null
+++ b/Test/irondafny0/xrefine3.dfy.expect
@@ -0,0 +1,6 @@
+
+Dafny program verifier finished with 14 verified, 0 errors
+Program compiled successfully
+Running...
+
+o hai!
diff --git a/Test/lit.site.cfg b/Test/lit.site.cfg
index d0b3a85b..2d57d389 100644
--- a/Test/lit.site.cfg
+++ b/Test/lit.site.cfg
@@ -22,7 +22,7 @@ config.suffixes = ['.dfy']
# excludes: A list of directories to exclude from the testsuite. The 'Inputs'
# subdirectories contain auxiliary inputs for various tests in their parent
# directories.
-config.excludes = []
+config.excludes = ['Inputs', 'sandbox']
# test_source_root: The root path where tests are located.
config.test_source_root = os.path.dirname(os.path.abspath(__file__))
@@ -79,19 +79,26 @@ lit_config.note('Repository root is {}'.format(repositoryRoot))
binaryDir = os.path.join( repositoryRoot, 'Binaries')
dafnyExecutable = os.path.join( binaryDir, 'Dafny.exe')
+serverExecutable = os.path.join( binaryDir, 'DafnyServer.exe')
if not os.path.exists(dafnyExecutable):
lit_config.fatal('Could not find Dafny.exe at {}'.format(dafnyExecutable))
+if not os.path.exists(serverExecutable):
+ lit_config.warning('Could not find DafnyServer.exe at {}'.format(serverExecutable))
+else:
+ config.suffixes.append('.transcript')
+
dafnyExecutable = quotePath(dafnyExecutable)
if os.name == 'posix':
dafnyExecutable = 'mono ' + dafnyExecutable
+ serverExecutable = 'mono ' + serverExecutable
if lit.util.which('mono') == None:
lit_config.fatal('Cannot find mono. Make sure it is your PATH')
# Expected output does not contain logo
-dafnyExecutable += ' -nologo'
+dafnyExecutable += ' -nologo -countVerificationErrors:0'
# We do not want absolute or relative paths in error messages, just the basename of the file
dafnyExecutable += ' -useBaseNameForFileName'
@@ -105,6 +112,7 @@ if len(dafnyParams) > 0:
lit_config.note('Using Dafny: {}\n'.format(dafnyExecutable))
config.substitutions.append( ('%dafny', dafnyExecutable) )
+config.substitutions.append( ('%server', serverExecutable) )
# Sanity check: Check solver executable is available
# FIXME: Should this check be removed entirely?
diff --git a/Test/pydiff.py b/Test/pydiff.py
index 407fe251..94267960 100644
--- a/Test/pydiff.py
+++ b/Test/pydiff.py
@@ -75,7 +75,8 @@ def preProcess(openFile, stripTrailingCR=False, ignoreAllSpace=False):
# newline characters because this will create a mess when outputting the
# diff. Is this the right behaviour?
deleteChars=' \t'
- translationTable = str.maketrans('','', deleteChars)
+ if sys.version_info.major >= 3:
+ translationTable = str.maketrans('','', deleteChars)
copy = [ ]
for line in original:
diff --git a/Test/runTests.bat b/Test/runTests.bat
new file mode 100644
index 00000000..4856faf8
--- /dev/null
+++ b/Test/runTests.bat
@@ -0,0 +1,2 @@
+@REM runTests.bat -f "/dprelude PRELUDE_FILE" -r REPORT_NAME INPUT_FILES
+C:/Python34/python.exe runTests.py --difftool "C:/Program Files (x86)/Meld/Meld.exe" %*
diff --git a/Test/runTests.py b/Test/runTests.py
new file mode 100644
index 00000000..8caa65d1
--- /dev/null
+++ b/Test/runTests.py
@@ -0,0 +1,567 @@
+import os
+import re
+import sys
+import csv
+import shutil
+import argparse
+import operator
+import platform
+from math import floor, ceil
+from enum import Enum
+from time import time, strftime
+from collections import defaultdict
+from multiprocessing import Pool, Manager
+from subprocess import Popen, call, PIPE, TimeoutExpired
+
+# C:/Python34/python.exe runTests.py --compiler "c:/MSR/dafny/Binaries/Dafny.exe" --flags "/useBaseNameForFileName /compile:1 /nologo" --difftool "C:\Program Files (x86)\Meld\Meld.exe" -j4 --flags "/dprelude preludes\AlmostAllTriggers.bpl" dafny0\SeqFromArray.dfy
+
+# c:/Python34/python.exe runTests.py --compare ../TestStable/results/SequenceAxioms/2015-06-06-00-54-52--PrettyPrinted.report.csv ../TestStable/results/SequenceAxioms/*.csv
+
+VERBOSITY = None
+KILLED = False
+ANSI = False
+
+try:
+ import colorama
+ no_native_ansi = os.name == 'nt' and os.environ.get("TERM") in [None, "cygwin"]
+ tty = all(hasattr(stream, 'isatty') and stream.isatty() for stream in (sys.stdout, sys.stderr))
+ colorama.init(strip=no_native_ansi, convert=no_native_ansi and tty)
+ ANSI = True
+except ImportError:
+ try:
+ import tendo.ansiterm
+ ANSI = True
+ except ImportError:
+ pass
+
+class Defaults:
+ EXCLUDED_FILES = ["^flycheck_"]
+ EXCLUDED_FOLDERS = ["Inputs", "Output", "sandbox", "desktop"]
+ DAFNY_BIN = os.path.realpath(os.path.join(os.path.dirname(__file__), "../Binaries/Dafny.exe"))
+ COMPILER = [DAFNY_BIN]
+ FLAGS = ["/useBaseNameForFileName", "/compile:1", "/nologo", "/timeLimit:300"]
+ EXTENSIONS = [".dfy", ".transcript"]
+
+class Colors:
+ RED = '\033[91m'
+ GREEN = '\033[92m'
+ YELLOW = '\033[93m'
+ BRIGHT = '\033[1m'
+ DIM = '\033[2m'
+ RESET = '\033[0m'
+
+class Debug(Enum):
+ ERROR = (-1, Colors.RED)
+ WARNING = (-1, Colors.YELLOW)
+ REPORT = (0, Colors.RESET, True)
+ INFO = (1, Colors.RESET, True)
+ DEBUG = (2, Colors.RESET)
+ TRACE = (3, Colors.RESET)
+
+ def __init__(self, index, color, elide=False):
+ self.index = index
+ self.color = color
+ self.elide = elide
+
+def wrap_color(string, color, silent=False):
+ if silent:
+ return " " * len(string)
+ elif ANSI:
+ return color + string + Colors.RESET
+ else:
+ return string
+
+def debug(level, *args, **kwargs):
+ kwargs["file"] = sys.stderr
+ kwargs["flush"] = True
+
+ headers = kwargs.pop("headers", [])
+ if isinstance(headers, Enum):
+ headers = [headers]
+
+ silentheaders = kwargs.pop("silentheaders", False)
+
+ if level and level.index <= VERBOSITY:
+ if level:
+ headers = [level] + headers
+
+ headers = tuple(wrap_color("{: <8}".format("[" + h.name + "]"), h.color, silent = silentheaders)
+ for h in headers if not h.elide)
+ print(*(headers + args), **kwargs)
+
+class TestStatus(Enum):
+ PENDING = (0, Colors.RESET)
+ PASSED = (1, Colors.GREEN)
+ FAILED = (2, Colors.RED)
+ UNKNOWN = (3, Colors.RED)
+ TIMEOUT = (4, Colors.RED)
+
+ def __init__(self, index, color):
+ self.index = index
+ self.color = color
+ self.elide = False
+
+class Test:
+ COLUMNS = ["name", "status", "start", "end", "duration", "returncodes", "suite_time", "njobs", "proc_info", "source_path", "temp_directory", "cmds", "expected", "output"]
+
+ def __init__(self, name, source_path, cmds, timeout, compiler_id = 0):
+ self.name = name
+ self.source_path = Test.uncygdrive(source_path)
+ self.expect_path = Test.source_to_expect_path(self.source_path)
+ self.source_directory, self.fname = os.path.split(self.source_path)
+ self.temp_directory = os.path.join(self.source_directory, "Output")
+ self.temp_output_path = os.path.join(self.temp_directory, self.fname + ".tmp")
+
+ self.output = None
+ self.expected = Test.read_normalize(self.expect_path)
+
+ self.cmds = cmds
+ self.timeout = timeout
+ self.compiler_id = compiler_id
+ self.cmds = [cmd.replace("%s", self.source_path) for cmd in self.cmds]
+ self.cmds = [cmd.replace("%S", self.source_directory) for cmd in self.cmds]
+ self.cmds = [cmd.replace("%t", self.temp_output_path) for cmd in self.cmds]
+ self.cmds = [cmd.replace("%T", self.temp_directory) for cmd in self.cmds]
+
+ self.status = TestStatus.PENDING
+ self.proc_info = platform.processor()
+
+ self.time, self.suite_time = None, None
+ self.njobs, self.returncodes = None, []
+ self.start, self.end, self.duration = None, None, None
+
+ @staticmethod
+ def source_to_expect_path(source):
+ return source + ".expect"
+
+ @staticmethod
+ def uncygdrive(path):
+ return re.sub("^/cygdrive/([a-zA-Z])/", r"\1:/", path)
+
+ @staticmethod
+ def read_normalize(path):
+ try:
+ with open(path, mode="rb") as reader:
+ return reader.read().replace(b'\r\n', b'\n').replace(b'\r', b'\n')
+ except FileNotFoundError:
+ debug(Debug.WARNING, "{} not found".format(path))
+ return ""
+
+ @staticmethod
+ def build_report(tests, name):
+ now = strftime("%Y-%m-%d-%H-%M-%S")
+ if name:
+ directory, fname = os.path.split(name)
+ name = os.path.join(directory, now + "--" + fname)
+ else:
+ name = now
+
+ with open(name + ".csv", mode='w', newline='') as writer:
+ csv_writer = csv.DictWriter(writer, Test.COLUMNS, dialect='excel')
+ csv_writer.writeheader()
+ for test in tests:
+ test.serialize(csv_writer)
+
+ @staticmethod
+ def load_report(path):
+ results = []
+ with open(path) as csvfile:
+ for row in csv.DictReader(csvfile): #, fieldnames=Test.COLUMNS):
+ results.append(Test.deserialize(row))
+ return results
+
+ @staticmethod
+ def mean_duration(results, margin):
+ durations = sorted(result.duration for result in results
+ if result.status in (TestStatus.PASSED, TestStatus.FAILED))
+ if len(durations) >= 15:
+ lq = durations[floor(0.25 * len(durations))]
+ hq = durations[ceil(0.85 * len(durations))]
+ iqr = hq - lq
+ filtered = [d for d in durations if (lq - margin * iqr) <= d <= (hq + margin * iqr)]
+ if filtered:
+ avg = sum(durations) / len(durations)
+ trimmed_avg = sum(filtered) / len(filtered)
+ outliers_count = len(durations) - len(filtered)
+ msg = "mean completion time: {:.2f}s".format(avg)
+ if outliers_count > 0:
+ msg += "; ignoring {} outliers: {:.2f}s".format(outliers_count, trimmed_avg)
+ return " ({})".format(msg)
+ return ""
+
+ @staticmethod
+ def summarize(results):
+ debug(Debug.INFO, "\nTesting complete ({} test(s))".format(len(results)))
+
+ if results:
+ grouped = defaultdict(list)
+ for test in results:
+ grouped[test.status].append(test)
+
+ for status, tests in sorted(grouped.items(), key=lambda x: x[0].index):
+ if tests:
+ debug(Debug.REPORT, "{} of {}".format(len(tests), len(results)), headers=status)
+ if status != TestStatus.PASSED:
+ for test in tests:
+ debug(Debug.REPORT, "* " + test.name, headers=status, silentheaders=True)
+
+ debug(Debug.REPORT)
+
+ failing = [t for t in results if t.status != TestStatus.PASSED]
+ if failing:
+ with open("failing.lst", mode='w') as writer:
+ for t in failing:
+ writer.write("{}\n".format(t.name))
+ debug(Debug.REPORT, "Some tests failed: use [runTests.py failing.lst] to rerun the failing tests")
+
+ debug(Debug.REPORT, "Testing took {:.2f}s on {} thread(s){}".format(
+ results[0].suite_time, results[0].njobs, Test.mean_duration(results, 1.5)))
+
+
+ def run(self):
+ debug(Debug.DEBUG, "Starting {}".format(self.name))
+ os.makedirs(self.temp_directory, exist_ok=True)
+ # os.chdir(self.source_directory)
+
+ stdout, stderr = b'', b''
+ self.start = time()
+
+ try:
+ for cmd in self.cmds:
+ debug(Debug.DEBUG, "> {}".format(cmd))
+ try:
+ proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True)
+ _stdout, _stderr = proc.communicate(timeout=self.timeout)
+ stdout, stderr = stdout + _stdout, stderr + _stderr
+ self.returncodes.append(proc.returncode)
+ except FileNotFoundError:
+ debug(Debug.ERROR, "Program '{}' not found".format(cmd))
+ self.status = TestStatus.UNKNOWN
+ return
+ except TimeoutExpired:
+ self.status = TestStatus.TIMEOUT
+ self.end = self.start + self.timeout
+ self.duration = self.timeout
+ return
+
+ self.end = time()
+ self.duration = self.end - self.start
+
+ stdout, stderr = stdout.strip(), stderr.strip()
+ if stdout != b"":
+ debug(Debug.TRACE, "Writing the output of {} to {}".format(self.name, self.temp_output_path))
+ with open(self.temp_output_path, mode='ab') as writer:
+ writer.write(stdout)
+ if stderr != b"":
+ debug(Debug.INFO, stderr.decode("utf-8"))
+
+ self.update_status()
+ except TimeoutExpired:
+ self.status = TestStatus.TIMEOUT
+ except KeyboardInterrupt:
+ raise
+
+ def update_status(self):
+ self.output = Test.read_normalize(self.temp_output_path)
+ self.status = TestStatus.PASSED if self.expected == self.output else TestStatus.FAILED
+
+ def report(self, tid, running, alltests):
+ running = [alltests[rid].fname for rid in running]
+ running = "; oldest: {}".format(running[0]) if running else ""
+
+ fstring = "[{:5.2f}s] {} ({}{})"
+ progress = "{}/{}".format(tid, len(alltests))
+ message = fstring.format(self.duration, wrap_color(self.name, Colors.BRIGHT),
+ wrap_color(progress, Colors.BRIGHT), running)
+
+ debug(Debug.INFO, message, headers=self.status)
+
+ @staticmethod
+ def write_bytes(base_directory, relative_path, extension, contents):
+ with open(os.path.join(base_directory, relative_path + extension), mode='wb') as writer:
+ writer.write(contents)
+
+ def serialize(self, csv_writer):
+ csv_writer.writerow({col: getattr(self, col) for col in Test.COLUMNS})
+
+ @classmethod
+ def deserialize(cls, row):
+ test = cls.__new__(cls)
+ for col, val in row.items():
+ setattr(test, col, val)
+ test.duration = float(test.duration)
+ test.status = next(x for x in TestStatus if str(x) == test.status)
+ return test
+
+def setup_parser():
+ parser = argparse.ArgumentParser(description='Run the Dafny test suite.')
+
+ parser.add_argument('path', type=str, action='store', nargs='+',
+ help='Input files or folders. Folders are searched for test files. Lists of files can also be specified by passing a .lst file (for an example of such a file, look at failing.lst after running failing tests.')
+
+ parser.add_argument('--compiler', type=str, action='append', default=None,
+ help='Dafny executable. Default: {}'.format(Defaults.DAFNY_BIN))
+
+ parser.add_argument('--base-flags', type=str, action='append', default=None,
+ help='Arguments to pass to dafny. Multiple --flags are concatenated. Default: {}'.format(Defaults.FLAGS))
+
+ parser.add_argument('--flags', '-f', type=str, action='append', default=[],
+ help='Additional arguments to pass to dafny. Useful to override some of the defaults found in --base-flags.')
+
+ parser.add_argument('--njobs', '-j', action='store', type=int, default=None,
+ help='Number of test workers.')
+
+ parser.add_argument('--exclude', action='append', type=str, default=[],
+ help='Excluded directories. {} are automatically added.'.format(Defaults.EXCLUDED_FOLDERS))
+
+ parser.add_argument('--verbosity', action='store', type=int, default=1,
+ help='Set verbosity level. 0: Minimal; 1: Some info; 2: More info; 3: Trace.')
+
+ parser.add_argument('-v', action='store_const', default=1, dest="verbosity", const=2,
+ help='Short for --verbosity 2.')
+
+ parser.add_argument('-vv', action='store_const', default=1, dest="verbosity", const=3,
+ help='Short for --verbosity 3.')
+
+ parser.add_argument('--report', '-r', action='store', type=str, default=None,
+ help='Give an explicit name to the report file. Defaults to the current date and time.')
+
+ parser.add_argument('--timeout', action='store', type=float, default=15*60.0,
+ help='Prover timeout')
+
+ parser.add_argument('--compare', action='store_true',
+ help="Compare two previously generated reports.")
+
+ parser.add_argument('--time-all', action='store_true',
+ help="When comparing, include all timings.")
+
+ parser.add_argument('--diff', '-d', action='store_true',
+ help="Don't run tests; show differences between outputs and .expect files, optionally overwritting .expect files.")
+
+ parser.add_argument('--accept', '-a', action='store_true',
+ help="Don't run tests; copy outputs to .expect files.")
+
+ parser.add_argument('--open', '-o', action='store_true',
+ help="Don't run tests; open one file.")
+
+ parser.add_argument('--difftool', action='store', type=str, default="diff",
+ help='Diff program. Default: diff.')
+
+ return parser
+
+def run_one_internal(test, test_id, args, running):
+ global KILLED
+ global VERBOSITY
+ VERBOSITY = args.verbosity
+
+ if not KILLED:
+ try:
+ running.append(test_id)
+ test.run()
+ except KeyboardInterrupt:
+ # There's no reliable way to handle this cleanly on Windows: if one
+ # of the worker dies, it gets respawned. The reliable solution is to
+ # ignore further work once you receive a kill signal
+ KILLED = True
+ except Exception as e:
+ debug(Debug.ERROR, "[{}] {}".format(test.name, e))
+ test.status = TestStatus.UNKNOWN
+ finally:
+ running.remove(test_id)
+
+ return test
+
+def run_one(args):
+ return run_one_internal(*args)
+
+def get_server_path(compiler):
+ REGEXP = r"\bDafny.exe\b.*"
+ if re.search(REGEXP, compiler):
+ return re.sub(REGEXP, "DafnyServer.exe", compiler)
+ else:
+ return None
+
+def substitute_binaries(cmd, compiler):
+ cmd = cmd.replace("%dafny", compiler)
+ cmd = cmd.replace("%server", get_server_path(compiler))
+ return cmd
+
+def read_one_test(fname, compiler_cmds, timeout):
+ for cid, compiler_cmd in enumerate(compiler_cmds):
+ source_path = os.path.realpath(fname)
+ with open(source_path, mode='r') as reader:
+ cmds = []
+ for line in reader:
+ line = line.strip()
+ match = re.match("^[/# ]*RUN: *(?!%diff)([^ ].*)$", line)
+ if match:
+ debug(Debug.TRACE, "Found RUN spec: {}".format(line))
+ cmds.append(substitute_binaries(match.groups()[0], compiler_cmd))
+ else:
+ break
+ if cmds:
+ yield Test(fname, source_path, cmds, timeout, cid)
+ else:
+ debug(Debug.WARNING, "Test file {} has no RUN specification".format(fname))
+
+
+def find_one(fname, compiler_cmds, timeout):
+ _, name = os.path.split(fname)
+ _, ext = os.path.splitext(name)
+ if ext in Defaults.EXTENSIONS and not any(re.search(pattern, name, re.IGNORECASE) for pattern in Defaults.EXCLUDED_FILES):
+ if os.path.exists(fname):
+ debug(Debug.TRACE, "Found test file: {}".format(fname))
+ yield from read_one_test(fname, compiler_cmds, timeout)
+ else:
+ debug(Debug.ERROR, "Test file {} not found".format(fname))
+ else:
+ debug(Debug.TRACE, "Ignoring {}".format(fname))
+
+
+def expand_lsts(paths):
+ for path in paths:
+ _, ext = os.path.splitext(path)
+ if ext == ".lst": #lst files are only read if explicitly listed on the CLI
+ debug(Debug.INFO, "Loading tests from {}".format(path))
+ with open(path) as reader:
+ for line in reader:
+ _path = line.strip()
+ yield _path
+ else:
+ yield path
+
+def find_tests(paths, compiler_cmds, excluded, timeout):
+ for path in expand_lsts(paths):
+ if os.path.isdir(path):
+ debug(Debug.TRACE, "Searching for tests in {}".format(path))
+ for base, dirnames, fnames in os.walk(path):
+ dirnames[:] = [d for d in dirnames if d not in excluded]
+ for fname in fnames:
+ yield from find_one(os.path.join(base, fname), compiler_cmds, timeout)
+ else:
+ yield from find_one(path, compiler_cmds, timeout)
+
+def run_tests(args):
+ if args.compiler is None:
+ args.compiler = Defaults.COMPILER
+ if args.base_flags is None:
+ args.base_flags = Defaults.FLAGS
+
+ for compiler in args.compiler:
+ server = get_server_path(compiler)
+ if not os.path.exists(compiler):
+ debug(Debug.ERROR, "Compiler not found: {}".format(compiler))
+ return
+ if not os.path.exists(server):
+ debug(Debug.WARNING, "Server not found")
+
+ tests = list(find_tests(args.path, [compiler + ' ' + " ".join(args.base_flags + args.flags)
+ for compiler in args.compiler],
+ args.exclude + Defaults.EXCLUDED_FOLDERS, args.timeout))
+ tests.sort(key=operator.attrgetter("name"))
+
+ args.njobs = max(1, min(args.njobs or os.cpu_count() or 1, len(tests)))
+ debug(Debug.INFO, "\nRunning {} test(s) on {} testing thread(s), timeout is {:.2f}s, started at {}".format(len(tests), args.njobs, args.timeout, strftime("%H:%M:%S")))
+
+ try:
+ pool = Pool(args.njobs)
+
+ results = []
+ start = time()
+ with Manager() as manager:
+ running = manager.list()
+ payloads = [(t, tid, args, running) for (tid, t) in enumerate(tests)]
+ for tid, test in enumerate(pool.imap_unordered(run_one, payloads, 1)):
+ test.report(tid + 1, running, tests)
+ results.append(test)
+ pool.close()
+ pool.join()
+ suite_time = time() - start
+
+ for t in results:
+ t.njobs = args.njobs
+ t.suite_time = suite_time
+
+ Test.summarize(results)
+ Test.build_report(results, args.report)
+ except KeyboardInterrupt:
+ try:
+ pool.terminate()
+ pool.join()
+ except (FileNotFoundError, EOFError, ConnectionAbortedError):
+ pass
+ debug(Debug.ERROR, "Testing interrupted")
+
+
+def diff(paths, force_accept, difftool):
+ for path in expand_lsts(paths):
+ if not os.path.exists(path):
+ debug(Debug.ERROR, "Not found: {}".format(path))
+ else:
+ test = Test(None, path, [], None)
+ accept = force_accept
+
+ if not accept:
+ call([difftool, test.expect_path, test.temp_output_path])
+ accept = input("Accept this change? (y/N) ") == "y"
+
+ if accept:
+ debug(Debug.INFO, path, "accepted.")
+ shutil.copy(test.temp_output_path, test.expect_path)
+ else:
+ debug(Debug.INFO, path, "not accepted.")
+
+def compare_results(globs, time_all):
+ from glob import glob
+ paths = [path for g in globs for path in glob(g)]
+ reports = {path: Test.load_report(path) for path in paths}
+ resultsets = {path: {test.name: (test.status, test.duration) for test in report}
+ for path, report in reports.items()}
+
+ all_tests = set(name for resultset in resultsets.values() for name in resultset.keys())
+
+ reference = resultsets[paths[0]]
+ for path, resultset in resultsets.items():
+ resultset["$$TOTAL$$"] = None, sum(v[1] for v in resultset.values() if v[1] and v[0] != TestStatus.TIMEOUT)
+
+ with open("compare.csv", mode='w', newline='') as writer:
+ csv_writer = csv.writer(writer, dialect='excel')
+ csv_writer.writerow(["Name"] + [os.path.split(path)[1].lstrip("0123456789-") for path in paths])
+
+ for name in sorted(all_tests) + ["$$TOTAL$$"]:
+ ref_status, ref_duration = reference[name]
+
+ row = []
+ row.append(name)
+ row.append(ref_duration)
+ for path in paths[1:]:
+ res = resultsets[path].get(name)
+ test_status, test_duration = res if res else (TestStatus.UNKNOWN, None)
+ if res is not None and (test_status == ref_status or time_all):
+ result = "{:.2%}".format((test_duration - ref_duration) / ref_duration)
+ else:
+ result = test_status.name + "?!"
+ row.append(result)
+
+ csv_writer.writerow(row)
+
+def main():
+ global VERBOSITY
+ parser = setup_parser()
+ args = parser.parse_args()
+ VERBOSITY = args.verbosity
+
+ if os.name != 'nt' and os.environ.get("TERM") == "cygwin":
+ debug(Debug.WARNING, "If you run into issues, try using Windows' Python instead of Cygwin's")
+
+ if args.diff or args.accept:
+ diff(args.path, args.accept, args.difftool)
+ elif args.open:
+ os.startfile(args.path[0])
+ elif args.compare:
+ compare_results(args.path, args.time_all)
+ else:
+ run_tests(args)
+
+if __name__ == '__main__':
+ main()
diff --git a/Test/server/minimal.transcript b/Test/server/minimal.transcript
new file mode 100644
index 00000000..394fd921
--- /dev/null
+++ b/Test/server/minimal.transcript
@@ -0,0 +1,8 @@
+# RUN: "%server" "%s" > "%t"
+# RUN: %diff "%s.expect" "%t"
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xuICBhc3NlcnQgZmFsc2U7XG59XG4iLCJz
+b3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
diff --git a/Test/server/minimal.transcript.expect b/Test/server/minimal.transcript.expect
new file mode 100644
index 00000000..bf3f9dfb
--- /dev/null
+++ b/Test/server/minimal.transcript.expect
@@ -0,0 +1,14 @@
+# Reading from minimal.transcript
+
+Verifying CheckWellformed$$_module.__default.A ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+ [2 proof obligations] error
+transcript(3,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
diff --git a/Test/server/simple-session.transcript b/Test/server/simple-session.transcript
new file mode 100644
index 00000000..d095f6dd
--- /dev/null
+++ b/Test/server/simple-session.transcript
@@ -0,0 +1,637 @@
+# RUN: "%server" "%s" > "%t"
+# RUN: %diff "%s.expect" "%t"
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xuICBhc3NlcnQgZmFsc2U7XG59XG4iLCJz
+b3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xuICBhc3NlcnQgZmFsc2U7XG4gfVxuIiwi
+c291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xuICBhc3NlcnQgZmFsc2U7XG59XG4iLCJz
+b3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xuICBhc3NlcnQgZmFsc2U7XG59XG4iLCJz
+b3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG4iLCJzb3VyY2VJc0ZpbGUiOmZh
+bHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7Iiwic291
+cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gICIs
+InNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG59Iiwi
+c291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG5cbn0i
+LCJzb3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIFxu
+fSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIHhc
+bn0iLCJzb3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIFxu
+fSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIHZh
+clxufSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIHZh
+ciB4IDo9IDE7XG59Iiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIHZh
+ciB4IDo9IDE7XG4gIFxufSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIHZh
+ciB4IDo9IDE7XG4gIHZhciB5LCB6IDo9IDI7XG59Iiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIHZh
+ciB4IDo9IDE7XG4gIHZhciB5LCB6IDo9IDI7XG4gIFxufSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIHZh
+ciB4IDo9IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIFxufSIsInNvdXJjZUlzRmlsZSI6ZmFs
+c2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKCkg
+e1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAyLCAzO1xuICBcbn0iLCJzb3VyY2VJc0Zp
+bGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKCkg
+e1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAyLCAzO1xuICBcbn0iLCJzb3VyY2VJc0Zp
+bGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcblxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAyLCAzO1xuICBcbn0iLCJz
+b3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcblxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAyLCAzO1xuICBcbn0iLCJz
+b3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgXG57XG4gIHZhciB4IDo9IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIFxufSIs
+InNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxXG57XG4gIHZhciB4IDo9IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIFxu
+fSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxXG57XG4gIHZhciB4IDo9IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIFxu
+fSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgXG59Iiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzXG59Iiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvclxufSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JyA6OiBcbn0iLCJzb3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JyA6OiBpbnQsIHgnICogeCcgPiAwO1xufSIsInNvdXJj
+ZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JyA6OiBpbnQsIHgnICogeCcgPiAwO1xufSIsInNvdXJj
+ZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4J2ludCwgeCcgKiB4JyA+IDA7XG59Iiwic291cmNlSXNG
+aWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50LCB4JyAqIHgnID4gMDtcbn0iLCJzb3VyY2VJ
+c0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6ICwgeCcgKiB4JyA+IDA7XG59Iiwic291
+cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPiAwO1xufSIsInNvdXJj
+ZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbn0iLCJzb3Vy
+Y2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgXG59Iiwi
+c291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzXG59
+Iiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn0iLCJzb3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn0iLCJz
+b3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn0iLCJz
+b3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cbiIs
+InNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNJyh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59Iiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNJyh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59Iiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNJyh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG4iLCJzb3VyY2VJc0ZpbGUiOmZhbHNl
+fQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNJyh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTScodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiB4JyA+PSAwO1xuICBhc3NlcnQg
+eCAqIHggPiAwO1xufSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0odDogbmF0KVxuICByZXF1aXJlcyB0
+ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAyLCAzO1xuICBhc3NlcnQgZm9y
+YWxsIHgnOiBpbnQgOjogeCcgKiB4JyA+PSAwO1xuICBhc3NlcnQgeCAqIHggPiAwO1xufVxuXG5z
+dGF0aWMgbWV0aG9kIE0odDogbmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAx
+O1xuICB2YXIgeSwgeiA6PSAyLCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiB4
+JyA+PSAwO1xuICBhc3NlcnQgeCAqIHggPiAwO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0odDogbmF0
+KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAyLCAz
+O1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiB4JyA+PSAwO1xuICBhc3NlcnQgeCAq
+IHggPiAwO1xufSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiB4JyA+PSAwO1xuICBhc3NlcnQg
+eCAqIHggPiAwO1xufSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiB4JyA+PSAwO1xuICBhc3NlcnQg
+eCAqIHggPiAwO1xufVxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnID49IDA7XG4gIGFzc2Vy
+dCB4ICogeCA+IDA7XG59XG4iLCJzb3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnICAwO1xuICBhc3NlcnQg
+eCAqIHggPiAwO1xufVxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDwgMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cbiIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc2Vy
+dCB4ICogeCA+IDA7XG59XG4iLCJzb3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIFxuICBh
+c3NlcnQgeCAqIHggPiAwO1xufVxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc1xu
+ICBhc3NlcnQgeCAqIHggPiAwO1xufVxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc2Vy
+XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG4iLCJzb3VyY2VJc0ZpbGUiOmZhbHNlfQ==
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc1xu
+ICBhc3NlcnQgeCAqIHggPiAwO1xufVxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc3Vt
+ZSB5IDwgMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cbiIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc3Vt
+ZSB5IDwgMDtcbiAgYXNzZXJ0IDtcbn1cbiIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc3Vt
+ZSB5IDwgMDtcbiAgYXNzZXJ0IDtcbn1cbiIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc3Vt
+ZSB5IDwgMDtcbiAgYXNzZXJ0IGZhbDtcbn1cbiIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc3Vt
+ZSB5IDwgMDtcbiAgYXNzZXJ0IGZhbHNlO1xufVxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
+verify
+eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp
+bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50
+KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6
+IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g
+MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0
+IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg
+dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv
+cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu
+c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9
+IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq
+IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog
+bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy
+LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc3Vt
+ZSB5IDwgMDtcbiAgYXNzZXJ0IGZhbHNlO1xufVxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0=
+[[DAFNY-CLIENT: EOM]]
diff --git a/Test/server/simple-session.transcript.expect b/Test/server/simple-session.transcript.expect
new file mode 100644
index 00000000..a5f841bc
--- /dev/null
+++ b/Test/server/simple-session.transcript.expect
@@ -0,0 +1,992 @@
+# Reading from simple-session.transcript
+
+Verifying CheckWellformed$$_module.__default.A ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+ [2 proof obligations] error
+transcript(3,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [2 proof obligations] error
+transcript(3,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [2 proof obligations] error
+transcript(3,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [2 proof obligations] error
+transcript(3,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+
+Verifying CheckWellformed$$_module.__default.A ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+ [0 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,13): Error: rbrace expected
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(6,2): Error: rbrace expected
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+ [0 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [0 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [0 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(7,0): Error: invalid UpdateStmt
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [0 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(7,0): Error: invalid VarDeclStatement
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+ [1 proof obligation] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [1 proof obligation] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(7,11): Error: the number of left-hand sides (2) and right-hand sides (1) must match for a multi-assignment
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(7,11): Error: the number of left-hand sides (2) and right-hand sides (1) must match for a multi-assignment
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+ [2 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [2 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [2 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+ [3 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [3 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [3 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(6,2): Error: EOF expected
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(6,2): Error: EOF expected
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+ [3 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(11,0): Error: invalid UpdateStmt
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(11,0): Error: semi expected
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(11,0): Error: invalid UnaryExpression
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,25): Error: openparen expected
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,25): Error: openparen expected
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,26): Error: invalid QSep
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,28): Error: invalid QSep
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,27): Error: invalid UnaryExpression
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,9): Warning: /!\ No terms found to trigger on.
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+ [1 proof obligation] error
+transcript(10,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,9): Warning: /!\ No terms found to trigger on.
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+ [1 proof obligation] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,9): Warning: /!\ No terms found to trigger on.
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [1 proof obligation] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(12,0): Error: invalid UpdateStmt
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,9): Warning: /!\ No terms found to trigger on.
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+ [1 proof obligation] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,9): Warning: /!\ No terms found to trigger on.
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [1 proof obligation] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,14): Error: Duplicate member name: M'
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,14): Error: Duplicate member name: M'
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,14): Error: Duplicate member name: M'
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,14): Error: Duplicate member name: M'
+transcript(24,14): Error: Duplicate member name: M'
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,14): Error: Duplicate member name: M'
+transcript(24,14): Error: Duplicate member name: M'
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,14): Error: Duplicate member name: M'
+transcript(24,14): Error: Duplicate member name: M'
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,14): Error: Duplicate member name: M'
+transcript(24,14): Error: Duplicate member name: M'
+transcript(33,14): Error: Duplicate member name: M'
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,14): Error: Duplicate member name: M
+transcript(33,14): Error: Duplicate member name: M
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,9): Warning: /!\ No terms found to trigger on.
+transcript(20,9): Warning: /!\ No terms found to trigger on.
+transcript(29,9): Warning: /!\ No terms found to trigger on.
+transcript(38,9): Warning: /!\ No terms found to trigger on.
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [1 proof obligation] verified
+
+Verifying CheckWellformed$$_module.__default.M0 ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M0 ...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M1 ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M1 ...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M2 ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M2 ...
+ [5 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,9): Warning: /!\ No terms found to trigger on.
+transcript(20,9): Warning: /!\ No terms found to trigger on.
+transcript(29,9): Warning: /!\ No terms found to trigger on.
+transcript(38,9): Warning: /!\ No terms found to trigger on.
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [1 proof obligation] verified
+
+Verifying CheckWellformed$$_module.__default.M0 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M0 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M0...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M1 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M1 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M1...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M2 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M2...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M2 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M2...
+ [5 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,9): Warning: /!\ No terms found to trigger on.
+transcript(20,9): Warning: /!\ No terms found to trigger on.
+transcript(29,9): Warning: /!\ No terms found to trigger on.
+transcript(38,9): Warning: /!\ No terms found to trigger on.
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [1 proof obligation] verified
+
+Verifying CheckWellformed$$_module.__default.M0 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M0 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M0...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M1 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M1 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M1...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M2 ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M2 ...
+ [2 proof obligations] error
+transcript(38,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(38,38): Error: semi expected
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,9): Warning: /!\ No terms found to trigger on.
+transcript(20,9): Warning: /!\ No terms found to trigger on.
+transcript(29,9): Warning: /!\ No terms found to trigger on.
+transcript(38,9): Warning: /!\ No terms found to trigger on.
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [1 proof obligation] verified
+
+Verifying CheckWellformed$$_module.__default.M0 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M0 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M0...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M1 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M1 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M1...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M2 ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M2 ...
+ [2 proof obligations] error
+transcript(38,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,9): Warning: /!\ No terms found to trigger on.
+transcript(20,9): Warning: /!\ No terms found to trigger on.
+transcript(29,9): Warning: /!\ No terms found to trigger on.
+transcript(38,9): Warning: /!\ No terms found to trigger on.
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [1 proof obligation] verified
+
+Verifying CheckWellformed$$_module.__default.M0 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M0 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M0...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M1 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M1 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M1...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M2 ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M2 ...
+ [2 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,9): Warning: /!\ No terms found to trigger on.
+transcript(20,9): Warning: /!\ No terms found to trigger on.
+transcript(29,9): Warning: /!\ No terms found to trigger on.
+transcript(38,9): Warning: /!\ No terms found to trigger on.
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [1 proof obligation] verified
+
+Verifying CheckWellformed$$_module.__default.M0 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M0 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M0...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M1 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M1 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M1...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M2 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M2...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M2 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M2...
+ [2 proof obligations] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(40,2): Error: invalid UpdateStmt
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(40,2): Error: invalid UpdateStmt
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(40,2): Error: invalid UpdateStmt
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,9): Warning: /!\ No terms found to trigger on.
+transcript(20,9): Warning: /!\ No terms found to trigger on.
+transcript(29,9): Warning: /!\ No terms found to trigger on.
+transcript(38,9): Warning: /!\ No terms found to trigger on.
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [1 proof obligation] verified
+
+Verifying CheckWellformed$$_module.__default.M0 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M0 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M0...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M1 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M1 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M1...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M2 ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M2 ...
+ [1 proof obligation] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(40,9): Error: invalid AssertStmt
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(40,9): Error: invalid AssertStmt
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(40,9): Error: unresolved identifier: fal
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,9): Warning: /!\ No terms found to trigger on.
+transcript(20,9): Warning: /!\ No terms found to trigger on.
+transcript(29,9): Warning: /!\ No terms found to trigger on.
+transcript(38,9): Warning: /!\ No terms found to trigger on.
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [1 proof obligation] verified
+
+Verifying CheckWellformed$$_module.__default.M0 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M0 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M0...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M1 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M1 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M1...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M2 ...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M2 ...
+ [1 proof obligation] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here
+transcript(10,9): Warning: /!\ No terms found to trigger on.
+transcript(20,9): Warning: /!\ No terms found to trigger on.
+transcript(29,9): Warning: /!\ No terms found to trigger on.
+transcript(38,9): Warning: /!\ No terms found to trigger on.
+
+Verifying CheckWellformed$$_module.__default.A ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.A ...
+Retrieving cached verification result for implementation Impl$$_module.__default.A...
+ [0 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M_k ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M_k ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M_k...
+ [1 proof obligation] verified
+
+Verifying CheckWellformed$$_module.__default.M0 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M0 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M0...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M1 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M1 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M1...
+ [5 proof obligations] verified
+
+Verifying CheckWellformed$$_module.__default.M2 ...
+Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M2...
+ [0 proof obligations] verified
+
+Verifying Impl$$_module.__default.M2 ...
+Retrieving cached verification result for implementation Impl$$_module.__default.M2...
+ [1 proof obligation] verified
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
+Verification completed successfully!
+[SUCCESS] [[DAFNY-SERVER: EOM]]
diff --git a/Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy b/Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy
new file mode 100644
index 00000000..09032453
--- /dev/null
+++ b/Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy
@@ -0,0 +1,23 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This example was listed in IronClad's notebook as one place were z3 picked
+// much too liberal triggers. THe Boogie code for this is shown below:
+//
+// forall k#2: Seq Box :: $Is(k#2, TSeq(TInt)) && $IsAlloc(k#2, TSeq(TInt), $Heap)
+// ==> Seq#Equal(_module.__default.HashtableLookup($Heap, h1#0, k#2),
+// _module.__default.HashtableLookup($Heap, h2#0, k#2))
+//
+// and z3 would pick $Is(k#2, TSeq(TInt)) or $IsAlloc(k#2, TSeq(TInt), $Heap) as
+// triggers.
+
+type Key = seq<int>
+type Value = seq<int>
+
+type Hashtable = map<Key, Value>
+function HashtableLookup(h: Hashtable, k: Key): Value
+
+lemma HashtableAgreement(h1:Hashtable, h2:Hashtable, k:Key)
+ requires forall k :: HashtableLookup(h1,k) == HashtableLookup(h2,k) {
+ assert true || (k in h1) == (k in h2);
+}
diff --git a/Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy.expect b/Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy.expect
new file mode 100644
index 00000000..46ec143e
--- /dev/null
+++ b/Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy.expect
@@ -0,0 +1,4 @@
+auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy(21,11): Info: Selected triggers:
+ {HashtableLookup(h2, k)}, {HashtableLookup(h1, k)}
+
+Dafny program verifier finished with 3 verified, 0 errors
diff --git a/Test/triggers/constructors-cause-matching-loops.dfy b/Test/triggers/constructors-cause-matching-loops.dfy
new file mode 100644
index 00000000..61e6a66b
--- /dev/null
+++ b/Test/triggers/constructors-cause-matching-loops.dfy
@@ -0,0 +1,11 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file is just a small test to check that constructors do cause loops
+
+datatype Nat = Zero | Succ(x: Nat)
+function f(n: Nat): Nat
+
+method M() {
+ assert forall s :: true || f(Succ(s)) == f(s);
+}
diff --git a/Test/triggers/constructors-cause-matching-loops.dfy.expect b/Test/triggers/constructors-cause-matching-loops.dfy.expect
new file mode 100644
index 00000000..e7a671ab
--- /dev/null
+++ b/Test/triggers/constructors-cause-matching-loops.dfy.expect
@@ -0,0 +1,6 @@
+constructors-cause-matching-loops.dfy(10,9): Info: Selected triggers: {Succ(s)}
+ Rejected triggers:
+ {f(s)} (may loop with "f(Succ(s))")
+ {f(Succ(s))} (more specific than {Succ(s)})
+
+Dafny program verifier finished with 3 verified, 0 errors
diff --git a/Test/triggers/function-applications-are-triggers.dfy b/Test/triggers/function-applications-are-triggers.dfy
new file mode 100644
index 00000000..0aad9018
--- /dev/null
+++ b/Test/triggers/function-applications-are-triggers.dfy
@@ -0,0 +1,15 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file checks that function applications yield trigger candidates
+
+method M(P: (int -> int) -> bool, g: int -> int)
+ requires P.requires(g)
+ requires P(g) {
+ assume forall f: int -> int :: P.requires(f);
+ assume forall f: int -> int :: P(f) ==> f.requires(10) && f(10) == 0;
+ assert forall f: int -> int ::
+ (forall x :: f.requires(x) && g.requires(x) ==> f(x) == g(x)) ==>
+ f.requires(10) ==>
+ f(10) == 0;
+}
diff --git a/Test/triggers/function-applications-are-triggers.dfy.expect b/Test/triggers/function-applications-are-triggers.dfy.expect
new file mode 100644
index 00000000..1214536d
--- /dev/null
+++ b/Test/triggers/function-applications-are-triggers.dfy.expect
@@ -0,0 +1,13 @@
+function-applications-are-triggers.dfy(9,9): Info: Selected triggers: {P.requires(f)}
+function-applications-are-triggers.dfy(10,9): Info: For expression "P(f) ==> f.requires(10)":
+ Selected triggers:
+ {f(10)}, {f.requires(10)}, {P(f)}
+function-applications-are-triggers.dfy(10,9): Info: For expression "P(f) ==> f(10) == 0":
+ Selected triggers:
+ {f(10)}, {f.requires(10)}, {P(f)}
+function-applications-are-triggers.dfy(11,9): Info: Selected triggers:
+ {f(10)}, {f.requires(10)}
+function-applications-are-triggers.dfy(12,5): Info: Selected triggers:
+ {g(x)}, {f(x)}, {g.requires(x)}, {f.requires(x)}
+
+Dafny program verifier finished with 2 verified, 0 errors
diff --git a/Test/triggers/large-quantifiers-dont-break-dafny.dfy b/Test/triggers/large-quantifiers-dont-break-dafny.dfy
new file mode 100644
index 00000000..58eb56e1
--- /dev/null
+++ b/Test/triggers/large-quantifiers-dont-break-dafny.dfy
@@ -0,0 +1,61 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This test ensures that the trigger collector (the routine that picks trigger
+// candidates) does not actually consider all subsets of terms; if it did, the
+// following would take horribly long
+
+predicate P0(x: bool)
+predicate P1(x: bool)
+predicate P2(x: bool)
+predicate P3(x: bool)
+predicate P4(x: bool)
+predicate P5(x: bool)
+predicate P6(x: bool)
+predicate P7(x: bool)
+predicate P8(x: bool)
+predicate P9(x: bool)
+predicate P10(x: bool)
+predicate P11(x: bool)
+predicate P12(x: bool)
+predicate P13(x: bool)
+predicate P14(x: bool)
+predicate P15(x: bool)
+predicate P16(x: bool)
+predicate P17(x: bool)
+predicate P18(x: bool)
+predicate P19(x: bool)
+predicate P20(x: bool)
+predicate P21(x: bool)
+predicate P22(x: bool)
+predicate P23(x: bool)
+predicate P24(x: bool)
+predicate P25(x: bool)
+predicate P26(x: bool)
+predicate P27(x: bool)
+predicate P28(x: bool)
+predicate P29(x: bool)
+predicate P30(x: bool)
+predicate P31(x: bool)
+predicate P32(x: bool)
+predicate P33(x: bool)
+predicate P34(x: bool)
+predicate P35(x: bool)
+predicate P36(x: bool)
+predicate P37(x: bool)
+predicate P38(x: bool)
+predicate P39(x: bool)
+predicate P40(x: bool)
+predicate P41(x: bool)
+predicate P42(x: bool)
+predicate P43(x: bool)
+predicate P44(x: bool)
+predicate P45(x: bool)
+predicate P46(x: bool)
+predicate P47(x: bool)
+predicate P48(x: bool)
+predicate P49(x: bool)
+
+method M() {
+ assert forall x :: true || P0(x) || P1(x) || P2(x) || P3(x) || P4(x) || P5(x) || P6(x) || P7(x) || P8(x) || P9(x) || P10(x) || P11(x) || P12(x) || P13(x) || P14(x) || P15(x) || P16(x) || P17(x) || P18(x) || P19(x) || P20(x) || P21(x) || P22(x) || P23(x) || P24(x) || P25(x) || P26(x) || P27(x) || P28(x) || P29(x) || P30(x) || P31(x) || P32(x) || P33(x) || P34(x) || P35(x) || P36(x) || P37(x) || P38(x) || P39(x) || P40(x) || P41(x) || P42(x) || P43(x) || P44(x) || P45(x) || P46(x) || P47(x) || P48(x) || P49(x);
+}
diff --git a/Test/triggers/large-quantifiers-dont-break-dafny.dfy.expect b/Test/triggers/large-quantifiers-dont-break-dafny.dfy.expect
new file mode 100644
index 00000000..5e7c14b9
--- /dev/null
+++ b/Test/triggers/large-quantifiers-dont-break-dafny.dfy.expect
@@ -0,0 +1,4 @@
+large-quantifiers-dont-break-dafny.dfy(60,9): Info: Selected triggers:
+ {P49(x)}, {P48(x)}, {P47(x)}, {P46(x)}, {P45(x)}, {P44(x)}, {P43(x)}, {P42(x)}, {P41(x)}, {P40(x)}, {P39(x)}, {P38(x)}, {P37(x)}, {P36(x)}, {P35(x)}, {P34(x)}, {P33(x)}, {P32(x)}, {P31(x)}, {P30(x)}, {P29(x)}, {P28(x)}, {P27(x)}, {P26(x)}, {P25(x)}, {P24(x)}, {P23(x)}, {P22(x)}, {P21(x)}, {P20(x)}, {P19(x)}, {P18(x)}, {P17(x)}, {P16(x)}, {P15(x)}, {P14(x)}, {P13(x)}, {P12(x)}, {P11(x)}, {P10(x)}, {P9(x)}, {P8(x)}, {P7(x)}, {P6(x)}, {P5(x)}, {P4(x)}, {P3(x)}, {P2(x)}, {P1(x)}, {P0(x)}
+
+Dafny program verifier finished with 52 verified, 0 errors
diff --git a/Test/triggers/loop-detection-is-not-too-strict.dfy b/Test/triggers/loop-detection-is-not-too-strict.dfy
new file mode 100644
index 00000000..81f764ad
--- /dev/null
+++ b/Test/triggers/loop-detection-is-not-too-strict.dfy
@@ -0,0 +1,40 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This test shows that the loop detection engine makes compromises when looking
+// for subexpressions matching a trigger; in particular, it allows a
+// subexpression to match a trigger without reporting a loop and without being
+// equal to that trigger, as long as the only differences are variable
+
+predicate P(x: int, y: int)
+predicate Q(x: int)
+
+method Test(z: int) {
+ // P(x, y) and P(y, x) might look like they would cause a loop. Since they
+ // only differ by their variables, though, they won't raise flags.
+ assume forall x: int, y: int :: P(x, y) == P(y, x);
+
+ // This works independent of extra parentheses:
+ assume forall x: int, y: int :: P(x, y) == (P(y, x));
+
+ // Contrast with the following:
+ assume forall x: int, y: int :: P(x, y) == P(x, y+1);
+
+ // The following examples were made legal after an exchange where Chris
+ // pointed examples in the IronClad sources where things like this were
+ // incorrectly flagged.
+ assert forall x :: true || Q(x) || Q(0);
+ assert forall x :: true || Q(x) || Q(z);
+ assert forall x :: true || P(x, 1) || P(x, z);
+
+ // Support for the following was added following a discussion with Rustan; in
+ // the second one the expression `if z > 1 then z else 3 * z + 1` is not
+ // directly a constant expression, but it does not involve x, so it's ok:
+ assert forall x :: true || Q(x) || Q(0+1);
+ assert forall x :: true || Q(x) || Q(if z > 1 then z else 3 * z + 1);
+ // Sanity check:
+ assert forall x :: true || Q(x) || Q(if z > 1 then x else 3 * z + 1);
+
+ // WISH: It might also be good to zeta-reduce before loop detection.
+ assert forall x :: true || Q(x) || (var xx := x+1; Q(xx));
+}
diff --git a/Test/triggers/loop-detection-is-not-too-strict.dfy.expect b/Test/triggers/loop-detection-is-not-too-strict.dfy.expect
new file mode 100644
index 00000000..65ea0d79
--- /dev/null
+++ b/Test/triggers/loop-detection-is-not-too-strict.dfy.expect
@@ -0,0 +1,17 @@
+loop-detection-is-not-too-strict.dfy(15,9): Info: Selected triggers:
+ {P(y, x)}, {P(x, y)}
+loop-detection-is-not-too-strict.dfy(18,9): Info: Selected triggers:
+ {P(y, x)}, {P(x, y)}
+loop-detection-is-not-too-strict.dfy(21,9): Warning: Selected triggers: {P(x, y)} (may loop with "P(x, y + 1)")
+ /!\ Suppressing loops would leave this expression without triggers.
+loop-detection-is-not-too-strict.dfy(26,9): Info: Selected triggers: {Q(x)}
+loop-detection-is-not-too-strict.dfy(27,9): Info: Selected triggers: {Q(x)}
+loop-detection-is-not-too-strict.dfy(28,9): Info: Selected triggers:
+ {P(x, z)}, {P(x, 1)}
+loop-detection-is-not-too-strict.dfy(33,9): Info: Selected triggers: {Q(x)}
+loop-detection-is-not-too-strict.dfy(34,9): Info: Selected triggers: {Q(x)}
+loop-detection-is-not-too-strict.dfy(36,9): Warning: Selected triggers: {Q(x)} (may loop with "Q(if z > 1 then x else 3 * z + 1)")
+ /!\ Suppressing loops would leave this expression without triggers.
+loop-detection-is-not-too-strict.dfy(39,9): Info: Selected triggers: {Q(x)}
+
+Dafny program verifier finished with 4 verified, 0 errors
diff --git a/Test/triggers/loop-detection-looks-at-ranges-too.dfy b/Test/triggers/loop-detection-looks-at-ranges-too.dfy
new file mode 100644
index 00000000..7a99ea2d
--- /dev/null
+++ b/Test/triggers/loop-detection-looks-at-ranges-too.dfy
@@ -0,0 +1,14 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file checks that loops between the range and the term of a quantifier
+// are properly detected.
+
+predicate P(x: int)
+
+method M(x: int) {
+ // This will be flagged as a loop even without looking at the range
+ assert true || forall x: int | P(x) :: P(x+1);
+ // This requires checking the range for looping terms
+ assert true || forall x: int | P(x+1) :: P(x);
+}
diff --git a/Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect b/Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect
new file mode 100644
index 00000000..a32e4a60
--- /dev/null
+++ b/Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect
@@ -0,0 +1,6 @@
+loop-detection-looks-at-ranges-too.dfy(11,17): Warning: Selected triggers: {P(x)} (may loop with "P(x + 1)")
+ /!\ Suppressing loops would leave this expression without triggers.
+loop-detection-looks-at-ranges-too.dfy(13,17): Warning: Selected triggers: {P(x)} (may loop with "P(x + 1)")
+ /!\ Suppressing loops would leave this expression without triggers.
+
+Dafny program verifier finished with 3 verified, 0 errors
diff --git a/Test/triggers/loop-detection-messages--unit-tests.dfy b/Test/triggers/loop-detection-messages--unit-tests.dfy
new file mode 100644
index 00000000..c1560317
--- /dev/null
+++ b/Test/triggers/loop-detection-messages--unit-tests.dfy
@@ -0,0 +1,29 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file is a series of basic tests for loop detection, focusing on the
+// warnings and information messages
+
+function f(i: int): int
+function g(i: int): int
+
+method M() {
+ assert forall i :: false ==> f(i) == f(f(i));
+ assert forall i :: false ==> f(i) == f(i+1);
+ assert forall i {:matchingloop} :: false ==> f(i) == f(i+1);
+
+ assert forall i :: false ==> f(i) == f(i+1) && f(i) == g(i);
+ assert forall i :: false ==> f(i) == f(i+1) && f(i) == f(i);
+ assert forall i {:matchingloop} :: false ==> f(i) == f(i+1) && f(i) == f(i);
+
+ assert forall i :: false ==> f(i) == 0;
+ assert forall i :: false ==> f(i+1) == 0;
+ assert forall i {:autotriggers false} :: false ==> f(i+1) == 0;
+
+ assert forall i, j: int :: false ==> f(i) == f(j);
+ assert forall i, j: int :: false ==> f(i) == f(i);
+ assert forall i, j: int :: false ==> f(i) == f(i) && g(j) == 0;
+ assert forall i, j: int :: false ==> f(i) == f(i) && g(j+1) == 0;
+ assert forall i, j: int {:autotriggers false} :: false ==> f(i) == f(i);
+ assert forall i, j: int {:trigger f(i), g(j)} :: false ==> f(i) == f(i);
+}
diff --git a/Test/triggers/loop-detection-messages--unit-tests.dfy.expect b/Test/triggers/loop-detection-messages--unit-tests.dfy.expect
new file mode 100644
index 00000000..eba8c179
--- /dev/null
+++ b/Test/triggers/loop-detection-messages--unit-tests.dfy.expect
@@ -0,0 +1,37 @@
+loop-detection-messages--unit-tests.dfy(11,9): Info: Selected triggers: {f(f(i))}
+ Rejected triggers: {f(i)} (may loop with "f(f(i))")
+loop-detection-messages--unit-tests.dfy(12,9): Warning: Selected triggers: {f(i)} (may loop with "f(i + 1)")
+ /!\ Suppressing loops would leave this expression without triggers.
+loop-detection-messages--unit-tests.dfy(13,9): Info: Selected triggers: {f(i)} (may loop with "f(i + 1)")
+loop-detection-messages--unit-tests.dfy(15,9): Info: For expression "false ==> f(i) == f(i + 1)":
+ Selected triggers: {g(i)}
+ Rejected triggers: {f(i)} (may loop with "f(i + 1)")
+loop-detection-messages--unit-tests.dfy(15,9): Info: For expression "false ==> f(i) == g(i)":
+ Selected triggers:
+ {g(i)}, {f(i)}
+loop-detection-messages--unit-tests.dfy(16,9): Warning: For expression "false ==> f(i) == f(i + 1)":
+ Selected triggers: {f(i)} (may loop with "f(i + 1)")
+ /!\ Suppressing loops would leave this expression without triggers.
+loop-detection-messages--unit-tests.dfy(16,9): Info: For expression "false ==> f(i) == f(i)":
+ Selected triggers: {f(i)}
+loop-detection-messages--unit-tests.dfy(17,9): Info: For expression "false ==> f(i) == f(i + 1)":
+ Selected triggers: {f(i)} (may loop with "f(i + 1)")
+loop-detection-messages--unit-tests.dfy(17,9): Info: For expression "false ==> f(i) == f(i)":
+ Selected triggers: {f(i)}
+loop-detection-messages--unit-tests.dfy(19,9): Info: Selected triggers: {f(i)}
+loop-detection-messages--unit-tests.dfy(20,9): Warning: /!\ No terms found to trigger on.
+loop-detection-messages--unit-tests.dfy(21,9): Info: Not generating triggers for "false ==> f(i + 1) == 0". Note that {:autotriggers false} can cause instabilities. Consider using {:nowarn}, {:matchingloop} (not great either), or a manual trigger instead.
+loop-detection-messages--unit-tests.dfy(23,9): Info: Selected triggers: {f(j), f(i)}
+loop-detection-messages--unit-tests.dfy(24,9): Warning: /!\ No trigger covering all quantified variables found.
+loop-detection-messages--unit-tests.dfy(25,9): Info: For expression "false ==> f(i) == f(i)":
+ Selected triggers: {g(j), f(i)}
+loop-detection-messages--unit-tests.dfy(25,9): Info: For expression "false ==> g(j) == 0":
+ Selected triggers: {g(j), f(i)}
+loop-detection-messages--unit-tests.dfy(26,9): Warning: For expression "false ==> f(i) == f(i)":
+ /!\ No trigger covering all quantified variables found.
+loop-detection-messages--unit-tests.dfy(26,9): Warning: For expression "false ==> g(j + 1) == 0":
+ /!\ No trigger covering all quantified variables found.
+loop-detection-messages--unit-tests.dfy(27,9): Info: Not generating triggers for "false ==> f(i) == f(i)". Note that {:autotriggers false} can cause instabilities. Consider using {:nowarn}, {:matchingloop} (not great either), or a manual trigger instead.
+loop-detection-messages--unit-tests.dfy(28,9): Info: Not generating triggers for "false ==> f(i) == f(i)".
+
+Dafny program verifier finished with 4 verified, 0 errors
diff --git a/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy b/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy
new file mode 100644
index 00000000..c54089f2
--- /dev/null
+++ b/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy
@@ -0,0 +1,32 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file shows cases where loops could hide behind equalities. In all three
+// cases we behave the same; that is, we don't warn for loops that would only
+// exist in the presence of an equality. The easiest way to understand the
+// issue, I (CPC) feel, is to look at the old case: f(x) could very well loop
+// with old(f(f(x))) if f(f(x)) did not change the heap at all.
+
+// This equality issue is generally undecidable. It could make sense to special
+// case `old`, but KRML and CPC decided against it on 2015-08-21. Future
+// experiences could cause a change of mind.
+
+class C { }
+function f(c: C): C
+function g(c: C): C
+function h(c: C, i: int): C
+
+// With explicit arguments
+method M0(i: int, j: int, sc: set<C>) {
+ assert forall c | c in sc :: true || h(c, i) == h(h(c, j), j);
+}
+
+// With implicit arguments (f and g respectively, to Apply)
+method M1(f: int -> int, g: int -> int) {
+ assert forall x :: true || f(x) == g(f(x));
+}
+
+// With implicit arguments (the heap, to old)
+method M2(sc: set<C>) {
+ assert forall c | c in sc :: true || f(c) == old(f(f(c)));
+}
diff --git a/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect b/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect
new file mode 100644
index 00000000..e900c1f9
--- /dev/null
+++ b/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect
@@ -0,0 +1,10 @@
+looping-is-hard-to-decide-modulo-equality.dfy(21,9): Info: Selected triggers:
+ {h(h(c, j), j)}, {h(c, i)}, {c in sc}
+ Rejected triggers: {h(c, j)} (may loop with "h(h(c, j), j)")
+looping-is-hard-to-decide-modulo-equality.dfy(26,9): Info: Selected triggers: {f(x)}
+ Rejected triggers: {g(f(x))} (more specific than {f(x)})
+looping-is-hard-to-decide-modulo-equality.dfy(31,9): Info: Selected triggers:
+ {old(f(f(c)))}, {f(c)}, {c in sc}
+ Rejected triggers: {old(f(c))} (may loop with "old(f(f(c)))")
+
+Dafny program verifier finished with 9 verified, 0 errors
diff --git a/Test/triggers/matrix-accesses-are-triggers.dfy b/Test/triggers/matrix-accesses-are-triggers.dfy
new file mode 100644
index 00000000..630fab9d
--- /dev/null
+++ b/Test/triggers/matrix-accesses-are-triggers.dfy
@@ -0,0 +1,9 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file checks that multi-dimensional array accesses yield trigger candidates
+
+method M(m: array2<int>)
+ requires m != null
+ requires forall i, j | 0 <= i < m.Length0 && 0 <= j < m.Length1 :: m[i, j] == m[j, i+1] {
+}
diff --git a/Test/triggers/matrix-accesses-are-triggers.dfy.expect b/Test/triggers/matrix-accesses-are-triggers.dfy.expect
new file mode 100644
index 00000000..572fc41f
--- /dev/null
+++ b/Test/triggers/matrix-accesses-are-triggers.dfy.expect
@@ -0,0 +1,12 @@
+matrix-accesses-are-triggers.dfy(8,11): Warning: Selected triggers: {m[i, j]} (may loop with "m[j, i + 1]")
+ /!\ Suppressing loops would leave this expression without triggers.
+matrix-accesses-are-triggers.dfy(8,81): Error: index 0 out of range
+Execution trace:
+ (0,0): anon0
+ (0,0): anon4_Then
+matrix-accesses-are-triggers.dfy(8,86): Error: index 1 out of range
+Execution trace:
+ (0,0): anon0
+ (0,0): anon4_Then
+
+Dafny program verifier finished with 1 verified, 2 errors
diff --git a/Test/triggers/nested-quantifiers-all-get-triggers.dfy b/Test/triggers/nested-quantifiers-all-get-triggers.dfy
new file mode 100644
index 00000000..a55019db
--- /dev/null
+++ b/Test/triggers/nested-quantifiers-all-get-triggers.dfy
@@ -0,0 +1,9 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This checks that nested quantifiers do get triggers, and that the parent
+// quantifier does not get annotated twice
+
+method M() {
+ ghost var x := forall s: set<int>, x: int :: (x in s ==> forall y :: y == x ==> y in s);
+}
diff --git a/Test/triggers/nested-quantifiers-all-get-triggers.dfy.expect b/Test/triggers/nested-quantifiers-all-get-triggers.dfy.expect
new file mode 100644
index 00000000..172f5607
--- /dev/null
+++ b/Test/triggers/nested-quantifiers-all-get-triggers.dfy.expect
@@ -0,0 +1,4 @@
+nested-quantifiers-all-get-triggers.dfy(8,17): Info: Selected triggers: {x in s}
+nested-quantifiers-all-get-triggers.dfy(8,59): Info: Selected triggers: {y in s}
+
+Dafny program verifier finished with 2 verified, 0 errors
diff --git a/Test/triggers/old-is-a-special-case-for-triggers.dfy b/Test/triggers/old-is-a-special-case-for-triggers.dfy
new file mode 100644
index 00000000..4424e8d3
--- /dev/null
+++ b/Test/triggers/old-is-a-special-case-for-triggers.dfy
@@ -0,0 +1,32 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file ensures that `old()` receives the special treatment that it
+// requires; that is, `old(f(x))` is not less liberal than `f(x)`, and
+// old(f(f(x))) does not loop with f(x) (doesn't it?)
+
+class C { }
+function f(c: C): C
+function g(c: C): C
+function h(c: C, i: int): C
+
+method M(sc: set<C>)
+ // Ensure that old(c) does not get picked as a trigger
+ ensures forall c | c in sc :: true || c == old(f(c))
+
+ // This checks whether loop detection handles `old` expressions properly.
+ // In the first one f(c)/old(f(f(c))) is not reported as a loop. See
+ // looping-is-hard-to-decide-modulo-equalities.dfy for an explanation.
+ ensures forall c | c in sc :: true || f(c) == old(f(f(c)))
+ ensures forall c | c in sc :: true || old(f(f(c))) == old(g(f(c))) || old(f(g(c))) == g(f(c)) || f(g(c)) == g(f(c))
+
+ // These check that the final trigger filtering step doesn't get confused
+ // between old expressions and regular expressions.
+ ensures forall c | c in sc :: true || f(c) == old(g(f(c)))
+ ensures forall c | c in sc :: true || f(c) == old(f(c)) || old(g(f(c))) == g(f(c))
+
+ // WISH: A Dafny rewriter could cleanup expressions so that adding the
+ // expression forall c :: c == old(c) in a quantifier would cause a warning,
+ // instead of a trigger generation error as it does now.
+{
+}
diff --git a/Test/triggers/old-is-a-special-case-for-triggers.dfy.expect b/Test/triggers/old-is-a-special-case-for-triggers.dfy.expect
new file mode 100644
index 00000000..7388a911
--- /dev/null
+++ b/Test/triggers/old-is-a-special-case-for-triggers.dfy.expect
@@ -0,0 +1,22 @@
+old-is-a-special-case-for-triggers.dfy(15,10): Info: Selected triggers:
+ {old(f(c))}, {c in sc}
+old-is-a-special-case-for-triggers.dfy(20,10): Info: Selected triggers:
+ {old(f(f(c)))}, {f(c)}, {c in sc}
+ Rejected triggers: {old(f(c))} (may loop with "old(f(f(c)))")
+old-is-a-special-case-for-triggers.dfy(21,10): Info: Selected triggers:
+ {f(g(c))}, {g(f(c))}, {old(f(g(c)))}, {old(g(f(c)))}, {old(f(f(c)))}, {c in sc}
+ Rejected triggers:
+ {g(c)} (may loop with "g(f(c))")
+ {f(c)} (may loop with "f(g(c))")
+ {old(g(c))} (may loop with "old(g(f(c)))")
+ {old(f(c))} (may loop with "old(f(f(c)))", "old(f(g(c)))")
+old-is-a-special-case-for-triggers.dfy(25,10): Info: Selected triggers:
+ {old(f(c))}, {f(c)}, {c in sc}
+ Rejected triggers: {old(g(f(c)))} (more specific than {old(f(c))})
+old-is-a-special-case-for-triggers.dfy(26,10): Info: Selected triggers:
+ {old(f(c))}, {f(c)}, {c in sc}
+ Rejected triggers:
+ {g(f(c))} (more specific than {f(c)})
+ {old(g(f(c)))} (more specific than {old(f(c))})
+
+Dafny program verifier finished with 5 verified, 0 errors
diff --git a/Test/triggers/redundancy-detection-is-bidirectional.dfy b/Test/triggers/redundancy-detection-is-bidirectional.dfy
new file mode 100644
index 00000000..06541b70
--- /dev/null
+++ b/Test/triggers/redundancy-detection-is-bidirectional.dfy
@@ -0,0 +1,29 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This test checks for tricky cases of redundancy suppression when building
+// triggers.
+
+predicate P(x: int, y: int)
+predicate Q(x: int)
+predicate R(x: int)
+
+method M() {
+ // For this term, it is enough to order the terms by number of variables
+ assert forall x, y :: true || P(x, y) || Q(y) || R(x);
+ assert forall x, y :: true || Q(y) || P(x, y) || R(x);
+ assert forall x, y :: true || Q(y) || R(x) || P(x, y);
+}
+
+predicate PP(x: int, y: int, z: int)
+predicate QQ(x: int, y: int)
+predicate RR(x: int, y: int)
+predicate SS(x: int, y: int)
+
+method MM() {
+ // Not for this one, though
+ assert forall x, y, z, u, v, w :: true || PP(x, y, z) || QQ(x, u) || RR(y, v) || SS(z, w);
+ assert forall x, y, z, u, v, w :: true || QQ(x, u) || PP(x, y, z) || RR(y, v) || SS(z, w);
+ assert forall x, y, z, u, v, w :: true || QQ(x, u) || RR(y, v) || PP(x, y, z) || SS(z, w);
+ assert forall x, y, z, u, v, w :: true || QQ(x, u) || RR(y, v) || SS(z, w) || PP(x, y, z);
+}
diff --git a/Test/triggers/redundancy-detection-is-bidirectional.dfy.expect b/Test/triggers/redundancy-detection-is-bidirectional.dfy.expect
new file mode 100644
index 00000000..78c9e7ca
--- /dev/null
+++ b/Test/triggers/redundancy-detection-is-bidirectional.dfy.expect
@@ -0,0 +1,12 @@
+redundancy-detection-is-bidirectional.dfy(13,9): Info: Selected triggers:
+ {R(x), Q(y)}, {P(x, y)}
+redundancy-detection-is-bidirectional.dfy(14,9): Info: Selected triggers:
+ {R(x), Q(y)}, {P(x, y)}
+redundancy-detection-is-bidirectional.dfy(15,9): Info: Selected triggers:
+ {P(x, y)}, {R(x), Q(y)}
+redundancy-detection-is-bidirectional.dfy(25,9): Info: Selected triggers: {SS(z, w), RR(y, v), QQ(x, u)}
+redundancy-detection-is-bidirectional.dfy(26,9): Info: Selected triggers: {SS(z, w), RR(y, v), QQ(x, u)}
+redundancy-detection-is-bidirectional.dfy(27,9): Info: Selected triggers: {SS(z, w), RR(y, v), QQ(x, u)}
+redundancy-detection-is-bidirectional.dfy(28,9): Info: Selected triggers: {SS(z, w), RR(y, v), QQ(x, u)}
+
+Dafny program verifier finished with 11 verified, 0 errors
diff --git a/Test/triggers/regression-tests.dfy b/Test/triggers/regression-tests.dfy
new file mode 100644
index 00000000..263e424a
--- /dev/null
+++ b/Test/triggers/regression-tests.dfy
@@ -0,0 +1,20 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This tests checks that quantifier splitting is resilient to the fact that
+// certain statements (like calc) can return duplicate subexpressions. This was
+// once a problem, because a quantifier that got returned twice would get split
+// on the first pass over it, and would have its nely created children re-split
+// on the second pass. This created a split quantifier whose children were split
+// quantifiers, which violated an invariant of spliit quantifiers.
+
+abstract module Base { }
+
+module Blah refines Base {
+ lemma A() {
+ calc {
+ forall b :: b;
+ }
+ }
+}
+
diff --git a/Test/triggers/regression-tests.dfy.expect b/Test/triggers/regression-tests.dfy.expect
new file mode 100644
index 00000000..e780e966
--- /dev/null
+++ b/Test/triggers/regression-tests.dfy.expect
@@ -0,0 +1,3 @@
+regression-tests.dfy(16,5): Warning: /!\ No terms found to trigger on.
+
+Dafny program verifier finished with 2 verified, 0 errors
diff --git a/Test/triggers/set-construction-is-a-good-trigger.dfy b/Test/triggers/set-construction-is-a-good-trigger.dfy
new file mode 100644
index 00000000..b3dee172
--- /dev/null
+++ b/Test/triggers/set-construction-is-a-good-trigger.dfy
@@ -0,0 +1,12 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file ensures that display expressions can be picked as triggers. This is
+// useful for code that checks if a set, sequence, or multiset is a singleton.
+
+method M(s: seq<int>, st: set<int>, mst: multiset<int>)
+ requires exists y :: s == [y] // Seq#Build(Seq#Empty(): Seq Box, $Box(y#3))
+ requires exists y :: st == {y} // Set#UnionOne(Set#Empty(): Set Box, $Box(y#4))
+ requires exists y :: mst == multiset{y} // MultiSet#UnionOne(MultiSet#Empty(): MultiSet Box, $Box(y#5))
+{
+}
diff --git a/Test/triggers/set-construction-is-a-good-trigger.dfy.expect b/Test/triggers/set-construction-is-a-good-trigger.dfy.expect
new file mode 100644
index 00000000..822b8498
--- /dev/null
+++ b/Test/triggers/set-construction-is-a-good-trigger.dfy.expect
@@ -0,0 +1,5 @@
+set-construction-is-a-good-trigger.dfy(8,11): Info: Selected triggers: {[y]}
+set-construction-is-a-good-trigger.dfy(9,11): Info: Selected triggers: {{y}}
+set-construction-is-a-good-trigger.dfy(10,11): Info: Selected triggers: {multiset{y}}
+
+Dafny program verifier finished with 2 verified, 0 errors
diff --git a/Test/triggers/some-proofs-only-work-without-autoTriggers.dfy b/Test/triggers/some-proofs-only-work-without-autoTriggers.dfy
new file mode 100644
index 00000000..bc2e0934
--- /dev/null
+++ b/Test/triggers/some-proofs-only-work-without-autoTriggers.dfy
@@ -0,0 +1,48 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// The examples below work nicely with /autoTriggers:0, but break when we use
+// /autoTriggers.
+
+// The issue is that the axioms for sequences are missing a number of facts,
+// which was not a problem before /autoTriggers and /stricterTriggers, but has
+// become one. Here are examples of things that Dafny won’t prove with
+// /autoTriggers (I would expect it wouldn’t with stricterTriggers either,
+// though the second example is trickier than the first):
+
+method M(a: seq<int>) {
+ if * {
+ // This fails; it needs the following axiom:
+ // axiom (forall<T> s: Seq T ::
+ // { Seq#Take(s, Seq#Length(s)) }
+ // Seq#Take(s, Seq#Length(s)) == s);
+ assume forall x :: x in a ==> x > 0;
+ assert forall x :: x in a[..|a|] ==> x > 0;
+ } else if * {
+ // This fails; it needs the following axiom:
+ // axiom (forall<T> s: Seq T, i: int ::
+ // { Seq#Index(s, i) }
+ // 0 <= i && i < Seq#Length(s) ==>
+ // Seq#Contains(s, Seq#Index(s, i)));
+ assume forall x :: x in a ==> x > 0;
+ assert forall i | 0 <= i < |a| :: a[i] > 0;
+ } else if * {
+ assume |a| > 3;
+ assume forall x | x in a[..3] :: x > 1;
+ // This fails, but here it's a lot harder to know what a good axiom would be.
+ assert forall x | x in a[..2] :: x > 1;
+ }
+}
+
+
+// In the first case, the Boogie version is
+//
+// Seq#Contains(Seq#Take(a#0, Seq#Length(a#0)), $Box(x#0_1)) ⟹ x#0_1 > 0
+//
+// And of course Z3 picks $Box(x#0_1). The third case is similar.
+//
+// The problem is of course that knowing that x ∈ a[..2] doesn’t magically give
+// you a term that matches x ∈ a[..3]. One could imagine introducing an extra
+// symbol in the translation to put x and a together for triggering purposes,
+// but that would have the same sort of issues as adding symbols for arithmetic
+// operators.
diff --git a/Test/triggers/some-proofs-only-work-without-autoTriggers.dfy.expect b/Test/triggers/some-proofs-only-work-without-autoTriggers.dfy.expect
new file mode 100644
index 00000000..d48840b8
--- /dev/null
+++ b/Test/triggers/some-proofs-only-work-without-autoTriggers.dfy.expect
@@ -0,0 +1,31 @@
+some-proofs-only-work-without-autoTriggers.dfy(19,11): Info: Selected triggers: {x in a}
+some-proofs-only-work-without-autoTriggers.dfy(20,11): Info: Selected triggers: {x in a[..|a|]}
+some-proofs-only-work-without-autoTriggers.dfy(27,11): Info: Selected triggers: {x in a}
+some-proofs-only-work-without-autoTriggers.dfy(28,11): Info: Selected triggers: {a[i]}
+some-proofs-only-work-without-autoTriggers.dfy(31,11): Info: Selected triggers: {x in a[..3]}
+some-proofs-only-work-without-autoTriggers.dfy(33,11): Info: Selected triggers: {x in a[..2]}
+some-proofs-only-work-without-autoTriggers.dfy(20,11): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon22_Then
+ (0,0): anon3
+ (0,0): anon23_Then
+ (0,0): anon5
+some-proofs-only-work-without-autoTriggers.dfy(28,11): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon25_Then
+ (0,0): anon9
+ (0,0): anon26_Then
+ (0,0): anon27_Then
+ (0,0): anon13
+some-proofs-only-work-without-autoTriggers.dfy(33,11): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon28_Then
+ (0,0): anon29_Then
+ (0,0): anon17
+ (0,0): anon30_Then
+ (0,0): anon19
+
+Dafny program verifier finished with 1 verified, 3 errors
diff --git a/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy
new file mode 100644
index 00000000..d7636ea2
--- /dev/null
+++ b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy
@@ -0,0 +1,16 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file shows how Dafny detects loops even for terms that are not literal
+// AST matches. This file also checks that triggers are reported exactly as
+// picked (that is, `x in s` yields `s[x]` for a multiset s), but matches as
+// they appear in the buffer text (that is, `x+1 in s` is not translated to
+// s[x+1] when highlited as a cause for a potential matching loop.
+
+method M() {
+ // This is an obvious loop
+ ghost var b := forall s: multiset<int>, x: int :: s[x] > 0 ==> s[x+1] > 0;
+
+ // x in s loops with s[x+1] due to the way [x in s] is translated
+ ghost var a := forall s: multiset<int>, x: int :: x in s ==> s[x+1] > 0 && x+2 !in s;
+}
diff --git a/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect
new file mode 100644
index 00000000..1a143edb
--- /dev/null
+++ b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect
@@ -0,0 +1,10 @@
+some-terms-do-not-look-like-the-triggers-they-match.dfy(12,17): Warning: Selected triggers: {s[x]} (may loop with "s[x + 1]")
+ /!\ Suppressing loops would leave this expression without triggers.
+some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Warning: For expression "x in s ==> s[x + 1] > 0":
+ Selected triggers: {s[x]} (may loop with "s[x + 1]")
+ /!\ Suppressing loops would leave this expression without triggers.
+some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Warning: For expression "x in s ==> x + 2 !in s":
+ Selected triggers: {s[x]} (may loop with "x + 2 !in s")
+ /!\ Suppressing loops would leave this expression without triggers.
+
+Dafny program verifier finished with 2 verified, 0 errors
diff --git a/Test/triggers/splitting-picks-the-right-tokens.dfy b/Test/triggers/splitting-picks-the-right-tokens.dfy
new file mode 100644
index 00000000..76065eca
--- /dev/null
+++ b/Test/triggers/splitting-picks-the-right-tokens.dfy
@@ -0,0 +1,24 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file ensures that trigger splitting picks the right tokens
+
+function Id(i: int): int { i }
+
+method MSuchThat()
+ requires forall x | x > 0 :: Id(x) > 1 && x > 2 && x > -1 { }
+
+method MImplies()
+ // The bodies of the two terms that are produced here are both
+ // BinaryExpressions(==>); the token they use, however, is that of the RHS
+ // terms of these implications; otherwise, error messages would get stacked on
+ // the ==> sign
+ requires forall x :: x > 0 ==> Id(x) > 1 && x > 2 && x > -1 { }
+
+method M() {
+ if * {
+ MImplies();
+ } else {
+ MSuchThat();
+ }
+}
diff --git a/Test/triggers/splitting-picks-the-right-tokens.dfy.expect b/Test/triggers/splitting-picks-the-right-tokens.dfy.expect
new file mode 100644
index 00000000..f01ed1a0
--- /dev/null
+++ b/Test/triggers/splitting-picks-the-right-tokens.dfy.expect
@@ -0,0 +1,38 @@
+splitting-picks-the-right-tokens.dfy(9,11): Info: For expression "Id(x) > 1":
+ Selected triggers: {Id(x)}
+splitting-picks-the-right-tokens.dfy(9,11): Info: For expression "x > 2":
+ Selected triggers: {Id(x)}
+splitting-picks-the-right-tokens.dfy(9,11): Info: For expression "x > -1":
+ Selected triggers: {Id(x)}
+splitting-picks-the-right-tokens.dfy(16,11): Info: For expression "x > 0 ==> Id(x) > 1":
+ Selected triggers: {Id(x)}
+splitting-picks-the-right-tokens.dfy(16,11): Info: For expression "x > 0 ==> x > 2":
+ Selected triggers: {Id(x)}
+splitting-picks-the-right-tokens.dfy(16,11): Info: For expression "x > 0 ==> x > -1":
+ Selected triggers: {Id(x)}
+splitting-picks-the-right-tokens.dfy(20,12): Error BP5002: A precondition for this call might not hold.
+splitting-picks-the-right-tokens.dfy(16,11): Related location: This is the precondition that might not hold.
+splitting-picks-the-right-tokens.dfy(16,48): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon3_Then
+splitting-picks-the-right-tokens.dfy(20,12): Error BP5002: A precondition for this call might not hold.
+splitting-picks-the-right-tokens.dfy(16,11): Related location: This is the precondition that might not hold.
+splitting-picks-the-right-tokens.dfy(16,39): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon3_Then
+splitting-picks-the-right-tokens.dfy(22,13): Error BP5002: A precondition for this call might not hold.
+splitting-picks-the-right-tokens.dfy(9,11): Related location: This is the precondition that might not hold.
+splitting-picks-the-right-tokens.dfy(9,46): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon3_Else
+splitting-picks-the-right-tokens.dfy(22,13): Error BP5002: A precondition for this call might not hold.
+splitting-picks-the-right-tokens.dfy(9,11): Related location: This is the precondition that might not hold.
+splitting-picks-the-right-tokens.dfy(9,37): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon3_Else
+
+Dafny program verifier finished with 6 verified, 4 errors
diff --git a/Test/triggers/splitting-triggers-recovers-expressivity.dfy b/Test/triggers/splitting-triggers-recovers-expressivity.dfy
new file mode 100644
index 00000000..dd1bd81d
--- /dev/null
+++ b/Test/triggers/splitting-triggers-recovers-expressivity.dfy
@@ -0,0 +1,61 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+predicate P(i: int)
+predicate Q(i: int)
+
+/* This file demonstrates a case where automatic trigger splitting is useful to
+ prevent loop detection from reducing expressivity too much. */
+
+lemma exists_0()
+ requires P(0)
+ ensures exists i {:split false} :: P(i) || (Q(i) ==> P(i+1)) {
+ // Fails: P(i) is not a trigger
+}
+
+lemma forall_0(i: int)
+ requires forall j {:split false} :: j >= 0 ==> (P(j) && (Q(j) ==> P(j+1)))
+ requires i >= 0
+ ensures P(i) {
+ // Fails: P(i) is not a trigger
+}
+
+
+lemma exists_1()
+ requires P(0)
+ ensures exists i {:split false} :: P(i) || (Q(i) ==> P(i+1)) {
+ assert Q(0) || !Q(0);
+ // Works: the dummy assertion introduces a term that causes the quantifier
+ // to trigger, producing a witness.
+ }
+
+lemma forall_1(i: int)
+ requires forall j {:split false} :: j >= 0 ==> (P(j) && (Q(j) ==> P(j+1)))
+ requires i >= 0
+ ensures P(i) {
+ assert Q(i) || !Q(i);
+ // Works: the dummy assertion introduces a term that causes the quantifier
+ // to trigger, producing a witness.
+}
+
+
+lemma exists_2()
+ requires P(0)
+ ensures exists i :: P(i) || (Q(i) ==> P(i+1)) {
+ // Works: automatic trigger splitting allows P(i) to get its own triggers
+}
+
+lemma forall_2(i: int)
+ requires forall j :: j >= 0 ==> (P(j) && (Q(j) ==> P(j+1)))
+ requires i >= 0
+ ensures P(i) {
+ // Works: automatic trigger splitting allows P(i) to get its own triggers
+}
+
+
+lemma loop()
+ requires P(0)
+ requires forall i {:matchingloop} :: i >= 0 ==> Q(i) && (P(i) ==> P(i+1))
+ ensures P(100) {
+ // Works: the matching loop is explicitly allowed
+}
diff --git a/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect b/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect
new file mode 100644
index 00000000..a8bb2345
--- /dev/null
+++ b/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect
@@ -0,0 +1,39 @@
+splitting-triggers-recovers-expressivity.dfy(12,10): Info: Selected triggers: {Q(i)}
+ Rejected triggers: {P(i)} (may loop with "P(i + 1)")
+splitting-triggers-recovers-expressivity.dfy(17,11): Info: Selected triggers: {Q(j)}
+ Rejected triggers: {P(j)} (may loop with "P(j + 1)")
+splitting-triggers-recovers-expressivity.dfy(26,10): Info: Selected triggers: {Q(i)}
+ Rejected triggers: {P(i)} (may loop with "P(i + 1)")
+splitting-triggers-recovers-expressivity.dfy(33,11): Info: Selected triggers: {Q(j)}
+ Rejected triggers: {P(j)} (may loop with "P(j + 1)")
+splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression "P(i)":
+ Selected triggers:
+ {Q(i)}, {P(i)}
+splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression "!Q(i)":
+ Selected triggers:
+ {Q(i)}, {P(i)}
+splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression "P(i + 1)":
+ Selected triggers: {Q(i)}
+ Rejected triggers: {P(i)} (may loop with "P(i + 1)")
+splitting-triggers-recovers-expressivity.dfy(49,11): Info: For expression "j >= 0 ==> P(j)":
+ Selected triggers:
+ {Q(j)}, {P(j)}
+splitting-triggers-recovers-expressivity.dfy(49,11): Info: For expression "j >= 0 ==> Q(j) ==> P(j + 1)":
+ Selected triggers: {Q(j)}
+ Rejected triggers: {P(j)} (may loop with "P(j + 1)")
+splitting-triggers-recovers-expressivity.dfy(58,11): Info: For expression "i >= 0 ==> Q(i)":
+ Selected triggers:
+ {P(i)}, {Q(i)}
+splitting-triggers-recovers-expressivity.dfy(58,11): Info: For expression "i >= 0 ==> P(i) ==> P(i + 1)":
+ Selected triggers:
+ {P(i)} (may loop with "P(i + 1)"), {Q(i)}
+splitting-triggers-recovers-expressivity.dfy(12,63): Error BP5003: A postcondition might not hold on this return path.
+splitting-triggers-recovers-expressivity.dfy(12,10): Related location: This is the postcondition that might not hold.
+Execution trace:
+ (0,0): anon0
+splitting-triggers-recovers-expressivity.dfy(19,15): Error BP5003: A postcondition might not hold on this return path.
+splitting-triggers-recovers-expressivity.dfy(19,10): Related location: This is the postcondition that might not hold.
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 14 verified, 2 errors
diff --git a/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy b/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy
new file mode 100644
index 00000000..20e90843
--- /dev/null
+++ b/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy
@@ -0,0 +1,21 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This tests shows that, since quantifiers are split, it becomes possible to know more precisely what part of a precondition did not hold at the call site.
+
+method f()
+ requires forall y :: y > 0 && y < 0 {
+}
+
+method g(x: int) {
+ f();
+}
+
+function gf(): int
+ requires forall y :: y > 0 && y < 0 {
+ 1
+}
+
+function gg(x: int): int {
+ gf()
+}
diff --git a/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy.expect b/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy.expect
new file mode 100644
index 00000000..27548ac9
--- /dev/null
+++ b/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy.expect
@@ -0,0 +1,32 @@
+splitting-triggers-yields-better-precondition-related-errors.dfy(7,11): Warning: For expression "y > 0":
+ /!\ No terms found to trigger on.
+splitting-triggers-yields-better-precondition-related-errors.dfy(7,11): Warning: For expression "y < 0":
+ /!\ No terms found to trigger on.
+splitting-triggers-yields-better-precondition-related-errors.dfy(15,11): Warning: For expression "y > 0":
+ /!\ No terms found to trigger on.
+splitting-triggers-yields-better-precondition-related-errors.dfy(15,11): Warning: For expression "y < 0":
+ /!\ No terms found to trigger on.
+splitting-triggers-yields-better-precondition-related-errors.dfy(11,3): Error BP5002: A precondition for this call might not hold.
+splitting-triggers-yields-better-precondition-related-errors.dfy(7,11): Related location: This is the precondition that might not hold.
+splitting-triggers-yields-better-precondition-related-errors.dfy(7,34): Related location
+Execution trace:
+ (0,0): anon0
+splitting-triggers-yields-better-precondition-related-errors.dfy(11,3): Error BP5002: A precondition for this call might not hold.
+splitting-triggers-yields-better-precondition-related-errors.dfy(7,11): Related location: This is the precondition that might not hold.
+splitting-triggers-yields-better-precondition-related-errors.dfy(7,25): Related location
+Execution trace:
+ (0,0): anon0
+splitting-triggers-yields-better-precondition-related-errors.dfy(20,2): Error: possible violation of function precondition
+splitting-triggers-yields-better-precondition-related-errors.dfy(15,11): Related location
+splitting-triggers-yields-better-precondition-related-errors.dfy(15,34): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon4_Else
+splitting-triggers-yields-better-precondition-related-errors.dfy(20,2): Error: possible violation of function precondition
+splitting-triggers-yields-better-precondition-related-errors.dfy(15,11): Related location
+splitting-triggers-yields-better-precondition-related-errors.dfy(15,25): Related location
+Execution trace:
+ (0,0): anon0
+ (0,0): anon4_Else
+
+Dafny program verifier finished with 4 verified, 4 errors
diff --git a/Test/triggers/suppressing-warnings-behaves-properly.dfy b/Test/triggers/suppressing-warnings-behaves-properly.dfy
new file mode 100644
index 00000000..237269e5
--- /dev/null
+++ b/Test/triggers/suppressing-warnings-behaves-properly.dfy
@@ -0,0 +1,21 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file checks that suppressing warnings works properly
+
+predicate f(x: int)
+predicate g(x: int)
+
+method M() {
+ assert forall n :: n >= 0 || n < 0;
+ assert forall n {:nowarn} :: n >= 0 || n < 0;
+ assert forall n {:autotriggers false} :: n >= 0 || n < 0;
+
+ assert forall n: nat :: (n != 0) == f(n) || true;
+ assert forall n: nat {:nowarn} :: (n != 0) == f(n) || true;
+ assert forall n: nat {:autotriggers false} :: (n != 0) == f(n) || true;
+
+ assert forall n: nat :: f(n) == f(n+1) || g(n) || true;
+ assert forall n: nat {:nowarn} :: (n != 0) == f(n) || true;
+ assert forall n: nat {:autotriggers false} :: (n != 0) == f(n) || true;
+}
diff --git a/Test/triggers/suppressing-warnings-behaves-properly.dfy.expect b/Test/triggers/suppressing-warnings-behaves-properly.dfy.expect
new file mode 100644
index 00000000..124984b1
--- /dev/null
+++ b/Test/triggers/suppressing-warnings-behaves-properly.dfy.expect
@@ -0,0 +1,14 @@
+suppressing-warnings-behaves-properly.dfy(10,9): Warning: /!\ No terms found to trigger on.
+suppressing-warnings-behaves-properly.dfy(11,9): Info: (Suppressed warning) No terms found to trigger on.
+suppressing-warnings-behaves-properly.dfy(12,9): Info: Not generating triggers for "n >= 0 || n < 0". Note that {:autotriggers false} can cause instabilities. Consider using {:nowarn}, {:matchingloop} (not great either), or a manual trigger instead.
+suppressing-warnings-behaves-properly.dfy(14,9): Info: Selected triggers: {f(n)}
+suppressing-warnings-behaves-properly.dfy(15,9): Warning: Selected triggers: {f(n)}
+ /!\ There is no warning here to suppress.
+suppressing-warnings-behaves-properly.dfy(16,9): Info: Not generating triggers for "(n != 0) == f(n) || true". Note that {:autotriggers false} can cause instabilities. Consider using {:nowarn}, {:matchingloop} (not great either), or a manual trigger instead.
+suppressing-warnings-behaves-properly.dfy(18,9): Info: Selected triggers: {g(n)}
+ Rejected triggers: {f(n)} (may loop with "f(n + 1)")
+suppressing-warnings-behaves-properly.dfy(19,9): Warning: Selected triggers: {f(n)}
+ /!\ There is no warning here to suppress.
+suppressing-warnings-behaves-properly.dfy(20,9): Info: Not generating triggers for "(n != 0) == f(n) || true". Note that {:autotriggers false} can cause instabilities. Consider using {:nowarn}, {:matchingloop} (not great either), or a manual trigger instead.
+
+Dafny program verifier finished with 4 verified, 0 errors
diff --git a/Test/triggers/triggers-prevent-some-inlining.dfy b/Test/triggers/triggers-prevent-some-inlining.dfy
new file mode 100644
index 00000000..90af62a3
--- /dev/null
+++ b/Test/triggers/triggers-prevent-some-inlining.dfy
@@ -0,0 +1,26 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file looks at the interactions between inlining and triggers. The
+// sum_is_sum predicate gets a {sum(a, b)} trigger, which explicitly depends on
+// one of the variables being passed in. Since triggers are generated prior to
+// inlining (inlining happens during translation), inlining the last two
+// instances of that call below would cause b+1 (a trigger killer) to pop up in
+// a trigger. This would create an invalid trigger, so Dafny doesn't let it
+// happen.
+
+function sum(a: int, b: int): int {
+ a + b
+}
+
+predicate sum_is_sum(b: int, c: int) {
+ forall a: int :: sum(a, b) + c == a + b + c
+}
+
+method can_we_inline(b: int, c: int)
+ ensures sum_is_sum(0, 0) // OK to inline
+ ensures sum_is_sum(b, c) // OK to inline
+ ensures sum_is_sum(b, c+1) // OK to inline
+ ensures sum_is_sum(b+1, c) // NOK to inline
+ ensures sum_is_sum(b+1, c+1) // NOK to inline
+{ }
diff --git a/Test/triggers/triggers-prevent-some-inlining.dfy.expect b/Test/triggers/triggers-prevent-some-inlining.dfy.expect
new file mode 100644
index 00000000..0b6f3e30
--- /dev/null
+++ b/Test/triggers/triggers-prevent-some-inlining.dfy.expect
@@ -0,0 +1,9 @@
+triggers-prevent-some-inlining.dfy(17,2): Info: Selected triggers: {sum(a, b)}
+triggers-prevent-some-inlining.dfy(24,10): Info: Some instances of this call cannot safely be inlined.
+triggers-prevent-some-inlining.dfy(25,10): Info: Some instances of this call cannot safely be inlined.
+triggers-prevent-some-inlining.dfy(24,10): Info: Some instances of this call cannot safely be inlined.
+triggers-prevent-some-inlining.dfy(25,10): Info: Some instances of this call cannot safely be inlined.
+triggers-prevent-some-inlining.dfy(24,10): Info: Some instances of this call cannot safely be inlined.
+triggers-prevent-some-inlining.dfy(25,10): Info: Some instances of this call cannot safely be inlined.
+
+Dafny program verifier finished with 4 verified, 0 errors
diff --git a/Test/triggers/useless-triggers-are-removed.dfy b/Test/triggers/useless-triggers-are-removed.dfy
new file mode 100644
index 00000000..658890f2
--- /dev/null
+++ b/Test/triggers/useless-triggers-are-removed.dfy
@@ -0,0 +1,25 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file ensures that Dafny does get rid of redundant triggers before
+// annotating a quantifier, and that ths process does not interfere with cycle
+// detection.
+
+function f(x: int): int
+function g(x: int): int
+function h(x: int): int
+
+method M()
+ // In the following, only f(x) is kept. Note that the subset enumeration was
+ // already smart enough to not build any trigger with multiple terms (it only
+ // built 5 candidates)
+ requires forall x: int :: f(x) + g(f(x)) + h(f(x)) + g(h(f(x))) + h(g(f(x))) == 0
+
+ // Loop detection still works fine: in the following example, the trigger is
+ // f(f(x))
+ requires forall x: int :: f(x) == f(f(x))
+
+ // This works for multi-triggers, too:
+ requires forall x, y :: f(x) + g(f(y)) + g(y) + g(f(x)) == 0
+{
+}
diff --git a/Test/triggers/useless-triggers-are-removed.dfy.expect b/Test/triggers/useless-triggers-are-removed.dfy.expect
new file mode 100644
index 00000000..d6b49a9e
--- /dev/null
+++ b/Test/triggers/useless-triggers-are-removed.dfy.expect
@@ -0,0 +1,17 @@
+useless-triggers-are-removed.dfy(16,11): Info: Selected triggers: {f(x)}
+ Rejected triggers:
+ {h(g(f(x)))} (more specific than {g(f(x))}, {f(x)})
+ {g(h(f(x)))} (more specific than {h(f(x))}, {f(x)})
+ {h(f(x))} (more specific than {f(x)})
+ {g(f(x))} (more specific than {f(x)})
+useless-triggers-are-removed.dfy(20,11): Info: Selected triggers: {f(f(x))}
+ Rejected triggers: {f(x)} (may loop with "f(f(x))")
+useless-triggers-are-removed.dfy(23,11): Info: Selected triggers:
+ {g(f(x)), g(y)}, {f(y), f(x)}
+ Rejected triggers:
+ {g(y), f(x)} (may loop with "g(f(y))", "g(f(x))")
+ {g(f(x)), g(f(y))} (more specific than {g(f(x)), f(y)}, {g(f(y)), f(x)}, {f(y), f(x)})
+ {g(f(x)), f(y)} (more specific than {f(y), f(x)})
+ {g(f(y)), f(x)} (more specific than {f(y), f(x)})
+
+Dafny program verifier finished with 5 verified, 0 errors
diff --git a/Test/triggers/wf-checks-use-the-original-quantifier.dfy b/Test/triggers/wf-checks-use-the-original-quantifier.dfy
new file mode 100644
index 00000000..a1a2bd90
--- /dev/null
+++ b/Test/triggers/wf-checks-use-the-original-quantifier.dfy
@@ -0,0 +1,28 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This test checks that typical expressions requiring WF checks do not suddenly
+// loose expressivity due to quantifier splitting. Without special care, the
+// expression (forall x :: x != null && x.a == 0) could fail to verify.
+
+// The logic about split quantifiers is that Boogie (and z3) should never realize
+// that there was an unsplit quantifier. The WF check code does not produce a
+// quantifier, at least in it's checking part; thus, it should use original
+// quantifier. This fixes a problem in VerifyThis2015/Problem2.dfy with a null
+// check, and a problem spotted by Chris, made into a test case saved in
+// triggers/wf-checks-use-the-original-quantifier.dfy.
+
+// Of course, the assumption that WF checks produce for a quantifier is a
+// quantifier, so the assumption part that comes after the WF check does use the
+// split expression.
+
+// This test case is inspired by the example that Chris gave.
+
+predicate P(b: nat)
+function f(a: int): int
+class C { var x: int; }
+
+method M(s: set<C>)
+ requires forall n: nat :: 0 <= f(n) && P(f(n))
+ requires forall c, c' | c in s && c' in s :: c != null && c'!= null && c.x == c'.x {
+}
diff --git a/Test/triggers/wf-checks-use-the-original-quantifier.dfy.expect b/Test/triggers/wf-checks-use-the-original-quantifier.dfy.expect
new file mode 100644
index 00000000..6c3e4853
--- /dev/null
+++ b/Test/triggers/wf-checks-use-the-original-quantifier.dfy.expect
@@ -0,0 +1,17 @@
+wf-checks-use-the-original-quantifier.dfy(26,11): Info: For expression "0 <= f(n)":
+ Selected triggers: {f(n)}
+ Rejected triggers: {P(f(n))} (more specific than {f(n)})
+wf-checks-use-the-original-quantifier.dfy(26,11): Info: For expression "P(f(n))":
+ Selected triggers: {f(n)}
+ Rejected triggers: {P(f(n))} (more specific than {f(n)})
+wf-checks-use-the-original-quantifier.dfy(27,11): Info: For expression "c != null":
+ Selected triggers:
+ {c'.x, c.x}, {c'.x, c in s}, {c.x, c' in s}, {c' in s, c in s}
+wf-checks-use-the-original-quantifier.dfy(27,11): Info: For expression "c' != null":
+ Selected triggers:
+ {c'.x, c.x}, {c'.x, c in s}, {c.x, c' in s}, {c' in s, c in s}
+wf-checks-use-the-original-quantifier.dfy(27,11): Info: For expression "c.x == c'.x":
+ Selected triggers:
+ {c'.x, c.x}, {c'.x, c in s}, {c.x, c' in s}, {c' in s, c in s}
+
+Dafny program verifier finished with 4 verified, 0 errors
diff --git a/Test/tutorial/maximum.dfy b/Test/tutorial/maximum.dfy
new file mode 100644
index 00000000..81faa219
--- /dev/null
+++ b/Test/tutorial/maximum.dfy
@@ -0,0 +1,32 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// This file shows how to specify and implement a function to compute the
+// largest element of a list. The function is fully specified by two
+// preconditions, as proved by the MaximumIsUnique lemma below.
+
+method Maximum(values: seq<int>) returns (max: int)
+ requires values != []
+ ensures max in values
+ ensures forall i | 0 <= i < |values| :: values[i] <= max
+{
+ max := values[0];
+ var idx := 0;
+ while (idx < |values|)
+ invariant max in values
+ invariant idx <= |values|
+ invariant forall j | 0 <= j < idx :: values[j] <= max
+ {
+ if (values[idx] > max) {
+ max := values[idx];
+ }
+ idx := idx + 1;
+ }
+}
+
+lemma MaximumIsUnique(values: seq<int>, m1: int, m2: int)
+ requires m1 in values && forall i | 0 <= i < |values| :: values[i] <= m1
+ requires m2 in values && forall i | 0 <= i < |values| :: values[i] <= m2
+ ensures m1 == m2 {
+ // This lemma does not need a body: Dafny is able to prove it correct entirely automatically.
+}
diff --git a/Test/tutorial/maximum.dfy.expect b/Test/tutorial/maximum.dfy.expect
new file mode 100644
index 00000000..16a67088
--- /dev/null
+++ b/Test/tutorial/maximum.dfy.expect
@@ -0,0 +1,7 @@
+maximum.dfy(11,10): Info: Selected triggers: {values[i]}
+maximum.dfy(18,14): Info: Selected triggers: {values[j]}
+maximum.dfy(28,27): Info: Selected triggers: {values[i]}
+maximum.dfy(29,27): Info: Selected triggers: {values[i]}
+maximum.dfy(15,2): Info: decreases |values| - idx
+
+Dafny program verifier finished with 4 verified, 0 errors
diff --git a/Test/vacid0/Composite.dfy b/Test/vacid0/Composite.dfy
index d5551d82..bc3b5baf 100644
--- a/Test/vacid0/Composite.dfy
+++ b/Test/vacid0/Composite.dfy
@@ -68,7 +68,7 @@ class Composite {
// sets child.parent to this:
ensures child.parent == this;
// leaves everything in S+U valid:
- ensures (forall c :: c in S+U ==> c.Valid(S+U));
+ ensures (forall c {:autotriggers false} :: c in S+U ==> c.Valid(S+U)); // We can't generate a trigger for this at the moment; if we did, we would still need to prevent TrSplitExpr from translating c in S+U to S[c] || U[c].
{
if (left == null) {
left := child;
diff --git a/Test/vstte2012/BreadthFirstSearch.dfy b/Test/vstte2012/BreadthFirstSearch.dfy
index b111a438..375f4a09 100644
--- a/Test/vstte2012/BreadthFirstSearch.dfy
+++ b/Test/vstte2012/BreadthFirstSearch.dfy
@@ -1,4 +1,4 @@
-// RUN: %dafny /compile:0 /dprint:"%t.dprint" /vcsMaxKeepGoingSplits:10 "%s" > "%t"
+// RUN: %dafny /compile:0 /dprint:"%t.dprint" /vcsMaxKeepGoingSplits:10 /autoTriggers:0 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"
class BreadthFirstSearch<Vertex(==)>
diff --git a/Test/vstte2012/Combinators.dfy b/Test/vstte2012/Combinators.dfy
index be7bc25f..ba4a4141 100644
--- a/Test/vstte2012/Combinators.dfy
+++ b/Test/vstte2012/Combinators.dfy
@@ -170,7 +170,7 @@ function IsTerminal(t: Term): bool
// The following theorem states the correctness of the FindAndStep function:
-ghost method Theorem_FindAndStep(t: Term)
+lemma Theorem_FindAndStep(t: Term)
// If FindAndStep returns the term it started from, then there is no
// way to take a step. More precisely, there is no C[u] == t for which the
// Step applies to "u".
@@ -194,7 +194,7 @@ ghost method Theorem_FindAndStep(t: Term)
// computes the value of FindAndStep(t) as it goes along and it returns
// that value.
-ghost method Lemma_FindAndStep(t: Term) returns (r: Term, C: Context, u: Term)
+lemma Lemma_FindAndStep(t: Term) returns (r: Term, C: Context, u: Term)
ensures r == FindAndStep(t);
ensures r == t ==> IsTerminal(t);
ensures r != t ==>
@@ -255,7 +255,7 @@ ghost method Lemma_FindAndStep(t: Term) returns (r: Term, C: Context, u: Term)
// The proof of the lemma above used one more lemma, namely one that enumerates
// lays out the options for how to represent a term as a C[u] pair.
-ghost method Lemma_ContextPossibilities(t: Term)
+lemma Lemma_ContextPossibilities(t: Term)
ensures forall C,u :: IsContext(C) && t == EvalExpr(C, u) ==>
(C == Hole && t == u) ||
(t.Apply? && exists D :: C == C_term(D, t.cdr) && t.car == EvalExpr(D, u)) ||
@@ -442,7 +442,7 @@ function method ks(n: nat): Term
// VerificationTask2) it computes the same thing as method VerificationTask2
// does.
-ghost method VerificationTask3()
+lemma VerificationTask3()
ensures forall n: nat ::
TerminatingReduction(ks(n)) == if n % 2 == 0 then K else Apply(K, K);
{
@@ -451,13 +451,13 @@ ghost method VerificationTask3()
}
}
-ghost method VT3(n: nat)
+lemma VT3(n: nat)
ensures TerminatingReduction(ks(n)) == if n % 2 == 0 then K else Apply(K, K);
{
// Dafny's (way cool) induction tactic kicks in and proves the following
// assertion automatically:
assert forall p :: 2 <= p ==> FindAndStep(ks(p)) == ks(p-2);
- // And then Dafny's (cool beyond words) induction tactic for ghost methods kicks
+ // And then Dafny's (cool beyond words) induction tactic for lemmas kicks
// in to prove the postcondition. (If this got you curious, scope out Leino's
// VMCAI 2012 paper "Automating Induction with an SMT Solver".)
}
diff --git a/Test/vstte2012/Tree.dfy b/Test/vstte2012/Tree.dfy
index 4a45d011..662024e4 100644
--- a/Test/vstte2012/Tree.dfy
+++ b/Test/vstte2012/Tree.dfy
@@ -22,9 +22,9 @@ datatype Result = Fail | Res(t: Tree, sOut: seq<int>)
// The postconditions state properties that are needed
// in the completeness proof.
function toList(d: int, t: Tree): seq<int>
- ensures toList(d, t) != [] && toList(d, t)[0] >= d;
- ensures (toList(d, t)[0] == d) == (t == Leaf);
- decreases t;
+ ensures toList(d, t) != [] && toList(d, t)[0] >= d
+ ensures (toList(d, t)[0] == d) == (t == Leaf)
+ decreases t
{
match t
case Leaf => [d]
@@ -43,10 +43,10 @@ function toList(d: int, t: Tree): seq<int>
function method build_rec(d: int, s: seq<int>): Result
ensures build_rec(d, s).Res? ==>
|build_rec(d, s).sOut| < |s| &&
- build_rec(d, s).sOut == s[|s|-|build_rec(d, s).sOut|..];
+ build_rec(d, s).sOut == s[|s|-|build_rec(d, s).sOut|..]
ensures build_rec(d, s).Res? ==>
- toList(d,build_rec(d, s).t) == s[..|s|-|build_rec(d, s).sOut|];
- decreases |s|, (if s==[] then 0 else s[0]-d);
+ toList(d,build_rec(d, s).t) == s[..|s|-|build_rec(d, s).sOut|]
+ decreases |s|, (if s==[] then 0 else s[0]-d)
{
if s==[] || s[0] < d then
Fail
@@ -68,14 +68,14 @@ function method build_rec(d: int, s: seq<int>): Result
// sequence yields exactly the input sequence.
// Completeness is proved as a lemma, see below.
function method build(s: seq<int>): Result
- ensures build(s).Res? ==> toList(0,build(s).t) == s;
+ ensures build(s).Res? ==> toList(0,build(s).t) == s
{
var r := build_rec(0, s);
if r.Res? && r.sOut == [] then r else Fail
}
-// This ghost methods encodes the main lemma for the
+// This is the main lemma for the
// completeness theorem. If a sequence s starts with a
// valid encoding of a tree t then build_rec yields a
// result (i.e., does not fail) and the rest of the sequence.
@@ -83,41 +83,39 @@ function method build(s: seq<int>): Result
// induction on t. Dafny proves termination (using the
// height of the term t as termination measure), which
// ensures that the induction hypothesis is applied
-// correctly (encoded by calls to this ghost method).
-ghost method lemma0(t: Tree, d: int, s: seq<int>)
+// correctly (encoded by calls to this lemma).
+lemma lemma0(t: Tree, d: int, s: seq<int>)
ensures build_rec(d, toList(d, t) + s).Res? &&
- build_rec(d, toList(d, t) + s).sOut == s;
+ build_rec(d, toList(d, t) + s).sOut == s
{
match(t) {
case Leaf =>
assert toList(d, t) == [d];
case Node(l, r) =>
assert toList(d, t) + s == toList(d+1, l) + (toList(d+1, r) + s);
-
- lemma0(l, d+1, toList(d+1, r) + s); // apply the induction hypothesis
- lemma0(r, d+1, s); // apply the induction hypothesis
+ // the rest follows from (two invocations of) the (automatically applied) induction hypothesis
}
}
-// This ghost method encodes a lemma that states the
+// This lemma states the
// completeness property. It is proved by applying the
// main lemma (lemma0). In this lemma, the bound variables
// of the completeness theorem are passed as arguments;
-// the following two ghost methods replace these arguments
+// the following two lemmas replace these arguments
// by quantified variables.
-ghost method lemma1(t: Tree, s:seq<int>)
- requires s == toList(0, t) + [];
- ensures build(s).Res?;
+lemma lemma1(t: Tree, s:seq<int>)
+ requires s == toList(0, t) + []
+ ensures build(s).Res?
{
lemma0(t, 0, []);
}
-// This ghost method encodes a lemma that introduces the
-// existential quantifier in the completeness property.
-ghost method lemma2(s: seq<int>)
- ensures (exists t: Tree :: toList(0,t) == s) ==> build(s).Res?;
+// This lemma introduces the existential quantifier in the completeness
+// property.
+lemma lemma2(s: seq<int>)
+ ensures (exists t: Tree :: toList(0,t) == s) ==> build(s).Res?
{
forall t | toList(0,t) == s {
lemma1(t, s);
@@ -125,13 +123,13 @@ ghost method lemma2(s: seq<int>)
}
-// This ghost method encodes the completeness theorem.
+// This lemma encodes the completeness theorem.
// For each sequence for which there is a corresponding
// tree, function build yields a result different from Fail.
// The body of the method converts the argument of lemma2
// into a universally quantified variable.
-ghost method completeness()
- ensures forall s: seq<int> :: ((exists t: Tree :: toList(0,t) == s) ==> build(s).Res?);
+lemma completeness()
+ ensures forall s: seq<int> :: ((exists t: Tree :: toList(0,t) == s) ==> build(s).Res?)
{
forall s {
lemma2(s);
@@ -145,21 +143,8 @@ ghost method completeness()
// unfold the necessary definitions.
method harness0()
ensures build([1,3,3,2]).Res? &&
- build([1,3,3,2]).t == Node(Leaf, Node(Node(Leaf, Leaf), Leaf));
+ build([1,3,3,2]).t == Node(Leaf, Node(Node(Leaf, Leaf), Leaf))
{
- assert build_rec(2, [2]) ==
- Res(Leaf, []);
- assert build_rec(2, [3,3,2]) ==
- Res(Node(Leaf, Leaf), [2]);
- assert build_rec(1, [3,3,2]) ==
- Res(Node(Node(Leaf, Leaf), Leaf), []);
- assert build_rec(1, [1,3,3,2]) ==
- Res(Leaf, [3,3,2]);
- assert build_rec(0, [1,3,3,2]) ==
- Res(
- Node(build_rec(1, [1,3,3,2]).t,
- build_rec(1, [3,3,2]).t),
- []);
}
@@ -168,10 +153,6 @@ method harness0()
// assertions are required by the verifier to
// unfold the necessary definitions.
method harness1()
- ensures build([1,3,2,2]).Fail?;
+ ensures build([1,3,2,2]).Fail?
{
- assert build_rec(1,[1,3,2,2]) == Res(Leaf, [3,2,2]);
- assert build_rec(3,[2,2]).Fail?;
- assert build_rec(2,[3,2,2]).Fail?;
- assert build_rec(1,[3,2,2]).Fail?;
}
diff --git a/Test/wishlist/calc.dfy b/Test/wishlist/calc.dfy
new file mode 100644
index 00000000..308fbb9a
--- /dev/null
+++ b/Test/wishlist/calc.dfy
@@ -0,0 +1,17 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// There is a bug in Dafny that causes the error from `L` to be reported at
+// position 0 in this file, instead of on a curly brace.
+
+lemma L()
+ ensures false {
+ calc { true; }
+}
+
+// Empty calc statements work fine, though:
+
+lemma L'()
+ ensures false {
+ calc { }
+}
diff --git a/Test/wishlist/calc.dfy.expect b/Test/wishlist/calc.dfy.expect
new file mode 100644
index 00000000..9e4e7b44
--- /dev/null
+++ b/Test/wishlist/calc.dfy.expect
@@ -0,0 +1,11 @@
+calc.dfy(8,16): Error BP5003: A postcondition might not hold on this return path.
+calc.dfy(8,10): Related location: This is the postcondition that might not hold.
+Execution trace:
+ (0,0): anon0
+ calc.dfy(9,5): anon2_Else
+calc.dfy(15,16): Error BP5003: A postcondition might not hold on this return path.
+calc.dfy(15,10): Related location: This is the postcondition that might not hold.
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 2 verified, 2 errors
diff --git a/Test/wishlist/exists-b-exists-not-b.dfy b/Test/wishlist/exists-b-exists-not-b.dfy
new file mode 100644
index 00000000..2573b2f2
--- /dev/null
+++ b/Test/wishlist/exists-b-exists-not-b.dfy
@@ -0,0 +1,10 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// It would be great if Dafny was able to verify the following statements;
+// otherwise, trigger splitting prevents `exists b :: b || not b` from verifying
+
+method M() {
+ assert exists b : bool {:nowarn} :: b; // WISH
+ assert exists b : bool {:nowarn} :: !b; // WISH
+}
diff --git a/Test/wishlist/exists-b-exists-not-b.dfy.expect b/Test/wishlist/exists-b-exists-not-b.dfy.expect
new file mode 100644
index 00000000..c785ee97
--- /dev/null
+++ b/Test/wishlist/exists-b-exists-not-b.dfy.expect
@@ -0,0 +1,8 @@
+exists-b-exists-not-b.dfy(8,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+exists-b-exists-not-b.dfy(9,9): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 1 verified, 2 errors
diff --git a/Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy b/Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy
new file mode 100644
index 00000000..9b002d47
--- /dev/null
+++ b/Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy
@@ -0,0 +1,9 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+method M() {
+ var pos := 10;
+ while (pos > 0) { // This shouldn't print int(pos) - int(0); pos - 0 would be better
+ pos := pos - 1;
+ }
+}
diff --git a/Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy.expect b/Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy.expect
new file mode 100644
index 00000000..36d7e6b8
--- /dev/null
+++ b/Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy.expect
@@ -0,0 +1,3 @@
+useless-casts-in-decreases-clauses.dfy(6,2): Info: decreases pos - 0
+
+Dafny program verifier finished with 2 verified, 0 errors
diff --git a/Test/wishlist/naked-function-in-recursive-setting.dfy b/Test/wishlist/naked-function-in-recursive-setting.dfy
new file mode 100644
index 00000000..650fc4c3
--- /dev/null
+++ b/Test/wishlist/naked-function-in-recursive-setting.dfy
@@ -0,0 +1,13 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+function fact(n: int): int
+ requires n >= 0
+{
+ if n == 0 then
+ 1
+ else (
+ assert fact.requires(n-1); //WISH
+ n * fact(n-1)
+ )
+}
diff --git a/Test/wishlist/naked-function-in-recursive-setting.dfy.expect b/Test/wishlist/naked-function-in-recursive-setting.dfy.expect
new file mode 100644
index 00000000..4b1691b4
--- /dev/null
+++ b/Test/wishlist/naked-function-in-recursive-setting.dfy.expect
@@ -0,0 +1,8 @@
+naked-function-in-recursive-setting.dfy(4,9): Info: decreases n
+naked-function-in-recursive-setting.dfy(10,11): Error: cannot use naked function in recursive setting. Possible solution: eta expansion.
+Execution trace:
+ (0,0): anon0
+ (0,0): anon7_Else
+ (0,0): anon8_Else
+
+Dafny program verifier finished with 0 verified, 1 error
diff --git a/Test/wishlist/sequences-literals.dfy b/Test/wishlist/sequences-literals.dfy
new file mode 100644
index 00000000..382349a4
--- /dev/null
+++ b/Test/wishlist/sequences-literals.dfy
@@ -0,0 +1,58 @@
+// RUN: %dafny /compile:0 /autoTriggers:1 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// Note: in the tests below, it could be useful to experiment with the
+// following triggers for some of the library axioms:
+//
+// axiom (forall<T> s0: Seq T, s1: Seq T, x: T ::
+// { Seq#Contains(s0, x), Seq#Append(s0, s1) }
+// { Seq#Contains(s1, x), Seq#Append(s0, s1) }
+// Seq#Contains(Seq#Append(s0, s1), x)
+// <==> Seq#Contains(s0, x) || Seq#Contains(s1, x));
+//
+// axiom (forall<T> s: Seq T, v: T, x: T ::
+// { Seq#Contains(s, x), Seq#Build(s, v) }
+// Seq#Contains(Seq#Build(s, v), x) <==> v == x || Seq#Contains(s, x));
+//
+// Another, not necessarily incompatible approach would be to explicitly add
+// `assume k in s` for each element k of constant lists.
+
+method SmallList() {
+ var s := [0, 1, 5, 6];
+ if * {
+ // This fails: Dafny needs a hint here, because the triggers on the library axioms are pretty strict:
+ assert exists n :: n in s; // WISH
+ } else if * {
+ // This works
+ assert 0 in s;
+ assert exists n :: n in s;
+ } else if * {
+ // This also works, thanks to the magic of triggering on `$Box`.
+ assert exists n {:autotriggers false} :: n in s;
+ }
+}
+
+method LargeList() {
+ var s := [0, 1, 2, 3, 4, 5, 6, 7, 8, /* 9, 10, 11, */ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, /* 119, 120, 121, */ 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136];
+ if * {
+ // The hint fails here. Maybe because z3 gets into a loop trying to unwrap
+ // this large list? This is also very slow.
+ assert 0 in s; // WISH
+ assert exists n :: n in s;
+ } else if * {
+ // Strangely, the hint works here. Why?
+ assert 122 in s;
+ assert exists n :: n in s;
+ } else if * {
+ // This also fails; since z3 only goes to a depth of 100, this probably
+ // wouldn't work with relaxed triggers eithers
+ assert exists n :: n in s && n >= 120;
+ } else if * {
+ // This works: this is certainly more `triggering-on-$Box` magic, but I'm
+ // not sure exactly how it works
+ assert exists n {:autotriggers false} :: n in s && n >= 120;
+ } else if * {
+ // `$Box` only offers limited solace, though
+ assert exists n {:autotriggers false} :: n in s && n < 3;
+ }
+}
diff --git a/Test/wishlist/sequences-literals.dfy.expect b/Test/wishlist/sequences-literals.dfy.expect
new file mode 100644
index 00000000..18e3f98a
--- /dev/null
+++ b/Test/wishlist/sequences-literals.dfy.expect
@@ -0,0 +1,20 @@
+sequences-literals.dfy(24,11): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon7_Then
+sequences-literals.dfy(40,13): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon17_Then
+sequences-literals.dfy(49,11): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon20_Then
+ (0,0): anon7
+sequences-literals.dfy(56,11): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon24_Then
+ (0,0): anon15
+
+Dafny program verifier finished with 2 verified, 4 errors
diff --git a/Test/wishlist/sequences-s0-in-s.dfy b/Test/wishlist/sequences-s0-in-s.dfy
new file mode 100644
index 00000000..c221dbb2
--- /dev/null
+++ b/Test/wishlist/sequences-s0-in-s.dfy
@@ -0,0 +1,25 @@
+// RUN: %dafny /compile:0 /autoTriggers:1 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// The following is also due to a weakness in the axiomatization: namely, it is
+// not easy to learn, using Dafny's axioms, that s[0] in s. One can of course
+// prove it, but it doesn't come for free.
+
+method InSeqTriggers(s: seq<int>, i: nat)
+ requires forall x :: x in s ==> x > 0;
+ requires |s| > 0 {
+ if * {
+ // Fails
+ assert s[0] > 0; // WISH
+ } else if * {
+ // Works
+ assert s[0] in s;
+ assert s[0] > 0;
+ }
+}
+
+method InSeqNoAutoTriggers(s: seq<int>, i: nat)
+ requires forall x {:autotriggers false} :: x in s ==> x > 0;
+ requires |s| > 0 {
+ assert s[0] > 0; // Works (Z3 matches on $Box above)
+}
diff --git a/Test/wishlist/sequences-s0-in-s.dfy.expect b/Test/wishlist/sequences-s0-in-s.dfy.expect
new file mode 100644
index 00000000..4633e5f6
--- /dev/null
+++ b/Test/wishlist/sequences-s0-in-s.dfy.expect
@@ -0,0 +1,6 @@
+sequences-s0-in-s.dfy(13,18): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+ (0,0): anon5_Then
+
+Dafny program verifier finished with 3 verified, 1 error
diff --git a/Test/wishlist/strings.dfy b/Test/wishlist/strings.dfy
new file mode 100644
index 00000000..372711b0
--- /dev/null
+++ b/Test/wishlist/strings.dfy
@@ -0,0 +1,6 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+method EqualityOfStrings() {
+ assert "a" != "b"; // WISH
+}
diff --git a/Test/wishlist/strings.dfy.expect b/Test/wishlist/strings.dfy.expect
new file mode 100644
index 00000000..2817a66e
--- /dev/null
+++ b/Test/wishlist/strings.dfy.expect
@@ -0,0 +1,5 @@
+strings.dfy(5,13): Error: assertion violation
+Execution trace:
+ (0,0): anon0
+
+Dafny program verifier finished with 1 verified, 1 error
diff --git a/Test/wishlist/we-should-always-print-tooltips.dfy b/Test/wishlist/we-should-always-print-tooltips.dfy
new file mode 100644
index 00000000..d7a55845
--- /dev/null
+++ b/Test/wishlist/we-should-always-print-tooltips.dfy
@@ -0,0 +1,4 @@
+// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+// WISH it would be great to add /printTooltips to all tests
diff --git a/Test/wishlist/we-should-always-print-tooltips.dfy.expect b/Test/wishlist/we-should-always-print-tooltips.dfy.expect
new file mode 100644
index 00000000..a1c1f7b9
--- /dev/null
+++ b/Test/wishlist/we-should-always-print-tooltips.dfy.expect
@@ -0,0 +1,2 @@
+
+Dafny program verifier finished with 0 verified, 0 errors
diff --git a/Util/Emacs/README b/Util/Emacs/README
new file mode 100644
index 00000000..9140f20a
--- /dev/null
+++ b/Util/Emacs/README
@@ -0,0 +1,2 @@
+Emacs support for dafny is provided by the boogie-friends package, available from MELPA.
+See https://github.com/boogie-org/boogie-friends for setup instructions and tips. \ No newline at end of file
diff --git a/Util/Emacs/dafny-mode.el b/Util/Emacs/dafny-mode.el
deleted file mode 100644
index 2846c824..00000000
--- a/Util/Emacs/dafny-mode.el
+++ /dev/null
@@ -1,121 +0,0 @@
-;; dafny-mode.el - GNU Emacs mode for Dafny
-;; Adapted by Rustan Leino from Jean-Christophe FILLIATRE's GNU Emancs mode for Why
-
-(defvar dafny-mode-hook nil)
-
-(defvar dafny-mode-map nil
- "Keymap for Dafny major mode")
-
-(if dafny-mode-map nil
- (setq dafny-mode-map (make-keymap))
- (define-key dafny-mode-map "\C-c\C-c" 'dafny-run-verifier)
- (define-key dafny-mode-map [(control return)] 'font-lock-fontify-buffer))
-
-(setq auto-mode-alist
- (append
- '(("\\.dfy" . dafny-mode))
- auto-mode-alist))
-
-;; font-lock
-
-(defun dafny-regexp-opt (l)
- (concat "\\_<" (concat (regexp-opt l t) "\\_>")))
-
-(defconst dafny-font-lock-keywords-1
- (list
- ; comments have the form /* ... */
- '("/\\*\\([^*]\\|\\*[^/]\\)*\\*/" . font-lock-comment-face)
- ; or // ...
- '("//\\([^
-]\\)*" . font-lock-comment-face)
-
- `(,(dafny-regexp-opt '(
- "class" "trait" "datatype" "codatatype" "newtype" "type" "iterator"
- "function" "predicate" "copredicate" "inductive"
- "var" "method" "constructor" "lemma" "colemma"
- "ghost" "static" "protected" "abstract"
- "module" "import" "default" "as" "opened"
- "include"
- "extends" "refines" "returns" "yields"
- "requires" "ensures" "modifies" "reads" "free" "invariant" "decreases"
- )) . font-lock-builtin-face)
- `(,(dafny-regexp-opt '(
- "assert" "assume" "break" "then" "else" "if" "label" "return" "yield"
- "while" "print" "where"
- "old" "forall" "exists" "new" "calc" "modify" "in" "this" "fresh"
- "match" "case" "false" "true" "null")) . font-lock-keyword-face)
- `(,(dafny-regexp-opt '(
- "bool" "char" "int" "nat" "real"
- "set" "multiset" "seq" "string" "map" "imap"
- "object" "array" "array2" "array3")) . font-lock-type-face)
- )
- "Minimal highlighting for Dafny mode")
-
-(defvar dafny-font-lock-keywords dafny-font-lock-keywords-1
- "Default highlighting for Dafny mode")
-
-;; syntax
-
-(defvar dafny-mode-syntax-table nil
- "Syntax table for dafny-mode")
-
-(defun dafny-create-syntax-table ()
- (if dafny-mode-syntax-table
- ()
- (setq dafny-mode-syntax-table (make-syntax-table))
- (set-syntax-table dafny-mode-syntax-table)
- (modify-syntax-entry ?' "w" dafny-mode-syntax-table)
- (modify-syntax-entry ?_ "w" dafny-mode-syntax-table)))
-
-;; menu
-
-(require 'easymenu)
-
-(defun dafny-menu ()
- (easy-menu-define
- dafny-mode-menu (list dafny-mode-map)
- "Dafny Mode Menu."
- '("Dafny"
- ["Run Dafny" dafny-run-verifier t]
- "---"
- ["Recolor buffer" font-lock-fontify-buffer t]
- "---"
- ))
- (easy-menu-add dafny-mode-menu))
-
-;; commands
-
-(defun dafny-command-line (file)
- (concat "boogie " file))
-
-(defun dafny-run-verifier ()
- "run Dafny verifier"
- (interactive)
- (let ((f (buffer-name)))
- (compile (dafny-command-line f))))
-
-;; setting the mode
-
-(defun dafny-mode ()
- "Major mode for editing Dafny programs.
-
-\\{dafny-mode-map}"
- (interactive)
- (kill-all-local-variables)
- (dafny-create-syntax-table)
- ; hilight
- (make-local-variable 'font-lock-defaults)
- (setq font-lock-defaults '(dafny-font-lock-keywords))
- ; indentation
- ; (make-local-variable 'indent-line-function)
- ; (setq indent-line-function 'dafny-indent-line)
- ; menu
- ; providing the mode
- (setq major-mode 'dafny-mode)
- (setq mode-name "Dafny")
- (use-local-map dafny-mode-map)
- (font-lock-mode 1)
- (dafny-menu)
- (run-hooks 'dafny-mode-hook))
-
-(provide 'dafny-mode)
diff --git a/Util/Emacs/jennisys-mode.el b/Util/Emacs/jennisys-mode.el
deleted file mode 100644
index d8f20a31..00000000
--- a/Util/Emacs/jennisys-mode.el
+++ /dev/null
@@ -1,113 +0,0 @@
-;; jennisys-mode.el - GNU Emacs mode for Jennisys
-;; Adapted by Rustan Leino from Jean-Christophe FILLIATRE's GNU Emancs mode for Why
-
-(defvar jennisys-mode-hook nil)
-
-(defvar jennisys-mode-map nil
- "Keymap for Jennisys major mode")
-
-(if jennisys-mode-map nil
- (setq jennisys-mode-map (make-keymap))
- (define-key jennisys-mode-map "\C-c\C-c" 'jennisys-run-boogie)
- (define-key jennisys-mode-map [(control return)] 'font-lock-fontify-buffer))
-
-(setq auto-mode-alist
- (append
- '(("\\.jen" . jennisys-mode))
- auto-mode-alist))
-
-;; font-lock
-
-(defun jennisys-regexp-opt (l)
- (concat "\\<" (concat (regexp-opt l t) "\\>")))
-
-(defconst jennisys-font-lock-keywords-1
- (list
- ; comments have the form /* ... */
- '("/\\*\\([^*]\\|\\*[^/]\\)*\\*/" . font-lock-comment-face)
- ; or // ...
- '("//\\([^
-]\\)*" . font-lock-comment-face)
-
- `(,(jennisys-regexp-opt '(
- "interface" "datamodel" "code"
- "var" "constructor" "method"
- "frame" "invariant" "returns" "requires" "ensures"
- )) . font-lock-builtin-face)
- `(,(jennisys-regexp-opt '(
- "if" "then" "else"
- "forall" "exists"
- "this" "in"
- "false" "true" "null")) . font-lock-keyword-face)
- `(,(jennisys-regexp-opt '("array" "bool" "int" "set" "seq")) . font-lock-type-face)
- )
- "Minimal highlighting for Jennisys mode")
-
-(defvar jennisys-font-lock-keywords jennisys-font-lock-keywords-1
- "Default highlighting for Jennisys mode")
-
-;; syntax
-
-(defvar jennisys-mode-syntax-table nil
- "Syntax table for jennisys-mode")
-
-(defun jennisys-create-syntax-table ()
- (if jennisys-mode-syntax-table
- ()
- (setq jennisys-mode-syntax-table (make-syntax-table))
- (set-syntax-table jennisys-mode-syntax-table)
- (modify-syntax-entry ?' "w" jennisys-mode-syntax-table)
- (modify-syntax-entry ?_ "w" jennisys-mode-syntax-table)))
-
-;; menu
-
-(require 'easymenu)
-
-(defun jennisys-menu ()
- (easy-menu-define
- jennisys-mode-menu (list jennisys-mode-map)
- "Jennisys Mode Menu."
- '("Jennisys"
- ["Run Boogie" jennisys-run-boogie t]
- "---"
- ["Recolor buffer" font-lock-fontify-buffer t]
- "---"
- ))
- (easy-menu-add jennisys-mode-menu))
-
-;; commands
-
-(defun jennisys-command-line (file)
- (concat "boogie " file))
-
-(defun jennisys-run-boogie ()
- "run Boogie to check the Jennisys program"
- (interactive)
- (let ((f (buffer-name)))
- (compile (jennisys-command-line f))))
-
-;; setting the mode
-
-(defun jennisys-mode ()
- "Major mode for editing Jennisys programs.
-
-\\{jennisys-mode-map}"
- (interactive)
- (kill-all-local-variables)
- (jennisys-create-syntax-table)
- ; hilight
- (make-local-variable 'font-lock-defaults)
- (setq font-lock-defaults '(jennisys-font-lock-keywords))
- ; indentation
- ; (make-local-variable 'indent-line-function)
- ; (setq indent-line-function 'jennisys-indent-line)
- ; menu
- ; providing the mode
- (setq major-mode 'jennisys-mode)
- (setq mode-name "Jennisys")
- (use-local-map jennisys-mode-map)
- (font-lock-mode 1)
- (jennisys-menu)
- (run-hooks 'jennisys-mode-hook))
-
-(provide 'jennisys-mode)
diff --git a/Util/latex/dafny.sty b/Util/latex/dafny.sty
index 44a55f70..76d2278a 100644
--- a/Util/latex/dafny.sty
+++ b/Util/latex/dafny.sty
@@ -6,7 +6,7 @@
\lstdefinelanguage{dafny}{
morekeywords={class,datatype,codatatype,newtype,type,iterator,trait,extends,
- bool,char,nat,int,real,object,set,multiset,seq,string,map,imap,array,array2,array3,
+ bool,char,nat,int,real,object,set,iset,multiset,seq,string,map,imap,array,array2,array3,
function,predicate,copredicate,inductive,
ghost,var,static,protected,refines,
method,lemma,constructor,colemma,
diff --git a/Util/vim/dafny.vim b/Util/vim/dafny.vim
index 65d7165f..d81872aa 100644
--- a/Util/vim/dafny.vim
+++ b/Util/vim/dafny.vim
@@ -13,7 +13,7 @@ syntax keyword dafnyConditional if then else match case
syntax keyword dafnyRepeat while
syntax keyword dafnyStatement assume assert return yield new print break label where calc modify
syntax keyword dafnyKeyword var ghost returns yields null static protected this refines include
-syntax keyword dafnyType bool char nat int real set multiset seq string map imap object array array2 array3
+syntax keyword dafnyType bool char nat int real set iset multiset seq string map imap object array array2 array3
syntax keyword dafnyLogic requires ensures modifies reads decreases invariant
syntax keyword dafnyOperator forall exists old fresh
syntax keyword dafnyBoolean true false
diff --git a/package.py b/package.py
new file mode 100755
index 00000000..721bb0eb
--- /dev/null
+++ b/package.py
@@ -0,0 +1,219 @@
+#!/usr/bin/env python3
+
+from fnmatch import fnmatch
+from os import path
+import argparse
+import json
+import os
+import re
+import subprocess
+import sys
+import time
+import urllib.request
+import zipfile
+
+# Configuration
+
+## Where do we fetch the list of releases from?
+RELEASES_URL = "https://api.github.com/repos/Z3Prover/z3/releases/latest"
+## How do we extract info from the name of a release file?
+RELEASE_REGEXP = re.compile(r"^(?P<directory>z3-[0-9\.]+-(?P<platform>x86|x64)-(?P<os>[a-z0-9\.\-]+)).zip$", re.IGNORECASE)
+
+## Where are the sources?
+SOURCE_DIRECTORY = "Source"
+## Where do the binaries get put?
+BINARIES_DIRECTORY = "Binaries"
+## Where do we store the built packages and cache files?
+DESTINATION_DIRECTORY = "Package"
+
+## What's the root folder of the archive?
+DAFNY_PACKAGE_PREFIX = path.join("dafny")
+## What sub-folder of the packages does z3 go into?
+Z3_PACKAGE_PREFIX = path.join("z3")
+
+## What do we take from the z3 archive? (Glob syntax)
+Z3_INTERESTING_FILES = ["LICENSE.txt", "bin/*"]
+
+## On unix systems, which Dafny files should be marked as executable? (Glob syntax; Z3's permissions are preserved)
+UNIX_EXECUTABLES = ["dafny", "dafny-server"]
+
+## What do we take from Dafny's Binaries folder?
+DLLs = ["AbsInt",
+ "Basetypes",
+ "CodeContractsExtender",
+ "Concurrency",
+ "Core",
+ "DafnyPipeline",
+ "Doomed",
+ "ExecutionEngine",
+ "Graph",
+ "Houdini",
+ "Model",
+ "ModelViewer",
+ "ParserHelper",
+ "Provers.SMTLib",
+ "VCExpr",
+ "VCGeneration"]
+EXEs = ["Dafny", "DafnyServer"]
+ETCs = UNIX_EXECUTABLES + ["DafnyPrelude.bpl", "DafnyRuntime.cs", "DafnyLanguageService.vsix"]
+
+# Constants
+
+THIS_FILE = path.realpath(__file__)
+ROOT_DIRECTORY = path.dirname(THIS_FILE)
+SOURCE_DIRECTORY = path.join(ROOT_DIRECTORY, SOURCE_DIRECTORY)
+BINARIES_DIRECTORY = path.join(ROOT_DIRECTORY, BINARIES_DIRECTORY)
+DESTINATION_DIRECTORY = path.join(ROOT_DIRECTORY, DESTINATION_DIRECTORY)
+CACHE_DIRECTORY = path.join(DESTINATION_DIRECTORY, "cache")
+
+MONO = sys.platform not in ("win32", "cygwin")
+DLL_PDB_EXT = ".dll.mdb" if MONO else ".pdb"
+EXE_PDB_EXT = ".exe.mdb" if MONO else ".pdb"
+ARCHIVE_FNAMES = ([dll + ".dll" for dll in DLLs] + [dll + DLL_PDB_EXT for dll in DLLs] +
+ [exe + ".exe" for exe in EXEs] + [exe + EXE_PDB_EXT for exe in EXEs] +
+ ETCs)
+
+# Code
+
+def flush(*args, **kwargs):
+ print(*args, **kwargs)
+ sys.stdout.flush()
+
+class Release:
+ @staticmethod
+ def parse_zip_name(name):
+ m = RELEASE_REGEXP.match(name)
+ if not m:
+ raise Exception("{} does not match RELEASE_REGEXP".format(name))
+ return m.group('platform'), m.group('os'), m.group("directory")
+
+ def __init__(self, js, version):
+ self.z3_name = js["name"]
+ self.size = js["size"]
+ self.url = js["browser_download_url"]
+ self.platform, self.os, self.directory = Release.parse_zip_name(js["name"])
+ self.z3_zip = path.join(CACHE_DIRECTORY, self.z3_name)
+ self.dafny_name = "dafny-{}-{}-{}.zip".format(version, self.platform, self.os)
+ self.dafny_zip = path.join(DESTINATION_DIRECTORY, self.dafny_name)
+
+ @property
+ def cached(self):
+ return path.exists(self.z3_zip) and path.getsize(self.z3_zip) == self.size
+
+ @property
+ def MB(self):
+ return self.size / 1e6
+
+ def download(self):
+ if self.cached:
+ print("cached!")
+ else:
+ flush("downloading {:.2f}MB...".format(self.MB), end=' ')
+ with urllib.request.urlopen(self.url) as reader:
+ with open(self.z3_zip, mode="wb") as writer:
+ writer.write(reader.read())
+ flush("done!")
+
+ @staticmethod
+ def zipify_path(fpath):
+ """Zip entries always use '/' as the path separator."""
+ return fpath.replace(os.path.sep, '/')
+
+ def pack(self):
+ try:
+ os.remove(self.dafny_zip)
+ except FileNotFoundError:
+ pass
+ missing = []
+ with zipfile.ZipFile(self.dafny_zip, 'w', zipfile.ZIP_DEFLATED) as archive:
+ with zipfile.ZipFile(self.z3_zip) as Z3_archive:
+ z3_files_count = 0
+ for fileinfo in Z3_archive.infolist():
+ fname = path.relpath(fileinfo.filename, self.directory)
+ if any(fnmatch(fname, pattern) for pattern in Z3_INTERESTING_FILES):
+ z3_files_count += 1
+ contents = Z3_archive.read(fileinfo)
+ fileinfo.filename = Release.zipify_path(path.join(DAFNY_PACKAGE_PREFIX, Z3_PACKAGE_PREFIX, fname))
+ archive.writestr(fileinfo, contents)
+ for fname in ARCHIVE_FNAMES:
+ fpath = path.join(BINARIES_DIRECTORY, fname)
+ if path.exists(fpath):
+ fileinfo = zipfile.ZipInfo(fname, time.localtime(os.stat(fpath).st_mtime)[:6])
+ if any(fnmatch(fname, pattern) for pattern in UNIX_EXECUTABLES):
+ # http://stackoverflow.com/questions/434641/
+ fileinfo.external_attr = 0o777 << 16
+ contents = open(fpath, mode='rb').read()
+ fileinfo.compress_type = zipfile.ZIP_DEFLATED
+ fileinfo.filename = Release.zipify_path(path.join(DAFNY_PACKAGE_PREFIX, fname))
+ archive.writestr(fileinfo, contents)
+ else:
+ missing.append(fname)
+ flush("done! (imported {} files from z3's sources)".format(z3_files_count))
+ if missing:
+ flush(" WARNING: Not all files were found: {} were missing".format(", ".join(missing)))
+
+def discover(version):
+ flush(" - Getting information about latest release")
+ with urllib.request.urlopen(RELEASES_URL) as reader:
+ js = json.loads(reader.read().decode("utf-8"))
+
+ for release_js in js["assets"]:
+ release = Release(release_js, version)
+ if release.platform == "x64":
+ flush(" + Selecting {} ({:.2f}MB, {})".format(release.z3_name, release.MB, release.size))
+ yield release
+ else:
+ flush(" + Rejecting {}".format(release.z3_name))
+
+def download(releases):
+ flush(" - Downloading {} z3 archives".format(len(releases)))
+ for release in releases:
+ flush(" + {}:".format(release.z3_name), end=' ')
+ release.download()
+
+def run(cmd):
+ flush(" + {}...".format(" ".join(cmd)), end=' ')
+ retv = subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ if retv != 0:
+ flush("failed! (Is Dafny or the Dafny server running?)")
+ sys.exit(1)
+ else:
+ flush("done!")
+
+def build():
+ os.chdir(ROOT_DIRECTORY)
+ flush(" - Building")
+ builder = "xbuild" if MONO else "msbuild"
+ try:
+ run([builder, "Source/Dafny.sln", "/p:Configuration=Checked", "/p:Platform=Any CPU", "/t:Clean"])
+ run([builder, "Source/Dafny.sln", "/p:Configuration=Checked", "/p:Platform=Any CPU", "/t:Rebuild"])
+ except FileNotFoundError:
+ flush("Could not find '{}'! On Windows, you need to run this from the VS native tools command prompt.".format(builder))
+ sys.exit(1)
+
+def pack(releases):
+ flush(" - Packaging {} Dafny archives".format(len(releases)))
+ for release in releases:
+ flush(" + {}:".format(release.dafny_name), end=' ')
+ release.pack()
+
+def parse_arguments():
+ parser = argparse.ArgumentParser(description="Prepare a Dafny release. Configuration is hardcoded; edit the `# Configuration' section of this script to change it.")
+ parser.add_argument("version", help="Version number for this release")
+ return parser.parse_args()
+
+def main():
+ args = parse_arguments()
+ os.makedirs(CACHE_DIRECTORY, exist_ok=True)
+
+ # Z3
+ flush("* Finding and downloading Z3 releases")
+ releases = list(discover(args.version))
+ download(releases)
+
+ flush("* Building and packaging Dafny")
+ build()
+ pack(releases)
+
+if __name__ == '__main__':
+ main()