summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorGravatar Rustan Leino <leino@microsoft.com>2012-10-04 13:32:50 -0700
committerGravatar Rustan Leino <leino@microsoft.com>2012-10-04 13:32:50 -0700
commit8911e5c95d4715c2e2626aef67f19793d6f43201 (patch)
treed703bfd931802e780430e32f1339cf77adc342a4 /Source
parent1c375d1889e628fcd2a1a0fc041673a5f4230d84 (diff)
Put all sources under \Source directory
Diffstat (limited to 'Source')
-rw-r--r--Source/Dafny.sln59
-rw-r--r--Source/Dafny/Cloner.cs539
-rw-r--r--Source/Dafny/Compiler.cs2432
-rw-r--r--Source/Dafny/Dafny.atg1990
-rw-r--r--Source/Dafny/DafnyAst.cs4472
-rw-r--r--Source/Dafny/DafnyMain.cs88
-rw-r--r--Source/Dafny/DafnyOptions.cs155
-rw-r--r--Source/Dafny/DafnyPipeline.csproj199
-rw-r--r--Source/Dafny/Makefile22
-rw-r--r--Source/Dafny/Parser.cs3470
-rw-r--r--Source/Dafny/Printer.cs1410
-rw-r--r--Source/Dafny/RefinementTransformer.cs1411
-rw-r--r--Source/Dafny/Resolver.cs6706
-rw-r--r--Source/Dafny/Rewriter.cs342
-rw-r--r--Source/Dafny/Scanner.cs823
-rw-r--r--Source/Dafny/SccGraph.cs370
-rw-r--r--Source/Dafny/Translator.cs9249
-rw-r--r--Source/Dafny/Util.cs20
-rw-r--r--Source/Dafny/cce.cs112
-rw-r--r--Source/DafnyDriver/DafnyDriver.cs796
-rw-r--r--Source/DafnyDriver/DafnyDriver.csproj182
-rw-r--r--Source/DafnyDriver/app.config3
-rw-r--r--Source/DafnyExtension.sln20
-rw-r--r--Source/DafnyExtension/BraceMatching.cs253
-rw-r--r--Source/DafnyExtension/BufferIdleEventUtil.cs155
-rw-r--r--Source/DafnyExtension/ClassificationTagger.cs107
-rw-r--r--Source/DafnyExtension/ContentType.cs18
-rw-r--r--Source/DafnyExtension/DafnyDriver.cs419
-rw-r--r--Source/DafnyExtension/DafnyExtension.csproj191
-rw-r--r--Source/DafnyExtension/ErrorTagger.cs85
-rw-r--r--Source/DafnyExtension/HoverText.cs126
-rw-r--r--Source/DafnyExtension/IdentifierTagger.cs344
-rw-r--r--Source/DafnyExtension/OutliningTagger.cs185
-rw-r--r--Source/DafnyExtension/ProgressMargin.cs260
-rw-r--r--Source/DafnyExtension/Properties/AssemblyInfo.cs33
-rw-r--r--Source/DafnyExtension/ResolverTagger.cs321
-rw-r--r--Source/DafnyExtension/TokenTagger.cs342
-rw-r--r--Source/DafnyExtension/WordHighlighter.cs211
-rw-r--r--Source/DafnyExtension/source.extension.vsixmanifest25
-rw-r--r--Source/Jennisys.sln58
-rw-r--r--Source/Jennisys/Analyzer.fs953
-rw-r--r--Source/Jennisys/Ast.fs95
-rw-r--r--Source/Jennisys/AstUtils.fs1033
-rw-r--r--Source/Jennisys/CodeGen.fs429
-rw-r--r--Source/Jennisys/DafnyModelUtils.fs455
-rw-r--r--Source/Jennisys/DafnyPrinter.fs135
-rw-r--r--Source/Jennisys/EnvUtils.fs9
-rw-r--r--Source/Jennisys/FixpointSolver.fs374
-rw-r--r--Source/Jennisys/Getters.fs284
-rw-r--r--Source/Jennisys/Jennisys.fs72
-rw-r--r--Source/Jennisys/Jennisys.fsproj118
-rw-r--r--Source/Jennisys/Lexer.fsl83
-rw-r--r--Source/Jennisys/Logger.fs41
-rw-r--r--Source/Jennisys/MethodUnifier.fs107
-rw-r--r--Source/Jennisys/Modularizer.fs206
-rw-r--r--Source/Jennisys/Options.fs162
-rw-r--r--Source/Jennisys/Parser.fsy214
-rw-r--r--Source/Jennisys/PipelineUtils.fs63
-rw-r--r--Source/Jennisys/PrintUtils.fs12
-rw-r--r--Source/Jennisys/Printer.fs156
-rw-r--r--Source/Jennisys/README.txt28
-rw-r--r--Source/Jennisys/Resolver.fs380
-rw-r--r--Source/Jennisys/SymGen.fs9
-rw-r--r--Source/Jennisys/TypeChecker.fs67
-rw-r--r--Source/Jennisys/Utils.fs368
-rw-r--r--Source/Jennisys/examples/BHeap.jen33
-rw-r--r--Source/Jennisys/examples/DList.jen39
-rw-r--r--Source/Jennisys/examples/List.jen77
-rw-r--r--Source/Jennisys/examples/List2.jen68
-rw-r--r--Source/Jennisys/examples/List3.jen71
-rw-r--r--Source/Jennisys/examples/Number.jen44
-rw-r--r--Source/Jennisys/examples/NumberMethods.jen40
-rw-r--r--Source/Jennisys/examples/Set.jen72
-rw-r--r--Source/Jennisys/examples/Set2.jen60
-rw-r--r--Source/Jennisys/examples/Simple.jen31
-rw-r--r--Source/Jennisys/examples/jennisys-synth_List.dfy147
-rw-r--r--Source/Jennisys/examples/jennisys-synth_List2.dfy207
-rw-r--r--Source/Jennisys/examples/jennisys-synth_List3.dfy255
-rw-r--r--Source/Jennisys/examples/jennisys-synth_Number.dfy202
-rw-r--r--Source/Jennisys/examples/jennisys-synth_Set.dfy344
-rw-r--r--Source/Jennisys/examples/mod/jennisys-synth_List.dfy202
-rw-r--r--Source/Jennisys/examples/mod/jennisys-synth_List2.dfy323
-rw-r--r--Source/Jennisys/examples/mod/jennisys-synth_List3.dfy393
-rw-r--r--Source/Jennisys/examples/mod/jennisys-synth_Number.dfy233
-rw-r--r--Source/Jennisys/examples/mod/jennisys-synth_Set.dfy388
-rw-r--r--Source/Jennisys/examples/mod2/jennisys-synth_DList.dfy255
-rw-r--r--Source/Jennisys/examples/mod2/jennisys-synth_List.dfy249
-rw-r--r--Source/Jennisys/examples/mod2/jennisys-synth_List2.dfy225
-rw-r--r--Source/Jennisys/examples/mod2/jennisys-synth_List3.dfy309
-rw-r--r--Source/Jennisys/examples/mod2/jennisys-synth_Number.dfy181
-rw-r--r--Source/Jennisys/examples/mod2/jennisys-synth_NumberMethods.dfy167
-rw-r--r--Source/Jennisys/examples/mod2/jennisys-synth_Set.dfy304
-rw-r--r--Source/Jennisys/examples/oopsla12/BHeap.jen34
-rw-r--r--Source/Jennisys/examples/oopsla12/BHeap_synth.dfy220
-rw-r--r--Source/Jennisys/examples/oopsla12/DList.jen40
-rw-r--r--Source/Jennisys/examples/oopsla12/DList_synth.dfy154
-rw-r--r--Source/Jennisys/examples/oopsla12/IntSet.jen30
-rw-r--r--Source/Jennisys/examples/oopsla12/IntSet_synth.dfy130
-rw-r--r--Source/Jennisys/examples/oopsla12/List.jen29
-rw-r--r--Source/Jennisys/examples/oopsla12/List_synth.dfy146
-rw-r--r--Source/Jennisys/examples/oopsla12/Math.jen20
-rw-r--r--Source/Jennisys/examples/oopsla12/Math_synth.dfy105
-rw-r--r--Source/Jennisys/examples/set.dfy246
-rw-r--r--Source/Jennisys/scripts/StartDafny-jen.bat2
104 files changed, 49931 insertions, 0 deletions
diff --git a/Source/Dafny.sln b/Source/Dafny.sln
new file mode 100644
index 00000000..034dfd7b
--- /dev/null
+++ b/Source/Dafny.sln
@@ -0,0 +1,59 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyDriver", "DafnyDriver\DafnyDriver.csproj", "{63400D1F-05B2-453E-9592-1EAB74B2C9CC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyPipeline", "Dafny\DafnyPipeline.csproj", "{FE44674A-1633-4917-99F4-57635E6FA740}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Checked|.NET = Checked|.NET
+ Checked|Any CPU = Checked|Any CPU
+ Checked|Mixed Platforms = Checked|Mixed Platforms
+ Debug|.NET = Debug|.NET
+ Debug|Any CPU = Debug|Any CPU
+ Debug|Mixed Platforms = Debug|Mixed Platforms
+ Release|.NET = Release|.NET
+ Release|Any CPU = Release|Any CPU
+ Release|Mixed Platforms = Release|Mixed Platforms
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Checked|.NET.ActiveCfg = Checked|Any CPU
+ {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Checked|.NET.Build.0 = Checked|Any CPU
+ {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+ {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Checked|Any CPU.Build.0 = Debug|Any CPU
+ {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Checked|Mixed Platforms.ActiveCfg = Checked|Any CPU
+ {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Checked|Mixed Platforms.Build.0 = Checked|Any CPU
+ {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|.NET.ActiveCfg = Debug|Any CPU
+ {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 = 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
+ {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {FE44674A-1633-4917-99F4-57635E6FA740}.Checked|.NET.ActiveCfg = Checked|Any CPU
+ {FE44674A-1633-4917-99F4-57635E6FA740}.Checked|.NET.Build.0 = Checked|Any CPU
+ {FE44674A-1633-4917-99F4-57635E6FA740}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+ {FE44674A-1633-4917-99F4-57635E6FA740}.Checked|Any CPU.Build.0 = Debug|Any CPU
+ {FE44674A-1633-4917-99F4-57635E6FA740}.Checked|Mixed Platforms.ActiveCfg = Checked|Any CPU
+ {FE44674A-1633-4917-99F4-57635E6FA740}.Checked|Mixed Platforms.Build.0 = Checked|Any CPU
+ {FE44674A-1633-4917-99F4-57635E6FA740}.Debug|.NET.ActiveCfg = Debug|Any CPU
+ {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 = 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
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs
new file mode 100644
index 00000000..38c793c5
--- /dev/null
+++ b/Source/Dafny/Cloner.cs
@@ -0,0 +1,539 @@
+
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Diagnostics.Contracts;
+using IToken = Microsoft.Boogie.IToken;
+
+namespace Microsoft.Dafny
+{
+ class Cloner
+ {
+ public ModuleDefinition CloneModuleDefinition(ModuleDefinition m, string name) {
+ ModuleDefinition nw;
+ if (m is DefaultModuleDecl) {
+ nw = new DefaultModuleDecl();
+ } else {
+ nw = new ModuleDefinition(Tok(m.tok), name, m.IsGhost, m.IsAbstract, m.RefinementBaseName, CloneAttributes(m.Attributes), true);
+ }
+ foreach (var d in m.TopLevelDecls) {
+ nw.TopLevelDecls.Add(CloneDeclaration(d, nw));
+ }
+ nw.RefinementBase = m.RefinementBase;
+ nw.Height = m.Height;
+ return nw;
+ }
+ public TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition m) {
+ Contract.Requires(d != null);
+ Contract.Requires(m != null);
+
+ if (d is ArbitraryTypeDecl) {
+ var dd = (ArbitraryTypeDecl)d;
+ return new ArbitraryTypeDecl(Tok(dd.tok), dd.Name, m, dd.EqualitySupport, CloneAttributes(dd.Attributes));
+ } else if (d is IndDatatypeDecl) {
+ 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));
+ 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));
+ return dt;
+ } else if (d is IteratorDecl) {
+ var dd = (IteratorDecl)d;
+ var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
+ var ins = dd.Ins.ConvertAll(CloneFormal);
+ var outs = dd.Outs.ConvertAll(CloneFormal);
+ var reads = CloneSpecFrameExpr(dd.Reads);
+ var mod = CloneSpecFrameExpr(dd.Modifies);
+ var decr = CloneSpecExpr(dd.Decreases);
+ var req = dd.Requires.ConvertAll(CloneMayBeFreeExpr);
+ var yreq = dd.YieldRequires.ConvertAll(CloneMayBeFreeExpr);
+ var ens = dd.Ensures.ConvertAll(CloneMayBeFreeExpr);
+ var yens = dd.YieldEnsures.ConvertAll(CloneMayBeFreeExpr);
+ var body = CloneBlockStmt(dd.Body);
+ var iter = new IteratorDecl(Tok(dd.tok), dd.Name, dd.Module,
+ tps, ins, outs, reads, mod, decr,
+ req, ens, yreq, yens,
+ body, CloneAttributes(dd.Attributes), dd.SignatureIsOmitted);
+ return iter;
+ } else if (d is ClassDecl) {
+ if (d is DefaultClassDecl) {
+ var dd = (ClassDecl)d;
+ var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
+ var mm = dd.Members.ConvertAll(CloneMember);
+ var cl = new DefaultClassDecl(m, mm);
+ return cl;
+ } else {
+ var dd = (ClassDecl)d;
+ var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
+ var mm = dd.Members.ConvertAll(CloneMember);
+ var cl = new ClassDecl(Tok(dd.tok), dd.Name, m, tps, mm, CloneAttributes(dd.Attributes));
+ return cl;
+ }
+ } else if (d is ModuleDecl) {
+ if (d is LiteralModuleDecl) {
+ var l = new LiteralModuleDecl(((LiteralModuleDecl)d).ModuleDef, m);
+ l.Signature = ((ModuleDecl)d).Signature;
+ return l;
+ } else if (d is AliasModuleDecl) {
+ var a = (AliasModuleDecl)d;
+ var alias = new AliasModuleDecl(a.Path, a.tok, m, a.Opened);
+ alias.ModuleReference = a.ModuleReference;
+ alias.Signature = a.Signature;
+ return alias;
+ } else if (d is AbstractModuleDecl) {
+ var a = (AbstractModuleDecl)d;
+ var abs = new AbstractModuleDecl(a.Path, a.tok, m, a.CompilePath, a.Opened);
+ abs.Signature = a.Signature;
+ abs.OriginalSignature = a.OriginalSignature;
+ return abs;
+ } else {
+ Contract.Assert(false); // unexpected declaration
+ return null; // to please compiler
+ }
+ } else {
+ Contract.Assert(false); // unexpected declaration
+ return null; // to please compiler
+ }
+ }
+
+ public DatatypeCtor CloneCtor(DatatypeCtor ct) {
+ return new DatatypeCtor(Tok(ct.tok), ct.Name, ct.Formals.ConvertAll(CloneFormal), CloneAttributes(ct.Attributes));
+ }
+
+ public TypeParameter CloneTypeParam(TypeParameter tp) {
+ return new TypeParameter(Tok(tp.tok), tp.Name, tp.EqualitySupport);
+ }
+
+ public MemberDecl CloneMember(MemberDecl member) {
+ if (member is Field) {
+ Contract.Assert(!(member is SpecialField)); // we don't expect a SpecialField to be cloned (or do we?)
+ var f = (Field)member;
+ return new Field(Tok(f.tok), f.Name, f.IsGhost, f.IsMutable, f.IsUserMutable, CloneType(f.Type), CloneAttributes(f.Attributes));
+ } else if (member is Function) {
+ var f = (Function)member;
+ return CloneFunction(f);
+ } else {
+ var m = (Method)member;
+ return CloneMethod(m);
+ }
+ }
+
+ public Type CloneType(Type t) {
+ if (t is BasicType) {
+ return t;
+ } else if (t is SetType) {
+ var tt = (SetType)t;
+ return new SetType(CloneType(tt.Arg));
+ } else if (t is SeqType) {
+ var tt = (SeqType)t;
+ return new SeqType(CloneType(tt.Arg));
+ } else if (t is MultiSetType) {
+ var tt = (MultiSetType)t;
+ return new MultiSetType(CloneType(tt.Arg));
+ } else if (t is MapType) {
+ var tt = (MapType)t;
+ return new MapType(CloneType(tt.Domain), CloneType(tt.Range));
+ } else if (t is UserDefinedType) {
+ var tt = (UserDefinedType)t;
+ return new UserDefinedType(Tok(tt.tok), tt.Name, tt.TypeArgs.ConvertAll(CloneType), tt.Path.ConvertAll(x => Tok(x)));
+ } else if (t is InferredTypeProxy) {
+ return new InferredTypeProxy();
+ } else if (t is ParamTypeProxy) {
+ return new ParamTypeProxy(CloneTypeParam(((ParamTypeProxy)t).orig));
+ } else {
+ Contract.Assert(false); // unexpected type (e.g., no other type proxies are expected at this time)
+ return null; // to please compiler
+ }
+ }
+
+ public Formal CloneFormal(Formal formal) {
+ return new Formal(Tok(formal.tok), formal.Name, CloneType(formal.Type), formal.InParam, formal.IsGhost);
+ }
+
+ public BoundVar CloneBoundVar(BoundVar bv) {
+ return new BoundVar(Tok(bv.tok), bv.Name, CloneType(bv.Type));
+ }
+
+ public Specification<Expression> CloneSpecExpr(Specification<Expression> spec) {
+ var ee = spec.Expressions == null ? null : spec.Expressions.ConvertAll(CloneExpr);
+ return new Specification<Expression>(ee, CloneAttributes(spec.Attributes));
+ }
+
+ public Specification<FrameExpression> CloneSpecFrameExpr(Specification<FrameExpression> frame) {
+ var ee = frame.Expressions == null ? null : frame.Expressions.ConvertAll(CloneFrameExpr);
+ return new Specification<FrameExpression>(ee, CloneAttributes(frame.Attributes));
+ }
+
+ public FrameExpression CloneFrameExpr(FrameExpression frame) {
+ return new FrameExpression(Tok(frame.tok), CloneExpr(frame.E), frame.FieldName);
+ }
+ public Attributes CloneAttributes(Attributes attrs) {
+ if (attrs == null) {
+ return null;
+ } else {
+ return new Attributes(attrs.Name, attrs.Args.ConvertAll(CloneAttrArg), CloneAttributes(attrs.Prev));
+ }
+ }
+ public Attributes.Argument CloneAttrArg(Attributes.Argument aa) {
+ if (aa.E != null) {
+ return new Attributes.Argument(Tok(aa.Tok), CloneExpr(aa.E));
+ } else {
+ return new Attributes.Argument(Tok(aa.Tok), aa.S);
+ }
+ }
+
+ public MaybeFreeExpression CloneMayBeFreeExpr(MaybeFreeExpression expr) {
+ var mfe = new MaybeFreeExpression(CloneExpr(expr.E), expr.IsFree);
+ mfe.Attributes = CloneAttributes(expr.Attributes);
+ return mfe;
+ }
+
+ public virtual Expression CloneExpr(Expression expr) {
+ if (expr == null) {
+ return null;
+ } else if (expr is LiteralExpr) {
+ var e = (LiteralExpr)expr;
+ if (e.Value == null) {
+ return new LiteralExpr(Tok(e.tok));
+ } else if (e.Value is bool) {
+ return new LiteralExpr(Tok(e.tok), (bool)e.Value);
+ } else {
+ return new LiteralExpr(Tok(e.tok), (BigInteger)e.Value);
+ }
+
+ } else if (expr is ThisExpr) {
+ if (expr is ImplicitThisExpr) {
+ return new ImplicitThisExpr(Tok(expr.tok));
+ } else {
+ return new ThisExpr(Tok(expr.tok));
+ }
+
+ } else if (expr is IdentifierExpr) {
+ var e = (IdentifierExpr)expr;
+ return new IdentifierExpr(Tok(e.tok), e.Name);
+
+ } else if (expr is DatatypeValue) {
+ var e = (DatatypeValue)expr;
+ return new DatatypeValue(Tok(e.tok), e.DatatypeName, e.MemberName, e.Arguments.ConvertAll(CloneExpr));
+
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ if (expr is SetDisplayExpr) {
+ return new SetDisplayExpr(Tok(e.tok), e.Elements.ConvertAll(CloneExpr));
+ } else if (expr is MultiSetDisplayExpr) {
+ return new MultiSetDisplayExpr(Tok(e.tok), e.Elements.ConvertAll(CloneExpr));
+ } else {
+ Contract.Assert(expr is SeqDisplayExpr);
+ return new SeqDisplayExpr(Tok(e.tok), e.Elements.ConvertAll(CloneExpr));
+ }
+
+ } else if (expr is MapDisplayExpr) {
+ MapDisplayExpr e = (MapDisplayExpr)expr;
+ List<ExpressionPair> pp = new List<ExpressionPair>();
+ foreach (ExpressionPair p in e.Elements) {
+ pp.Add(new ExpressionPair(CloneExpr(p.A), CloneExpr(p.B)));
+ }
+ return new MapDisplayExpr(Tok(expr.tok), pp);
+ } else if (expr is ExprDotName) {
+ var e = (ExprDotName)expr;
+ return new ExprDotName(Tok(e.tok), CloneExpr(e.Obj), e.SuffixName);
+
+ } else if (expr is FieldSelectExpr) {
+ var e = (FieldSelectExpr)expr;
+ return new FieldSelectExpr(Tok(e.tok), CloneExpr(e.Obj), e.FieldName);
+
+ } else if (expr is SeqSelectExpr) {
+ var e = (SeqSelectExpr)expr;
+ return new SeqSelectExpr(Tok(e.tok), e.SelectOne, CloneExpr(e.Seq), CloneExpr(e.E0), CloneExpr(e.E1));
+
+ } else if (expr is MultiSelectExpr) {
+ var e = (MultiSelectExpr)expr;
+ return new MultiSelectExpr(Tok(e.tok), CloneExpr(e.Array), e.Indices.ConvertAll(CloneExpr));
+
+ } else if (expr is SeqUpdateExpr) {
+ var e = (SeqUpdateExpr)expr;
+ return new SeqUpdateExpr(Tok(e.tok), CloneExpr(e.Seq), CloneExpr(e.Index), CloneExpr(e.Value));
+
+ } 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));
+
+ } else if (expr is OldExpr) {
+ var e = (OldExpr)expr;
+ return new OldExpr(Tok(e.tok), CloneExpr(e.E));
+
+ } else if (expr is MultiSetFormingExpr) {
+ var e = (MultiSetFormingExpr)expr;
+ return new MultiSetFormingExpr(Tok(e.tok), CloneExpr(e.E));
+
+ } else if (expr is FreshExpr) {
+ var e = (FreshExpr)expr;
+ return new FreshExpr(Tok(e.tok), CloneExpr(e.E));
+
+ } else if (expr is UnaryExpr) {
+ var e = (UnaryExpr)expr;
+ return new UnaryExpr(Tok(e.tok), e.Op, CloneExpr(e.E));
+
+ } else if (expr is BinaryExpr) {
+ var e = (BinaryExpr)expr;
+ return new BinaryExpr(Tok(e.tok), e.Op, CloneExpr(e.E0), CloneExpr(e.E1));
+
+ } else if (expr is ChainingExpression) {
+ var e = (ChainingExpression)expr;
+ return CloneExpr(e.E); // just clone the desugaring, since it's already available
+
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ return new LetExpr(Tok(e.tok), e.Vars.ConvertAll(CloneBoundVar), e.RHSs.ConvertAll(CloneExpr), CloneExpr(e.Body));
+
+ } else if (expr is NamedExpr) {
+ var e = (NamedExpr)expr;
+ return new NamedExpr(Tok(e.tok), e.Name, CloneExpr(e.Body));
+ } else if (expr is ComprehensionExpr) {
+ var e = (ComprehensionExpr)expr;
+ var tk = Tok(e.tok);
+ var bvs = e.BoundVars.ConvertAll(CloneBoundVar);
+ var range = CloneExpr(e.Range);
+ var term = CloneExpr(e.Term);
+ if (e is ForallExpr) {
+ return new ForallExpr(tk, bvs, range, term, CloneAttributes(e.Attributes));
+ } else if (e is ExistsExpr) {
+ return new ExistsExpr(tk, bvs, range, term, CloneAttributes(e.Attributes));
+ } else if (e is MapComprehension) {
+ return new MapComprehension(tk, bvs, range, term);
+ } else {
+ Contract.Assert(e is SetComprehension);
+ return new SetComprehension(tk, bvs, range, term);
+ }
+
+ } else if (expr is WildcardExpr) {
+ return new WildcardExpr(Tok(expr.tok));
+
+ } else if (expr is PredicateExpr) {
+ var e = (PredicateExpr)expr;
+ if (e is AssertExpr) {
+ return new AssertExpr(Tok(e.tok), CloneExpr(e.Guard), CloneExpr(e.Body));
+ } else {
+ Contract.Assert(e is AssumeExpr);
+ return new AssumeExpr(Tok(e.tok), CloneExpr(e.Guard), CloneExpr(e.Body));
+ }
+
+ } else if (expr is ITEExpr) {
+ var e = (ITEExpr)expr;
+ return new ITEExpr(Tok(e.tok), CloneExpr(e.Test), CloneExpr(e.Thn), CloneExpr(e.Els));
+
+ } else if (expr is ParensExpression) {
+ var e = (ParensExpression)expr;
+ return CloneExpr(e.E); // skip the parentheses in the clone
+
+ } else if (expr is IdentifierSequence) {
+ var e = (IdentifierSequence)expr;
+ var aa = e.Arguments == null ? null : e.Arguments.ConvertAll(CloneExpr);
+ return new IdentifierSequence(e.Tokens.ConvertAll(tk => Tok(tk)), e.OpenParen == null ? null : Tok(e.OpenParen), aa);
+
+ } 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))));
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
+ }
+ }
+
+ public AssignmentRhs CloneRHS(AssignmentRhs rhs) {
+ AssignmentRhs c;
+ if (rhs is ExprRhs) {
+ var r = (ExprRhs)rhs;
+ c = new ExprRhs(CloneExpr(r.Expr));
+ } else if (rhs is HavocRhs) {
+ c = new HavocRhs(Tok(rhs.Tok));
+ } else {
+ var r = (TypeRhs)rhs;
+ if (r.ArrayDimensions != null) {
+ c = new TypeRhs(Tok(r.Tok), CloneType(r.EType), r.ArrayDimensions.ConvertAll(CloneExpr));
+ } else if (r.InitCall != null) {
+ c = new TypeRhs(Tok(r.Tok), CloneType(r.EType), (CallStmt)CloneStmt(r.InitCall));
+ } else {
+ c = new TypeRhs(Tok(r.Tok), CloneType(r.EType));
+ }
+ }
+ c.Attributes = CloneAttributes(rhs.Attributes);
+ return c;
+ }
+
+ public BlockStmt CloneBlockStmt(BlockStmt stmt) {
+ if (stmt == null) {
+ return null;
+ } else {
+ return new BlockStmt(Tok(stmt.Tok), stmt.Body.ConvertAll(CloneStmt));
+ }
+ }
+
+ public Statement CloneStmt(Statement stmt) {
+ if (stmt == null) {
+ return null;
+ }
+
+ Statement r;
+ if (stmt is AssertStmt) {
+ var s = (AssertStmt)stmt;
+ r = new AssertStmt(Tok(s.Tok), CloneExpr(s.Expr), null);
+
+ } else if (stmt is AssumeStmt) {
+ var s = (AssumeStmt)stmt;
+ r = new AssumeStmt(Tok(s.Tok), CloneExpr(s.Expr), null);
+
+ } else if (stmt is PrintStmt) {
+ var s = (PrintStmt)stmt;
+ r = new PrintStmt(Tok(s.Tok), s.Args.ConvertAll(CloneAttrArg));
+
+ } else if (stmt is BreakStmt) {
+ var s = (BreakStmt)stmt;
+ if (s.TargetLabel != null) {
+ r = new BreakStmt(Tok(s.Tok), s.TargetLabel);
+ } else {
+ r = new BreakStmt(Tok(s.Tok), s.BreakCount);
+ }
+
+ } else if (stmt is ReturnStmt) {
+ var s = (ReturnStmt)stmt;
+ r = new ReturnStmt(Tok(s.Tok), s.rhss == null ? null : s.rhss.ConvertAll(CloneRHS));
+
+ } else if (stmt is YieldStmt) {
+ var s = (YieldStmt)stmt;
+ r = new YieldStmt(Tok(s.Tok), s.rhss == null ? null : s.rhss.ConvertAll(CloneRHS));
+
+ } else if (stmt is AssignStmt) {
+ var s = (AssignStmt)stmt;
+ r = new AssignStmt(Tok(s.Tok), CloneExpr(s.Lhs), CloneRHS(s.Rhs));
+
+ } else if (stmt is VarDecl) {
+ var s = (VarDecl)stmt;
+ r = new VarDecl(Tok(s.Tok), s.Name, CloneType(s.OptionalType), s.IsGhost);
+
+ } else if (stmt is CallStmt) {
+ var s = (CallStmt)stmt;
+ r = new CallStmt(Tok(s.Tok), s.Lhs.ConvertAll(CloneExpr), CloneExpr(s.Receiver), s.MethodName, s.Args.ConvertAll(CloneExpr));
+
+ } else if (stmt is BlockStmt) {
+ r = CloneBlockStmt((BlockStmt)stmt);
+
+ } else if (stmt is IfStmt) {
+ var s = (IfStmt)stmt;
+ r = new IfStmt(Tok(s.Tok), CloneExpr(s.Guard), CloneBlockStmt(s.Thn), CloneStmt(s.Els));
+
+ } else if (stmt is AlternativeStmt) {
+ var s = (AlternativeStmt)stmt;
+ r = new AlternativeStmt(Tok(s.Tok), s.Alternatives.ConvertAll(CloneGuardedAlternative));
+
+ } else if (stmt is WhileStmt) {
+ var s = (WhileStmt)stmt;
+ r = new WhileStmt(Tok(s.Tok), CloneExpr(s.Guard), s.Invariants.ConvertAll(CloneMayBeFreeExpr), CloneSpecExpr(s.Decreases), CloneSpecFrameExpr(s.Mod), CloneBlockStmt(s.Body));
+
+ } else if (stmt is AlternativeLoopStmt) {
+ var s = (AlternativeLoopStmt)stmt;
+ r = new AlternativeLoopStmt(Tok(s.Tok), s.Invariants.ConvertAll(CloneMayBeFreeExpr), CloneSpecExpr(s.Decreases), CloneSpecFrameExpr(s.Mod), s.Alternatives.ConvertAll(CloneGuardedAlternative));
+
+ } else if (stmt is ParallelStmt) {
+ var s = (ParallelStmt)stmt;
+ r = new ParallelStmt(Tok(s.Tok), s.BoundVars.ConvertAll(CloneBoundVar), null, CloneExpr(s.Range), s.Ens.ConvertAll(CloneMayBeFreeExpr), CloneStmt(s.Body));
+
+ } else if (stmt is CalcStmt) {
+ var s = (CalcStmt)stmt;
+ r = new CalcStmt(Tok(s.Tok), s.Op, s.Lines.ConvertAll(CloneExpr), s.Hints.ConvertAll(CloneBlockStmt), new List<Nullable<BinaryExpr.Opcode>>(s.CustomOps));
+
+ } else if (stmt is MatchStmt) {
+ var s = (MatchStmt)stmt;
+ r = new MatchStmt(Tok(s.Tok), CloneExpr(s.Source),
+ s.Cases.ConvertAll(c => new MatchCaseStmt(Tok(c.tok), c.Id, c.Arguments.ConvertAll(CloneBoundVar), c.Body.ConvertAll(CloneStmt))));
+
+ } else if (stmt is AssignSuchThatStmt) {
+ var s = (AssignSuchThatStmt)stmt;
+ r = new AssignSuchThatStmt(Tok(s.Tok), s.Lhss.ConvertAll(CloneExpr), CloneExpr(s.Expr), s.AssumeToken == null ? null : Tok(s.AssumeToken));
+
+ } else if (stmt is UpdateStmt) {
+ var s = (UpdateStmt)stmt;
+ r = new UpdateStmt(Tok(s.Tok), s.Lhss.ConvertAll(CloneExpr), s.Rhss.ConvertAll(CloneRHS), s.CanMutateKnownState);
+
+ } else if (stmt is VarDeclStmt) {
+ var s = (VarDeclStmt)stmt;
+ r = new VarDeclStmt(Tok(s.Tok), s.Lhss.ConvertAll(c => (VarDecl)CloneStmt(c)), (ConcreteUpdateStatement)CloneStmt(s.Update));
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement
+ }
+
+ // add labels to the cloned statement
+ AddStmtLabels(r, stmt.Labels);
+ r.Attributes = CloneAttributes(stmt.Attributes);
+
+ return r;
+ }
+
+ public void AddStmtLabels(Statement s, LList<Label> node) {
+ if (node != null) {
+ AddStmtLabels(s, node.Next);
+ if (node.Data.Name == null) {
+ // this indicates an implicit-target break statement that has been resolved; don't add it
+ } else {
+ s.Labels = new LList<Label>(new Label(Tok(node.Data.Tok), node.Data.Name), s.Labels);
+ }
+ }
+ }
+
+ public GuardedAlternative CloneGuardedAlternative(GuardedAlternative alt) {
+ return new GuardedAlternative(Tok(alt.Tok), CloneExpr(alt.Guard), alt.Body.ConvertAll(CloneStmt));
+ }
+
+ public Function CloneFunction(Function f) {
+ var tps = f.TypeArgs.ConvertAll(CloneTypeParam);
+ var formals = f.Formals.ConvertAll(CloneFormal);
+ var req = f.Req.ConvertAll(CloneExpr);
+ var reads = f.Reads.ConvertAll(CloneFrameExpr);
+ var decreases = CloneSpecExpr(f.Decreases);
+ var ens = f.Ens.ConvertAll(CloneExpr);
+ var body = CloneExpr(f.Body);
+
+ if (f is Predicate) {
+ return new Predicate(Tok(f.tok), f.Name, f.IsStatic, f.IsGhost, tps, f.OpenParen, formals,
+ req, reads, ens, decreases, body, Predicate.BodyOriginKind.OriginalOrInherited, CloneAttributes(f.Attributes), false);
+ } else if (f is CoPredicate) {
+ return new CoPredicate(Tok(f.tok), f.Name, f.IsStatic, tps, f.OpenParen, formals,
+ req, reads, ens, body, CloneAttributes(f.Attributes), false);
+ } else {
+ return new Function(Tok(f.tok), f.Name, f.IsStatic, f.IsGhost, tps, f.OpenParen, formals, CloneType(f.ResultType),
+ req, reads, ens, decreases, body, CloneAttributes(f.Attributes), false);
+ }
+ }
+
+ public Method CloneMethod(Method m) {
+ Contract.Requires(m != null);
+
+ var tps = m.TypeArgs.ConvertAll(CloneTypeParam);
+ var ins = m.Ins.ConvertAll(CloneFormal);
+ var req = m.Req.ConvertAll(CloneMayBeFreeExpr);
+ var mod = CloneSpecFrameExpr(m.Mod);
+ var decreases = CloneSpecExpr(m.Decreases);
+
+ var ens = m.Ens.ConvertAll(CloneMayBeFreeExpr);
+
+ 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), false);
+ } else {
+ return new Method(Tok(m.tok), m.Name, m.IsStatic, m.IsGhost, tps, ins, m.Outs.ConvertAll(CloneFormal),
+ req, mod, ens, decreases, body, CloneAttributes(m.Attributes), false);
+ }
+ }
+ public virtual IToken Tok(IToken tok) {
+ return tok;
+ }
+ }
+} \ No newline at end of file
diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs
new file mode 100644
index 00000000..4c334033
--- /dev/null
+++ b/Source/Dafny/Compiler.cs
@@ -0,0 +1,2432 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.IO;
+using System.Diagnostics.Contracts;
+using Bpl = Microsoft.Boogie;
+using System.Text;
+
+namespace Microsoft.Dafny {
+ public class Compiler {
+ public Compiler(TextWriter wr) {
+ Contract.Requires(wr != null);
+ this.wr = wr;
+ }
+
+ [ContractInvariantMethod]
+ void ObjectInvariant()
+ {
+ Contract.Invariant(wr!=null);
+ }
+
+ TextWriter wr;
+ Method enclosingMethod; // non-null when a method body is being translated
+
+ public int ErrorCount;
+ void Error(string msg, params object[] args) {
+ Contract.Requires(msg != null);
+ Contract.Requires(args != null);
+
+ string s = string.Format("Compilation error: " + msg, args);
+ Console.WriteLine(s);
+ wr.WriteLine("/* {0} */", s);
+ ErrorCount++;
+ }
+
+ void ReadRuntimeSystem() {
+ 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)))
+ {
+ while (true) {
+ string s = rd.ReadLine();
+ if (s == null)
+ return;
+ wr.WriteLine(s);
+ }
+ }
+ }
+
+ readonly int IndentAmount = 2;
+ void Indent(int ind) {
+ string spaces = " ";
+ for (; spaces.Length < ind; ind -= spaces.Length) {
+ wr.Write(spaces);
+ }
+ wr.Write(spaces.Substring(0, ind));
+ }
+
+ public void Compile(Program program) {Contract.Requires(program != null);
+ wr.WriteLine("// Dafny program {0} compiled into C#", program.Name);
+ wr.WriteLine();
+ ReadRuntimeSystem();
+ CompileBuiltIns(program.BuiltIns);
+
+ foreach (ModuleDefinition m in program.CompileModules) {
+ if (m.IsGhost) {
+ // the purpose of a ghost module is to skip compilation
+ continue;
+ }
+ int indent = 0;
+ if (!m.IsDefaultModule) {
+ wr.WriteLine("namespace @{0} {{", m.CompileName);
+ indent += IndentAmount;
+ }
+ foreach (TopLevelDecl d in m.TopLevelDecls) {
+ wr.WriteLine();
+ if (d is ArbitraryTypeDecl) {
+ var at = (ArbitraryTypeDecl)d;
+ Error("Arbitrary type ('{0}') cannot be compiled", at.CompileName);
+ } else if (d is DatatypeDecl) {
+ var dt = (DatatypeDecl)d;
+ Indent(indent);
+ 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);
+ } else if (d is IteratorDecl) {
+ var iter = (IteratorDecl)d;
+ // An iterator is compiled as follows:
+ // public class MyIteratorExample<T>
+ // {
+ // public T q; // in-parameter
+ // public T x; // yield-parameter
+ // public int y; // yield-parameter
+ // IEnumerator<object> _iter;
+ //
+ // public void _MyIteratorExample(T q) {
+ // this.q = q;
+ // _iter = TheIterator();
+ // }
+ //
+ // public void MoveNext(out bool more) {
+ // more =_iter.MoveNext();
+ // }
+ //
+ // private IEnumerator<object> TheIterator() {
+ // // the translation of the body of the iterator, with each "yield" turning into a "yield return null;"
+ // yield break;
+ // }
+ // }
+
+ Indent(indent);
+ wr.Write("public class @{0}", iter.CompileName);
+ if (iter.TypeArgs.Count != 0) {
+ wr.Write("<{0}>", TypeParameters(iter.TypeArgs));
+ }
+ wr.WriteLine(" {");
+ var ind = indent + IndentAmount;
+ // here come the fields
+ Constructor ct = null;
+ 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));
+ } 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;");
+
+ // here's the initializer method
+ Indent(ind); 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);
+ sep = ", ";
+ }
+ }
+ wr.WriteLine(") {");
+ foreach (var p in ct.Ins) {
+ if (!p.IsGhost) {
+ Indent(ind + IndentAmount);
+ wr.WriteLine("this.@{0} = @{0};", p.CompileName);
+ }
+ }
+ Indent(ind + IndentAmount); wr.WriteLine("__iter = TheIterator();");
+ Indent(ind); 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() {");
+ if (iter.Body == null) {
+ Error("Iterator {0} has no body", iter.FullName);
+ } else {
+ TrStmt(iter.Body, ind + IndentAmount);
+ }
+ Indent(ind + IndentAmount); wr.WriteLine("yield break;");
+ Indent(ind); wr.WriteLine("}");
+ // end of the class
+ Indent(indent); wr.WriteLine("}");
+
+ } else if (d is ClassDecl) {
+ var cl = (ClassDecl)d;
+ Indent(indent);
+ wr.Write("public class @{0}", cl.CompileName);
+ if (cl.TypeArgs.Count != 0) {
+ wr.Write("<{0}>", TypeParameters(cl.TypeArgs));
+ }
+ wr.WriteLine(" {");
+ CompileClassMembers(cl, indent+IndentAmount);
+ Indent(indent); wr.WriteLine("}");
+ } else if (d is ModuleDecl) {
+ // nop
+ } else { Contract.Assert(false); }
+ }
+ if (!m.IsDefaultModule) {
+ wr.WriteLine("}} // end of namespace {0}", m.CompileName);
+ }
+ }
+ }
+
+ void CompileBuiltIns(BuiltIns builtIns) {
+ wr.WriteLine("namespace Dafny {");
+ Indent(IndentAmount);
+ 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);
+ wr.Write("public static T[");
+ RepeatWrite(wr, dims, "", ",");
+ wr.Write("] InitNewArray{0}<T>(", dims);
+ RepeatWrite(wr, dims, "BigInteger size{0}", ", ");
+ wr.WriteLine(") {");
+ // int s0 = (int)size0;
+ for (int i = 0; i < dims; i++) {
+ Indent(4 * IndentAmount);
+ wr.WriteLine("int s{0} = (int)size{0};", i);
+ }
+ // T[,] a = new T[s0, s1];
+ Indent(4 * IndentAmount);
+ 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);
+ wr.Write("BigInteger[");
+ RepeatWrite(wr, dims, "", ",");
+ wr.Write("] b = a as BigInteger[");
+ RepeatWrite(wr, dims, "", ",");
+ wr.WriteLine("];");
+ // if (b != null) {
+ Indent(4 * IndentAmount);
+ wr.WriteLine("if (b != null) {");
+ // BigInteger z = new BigInteger(0);
+ Indent(5 * IndentAmount);
+ 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);
+ wr.WriteLine("for (int i{0} = 0; i{0} < s{0}; i{0}++)", i);
+ }
+ // b[i0,i1] = z;
+ Indent((5+dims) * IndentAmount);
+ wr.Write("b[");
+ RepeatWrite(wr, dims, "i{0}", ",");
+ wr.WriteLine("] = z;");
+ // }
+ Indent(4 * IndentAmount);
+ wr.WriteLine("}");
+ // return a;
+ Indent(4 * IndentAmount);
+ wr.WriteLine("return a;");
+ // }
+ Indent(3 * IndentAmount);
+ wr.WriteLine("}"); // end of method
+ }
+ }
+ Indent(IndentAmount);
+ wr.WriteLine("}"); // end of class Helpers
+ wr.WriteLine("}"); // end of namespace
+ }
+
+ static void RepeatWrite(TextWriter wr, int times, string template, string separator) {
+ Contract.Requires(1 <= times);
+ string s = "";
+ for (int i = 0; i < times; i++) {
+ wr.Write(s);
+ wr.Write(template, i);
+ s = separator;
+ }
+ }
+
+ void CompileDatatypeConstructors(DatatypeDecl dt, int indent)
+ {
+ Contract.Requires(dt != null);
+
+ string typeParams = dt.TypeArgs.Count == 0 ? "" : string.Format("<{0}>", TypeParameters(dt.TypeArgs));
+ if (dt is CoDatatypeDecl) {
+ // public class Dt__Lazy<T> : Base_Dt<T> {
+ // public delegate Base_Dt<T> Computer();
+ // public delegate Computer ComputerComputer();
+ // Computer c;
+ // public Dt__Lazy(Computer c) { this.c = c; }
+ // public Base_Dt<T> Get() { return c(); }
+ // }
+ Indent(indent);
+ wr.WriteLine("public class {0}__Lazy{1} : Base_{0}{1} {{", dt.CompileName, typeParams);
+ int ind = indent + IndentAmount;
+ Indent(ind);
+ wr.WriteLine("public delegate Base_{0}{1} Computer();", dt.CompileName, typeParams);
+ Indent(ind);
+ wr.WriteLine("public delegate Computer ComputerComputer();");
+ Indent(ind);
+ wr.WriteLine("Computer c;");
+ Indent(ind);
+ wr.WriteLine("public {0}__Lazy(Computer c) {{ this.c = c; }}", dt.CompileName);
+ Indent(ind);
+ wr.WriteLine("public Base_{0}{1} Get() {{ return c(); }}", dt.CompileName, typeParams);
+ Indent(indent);
+ wr.WriteLine("}");
+ }
+
+ int constructorIndex = 0; // used to give each constructor a different
+ foreach (DatatypeCtor ctor in dt.Ctors) {
+ // class Dt_Ctor<T,U> : Base_Dt<T> {
+ // Fields;
+ // public Dt_Ctor(arguments) {
+ // Fields = arguments;
+ // }
+ // public override bool Equals(object other) {
+ // var oth = other as Dt_Dtor;
+ // return oth != null && equals(_field0, oth._field0) && ... ;
+ // }
+ // public override int GetHashCode() {
+ // return base.GetHashCode(); // surely this can be improved
+ // }
+ // public override string ToString() { // only for inductive datatypes
+ // // ...
+ // }
+ // }
+ Indent(indent);
+ wr.Write("public class {0}", DtCtorDeclarationName(ctor, dt.TypeArgs));
+ wr.WriteLine(" : Base_{0}{1} {{", dt.CompileName, typeParams);
+ int ind = indent + IndentAmount;
+
+ 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));
+ i++;
+ }
+ }
+
+ Indent(ind);
+ wr.Write("public {0}(", DtCtorDeclartionName(ctor));
+ WriteFormals("", ctor.Formals);
+ wr.WriteLine(") {");
+ i = 0;
+ foreach (Formal arg in ctor.Formals) {
+ if (!arg.IsGhost) {
+ Indent(ind + IndentAmount);
+ wr.WriteLine("this.@{0} = @{0};", FormalName(arg, i));
+ i++;
+ }
+ }
+ Indent(ind); wr.WriteLine("}");
+
+ // Equals method
+ Indent(ind); wr.WriteLine("public override bool Equals(object other) {");
+ Indent(ind + IndentAmount);
+ wr.Write("var oth = other as {0}", DtCtorName(ctor, dt.TypeArgs));
+ wr.WriteLine(";");
+ Indent(ind + IndentAmount);
+ wr.Write("return oth != null");
+ i = 0;
+ foreach (Formal arg in ctor.Formals) {
+ if (!arg.IsGhost) {
+ string nm = FormalName(arg, i);
+ if (arg.Type.IsDatatype || arg.Type.IsTypeParameter) {
+ wr.Write(" && this.@{0}.Equals(oth.@{0})", nm);
+ } else {
+ wr.Write(" && this.@{0} == oth.@{0}", nm);
+ }
+ i++;
+ }
+ }
+ wr.WriteLine(";");
+ Indent(ind); wr.WriteLine("}");
+
+ // GetHashCode method
+ Indent(ind); wr.WriteLine("public override int GetHashCode() {");
+ Indent(ind + IndentAmount); wr.Write("return " + constructorIndex);
+
+ i = 0;
+ foreach (Formal arg in ctor.Formals) {
+ if (!arg.IsGhost) {
+ string nm = FormalName(arg, i);
+ wr.Write(" ^ this.@{0}.GetHashCode()", nm);
+ i++;
+ }
+ }
+ wr.WriteLine(";");
+ Indent(ind); wr.WriteLine("}");
+
+ if (dt is IndDatatypeDecl) {
+ Indent(ind); wr.WriteLine("public override string ToString() {");
+ string nm = (dt.Module.IsDefaultModule ? "" : dt.Module.CompileName + ".") + dt.CompileName + "." + ctor.CompileName;
+ Indent(ind + IndentAmount); wr.WriteLine("string s = \"{0}\";", nm);
+ if (ctor.Formals.Count != 0) {
+ Indent(ind + IndentAmount); wr.WriteLine("s += \"(\";");
+ i = 0;
+ foreach (var arg in ctor.Formals) {
+ if (!arg.IsGhost) {
+ if (i != 0) {
+ Indent(ind + IndentAmount); wr.WriteLine("s += \", \";");
+ }
+ Indent(ind + IndentAmount); wr.WriteLine("s += @{0}.ToString();", FormalName(arg, i));
+ i++;
+ }
+ }
+ Indent(ind + IndentAmount); wr.WriteLine("s += \")\";");
+ }
+ Indent(ind + IndentAmount); wr.WriteLine("return s;");
+ Indent(ind); wr.WriteLine("}");
+ }
+
+ Indent(indent); wr.WriteLine("}");
+ }
+ constructorIndex ++;
+ }
+
+ void CompileDatatypeStruct(DatatypeDecl dt, int indent) {
+ Contract.Requires(dt != null);
+
+ // public struct Dt<T> : IDatatype{
+ // Base_Dt<T> _d;
+ // public Base_Dt<T> _D {
+ // get {
+ // if (_d == null) {
+ // _d = Default;
+ // } else if (_d is Dt__Lazy<T>) { // co-datatypes only
+ // _d = ((Dt__Lazy<T>)_d).Get(); // co-datatypes only
+ // }
+ // return _d;
+ // }
+ // }
+ // public Dt(Base_Dt<T> d) { this._d = d; }
+ // static Base_Dt<T> theDefault;
+ // public static Base_Dt<T> Default {
+ // get {
+ // if (theDefault == null) {
+ // theDefault = ...;
+ // }
+ // return theDefault;
+ // }
+ // }
+ // public override bool Equals(object other) {
+ // return other is Dt<T> && _D.Equals(((Dt<T>)other)._D);
+ // }
+ // public override int GetHashCode() { return _D.GetHashCode(); }
+ // public override string ToString() { return _D.ToString(); } // only for inductive datatypes
+ //
+ // public bool is_Ctor0 { get { return _D is Dt_Ctor0; } }
+ // ...
+ //
+ // public T0 dtor_Dtor0 { get { return ((DT_Ctor)_D).@Dtor0; } }
+ // ...
+ // }
+ string DtT = dt.CompileName;
+ string DtT_TypeArgs = "";
+ if (dt.TypeArgs.Count != 0) {
+ DtT_TypeArgs = "<" + TypeParameters(dt.TypeArgs) + ">";
+ DtT += DtT_TypeArgs;
+ }
+
+ Indent(indent);
+ wr.WriteLine("public struct @{0} {{", DtT);
+ int ind = indent + IndentAmount;
+
+ Indent(ind);
+ wr.WriteLine("Base_{0} _d;", DtT);
+
+ Indent(ind);
+ wr.WriteLine("public Base_{0} _D {{", DtT);
+ Indent(ind + IndentAmount);
+ wr.WriteLine("get {");
+ Indent(ind + 2 * IndentAmount);
+ wr.WriteLine("if (_d == null) {");
+ Indent(ind + 3 * IndentAmount);
+ wr.WriteLine("_d = Default;");
+ if (dt is CoDatatypeDecl) {
+ string typeParams = dt.TypeArgs.Count == 0 ? "" : string.Format("<{0}>", TypeParameters(dt.TypeArgs));
+ Indent(ind + 2 * IndentAmount);
+ wr.WriteLine("}} else if (_d is {0}__Lazy{1}) {{", dt.CompileName, typeParams);
+ Indent(ind + 3 * IndentAmount);
+ 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);
+ wr.WriteLine("public @{0}(Base_{1} d) {{ this._d = d; }}", dt.CompileName, DtT);
+
+ Indent(ind);
+ wr.WriteLine("static Base_{0} theDefault;", DtT);
+
+ Indent(ind);
+ wr.WriteLine("public static Base_{0} Default {{", DtT);
+ Indent(ind + IndentAmount);
+ wr.WriteLine("get {");
+ Indent(ind + 2 * IndentAmount);
+ wr.WriteLine("if (theDefault == null) {");
+ Indent(ind + 3 * IndentAmount);
+ wr.Write("theDefault = ");
+
+ DatatypeCtor defaultCtor;
+ if (dt is IndDatatypeDecl) {
+ defaultCtor = ((IndDatatypeDecl)dt).DefaultCtor;
+ } else {
+ defaultCtor = ((CoDatatypeDecl)dt).Ctors[0]; // pick any one of them
+ }
+ wr.Write("new {0}", DtCtorName(defaultCtor, dt.TypeArgs));
+ wr.Write("(");
+ string sep = "";
+ foreach (Formal f in defaultCtor.Formals) {
+ if (!f.IsGhost) {
+ wr.Write("{0}{1}", sep, DefaultValue(f.Type));
+ sep = ", ";
+ }
+ }
+ wr.Write(")");
+
+ wr.WriteLine(";");
+ Indent(ind + 2 * IndentAmount);
+ wr.WriteLine("}");
+ Indent(ind + 2 * IndentAmount);
+ wr.WriteLine("return theDefault;");
+ Indent(ind + IndentAmount); wr.WriteLine("}");
+
+ Indent(ind); wr.WriteLine("}");
+
+ Indent(ind); wr.WriteLine("public override bool Equals(object other) {");
+ Indent(ind + IndentAmount);
+ wr.WriteLine("return other is @{0} && _D.Equals(((@{0})other)._D);", DtT);
+ Indent(ind); wr.WriteLine("}");
+
+ Indent(ind);
+ wr.WriteLine("public override int GetHashCode() { return _D.GetHashCode(); }");
+ if (dt is IndDatatypeDecl) {
+ Indent(ind);
+ 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);
+ wr.WriteLine("public bool is_{0} {{ get {{ return _D is {1}_{0}{2}; }} }}", ctor.CompileName, dt.CompileName, DtT_TypeArgs);
+ }
+ if (dt.HasFinitePossibleValues) {
+ Indent(ind);
+ wr.WriteLine("public static System.Collections.Generic.IEnumerable<@{0}> AllSingletonConstructors {{", DtT);
+ Indent(ind + IndentAmount);
+ wr.WriteLine("get {");
+ foreach (var ctr in dt.Ctors) {
+ if (ctr.Formals.Count == 0) {
+ Indent(ind + IndentAmount + IndentAmount);
+ wr.WriteLine("yield return new @{0}(new {2}_{1}());", DtT, ctr.CompileName, dt.CompileName);
+ }
+ }
+ Indent(ind + IndentAmount + IndentAmount);
+ wr.WriteLine("yield break;");
+ Indent(ind + IndentAmount);
+ wr.WriteLine("}");
+ Indent(ind);
+ wr.WriteLine("}");
+ }
+
+ // destructors
+ foreach (var ctor in dt.Ctors) {
+ 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(indent);
+ wr.WriteLine("}");
+ }
+
+ int WriteFormals(string sep, List<Formal/*!*/>/*!*/ formals)
+ {
+ Contract.Requires(sep != null);
+ Contract.Requires(cce.NonNullElements(formals));
+ int i = 0;
+ 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);
+ sep = ", ";
+ i++;
+ }
+ }
+ return i; // the number of formals written
+ }
+
+ string FormalName(Formal formal, int i) {
+ Contract.Requires(formal != null);
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ return formal.HasName ? formal.CompileName : "_a" + i;
+ }
+
+ string DtName(DatatypeDecl decl) {
+ return decl.Module.IsDefaultModule ? decl.CompileName : decl.FullCompileName;
+ }
+ string DtCtorName(DatatypeCtor ctor) {
+ Contract.Requires(ctor != null);
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ return DtName(ctor.EnclosingDatatype) + "_" + ctor.CompileName;
+ }
+ string DtCtorDeclartionName(DatatypeCtor ctor) {
+ Contract.Requires(ctor != null);
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ return ctor.EnclosingDatatype.CompileName + "_" + ctor.CompileName;
+ }
+
+ string DtCtorName(DatatypeCtor ctor, List<TypeParameter> typeParams) {
+ Contract.Requires(ctor != null);
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ var s = DtCtorName(ctor);
+ if (typeParams != null && typeParams.Count != 0) {
+ s += "<" + TypeParameters(typeParams) + ">";
+ }
+ return s;
+ }
+ string DtCtorDeclarationName(DatatypeCtor ctor, List<TypeParameter> typeParams) {
+ Contract.Requires(ctor != null);
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ var s = DtCtorDeclartionName(ctor);
+ if (typeParams != null && typeParams.Count != 0) {
+ s += "<" + TypeParameters(typeParams) + ">";
+ }
+ return s;
+ }
+
+ string DtCtorName(DatatypeCtor ctor, List<Type> typeArgs) {
+ Contract.Requires(ctor != null);
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ var s = DtCtorName(ctor);
+ if (typeArgs != null && typeArgs.Count != 0) {
+ s += "<" + TypeNames(typeArgs) + ">";
+ }
+ return s;
+ }
+
+ public bool HasMain(Program program) {
+ foreach (var module in program.Modules) {
+ 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) {
+ if (!m.IsGhost && m.Name == "Main" && m.Ins.Count == 0 && m.Outs.Count == 0) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ void CompileClassMembers(ClassDecl c, int indent)
+ {
+ Contract.Requires(c != null);
+ foreach (MemberDecl member in c.Members) {
+ if (member is Field) {
+ Field f = (Field)member;
+ if (!f.IsGhost) {
+ Indent(indent);
+ wr.WriteLine("public {0} @{1} = {2};", TypeName(f.Type), f.CompileName, DefaultValue(f.Type));
+ }
+
+ } else if (member is Function) {
+ Function f = (Function)member;
+ if (f.IsGhost) {
+ // nothing to compile
+ } else if (f.Body == null) {
+ Error("Function {0} has no body", f.FullName);
+ } else {
+ Indent(indent);
+ wr.Write("public {0}{1} @{2}", f.IsStatic ? "static " : "", TypeName(f.ResultType), f.CompileName);
+ if (f.TypeArgs.Count != 0) {
+ wr.Write("<{0}>", TypeParameters(f.TypeArgs));
+ }
+ wr.Write("(");
+ WriteFormals("", f.Formals);
+ wr.WriteLine(") {");
+ CompileReturnBody(f.Body, indent + IndentAmount);
+ Indent(indent); wr.WriteLine("}");
+ }
+
+ } else if (member is Method) {
+ Method m = (Method)member;
+ if (!m.IsGhost) {
+ Indent(indent);
+ 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);
+ wr.WriteLine(")");
+ Indent(indent); wr.WriteLine("{");
+ foreach (Formal p in m.Outs) {
+ if (!p.IsGhost) {
+ Indent(indent + IndentAmount);
+ wr.WriteLine("@{0} = {1};", p.CompileName, DefaultValue(p.Type));
+ }
+ }
+ if (m.Body == null) {
+ Error("Method {0} has no body", m.FullName);
+ } else {
+ if (m.IsTailRecursive) {
+ Indent(indent); wr.WriteLine("TAIL_CALL_START: ;");
+ if (!m.IsStatic) {
+ Indent(indent + IndentAmount); wr.WriteLine("var _this = this;");
+ }
+ }
+ Contract.Assert(enclosingMethod == null);
+ enclosingMethod = m;
+ TrStmtList(m.Body.Body, indent);
+ Contract.Assert(enclosingMethod == m);
+ enclosingMethod = null;
+ }
+ Indent(indent); wr.WriteLine("}");
+
+ // allow the Main method to be an instance method
+ if (m.Name == "Main" && m.Ins.Count == 0 && m.Outs.Count == 0) {
+ Indent(indent);
+ 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 = ", ";
+ }
+ wr.Write(">");
+ }
+ wr.WriteLine("();");
+ Indent(indent + IndentAmount); wr.WriteLine("b.Main();");
+ Indent(indent); wr.WriteLine("}");
+ }
+ }
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member
+ }
+ }
+ }
+
+ void CompileReturnBody(Expression body, int indent) {
+ body = body.Resolved;
+ if (body is MatchExpr) {
+ MatchExpr me = (MatchExpr)body;
+ // Type source = e;
+ // if (source.is_Ctor0) {
+ // FormalType f0 = ((Dt_Ctor0)source._D).a0;
+ // ...
+ // return Body0;
+ // } else if (...) {
+ // ...
+ // } else if (true) {
+ // ...
+ // }
+
+ SpillLetVariableDecls(me.Source, indent);
+ string source = "_source" + tmpVarCount;
+ tmpVarCount++;
+ Indent(indent);
+ wr.Write("{0} {1} = ", TypeName(cce.NonNull(me.Source.Type)), source);
+ TrExpr(me.Source);
+ wr.WriteLine(";");
+
+ if (me.Cases.Count == 0) {
+ // the verifier would have proved we never get here; still, we need some code that will compile
+ Indent(indent);
+ wr.WriteLine("throw new System.Exception();");
+ } else {
+ int i = 0;
+ var sourceType = (UserDefinedType)me.Source.Type;
+ foreach (MatchCaseExpr mc in me.Cases) {
+ MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, me.Cases.Count, indent + IndentAmount);
+ CompileReturnBody(mc.Body, indent + IndentAmount);
+ i++;
+ }
+ Indent(indent); wr.WriteLine("}");
+ }
+
+ } else {
+ SpillLetVariableDecls(body, indent);
+ Indent(indent);
+ wr.Write("return ");
+ TrExpr(body);
+ wr.WriteLine(";");
+ }
+ }
+
+ void SpillLetVariableDecls(Expression expr, int indent) {
+ Contract.Requires(0 <= indent);
+ if (expr == null) {
+ // allow "null" as an argument; nothing to do
+ return;
+ }
+ if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ foreach (var v in e.Vars) {
+ Indent(indent);
+ wr.WriteLine("{0} @{1};", TypeName(v.Type), v.CompileName);
+ }
+ }
+ foreach (var ee in expr.SubExpressions) {
+ SpillLetVariableDecls(ee, indent);
+ }
+ }
+
+ // ----- Type ---------------------------------------------------------------------------------
+
+ readonly string DafnySetClass = "Dafny.Set";
+ readonly string DafnyMultiSetClass = "Dafny.MultiSet";
+ readonly string DafnySeqClass = "Dafny.Sequence";
+ readonly string DafnyMapClass = "Dafny.Map";
+
+ string TypeName(Type type)
+ {
+ Contract.Requires(type != null);
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ while (true) {
+ TypeProxy tp = type as TypeProxy;
+ if (tp == null) {
+ break;
+ } else if (tp.T == null) {
+ // unresolved proxy; just treat as ref, since no particular type information is apparently needed for this type
+ return "object";
+ } else {
+ type = tp.T;
+ }
+ }
+
+ if (type is BoolType) {
+ return "bool";
+ } else if (type is IntType) {
+ return "BigInteger";
+ } else if (type is ObjectType) {
+ return "object";
+ } else if (type.IsArrayType) {
+ ArrayClassDecl at = type.AsArrayType;
+ Contract.Assert(at != null); // follows from type.IsArrayType
+ Type elType = UserDefinedType.ArrayElementType(type);
+ string name = TypeName(elType) + "[";
+ for (int i = 1; i < at.Dims; i++) {
+ name += ",";
+ }
+ return name + "]";
+ } else if (type is UserDefinedType) {
+ UserDefinedType udt = (UserDefinedType)type;
+ string s = "@" + udt.FullCompileName;
+ if (udt.TypeArgs.Count != 0) {
+ if (Contract.Exists(udt.TypeArgs, argType =>argType is ObjectType)) {
+ Error("compilation does not support type 'object' as a type parameter; consider introducing a ghost");
+ }
+ s += "<" + TypeNames(udt.TypeArgs) + ">";
+ }
+ return s;
+ } else if (type is SetType) {
+ Type argType = ((SetType)type).Arg;
+ if (argType is ObjectType) {
+ Error("compilation of set<object> is not supported; consider introducing a ghost");
+ }
+ return DafnySetClass + "<" + TypeName(argType) + ">";
+ } else if (type is SeqType) {
+ Type argType = ((SeqType)type).Arg;
+ if (argType is ObjectType) {
+ Error("compilation of seq<object> is not supported; consider introducing a ghost");
+ }
+ return DafnySeqClass + "<" + TypeName(argType) + ">";
+ } else if (type is MultiSetType) {
+ Type argType = ((MultiSetType)type).Arg;
+ if (argType is ObjectType) {
+ Error("compilation of seq<object> is not supported; consider introducing a ghost");
+ }
+ return DafnyMultiSetClass + "<" + TypeName(argType) + ">";
+ } else if (type is MapType) {
+ Type domType = ((MapType)type).Domain;
+ Type ranType = ((MapType)type).Range;
+ if (domType is ObjectType || ranType is ObjectType) {
+ Error("compilation of map<object, _> or map<_, object> is not supported; consider introducing a ghost");
+ }
+ return DafnyMapClass + "<" + TypeName(domType) + "," + TypeName(ranType) + ">";
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
+ }
+ }
+
+ string/*!*/ TypeNames(List<Type/*!*/>/*!*/ types) {
+ Contract.Requires(cce.NonNullElements(types));
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ string s = "";
+ string sep = "";
+ foreach (Type t in types) {
+ s += sep + TypeName(t);
+ sep = ",";
+ }
+ return s;
+ }
+
+ 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;
+ }
+
+ string DefaultValue(Type type)
+ {
+ Contract.Requires(type != null);
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ while (true) {
+ TypeProxy tp = type as TypeProxy;
+ if (tp == null) {
+ break;
+ } else if (tp.T == null) {
+ // unresolved proxy; just treat as ref, since no particular type information is apparently needed for this type
+ return "null";
+ } else {
+ type = tp.T;
+ }
+ }
+
+ if (type is BoolType) {
+ return "false";
+ } else if (type is IntType) {
+ return "new BigInteger(0)";
+ } else if (type.IsRefType) {
+ return string.Format("({0})null", TypeName(type));
+ } else if (type.IsDatatype) {
+ UserDefinedType udt = (UserDefinedType)type;
+ string s = "@" + udt.FullCompileName;
+ if (udt.TypeArgs.Count != 0) {
+ s += "<" + TypeNames(udt.TypeArgs) + ">";
+ }
+ return string.Format("new {0}()", s);
+ } else if (type.IsTypeParameter) {
+ UserDefinedType 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 {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
+ }
+ }
+
+ // ----- Stmt ---------------------------------------------------------------------------------
+
+ void CheckHasNoAssumes(Statement stmt) {
+ Contract.Requires(stmt != null);
+ if (stmt is AssumeStmt) {
+ Error("an assume statement cannot be compiled (line {0})", stmt.Tok.line);
+ } else if (stmt is AssignSuchThatStmt) {
+ var s = (AssignSuchThatStmt)stmt;
+ if (s.AssumeToken != null) {
+ Error("an assume statement cannot be compiled (line {0})", s.AssumeToken.line);
+ }
+ } else {
+ foreach (var ss in stmt.SubStatements) {
+ CheckHasNoAssumes(ss);
+ }
+ }
+ }
+
+ void TrStmt(Statement stmt, int indent)
+ {
+ Contract.Requires(stmt != null);
+ if (stmt.IsGhost) {
+ CheckHasNoAssumes(stmt);
+ return;
+ }
+
+ if (stmt is PrintStmt) {
+ PrintStmt s = (PrintStmt)stmt;
+ foreach (Attributes.Argument arg in s.Args) {
+ SpillLetVariableDecls(arg.E, indent);
+ Indent(indent);
+ wr.Write("System.Console.Write(");
+ if (arg.S != null) {
+ wr.Write("\"{0}\"", arg.S);
+ } else {
+ Contract.Assert(arg.E != null);
+ TrExpr(arg.E);
+ }
+ wr.WriteLine(");");
+ }
+ } else if (stmt is BreakStmt) {
+ var s = (BreakStmt)stmt;
+ Indent(indent);
+ wr.WriteLine("goto after_{0};", s.TargetStmt.Labels.Data.UniqueId);
+ } else if (stmt is ProduceStmt) {
+ var s = (ProduceStmt)stmt;
+ if (s.hiddenUpdate != null)
+ TrStmt(s.hiddenUpdate, indent);
+ Indent(indent);
+ if (s is YieldStmt) {
+ wr.WriteLine("yield return null;");
+ } else {
+ wr.WriteLine("return;");
+ }
+ } else if (stmt is UpdateStmt) {
+ var s = (UpdateStmt)stmt;
+ var resolved = s.ResolvedStatements;
+ if (resolved.Count == 1) {
+ TrStmt(resolved[0], indent);
+ } else {
+ // multi-assignment
+ Contract.Assert(s.Lhss.Count == resolved.Count);
+ Contract.Assert(s.Rhss.Count == resolved.Count);
+ var lvalues = new List<string>();
+ var rhss = new List<string>();
+ for (int i = 0; i < resolved.Count; i++) {
+ if (!resolved[i].IsGhost) {
+ var lhs = s.Lhss[i];
+ var rhs = s.Rhss[i];
+ if (!(rhs is HavocRhs)) {
+ lvalues.Add(CreateLvalue(lhs, indent));
+
+ string target = "_rhs" + tmpVarCount;
+ tmpVarCount++;
+ rhss.Add(target);
+ TrRhs("var " + target, null, rhs, indent);
+ }
+ }
+ }
+ Contract.Assert(lvalues.Count == rhss.Count);
+ for (int i = 0; i < lvalues.Count; i++) {
+ Indent(indent);
+ wr.WriteLine("{0} = {1};", lvalues[i], rhss[i]);
+ }
+ }
+
+ } 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);
+
+ } else if (stmt is AssignSuchThatStmt) {
+ var s = (AssignSuchThatStmt)stmt;
+ foreach (var lhs in s.Lhss) {
+ // assigning to a local ghost variable or to a ghost field is okay
+ if (!AssignStmt.LhsIsToGhost(lhs)) {
+ Error("compiling an assign-such-that statement with a non-ghost left-hand side is currently not supported (line {0})", stmt.Tok.line);
+ break; // no need to say more
+ }
+ }
+
+ } else if (stmt is VarDecl) {
+ TrVarDecl((VarDecl)stmt, true, indent);
+
+ } else if (stmt is CallStmt) {
+ CallStmt s = (CallStmt)stmt;
+ TrCallStmt(s, null, indent);
+
+ } else if (stmt is BlockStmt) {
+ Indent(indent); wr.WriteLine("{");
+ TrStmtList(((BlockStmt)stmt).Body, indent);
+ Indent(indent); wr.WriteLine("}");
+
+ } else if (stmt is IfStmt) {
+ IfStmt s = (IfStmt)stmt;
+ if (s.Guard == null) {
+ // we can compile the branch of our choice
+ 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);
+ wr.WriteLine("if (!false) { }");
+ } else {
+ // let's compile the "then" branch
+ Indent(indent);
+ wr.WriteLine("if (true)");
+ TrStmt(s.Thn, indent);
+ }
+ } else {
+ SpillLetVariableDecls(s.Guard, indent);
+ Indent(indent); wr.Write("if (");
+ TrExpr(s.Guard);
+ wr.WriteLine(")");
+
+ TrStmt(s.Thn, indent);
+ if (s.Els != null) {
+ Indent(indent); wr.WriteLine("else");
+ TrStmt(s.Els, indent);
+ }
+ }
+
+ } else if (stmt is AlternativeStmt) {
+ var s = (AlternativeStmt)stmt;
+ foreach (var alternative in s.Alternatives) {
+ SpillLetVariableDecls(alternative.Guard, indent);
+ }
+ Indent(indent);
+ foreach (var alternative in s.Alternatives) {
+ wr.Write("if (");
+ TrExpr(alternative.Guard);
+ wr.WriteLine(") {");
+ TrStmtList(alternative.Body, indent);
+ Indent(indent);
+ wr.Write("} else ");
+ }
+ wr.WriteLine("{ /*unreachable alternative*/ }");
+
+ } else if (stmt is WhileStmt) {
+ WhileStmt s = (WhileStmt)stmt;
+ if (s.Guard == null) {
+ Indent(indent);
+ wr.WriteLine("while (false) { }");
+ } else {
+ SpillLetVariableDecls(s.Guard, indent);
+ Indent(indent);
+ wr.Write("while (");
+ TrExpr(s.Guard);
+ wr.WriteLine(")");
+ TrStmt(s.Body, indent);
+ }
+
+ } else if (stmt is AlternativeLoopStmt) {
+ var s = (AlternativeLoopStmt)stmt;
+ if (s.Alternatives.Count != 0) {
+ Indent(indent);
+ wr.WriteLine("while (true) {");
+ int ind = indent + IndentAmount;
+ foreach (var alternative in s.Alternatives) {
+ SpillLetVariableDecls(alternative.Guard, ind);
+ }
+ Indent(ind);
+ foreach (var alternative in s.Alternatives) {
+ wr.Write("if (");
+ TrExpr(alternative.Guard);
+ wr.WriteLine(") {");
+ TrStmtList(alternative.Body, ind);
+ Indent(ind);
+ wr.Write("} else ");
+ }
+ wr.WriteLine("{ break; }");
+ Indent(indent);
+ wr.WriteLine("}");
+ }
+
+ } else if (stmt is ParallelStmt) {
+ var s = (ParallelStmt)stmt;
+ if (s.Kind != ParallelStmt.ParBodyKind.Assign) {
+ // Call and Proof have no side effects, so they can simply be optimized away.
+ return;
+ } else if (s.BoundVars.Count == 0) {
+ // the bound variables just spell out a single point, so the parallel statement is equivalent to one execution of the body
+ TrStmt(s.Body, indent);
+ return;
+ }
+ var s0 = (AssignStmt)s.S0;
+ if (s0.Rhs is HavocRhs) {
+ // The parallel statement says to havoc a bunch of things. This can be efficiently compiled
+ // into doing nothing.
+ return;
+ }
+ var rhs = ((ExprRhs)s0.Rhs).Expr;
+
+ // Compile:
+ // parallel (w,x,y,z | Range(w,x,y,z)) {
+ // LHS(w,x,y,z) := RHS(w,x,y,z);
+ // }
+ // where w,x,y,z have types seq<W>,set<X>,int,bool and LHS has L-1 top-level subexpressions
+ // (that is, L denotes the number of top-level subexpressions of LHS plus 1),
+ // into:
+ // var ingredients = new List< L-Tuple >();
+ // foreach (W w in sq.UniqueElements) {
+ // foreach (X x in st.Elements) {
+ // for (BigInteger y = Lo; j < Hi; j++) {
+ // for (bool z in Helper.AllBooleans) {
+ // if (Range(w,x,y,z)) {
+ // ingredients.Add(new L-Tuple( LHS0(w,x,y,z), LHS1(w,x,y,z), ..., RHS(w,x,y,z) ));
+ // }
+ // }
+ // }
+ // }
+ // }
+ // foreach (L-Tuple l in ingredients) {
+ // LHS[ l0, l1, l2, ..., l(L-2) ] = l(L-1);
+ // }
+ //
+ // Note, because the .NET Tuple class only supports up to 8 components, the compiler implementation
+ // here supports arrays only up to 6 dimensions. This does not seem like a serious practical limitation.
+ // However, it may be more noticeable if the parallel statement supported parallel assignments in its
+ // body. To support cases where tuples would need more than 8 components, .NET Tuple's would have to
+ // be nested.
+
+ // Temporary names
+ string ingredients = "_ingredients" + tmpVarCount;
+ string tup = "_tup" + tmpVarCount;
+ tmpVarCount++;
+
+ // Compute L
+ int L;
+ string tupleTypeArgs;
+ if (s0.Lhs is FieldSelectExpr) {
+ var lhs = (FieldSelectExpr)s0.Lhs;
+ L = 2;
+ tupleTypeArgs = TypeName(lhs.Obj.Type);
+ } 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";
+ } 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 parallel statements");
+ return;
+ }
+ tupleTypeArgs = TypeName(lhs.Array.Type);
+ 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);
+
+ // declare and construct "ingredients"
+ Indent(indent);
+ wr.WriteLine("var {0} = new System.Collections.Generic.List<System.Tuple<{1}>>();", ingredients, tupleTypeArgs);
+
+ var n = s.BoundVars.Count;
+ Contract.Assert(s.Bounds.Count == n);
+ for (int i = 0; i < n; i++) {
+ var ind = indent + i * IndentAmount;
+ var bound = s.Bounds[i];
+ var bv = s.BoundVars[i];
+ if (bound is ComprehensionExpr.BoolBoundedPool) {
+ Indent(ind);
+ wr.Write("foreach (var @{0} in Dafny.Helpers.AllBooleans) {{ ", bv.CompileName);
+ } else if (bound is ComprehensionExpr.IntBoundedPool) {
+ var b = (ComprehensionExpr.IntBoundedPool)bound;
+ SpillLetVariableDecls(b.LowerBound, ind);
+ SpillLetVariableDecls(b.UpperBound, ind);
+ 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);
+ } else if (bound is ComprehensionExpr.SetBoundedPool) {
+ var b = (ComprehensionExpr.SetBoundedPool)bound;
+ SpillLetVariableDecls(b.Set, ind);
+ Indent(ind);
+ wr.Write("foreach (var @{0} in (", bv.CompileName);
+ TrExpr(b.Set);
+ wr.Write(").Elements) { ");
+ } else if (bound is ComprehensionExpr.SeqBoundedPool) {
+ var b = (ComprehensionExpr.SeqBoundedPool)bound;
+ SpillLetVariableDecls(b.Seq, ind);
+ Indent(ind);
+ wr.Write("foreach (var @{0} in (", bv.CompileName);
+ TrExpr(b.Seq);
+ 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));
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type
+ }
+ wr.WriteLine();
+ }
+
+ // if (range) {
+ // ingredients.Add(new L-Tuple( LHS0(w,x,y,z), LHS1(w,x,y,z), ..., RHS(w,x,y,z) ));
+ // }
+ SpillLetVariableDecls(s.Range, indent + n * IndentAmount);
+ Indent(indent + n * IndentAmount);
+ wr.Write("if ");
+ TrParenExpr(s.Range);
+ wr.WriteLine(" {");
+
+ var indFinal = indent + (n + 1) * IndentAmount;
+ SpillLetVariableDecls(s0.Lhs, indFinal);
+ SpillLetVariableDecls(rhs, indFinal);
+ Indent(indFinal);
+ wr.Write("{0}.Add(new System.Tuple<{1}>(", ingredients, tupleTypeArgs);
+ if (s0.Lhs is FieldSelectExpr) {
+ var lhs = (FieldSelectExpr)s0.Lhs;
+ TrExpr(lhs.Obj);
+ } else if (s0.Lhs is SeqSelectExpr) {
+ var lhs = (SeqSelectExpr)s0.Lhs;
+ TrExpr(lhs.Seq);
+ wr.Write(", (int)(");
+ TrExpr(lhs.E0);
+ wr.Write(")");
+ } else {
+ var lhs = (MultiSelectExpr)s0.Lhs;
+ TrExpr(lhs.Array);
+ for (int i = 0; i < lhs.Indices.Count; i++) {
+ wr.Write(", (int)(");
+ TrExpr(lhs.Indices[i]);
+ wr.Write(")");
+ }
+ wr.WriteLine("] = {0}.Item{1};", tup, L);
+ }
+ wr.Write(", ");
+ TrExpr(rhs);
+ wr.WriteLine("));");
+
+ Indent(indent + n * IndentAmount);
+ wr.WriteLine("}");
+
+ for (int i = n; 0 <= --i; ) {
+ Indent(indent + i * IndentAmount);
+ wr.WriteLine("}");
+ }
+
+ // foreach (L-Tuple l in ingredients) {
+ // LHS[ l0, l1, l2, ..., l(L-2) ] = l(L-1);
+ // }
+ Indent(indent);
+ wr.WriteLine("foreach (var {0} in {1}) {{", tup, ingredients);
+ Indent(indent + IndentAmount);
+ if (s0.Lhs is FieldSelectExpr) {
+ var lhs = (FieldSelectExpr)s0.Lhs;
+ wr.WriteLine("{0}.Item1.@{1} = {0}.Item2;", tup, lhs.FieldName);
+ } else if (s0.Lhs is SeqSelectExpr) {
+ var lhs = (SeqSelectExpr)s0.Lhs;
+ wr.WriteLine("{0}.Item1[{0}.Item2] = {0}.Item3;", tup);
+ } else {
+ var lhs = (MultiSelectExpr)s0.Lhs;
+ wr.Write("{0}.Item1[");
+ string sep = "";
+ for (int i = 0; i < lhs.Indices.Count; i++) {
+ wr.Write("{0}{1}.Item{2}", sep, tup, i + 2);
+ sep = ", ";
+ }
+ wr.WriteLine("] = {0}.Item{1};", tup, L);
+ }
+ Indent(indent);
+ wr.WriteLine("}");
+
+ } else if (stmt is MatchStmt) {
+ MatchStmt s = (MatchStmt)stmt;
+ // Type source = e;
+ // if (source.is_Ctor0) {
+ // FormalType f0 = ((Dt_Ctor0)source._D).a0;
+ // ...
+ // Body0;
+ // } else if (...) {
+ // ...
+ // } else if (true) {
+ // ...
+ // }
+ if (s.Cases.Count != 0) {
+ var sourceType = (UserDefinedType)s.Source.Type;
+
+ SpillLetVariableDecls(s.Source, indent);
+ string source = "_source" + tmpVarCount;
+ tmpVarCount++;
+ Indent(indent);
+ wr.Write("{0} {1} = ", TypeName(cce.NonNull(s.Source.Type)), source);
+ TrExpr(s.Source);
+ wr.WriteLine(";");
+
+ int i = 0;
+ foreach (MatchCaseStmt mc in s.Cases) {
+ MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, s.Cases.Count, indent);
+ TrStmtList(mc.Body, indent);
+ i++;
+ }
+ Indent(indent); wr.WriteLine("}");
+ }
+
+ } else if (stmt is ConcreteSyntaxStatement) {
+ var s = (ConcreteSyntaxStatement)stmt;
+ foreach (var ss in s.ResolvedStatements) {
+ TrStmt(ss, indent);
+ }
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement
+ }
+ }
+
+ string CreateLvalue(Expression lhs, int indent) {
+ lhs = lhs.Resolved;
+ SpillLetVariableDecls(lhs, indent);
+ if (lhs is IdentifierExpr) {
+ var ll = (IdentifierExpr)lhs;
+ return "@" + ll.Var.CompileName;
+ } else if (lhs is FieldSelectExpr) {
+ var ll = (FieldSelectExpr)lhs;
+ string obj = "_obj" + tmpVarCount;
+ tmpVarCount++;
+ Indent(indent);
+ wr.Write("var {0} = ", obj);
+ TrExpr(ll.Obj);
+ wr.WriteLine(";");
+ return string.Format("{0}.@{1}", obj, ll.Field.CompileName);
+ } else if (lhs is SeqSelectExpr) {
+ var ll = (SeqSelectExpr)lhs;
+ string arr = "_arr" + tmpVarCount;
+ string index = "_index" + tmpVarCount;
+ tmpVarCount++;
+ Indent(indent);
+ wr.Write("var {0} = ", arr);
+ TrExpr(ll.Seq);
+ wr.WriteLine(";");
+ Indent(indent);
+ wr.Write("var {0} = ", index);
+ TrExpr(ll.E0);
+ wr.WriteLine(";");
+ return string.Format("{0}[(int){1}]", arr, index);
+ } else {
+ var ll = (MultiSelectExpr)lhs;
+ string arr = "_arr" + tmpVarCount;
+ Indent(indent);
+ wr.Write("var {0} = ", arr);
+ TrExpr(ll.Array);
+ wr.WriteLine(";");
+ string fullString = arr + "[";
+ string sep = "";
+ int i = 0;
+ foreach (var idx in ll.Indices) {
+ string index = "_index" + i + "_" + tmpVarCount;
+ Indent(indent);
+ wr.Write("var {0} = ", index);
+ TrExpr(idx);
+ wr.WriteLine(";");
+ fullString += sep + "(int)" + index;
+ sep = ", ";
+ i++;
+ }
+ tmpVarCount++;
+ return fullString + "]";
+ }
+ }
+
+ void TrRhs(string target, Expression targetExpr, AssignmentRhs rhs, int indent) {
+ Contract.Requires((target == null) != (targetExpr == null));
+ SpillLetVariableDecls(targetExpr, indent);
+ var tRhs = rhs as TypeRhs;
+ if (tRhs != null && tRhs.InitCall != null) {
+ string nw = "_nw" + tmpVarCount;
+ tmpVarCount++;
+ Indent(indent);
+ wr.Write("var {0} = ", nw);
+ TrAssignmentRhs(rhs); // in this case, this call will not require us to spill any let variables first
+ wr.WriteLine(";");
+ TrCallStmt(tRhs.InitCall, nw, indent);
+ Indent(indent);
+ if (target != null) {
+ wr.Write(target);
+ } else {
+ TrExpr(targetExpr);
+ }
+ wr.WriteLine(" = {0};", nw);
+ } else if (rhs is HavocRhs) {
+ // do nothing
+ } else {
+ if (rhs is ExprRhs) {
+ SpillLetVariableDecls(((ExprRhs)rhs).Expr, indent);
+ } else if (tRhs != null && tRhs.ArrayDimensions != null) {
+ foreach (Expression dim in tRhs.ArrayDimensions) {
+ SpillLetVariableDecls(dim, indent);
+ }
+ }
+ Indent(indent);
+ if (target != null) {
+ wr.Write(target);
+ } else {
+ TrExpr(targetExpr);
+ }
+ wr.Write(" = ");
+ TrAssignmentRhs(rhs);
+ wr.WriteLine(";");
+ }
+ }
+
+ void 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
+
+ if (s.Method == enclosingMethod && enclosingMethod.IsTailRecursive) {
+ // compile call as tail-recursive
+
+ // assign the actual in-parameters to temporary variables
+ var inTmps = new List<string>();
+ for (int i = 0; i < s.Method.Ins.Count; i++) {
+ Formal p = s.Method.Ins[i];
+ if (!p.IsGhost) {
+ SpillLetVariableDecls(s.Args[i], indent);
+ }
+ }
+ if (receiverReplacement != null) {
+ // TODO: What to do here? When does this happen, what does it mean?
+ } else if (!s.Method.IsStatic) {
+ SpillLetVariableDecls(s.Receiver, indent);
+
+ string inTmp = "_in" + tmpVarCount;
+ tmpVarCount++;
+ inTmps.Add(inTmp);
+ Indent(indent);
+ wr.Write("var {0} = ", inTmp);
+ TrExpr(s.Receiver);
+ wr.WriteLine(";");
+ }
+ for (int i = 0; i < s.Method.Ins.Count; i++) {
+ Formal p = s.Method.Ins[i];
+ if (!p.IsGhost) {
+ string inTmp = "_in" + tmpVarCount;
+ tmpVarCount++;
+ inTmps.Add(inTmp);
+ Indent(indent);
+ wr.Write("var {0} = ", inTmp);
+ TrExpr(s.Args[i]);
+ wr.WriteLine(";");
+ }
+ }
+ // Now, assign to the formals
+ int n = 0;
+ if (!s.Method.IsStatic) {
+ Indent(indent);
+ wr.WriteLine("_this = {0};", inTmps[n]);
+ n++;
+ }
+ foreach (var p in s.Method.Ins) {
+ if (!p.IsGhost) {
+ Indent(indent);
+ 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);
+ wr.WriteLine("goto TAIL_CALL_START;");
+
+ } else {
+ // compile call as a regular call
+
+ var lvalues = new List<string>();
+ Contract.Assert(s.Lhs.Count == s.Method.Outs.Count);
+ 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));
+ }
+ }
+ var outTmps = new List<string>();
+ for (int i = 0; i < s.Method.Outs.Count; i++) {
+ Formal p = s.Method.Outs[i];
+ if (!p.IsGhost) {
+ string target = "_out" + tmpVarCount;
+ tmpVarCount++;
+ outTmps.Add(target);
+ Indent(indent);
+ wr.WriteLine("{0} {1};", TypeName(s.Lhs[i].Type), target);
+ }
+ }
+ Contract.Assert(lvalues.Count == outTmps.Count);
+
+ for (int i = 0; i < s.Method.Ins.Count; i++) {
+ Formal p = s.Method.Ins[i];
+ if (!p.IsGhost) {
+ SpillLetVariableDecls(s.Args[i], indent);
+ }
+ }
+ if (receiverReplacement != null) {
+ Indent(indent);
+ wr.Write("@" + receiverReplacement);
+ } else if (s.Method.IsStatic) {
+ Indent(indent);
+ wr.Write(TypeName(cce.NonNull(s.Receiver.Type)));
+ } else {
+ SpillLetVariableDecls(s.Receiver, indent);
+ Indent(indent);
+ TrParenExpr(s.Receiver);
+ }
+ wr.Write(".@{0}(", s.Method.CompileName);
+
+ string sep = "";
+ for (int i = 0; i < s.Method.Ins.Count; i++) {
+ Formal p = s.Method.Ins[i];
+ if (!p.IsGhost) {
+ wr.Write(sep);
+ TrExpr(s.Args[i]);
+ sep = ", ";
+ }
+ }
+
+ foreach (var outTmp in outTmps) {
+ wr.Write("{0}out {1}", sep, outTmp);
+ sep = ", ";
+ }
+ wr.WriteLine(");");
+
+ // assign to the actual LHSs
+ for (int j = 0; j < lvalues.Count; j++) {
+ Indent(indent);
+ wr.WriteLine("{0} = {1};", lvalues[j], outTmps[j]);
+ }
+ }
+ }
+
+ int tmpVarCount = 0;
+
+ /// <summary>
+ /// Before calling TrAssignmentRhs(rhs), the caller must have spilled the let variables declared in "rhs".
+ /// </summary>
+ void TrAssignmentRhs(AssignmentRhs rhs) {
+ Contract.Requires(rhs != null);
+ Contract.Requires(!(rhs is HavocRhs));
+ if (rhs is ExprRhs) {
+ ExprRhs e = (ExprRhs)rhs;
+ TrExpr(e.Expr);
+
+ } else {
+ TypeRhs tp = (TypeRhs)rhs;
+ if (tp.ArrayDimensions == null) {
+ wr.Write("new {0}()", TypeName(tp.EType));
+ } else {
+ if (tp.EType is IntType || 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));
+ string prefix = "(";
+ foreach (Expression dim in tp.ArrayDimensions) {
+ wr.Write(prefix);
+ TrParenExpr(dim);
+ prefix = ", ";
+ }
+ wr.Write(")");
+ } else {
+ wr.Write("new {0}", TypeName(tp.EType));
+ string prefix = "[";
+ foreach (Expression dim in tp.ArrayDimensions) {
+ wr.Write("{0}(int)", prefix);
+ TrParenExpr(dim);
+ prefix = ", ";
+ }
+ wr.Write("]");
+ }
+ }
+ }
+ }
+
+ void TrStmtList(List<Statement/*!*/>/*!*/ stmts, int indent) {Contract.Requires(cce.NonNullElements(stmts));
+ foreach (Statement ss in stmts) {
+ TrStmt(ss, indent + IndentAmount);
+ if (ss.Labels != null) {
+ Indent(indent); // labels are not indented as much as the statements
+ wr.WriteLine("after_{0}: ;", ss.Labels.Data.UniqueId);
+ }
+ }
+ }
+
+ void TrVarDecl(VarDecl s, bool alwaysInitialize, int indent) {
+ Contract.Requires(s != null);
+ if (s.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);
+ if (alwaysInitialize) {
+ // produce a default value
+ wr.WriteLine(" = {0};", DefaultValue(s.Type));
+ } else {
+ wr.WriteLine(";");
+ }
+ }
+
+ void MatchCasePrelude(string source, UserDefinedType sourceType, DatatypeCtor ctor, List<BoundVar/*!*/>/*!*/ arguments, int caseIndex, int caseCount, int indent) {
+ Contract.Requires(source != null);
+ Contract.Requires(sourceType != null);
+ Contract.Requires(ctor != null);
+ Contract.Requires(cce.NonNullElements(arguments));
+ // if (source.is_Ctor0) {
+ // FormalType f0 = ((Dt_Ctor0)source._D).a0;
+ // ...
+ Indent(indent);
+ wr.Write("{0}if (", caseIndex == 0 ? "" : "} else ");
+ if (caseIndex == caseCount - 1) {
+ wr.Write("true");
+ } else {
+ wr.Write("{0}.is_{1}", source, ctor.CompileName);
+ }
+ wr.WriteLine(") {");
+
+ int k = 0; // number of processed non-ghost arguments
+ for (int m = 0; m < ctor.Formals.Count; m++) {
+ Formal arg = ctor.Formals[m];
+ if (!arg.IsGhost) {
+ BoundVar bv = arguments[m];
+ // FormalType f0 = ((Dt_Ctor0)source._D).a0;
+ Indent(indent + IndentAmount);
+ wr.WriteLine("{0} @{1} = (({2}){3}._D).@{4};",
+ TypeName(bv.Type), bv.CompileName, DtCtorName(ctor, sourceType.TypeArgs), source, FormalName(arg, k));
+ k++;
+ }
+ }
+ }
+
+ // ----- Expression ---------------------------------------------------------------------------
+
+ /// <summary>
+ /// Before calling TrParenExpr(expr), the caller must have spilled the let variables declared in "expr".
+ /// </summary>
+ void TrParenExpr(string prefix, Expression expr) {
+ Contract.Requires(prefix != null);
+ Contract.Requires(expr != null);
+ wr.Write(prefix);
+ TrParenExpr(expr);
+ }
+
+ /// <summary>
+ /// Before calling TrParenExpr(expr), the caller must have spilled the let variables declared in "expr".
+ /// </summary>
+ void TrParenExpr(Expression expr) {
+ Contract.Requires(expr != null);
+ wr.Write("(");
+ TrExpr(expr);
+ 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) {
+ Contract.Requires(cce.NonNullElements(exprs));
+ wr.Write("(");
+ string sep = "";
+ foreach (Expression e in exprs) {
+ wr.Write(sep);
+ TrExpr(e);
+ sep = ", ";
+ }
+ wr.Write(")");
+ }
+ void TrExprPairList(List<ExpressionPair/*!*/>/*!*/ exprs) {
+ 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(",");
+ wr.Write(TypeName(p.B.Type));
+ wr.Write(">(");
+ TrExpr(p.A);
+ wr.Write(",");
+ TrExpr(p.B);
+ wr.Write(")");
+ sep = ", ";
+ }
+ wr.Write(")");
+ }
+
+ /// <summary>
+ /// Before calling TrExpr(expr), the caller must have spilled the let variables declared in "expr".
+ /// </summary>
+ void TrExpr(Expression expr)
+ {
+ Contract.Requires(expr != null);
+ if (expr is LiteralExpr) {
+ LiteralExpr e = (LiteralExpr)expr;
+ if (e.Value == null) {
+ wr.Write("({0})null", TypeName(e.Type));
+ } else if (e.Value is bool) {
+ wr.Write((bool)e.Value ? "true" : "false");
+ } else if (e.Value is BigInteger) {
+ BigInteger i = (BigInteger)e.Value;
+ if (new BigInteger(int.MinValue) <= i && i <= new BigInteger(int.MaxValue)) {
+ wr.Write("new BigInteger({0})", i);
+ } else {
+ wr.Write("BigInteger.Parse(\"{0}\")", i);
+ }
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected literal
+ }
+
+ } else if (expr is ThisExpr) {
+ wr.Write(enclosingMethod != null && enclosingMethod.IsTailRecursive ? "_this" : "this");
+
+ } else if (expr is IdentifierExpr) {
+ IdentifierExpr e = (IdentifierExpr)expr;
+ wr.Write("@" + e.Var.CompileName);
+
+ } else if (expr is SetDisplayExpr) {
+ SetDisplayExpr e = (SetDisplayExpr)expr;
+ Type elType = cce.NonNull((SetType)e.Type).Arg;
+ wr.Write("{0}<{1}>.FromElements", DafnySetClass, TypeName(elType));
+ TrExprList(e.Elements);
+
+ } else if (expr is MultiSetDisplayExpr) {
+ MultiSetDisplayExpr e = (MultiSetDisplayExpr)expr;
+ Type elType = cce.NonNull((MultiSetType)e.Type).Arg;
+ wr.Write("{0}<{1}>.FromElements", DafnyMultiSetClass, TypeName(elType));
+ TrExprList(e.Elements);
+
+ } else if (expr is SeqDisplayExpr) {
+ SeqDisplayExpr e = (SeqDisplayExpr)expr;
+ Type elType = cce.NonNull((SeqType)e.Type).Arg;
+ wr.Write("{0}<{1}>.FromElements", DafnySeqClass, TypeName(elType));
+ TrExprList(e.Elements);
+
+ } else if (expr is MapDisplayExpr) {
+ MapDisplayExpr e = (MapDisplayExpr)expr;
+ wr.Write("{0}.FromElements", TypeName(e.Type));
+ TrExprPairList(e.Elements);
+
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)expr;
+ SpecialField sf = e.Field as SpecialField;
+ if (sf != null) {
+ wr.Write(sf.PreString);
+ TrParenExpr(e.Obj);
+ wr.Write(".{0}", sf.CompiledName);
+ wr.Write(sf.PostString);
+ } else {
+ TrParenExpr(e.Obj);
+ wr.Write(".@{0}", e.Field.CompileName);
+ }
+
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ Contract.Assert(e.Seq.Type != null);
+ if (e.Seq.Type.IsArrayType) {
+ if (e.SelectOne) {
+ Contract.Assert(e.E0 != null && e.E1 == null);
+ TrParenExpr(e.Seq);
+ wr.Write("[(int)");
+ TrParenExpr(e.E0);
+ wr.Write("]");
+ } else {
+ TrParenExpr("Dafny.Helpers.SeqFromArray", e.Seq);
+ if (e.E1 != null) {
+ TrParenExpr(".Take", e.E1);
+ }
+ if (e.E0 != null) {
+ TrParenExpr(".Drop", e.E0);
+ }
+ }
+ } else if (e.SelectOne) {
+ Contract.Assert(e.E0 != null && e.E1 == null);
+ TrParenExpr(e.Seq);
+ TrParenExpr(".Select", e.E0);
+ } else {
+ TrParenExpr(e.Seq);
+ if (e.E1 != null) {
+ TrParenExpr(".Take", e.E1);
+ }
+ if (e.E0 != null) {
+ TrParenExpr(".Drop", e.E0);
+ }
+ }
+ } else if (expr is MultiSetFormingExpr) {
+ MultiSetFormingExpr e = (MultiSetFormingExpr)expr;
+ wr.Write("{0}<{1}>", DafnyMultiSetClass, TypeName(((CollectionType)e.E.Type).Arg));
+ if (e.E.Type is SeqType) {
+ TrParenExpr(".FromSeq", e.E);
+ } else if (e.E.Type is SetType) {
+ TrParenExpr(".FromSet", e.E);
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException();
+ }
+ } else if (expr is MultiSelectExpr) {
+ MultiSelectExpr e = (MultiSelectExpr)expr;
+ TrParenExpr(e.Array);
+ string prefix = "[";
+ foreach (Expression idx in e.Indices) {
+ wr.Write("{0}(int)", prefix);
+ TrParenExpr(idx);
+ prefix = ", ";
+ }
+ wr.Write("]");
+
+ } else if (expr is SeqUpdateExpr) {
+ SeqUpdateExpr e = (SeqUpdateExpr)expr;
+ TrParenExpr(e.Seq);
+ wr.Write(".Update(");
+ TrExpr(e.Index);
+ wr.Write(", ");
+ TrExpr(e.Value);
+ wr.Write(")");
+
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ CompileFunctionCallExpr(e, wr, TrExpr);
+
+ } 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);
+ 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));
+ 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]);
+ sep = ", ";
+ }
+ }
+ wr.Write(")");
+ } else {
+ // In the case of a co-recursive call, generate:
+ // new Dt__Lazy<T>( new Dt__Lazy<T>.ComputerComputer( LAMBDA )() )
+ // where LAMBDA is:
+ // () => { var someLocals = eagerlyEvaluatedArguments;
+ // return () => { return Dt_Cons<T>( ...args...using someLocals and including function calls to be evaluated lazily... ); };
+ // }
+ wr.Write("new {0}__Lazy{1}", dtv.DatatypeName, typeParams);
+ wr.Write("(new {0}__Lazy{1}.ComputerComputer(() => {{ ", dtv.DatatypeName, typeParams);
+
+ // locals
+ string args = "";
+ string sep = "";
+ for (int i = 0; i < dtv.Arguments.Count; i++) {
+ Formal formal = dtv.Ctor.Formals[i];
+ if (!formal.IsGhost) {
+ Expression actual = dtv.Arguments[i].Resolved;
+ string arg;
+ var fce = actual as FunctionCallExpr;
+ if (fce == null || fce.CoCall != FunctionCallExpr.CoCallResolution.Yes) {
+ string varName = "_ac" + tmpVarCount;
+ tmpVarCount++;
+ arg = varName;
+
+ wr.Write("var {0} = ", varName);
+ TrExpr(actual);
+ wr.Write("; ");
+ } else {
+ var sw = new StringWriter();
+ CompileFunctionCallExpr(fce, sw, (exp) => {
+ string varName = "_ac" + tmpVarCount;
+ tmpVarCount++;
+ sw.Write(varName);
+
+ wr.Write("var {0} = ", varName);
+ TrExpr(exp);
+ wr.Write("; ");
+
+ });
+ arg = sw.ToString();
+ }
+ args += sep + arg;
+ sep = ", ";
+ }
+ }
+
+ wr.Write("return () => { return ");
+
+ wr.Write("new {0}({1}", DtCtorName(dtv.Ctor, dtv.InferredTypeArgs), args);
+ wr.Write("); }; })())");
+ }
+ wr.Write(")");
+
+ } else if (expr is OldExpr) {
+ Contract.Assert(false); throw new cce.UnreachableException(); // 'old' is always a ghost (right?)
+
+ } else if (expr is FreshExpr) {
+ Contract.Assert(false); throw new cce.UnreachableException(); // 'fresh' is always a ghost
+
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ switch (e.Op) {
+ case UnaryExpr.Opcode.Not:
+ wr.Write("!");
+ TrParenExpr(e.E);
+ break;
+ case UnaryExpr.Opcode.SetChoose:
+ TrParenExpr(e.E);
+ wr.Write(".Choose()");
+ break;
+ case UnaryExpr.Opcode.SeqLength:
+ if (cce.NonNull(e.E.Type).IsArrayType) {
+ wr.Write("new BigInteger(");
+ TrParenExpr(e.E);
+ wr.Write(".Length)");
+ } else {
+ TrParenExpr(e.E);
+ wr.Write(".Length");
+ }
+ break;
+ default:
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected unary expression
+ }
+
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ string opString = null;
+ string preOpString = "";
+ string callString = null;
+
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.Iff:
+ opString = "=="; break;
+ case BinaryExpr.ResolvedOpcode.Imp:
+ preOpString = "!"; opString = "||"; break;
+ case BinaryExpr.ResolvedOpcode.Or:
+ opString = "||"; break;
+ case BinaryExpr.ResolvedOpcode.And:
+ opString = "&&"; break;
+
+ case BinaryExpr.ResolvedOpcode.EqCommon: {
+ Type t = cce.NonNull(e.E0.Type);
+ if (t.IsDatatype || t.IsTypeParameter) {
+ callString = "Equals";
+ } else if (t.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 {
+ opString = "==";
+ }
+ break;
+ }
+ case BinaryExpr.ResolvedOpcode.NeqCommon: {
+ Type t = cce.NonNull(e.E0.Type);
+ if (t.IsDatatype || t.IsTypeParameter) {
+ preOpString = "!";
+ callString = "Equals";
+ } else if (t.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 {
+ opString = "!=";
+ }
+ break;
+ }
+
+ case BinaryExpr.ResolvedOpcode.Lt:
+ opString = "<"; break;
+ case BinaryExpr.ResolvedOpcode.Le:
+ opString = "<="; break;
+ case BinaryExpr.ResolvedOpcode.Ge:
+ opString = ">="; break;
+ case BinaryExpr.ResolvedOpcode.Gt:
+ opString = ">"; break;
+ case BinaryExpr.ResolvedOpcode.Add:
+ opString = "+"; break;
+ case BinaryExpr.ResolvedOpcode.Sub:
+ opString = "-"; break;
+ case BinaryExpr.ResolvedOpcode.Mul:
+ opString = "*"; break;
+ case BinaryExpr.ResolvedOpcode.Div:
+ wr.Write("Dafny.Helpers.EuclideanDivision(");
+ TrParenExpr(e.E0);
+ wr.Write(", ");
+ TrExpr(e.E1);
+ wr.Write(")");
+ break;
+ case BinaryExpr.ResolvedOpcode.Mod:
+ wr.Write("Dafny.Helpers.EuclideanModulus(");
+ TrParenExpr(e.E0);
+ wr.Write(", ");
+ TrExpr(e.E1);
+ wr.Write(")");
+ break;
+ case BinaryExpr.ResolvedOpcode.SetEq:
+ case BinaryExpr.ResolvedOpcode.MultiSetEq:
+ case BinaryExpr.ResolvedOpcode.SeqEq:
+ case BinaryExpr.ResolvedOpcode.MapEq:
+ callString = "Equals"; break;
+ case BinaryExpr.ResolvedOpcode.SetNeq:
+ case BinaryExpr.ResolvedOpcode.MultiSetNeq:
+ case BinaryExpr.ResolvedOpcode.SeqNeq:
+ case BinaryExpr.ResolvedOpcode.MapNeq:
+ preOpString = "!"; callString = "Equals"; break;
+ case BinaryExpr.ResolvedOpcode.ProperSubset:
+ case BinaryExpr.ResolvedOpcode.ProperMultiSubset:
+ callString = "IsProperSubsetOf"; break;
+ case BinaryExpr.ResolvedOpcode.Subset:
+ case BinaryExpr.ResolvedOpcode.MultiSubset:
+ callString = "IsSubsetOf"; break;
+ case BinaryExpr.ResolvedOpcode.Superset:
+ case BinaryExpr.ResolvedOpcode.MultiSuperset:
+ callString = "IsSupersetOf"; break;
+ case BinaryExpr.ResolvedOpcode.ProperSuperset:
+ case BinaryExpr.ResolvedOpcode.ProperMultiSuperset:
+ callString = "IsProperSupersetOf"; break;
+ case BinaryExpr.ResolvedOpcode.Disjoint:
+ case BinaryExpr.ResolvedOpcode.MultiSetDisjoint:
+ case BinaryExpr.ResolvedOpcode.MapDisjoint:
+ callString = "IsDisjointFrom"; break;
+ case BinaryExpr.ResolvedOpcode.InSet:
+ case BinaryExpr.ResolvedOpcode.InMultiSet:
+ case BinaryExpr.ResolvedOpcode.InMap:
+ TrParenExpr(e.E1);
+ wr.Write(".Contains(");
+ TrExpr(e.E0);
+ wr.Write(")");
+ break;
+ case BinaryExpr.ResolvedOpcode.NotInSet:
+ case BinaryExpr.ResolvedOpcode.NotInMultiSet:
+ case BinaryExpr.ResolvedOpcode.NotInMap:
+ wr.Write("!");
+ TrParenExpr(e.E1);
+ wr.Write(".Contains(");
+ TrExpr(e.E0);
+ wr.Write(")");
+ break;
+ case BinaryExpr.ResolvedOpcode.Union:
+ case BinaryExpr.ResolvedOpcode.MultiSetUnion:
+ callString = "Union"; break;
+ case BinaryExpr.ResolvedOpcode.Intersection:
+ case BinaryExpr.ResolvedOpcode.MultiSetIntersection:
+ callString = "Intersect"; break;
+ case BinaryExpr.ResolvedOpcode.SetDifference:
+ case BinaryExpr.ResolvedOpcode.MultiSetDifference:
+ callString = "Difference"; break;
+
+ case BinaryExpr.ResolvedOpcode.ProperPrefix:
+ callString = "IsProperPrefixOf"; break;
+ case BinaryExpr.ResolvedOpcode.Prefix:
+ callString = "IsPrefixOf"; break;
+ case BinaryExpr.ResolvedOpcode.Concat:
+ callString = "Concat"; break;
+ case BinaryExpr.ResolvedOpcode.InSeq:
+ TrParenExpr(e.E1);
+ wr.Write(".Contains(");
+ TrExpr(e.E0);
+ wr.Write(")");
+ break;
+ case BinaryExpr.ResolvedOpcode.NotInSeq:
+ wr.Write("!");
+ TrParenExpr(e.E1);
+ wr.Write(".Contains(");
+ TrExpr(e.E0);
+ wr.Write(")");
+ break;
+
+ default:
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected binary expression
+ }
+ if (opString != null) {
+ wr.Write(preOpString);
+ TrParenExpr(e.E0);
+ wr.Write(" {0} ", opString);
+ TrParenExpr(e.E1);
+ } else if (callString != null) {
+ wr.Write(preOpString);
+ TrParenExpr(e.E0);
+ wr.Write(".{0}(", callString);
+ TrExpr(e.E1);
+ wr.Write(")");
+ }
+
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ // The Dafny "let" expression
+ // var x := G; E
+ // is translated into C# as:
+ // ExpressionSequence(x = G, E)
+ // preceded by the declaration of x.
+ Contract.Assert(e.Vars.Count == e.RHSs.Count); // checked by resolution
+ for (int i = 0; i < e.Vars.Count; i++) {
+ wr.Write("Dafny.Helpers.ExpressionSequence(@{0} = ", e.Vars[i].CompileName);
+ TrExpr(e.RHSs[i]);
+ wr.Write(", ");
+ }
+ TrExpr(e.Body);
+ for (int i = 0; i < e.Vars.Count; i++) {
+ wr.Write(")");
+ }
+
+ } else if (expr is QuantifierExpr) {
+ var e = (QuantifierExpr)expr;
+ 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);
+ for (int i = 0; i < n; i++) {
+ var bound = e.Bounds[i];
+ var bv = e.BoundVars[i];
+ // emit: Dafny.Helpers.QuantX(boundsInformation, isForall, bv => body)
+ if (bound is ComprehensionExpr.BoolBoundedPool) {
+ wr.Write("Dafny.Helpers.QuantBool(");
+ } else if (bound is ComprehensionExpr.IntBoundedPool) {
+ var b = (ComprehensionExpr.IntBoundedPool)bound;
+ wr.Write("Dafny.Helpers.QuantInt(");
+ TrExpr(b.LowerBound);
+ wr.Write(", ");
+ TrExpr(b.UpperBound);
+ wr.Write(", ");
+ } else if (bound is ComprehensionExpr.SetBoundedPool) {
+ var b = (ComprehensionExpr.SetBoundedPool)bound;
+ wr.Write("Dafny.Helpers.QuantSet(");
+ TrExpr(b.Set);
+ wr.Write(", ");
+ } else if (bound is ComprehensionExpr.MapBoundedPool) {
+ var b = (ComprehensionExpr.MapBoundedPool)bound;
+ wr.Write("Dafny.Helpers.QuantMap(");
+ TrExpr(b.Map);
+ wr.Write(", ");
+ } else if (bound is ComprehensionExpr.SeqBoundedPool) {
+ var b = (ComprehensionExpr.SeqBoundedPool)bound;
+ wr.Write("Dafny.Helpers.QuantSeq(");
+ TrExpr(b.Seq);
+ wr.Write(", ");
+ } else if (bound is ComprehensionExpr.DatatypeBoundedPool) {
+ var b = (ComprehensionExpr.DatatypeBoundedPool)bound;
+ wr.Write("Dafny.Helpers.QuantDatatype(");
+
+ wr.Write("{0}.AllSingletonConstructors, ", DtName(b.Decl));
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type
+ }
+ wr.Write("{0}, ", expr is ForallExpr ? "true" : "false");
+ wr.Write("@{0} => ", bv.CompileName);
+ }
+ TrExpr(e.LogicalBody());
+ for (int i = 0; i < n; i++) {
+ wr.Write(")");
+ }
+
+ } else if (expr is SetComprehension) {
+ var e = (SetComprehension)expr;
+ // For "set i,j,k,l | R(i,j,k,l) :: Term(i,j,k,l)" where the term has type "G", emit something like:
+ // ((ComprehensionDelegate<G>)delegate() {
+ // var _coll = new List<G>();
+ // foreach (L l in sq.Elements) {
+ // foreach (K k in st.Elements) {
+ // for (BigInteger j = Lo; j < Hi; j++) {
+ // for (bool i in Helper.AllBooleans) {
+ // if (R(i,j,k,l)) {
+ // _coll.Add(Term(i,j,k,l));
+ // }
+ // }
+ // }
+ // }
+ // }
+ // return Dafny.Set<G>.FromCollection(_coll);
+ // })()
+ Contract.Assert(e.Bounds != null); // the resolver would have insisted on finding bounds
+ var typeName = TypeName(((SetType)e.Type).Arg);
+ wr.Write("((Dafny.Helpers.ComprehensionDelegate<{0}>)delegate() {{ ", typeName);
+ wr.Write("var _coll = new System.Collections.Generic.List<{0}>(); ", typeName);
+ var n = e.BoundVars.Count;
+ Contract.Assert(e.Bounds.Count == n);
+ for (int i = 0; i < n; i++) {
+ var bound = e.Bounds[i];
+ 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.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);
+ } else if (bound is ComprehensionExpr.SetBoundedPool) {
+ var b = (ComprehensionExpr.SetBoundedPool)bound;
+ wr.Write("foreach (var @{0} in (", bv.CompileName);
+ TrExpr(b.Set);
+ 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);
+ 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);
+ 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));
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type
+ }
+ }
+ wr.Write("if (");
+ TrExpr(e.Range);
+ wr.Write(") { _coll.Add(");
+ TrExpr(e.Term);
+ wr.Write("); }");
+ for (int i = 0; i < n; i++) {
+ wr.Write("}");
+ }
+ wr.Write("return Dafny.Set<{0}>.FromCollection(_coll); ", typeName);
+ wr.Write("})()");
+
+ } else if (expr is MapComprehension) {
+ var e = (MapComprehension)expr;
+ // For "map i | R(i) :: Term(i)" where the term has type "V" and i has type "U", emit something like:
+ // ((MapComprehensionDelegate<U, V>)delegate() {
+ // var _coll = new List<Pair<U,V>>();
+ // foreach (L l in sq.Elements) {
+ // foreach (K k in st.Elements) {
+ // for (BigInteger j = Lo; j < Hi; j++) {
+ // for (bool i in Helper.AllBooleans) {
+ // if (R(i,j,k,l)) {
+ // _coll.Add(new Pair(i, Term(i));
+ // }
+ // }
+ // }
+ // }
+ // }
+ // return Dafny.Map<U, V>.FromElements(_coll);
+ // })()
+ Contract.Assert(e.Bounds != null); // the resolver would have insisted on finding bounds
+ var domtypeName = TypeName(((MapType)e.Type).Domain);
+ var rantypeName = TypeName(((MapType)e.Type).Range);
+ 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);
+ 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.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);
+ } else if (bound is ComprehensionExpr.SetBoundedPool) {
+ var b = (ComprehensionExpr.SetBoundedPool)bound;
+ wr.Write("foreach (var @{0} in (", bv.CompileName);
+ TrExpr(b.Set);
+ 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);
+ 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);
+ wr.Write(").Elements) { ");
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type
+ }
+ wr.Write("if (");
+ TrExpr(e.Range);
+ wr.Write(") { ");
+ wr.Write("_coll.Add(new Dafny.Pair<{0},{1}>(@{2},", domtypeName, rantypeName, bv.CompileName);
+ TrExpr(e.Term);
+ wr.Write(")); }");
+ wr.Write("}");
+ wr.Write("return Dafny.Map<{0},{1}>.FromCollection(_coll); ", domtypeName, rantypeName);
+ wr.Write("})()");
+
+ } else if (expr is PredicateExpr) {
+ var e = (PredicateExpr)expr;
+ TrExpr(e.Body);
+
+ } else if (expr is ITEExpr) {
+ ITEExpr e = (ITEExpr)expr;
+ wr.Write("(");
+ TrExpr(e.Test);
+ wr.Write(") ? (");
+ TrExpr(e.Thn);
+ wr.Write(") : (");
+ TrExpr(e.Els);
+ wr.Write(")");
+
+ } else if (expr is ConcreteSyntaxExpression) {
+ var e = (ConcreteSyntaxExpression)expr;
+ TrExpr(e.ResolvedExpression);
+
+ } else if (expr is NamedExpr) {
+ TrExpr(((NamedExpr)expr).Body);
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
+ }
+ }
+
+ delegate void FCE_Arg_Translator(Expression e);
+
+ void CompileFunctionCallExpr(FunctionCallExpr e, TextWriter twr, FCE_Arg_Translator tr) {
+ Function f = cce.NonNull(e.Function);
+ if (f.IsStatic) {
+ twr.Write(TypeName(cce.NonNull(e.Receiver.Type)));
+ } else {
+ twr.Write("(");
+ tr(e.Receiver);
+ twr.Write(")");
+ }
+ twr.Write(".@{0}", f.CompileName);
+ 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]);
+ sep = ", ";
+ }
+ }
+ twr.Write(")");
+ }
+ }
+}
diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg
new file mode 100644
index 00000000..4c425497
--- /dev/null
+++ b/Source/Dafny/Dafny.atg
@@ -0,0 +1,1990 @@
+/*-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------
+// Dafny
+// Rustan Leino, first created 25 January 2008
+//--------------------------------------------------------------------------*/
+using System.Collections.Generic;
+using System.Numerics;
+using Microsoft.Boogie;
+using System.IO;
+using System.Text;
+COMPILER Dafny
+/*--------------------------------------------------------------------------*/
+readonly Expression/*!*/ dummyExpr;
+readonly AssignmentRhs/*!*/ dummyRhs;
+readonly FrameExpression/*!*/ dummyFrameExpr;
+readonly Statement/*!*/ dummyStmt;
+readonly Attributes.Argument/*!*/ dummyAttrArg;
+readonly ModuleDecl theModule;
+readonly BuiltIns theBuiltIns;
+int anonymousIds = 0;
+
+struct MemberModifiers {
+ public bool IsGhost;
+ public bool IsStatic;
+}
+// helper routine for parsing call statements
+///<summary>
+/// Parses top-level things (modules, classes, datatypes, class members) from "filename"
+/// and appends them in appropriate form to "module".
+/// Returns the number of parsing errors encountered.
+/// Note: first initialize the Scanner.
+///</summary>
+public static int Parse (string/*!*/ filename, ModuleDecl module, BuiltIns builtIns) /* throws System.IO.IOException */ {
+ Contract.Requires(filename != null);
+ Contract.Requires(module != null);
+ string s;
+ if (filename == "stdin.dfy") {
+ s = Microsoft.Boogie.ParserHelper.Fill(System.Console.In, new List<string>());
+ return Parse(s, filename, module, builtIns);
+ } else {
+ using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) {
+ s = Microsoft.Boogie.ParserHelper.Fill(reader, new List<string>());
+ return Parse(s, filename, module, builtIns);
+ }
+ }
+}
+///<summary>
+/// Parses top-level things (modules, classes, datatypes, class members)
+/// and appends them in appropriate form to "module".
+/// 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) {
+ Contract.Requires(s != null);
+ Contract.Requires(filename != null);
+ Contract.Requires(module != null);
+ Errors errors = new Errors();
+ return Parse(s, filename, module, builtIns, errors);
+}
+///<summary>
+/// Parses top-level things (modules, classes, datatypes, class members)
+/// and appends them in appropriate form to "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) {
+ 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);
+ Parser parser = new Parser(scanner, errors, module, builtIns);
+ parser.Parse();
+ return parser.errors.count;
+}
+public Parser(Scanner/*!*/ scanner, Errors/*!*/ errors, ModuleDecl module, BuiltIns builtIns)
+ : this(scanner, errors) // the real work
+{
+ // initialize readonly fields
+ dummyExpr = new LiteralExpr(Token.NoToken);
+ dummyRhs = new ExprRhs(dummyExpr, null);
+ dummyFrameExpr = new FrameExpression(dummyExpr.tok, dummyExpr, null);
+ dummyStmt = new ReturnStmt(Token.NoToken, null);
+ dummyAttrArg = new Attributes.Argument(Token.NoToken, "dummyAttrArg");
+ theModule = module;
+ theBuiltIns = builtIns;
+}
+
+bool IsAttribute() {
+ Token x = scanner.Peek();
+ return la.kind == _lbrace && x.kind == _colon;
+}
+/*--------------------------------------------------------------------------*/
+CHARACTERS
+ letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".
+ digit = "0123456789".
+ posDigit = "123456789".
+ special = "'_?\\".
+ glyph = "`~!@#$%^&*()-_=+[{]}|;:',<.>/?\\".
+ cr = '\r'.
+ lf = '\n'.
+ tab = '\t'.
+ space = ' '.
+ quote = '"'.
+ nondigit = letter + special.
+ idchar = nondigit + digit.
+ nonquote = letter + digit + space + glyph.
+ /* exclude the characters in 'array' */
+ nondigitMinusA = nondigit - 'a'.
+ idcharMinusA = idchar - 'a'.
+ idcharMinusR = idchar - 'r'.
+ idcharMinusY = idchar - 'y'.
+ idcharMinusPosDigit = idchar - posDigit.
+/*------------------------------------------------------------------------*/
+TOKENS
+ ident = nondigitMinusA {idchar} /* if char 0 is not an 'a', then anything else is fine */
+ | 'a' [ idcharMinusR {idchar} ] /* if char 0 is an 'a', then either there is no char 1 or char 1 is not an 'r' */
+ | 'a' 'r' [ idcharMinusR {idchar} ] /* etc. */
+ | 'a' 'r' 'r' [ idcharMinusA {idchar} ]
+ | 'a' 'r' 'r' 'a' [ idcharMinusY {idchar} ]
+ | 'a' 'r' 'r' 'a' 'y' idcharMinusPosDigit {idchar}
+ | 'a' 'r' 'r' 'a' 'y' posDigit {idchar} nondigit {idchar}.
+ digits = digit {digit}.
+ arrayToken = 'a' 'r' 'r' 'a' 'y' [posDigit {digit}].
+ string = quote {nonquote} quote.
+ colon = ':'.
+ lbrace = '{'.
+ rbrace = '}'.
+COMMENTS FROM "/*" TO "*/" NESTED
+COMMENTS FROM "//" TO lf
+IGNORE cr + lf + tab
+/*------------------------------------------------------------------------*/
+PRODUCTIONS
+Dafny
+= (. ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; ArbitraryTypeDecl at; 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)
+ Contract.Assert(defaultModule != null);
+ bool isGhost;
+ .)
+ { (. isGhost = false; .)
+ [ "ghost" (. isGhost = true; .) ]
+
+ ( SubModuleDecl<defaultModule, isGhost, out submodule>
+ (. defaultModule.TopLevelDecls.Add(submodule); .)
+ | (. if (isGhost) { SemErr(t, "a class is not allowed to be declared as 'ghost'"); } .)
+ ClassDecl<defaultModule, out c> (. defaultModule.TopLevelDecls.Add(c); .)
+ | (. if (isGhost) { SemErr(t, "a datatype/codatatype is not allowed to be declared as 'ghost'"); } .)
+ DatatypeDecl<defaultModule, out dt> (. defaultModule.TopLevelDecls.Add(dt); .)
+ | (. if (isGhost) { SemErr(t, "a type is not allowed to be declared as 'ghost'"); } .)
+ ArbitraryTypeDecl<defaultModule, out at> (. defaultModule.TopLevelDecls.Add(at); .)
+ | (. if (isGhost) { SemErr(t, "an iterator is not allowed to be declared as 'ghost'"); } .)
+ IteratorDecl<defaultModule, out iter> (. defaultModule.TopLevelDecls.Add(iter); .)
+ | ClassMemberDecl<membersDefaultClass, isGhost, 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) {
+ defaultClass = topleveldecl as DefaultClassDecl;
+ if (defaultClass != null) {
+ defaultClass.Members.AddRange(membersDefaultClass);
+ break;
+ }
+ }
+ if (defaultClass == null) { // create the default class here, because it wasn't found
+ defaultClass = new DefaultClassDecl(defaultModule, membersDefaultClass);
+ defaultModule.TopLevelDecls.Add(defaultClass);
+ } .)
+ EOF
+ .
+SubModuleDecl<ModuleDefinition parent, bool isOverallModuleGhost, out ModuleDecl submodule>
+= (. ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; ArbitraryTypeDecl at; IteratorDecl iter;
+ Attributes attrs = null; IToken/*!*/ id;
+ List<MemberDecl/*!*/> namedModuleDefaultClassMembers = new List<MemberDecl>();;
+ List<IToken> idRefined = null, idPath = null, idAssignment = null;
+ bool isGhost = false;
+ ModuleDefinition module;
+ ModuleDecl sm;
+ submodule = null; // appease compiler
+ bool opened = false;
+ .)
+ ( "module"
+ { Attribute<ref attrs> }
+ NoUSIdent<out id>
+
+ [ "refines" QualifiedName<out idRefined> ] (. module = new ModuleDefinition(id, id.val, isOverallModuleGhost, false, idRefined == null ? null : idRefined, attrs, false); .)
+ "{" (. module.BodyStartTok = t; .)
+ { (. isGhost = false; .)
+ [ "ghost" (. isGhost = true; .) ]
+ ( SubModuleDecl<module, isGhost, out sm> (. module.TopLevelDecls.Add(sm); .)
+ | (. if (isGhost) { SemErr(t, "a class is not allowed to be declared as 'ghost'"); } .)
+ ClassDecl<module, out c> (. module.TopLevelDecls.Add(c); .)
+ | (. if (isGhost) { SemErr(t, "a datatype/codatatype is not allowed to be declared as 'ghost'"); } .)
+ DatatypeDecl<module, out dt> (. module.TopLevelDecls.Add(dt); .)
+ | (. if (isGhost) { SemErr(t, "a type is not allowed to be declared as 'ghost'"); } .)
+ ArbitraryTypeDecl<module, out at> (. module.TopLevelDecls.Add(at); .)
+ | (. if (isGhost) { SemErr(t, "an iterator is not allowed to be declared as 'ghost'"); } .)
+ IteratorDecl<module, out iter> (. module.TopLevelDecls.Add(iter); .)
+ | ClassMemberDecl<namedModuleDefaultClassMembers, isGhost, false>
+ )
+ }
+ "}" (. module.BodyEndTok = t;
+ module.TopLevelDecls.Add(new DefaultClassDecl(module, namedModuleDefaultClassMembers));
+ submodule = new LiteralModuleDecl(module, parent); .)
+ |
+ "import" ["opened" (.opened = true;.)]
+ ( NoUSIdent<out id> ( "=" QualifiedName<out idPath> ";"
+ (. submodule = new AliasModuleDecl(idPath, id, parent, opened); .)
+ | ";"
+ (. idPath = new List<IToken>(); idPath.Add(id); submodule = new AliasModuleDecl(idPath, id, parent, opened); .)
+ | "as" QualifiedName<out idPath> ["default" QualifiedName<out idAssignment> ] ";"
+ (.submodule = new AbstractModuleDecl(idPath, id, parent, idAssignment, opened); .)
+ )
+ )
+ )
+.
+
+QualifiedName<.out List<IToken> ids.>
+= (. IToken id; ids = new List<IToken>(); .)
+ Ident<out id> (. ids.Add(id); .)
+ { "." Ident<out id> (. ids.Add(id); .)
+ }
+ .
+
+ClassDecl<ModuleDefinition/*!*/ module, out ClassDecl/*!*/ c>
+= (. Contract.Requires(module != null);
+ Contract.Ensures(Contract.ValueAtReturn(out c) != null);
+ IToken/*!*/ id;
+ Attributes attrs = null;
+ List<TypeParameter/*!*/> typeArgs = new List<TypeParameter/*!*/>();
+ List<MemberDecl/*!*/> members = new List<MemberDecl/*!*/>();
+ IToken bodyStart;
+ .)
+ SYNC
+ "class"
+ { Attribute<ref attrs> }
+ NoUSIdent<out id>
+ [ GenericParameters<typeArgs> ]
+ "{" (. bodyStart = t; .)
+ { ClassMemberDecl<members, false, true>
+ }
+ "}"
+ (. c = new ClassDecl(id, id.val, module, typeArgs, members, attrs);
+ c.BodyStartTok = bodyStart;
+ c.BodyEndTok = t;
+ .)
+ .
+ClassMemberDecl<.List<MemberDecl/*!*/>/*!*/ mm, bool isAlreadyGhost, bool allowConstructors.>
+= (. Contract.Requires(cce.NonNullElements(mm));
+ Method/*!*/ m;
+ Function/*!*/ f;
+ MemberModifiers mmod = new MemberModifiers();
+ mmod.IsGhost = isAlreadyGhost;
+ .)
+ { "ghost" (. mmod.IsGhost = true; .)
+ | "static" (. mmod.IsStatic = true; .)
+ }
+ ( FieldDecl<mmod, mm>
+ | FunctionDecl<mmod, out f> (. mm.Add(f); .)
+ | MethodDecl<mmod, allowConstructors, out m> (. mm.Add(m); .)
+ )
+ .
+DatatypeDecl<ModuleDefinition/*!*/ module, out DatatypeDecl/*!*/ dt>
+= (. Contract.Requires(module != null);
+ Contract.Ensures(Contract.ValueAtReturn(out dt)!=null);
+ IToken/*!*/ id;
+ Attributes attrs = null;
+ List<TypeParameter/*!*/> typeArgs = new List<TypeParameter/*!*/>();
+ List<DatatypeCtor/*!*/> ctors = new List<DatatypeCtor/*!*/>();
+ IToken bodyStart = Token.NoToken; // dummy assignment
+ bool co = false;
+ .)
+ SYNC
+ ( "datatype"
+ | "codatatype" (. co = true; .)
+ )
+ { Attribute<ref attrs> }
+ NoUSIdent<out id>
+ [ GenericParameters<typeArgs> ]
+ "=" (. bodyStart = t; .)
+ DatatypeMemberDecl<ctors>
+ { "|" DatatypeMemberDecl<ctors> }
+ SYNC ";"
+ (. if (co) {
+ dt = new CoDatatypeDecl(id, id.val, module, typeArgs, ctors, attrs);
+ } else {
+ dt = new IndDatatypeDecl(id, id.val, module, typeArgs, ctors, attrs);
+ }
+ dt.BodyStartTok = bodyStart;
+ dt.BodyEndTok = t;
+ .)
+ .
+DatatypeMemberDecl<.List<DatatypeCtor/*!*/>/*!*/ ctors.>
+= (. Contract.Requires(cce.NonNullElements(ctors));
+ Attributes attrs = null;
+ IToken/*!*/ id;
+ List<Formal/*!*/> formals = new List<Formal/*!*/>();
+ .)
+ { Attribute<ref attrs> }
+ NoUSIdent<out id>
+ [ FormalsOptionalIds<formals> ]
+ (. ctors.Add(new DatatypeCtor(id, id.val, formals, attrs)); .)
+ .
+FieldDecl<.MemberModifiers mmod, List<MemberDecl/*!*/>/*!*/ mm.>
+= (. Contract.Requires(cce.NonNullElements(mm));
+ Attributes attrs = null;
+ IToken/*!*/ id; Type/*!*/ ty;
+ .)
+ SYNC
+ "var"
+ (. if (mmod.IsStatic) { SemErr(t, "fields cannot be declared 'static'"); }
+ .)
+ { Attribute<ref attrs> }
+ IdentType<out id, out ty, false> (. mm.Add(new Field(id, id.val, mmod.IsGhost, ty, attrs)); .)
+ { "," IdentType<out id, out ty, false> (. mm.Add(new Field(id, id.val, mmod.IsGhost, ty, attrs)); .)
+ }
+ SYNC ";"
+ .
+ArbitraryTypeDecl<ModuleDefinition/*!*/ module, out ArbitraryTypeDecl at>
+= (. IToken/*!*/ id;
+ Attributes attrs = null;
+ var eqSupport = TypeParameter.EqualitySupportValue.Unspecified;
+ .)
+ "type"
+ { Attribute<ref attrs> }
+ NoUSIdent<out id>
+ [ "(" "==" ")" (. eqSupport = TypeParameter.EqualitySupportValue.Required; .)
+ ] (. at = new ArbitraryTypeDecl(id, id.val, module, eqSupport, attrs); .)
+ SYNC ";"
+ .
+GIdentType<bool allowGhostKeyword, out IToken/*!*/ id, out Type/*!*/ ty, out bool isGhost>
+/* isGhost always returns as false if allowGhostKeyword is false */
+= (. Contract.Ensures(Contract.ValueAtReturn(out id)!=null);
+ Contract.Ensures(Contract.ValueAtReturn(out ty)!=null);
+ isGhost = false; .)
+ [ "ghost" (. if (allowGhostKeyword) { isGhost = true; } else { SemErr(t, "formal cannot be declared 'ghost' in this context"); } .)
+ ]
+ IdentType<out id, out ty, true>
+ .
+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>
+ ":"
+ Type<out ty>
+ .
+LocalIdentTypeOptional<out VarDecl/*!*/ var, bool isGhost>
+= (. IToken/*!*/ id; Type/*!*/ ty; Type optType = null;
+ .)
+ WildIdent<out id, true>
+ [ ":" Type<out ty> (. optType = ty; .)
+ ]
+ (. var = new VarDecl(id, id.val, optType == null ? new InferredTypeProxy() : optType, isGhost); .)
+ .
+IdentTypeOptional<out BoundVar/*!*/ var>
+= (. Contract.Ensures(Contract.ValueAtReturn(out var)!=null); IToken/*!*/ id; Type/*!*/ ty; Type optType = null;
+ .)
+ WildIdent<out id, true>
+ [ ":" Type<out ty> (. optType = ty; .)
+ ]
+ (. var = new BoundVar(id, id.val, optType == null ? new InferredTypeProxy() : optType); .)
+ .
+TypeIdentOptional<out IToken/*!*/ id, out string/*!*/ identName, out Type/*!*/ ty, out bool isGhost>
+= (.Contract.Ensures(Contract.ValueAtReturn(out id)!=null);
+ Contract.Ensures(Contract.ValueAtReturn(out ty)!=null);
+ Contract.Ensures(Contract.ValueAtReturn(out identName)!=null);
+ string name = null; isGhost = false; .)
+ [ "ghost" (. isGhost = true; .)
+ ]
+ TypeAndToken<out id, out ty>
+ [ ":"
+ (. /* try to convert ty to an identifier */
+ UserDefinedType udt = ty as UserDefinedType;
+ if (udt != null && udt.TypeArgs.Count == 0) {
+ name = udt.Name;
+ } else {
+ SemErr(id, "invalid formal-parameter name in datatype constructor");
+ }
+ .)
+ Type<out ty>
+ ]
+ (. if (name != null) {
+ identName = name;
+ } else {
+ identName = "#" + anonymousIds++;
+ }
+ .)
+ .
+/*------------------------------------------------------------------------*/
+IteratorDecl<ModuleDefinition module, out IteratorDecl/*!*/ iter>
+= (. Contract.Ensures(Contract.ValueAtReturn(out iter) != null);
+ IToken/*!*/ id;
+ Attributes attrs = null;
+ List<TypeParameter/*!*/>/*!*/ typeArgs = new List<TypeParameter/*!*/>();
+ IToken openParen;
+ List<Formal/*!*/> ins = new List<Formal/*!*/>();
+ List<Formal/*!*/> outs = new List<Formal/*!*/>();
+ List<FrameExpression/*!*/> reads = new List<FrameExpression/*!*/>();
+ List<FrameExpression/*!*/> mod = new List<FrameExpression/*!*/>();
+ List<Expression/*!*/> decreases = new List<Expression>();
+ List<MaybeFreeExpression/*!*/> req = new List<MaybeFreeExpression/*!*/>();
+ List<MaybeFreeExpression/*!*/> ens = new List<MaybeFreeExpression/*!*/>();
+ List<MaybeFreeExpression/*!*/> yieldReq = new List<MaybeFreeExpression/*!*/>();
+ List<MaybeFreeExpression/*!*/> yieldEns = new List<MaybeFreeExpression/*!*/>();
+ List<Expression/*!*/> dec = new List<Expression/*!*/>();
+ Attributes readsAttrs = null;
+ Attributes modAttrs = null;
+ Attributes decrAttrs = null;
+ BlockStmt body = null;
+ bool signatureOmitted = false;
+ IToken bodyStart = Token.NoToken;
+ IToken bodyEnd = Token.NoToken;
+ .)
+ SYNC
+ "iterator"
+ { Attribute<ref attrs> }
+ NoUSIdent<out id>
+ (
+ [ GenericParameters<typeArgs> ]
+ Formals<true, true, ins, out openParen>
+ [ "yields"
+ Formals<false, true, outs, out openParen>
+ ]
+ | "..." (. signatureOmitted = true; openParen = Token.NoToken; .)
+ )
+ { IteratorSpec<reads, mod, decreases, req, ens, yieldReq, yieldEns, ref readsAttrs, ref modAttrs, ref decrAttrs> }
+ [ BlockStmt<out body, out bodyStart, out bodyEnd>
+ ]
+ (. iter = new IteratorDecl(id, id.val, module, typeArgs, ins, outs,
+ new Specification<FrameExpression>(reads, readsAttrs),
+ new Specification<FrameExpression>(mod, modAttrs),
+ new Specification<Expression>(decreases, decrAttrs),
+ req, ens, yieldReq, yieldEns,
+ body, attrs, signatureOmitted);
+ iter.BodyStartTok = bodyStart;
+ iter.BodyEndTok = bodyEnd;
+ .)
+ .
+/*------------------------------------------------------------------------*/
+GenericParameters<.List<TypeParameter/*!*/>/*!*/ typeArgs.>
+= (. Contract.Requires(cce.NonNullElements(typeArgs));
+ IToken/*!*/ id;
+ TypeParameter.EqualitySupportValue eqSupport;
+ .)
+ "<"
+ NoUSIdent<out id> (. eqSupport = TypeParameter.EqualitySupportValue.Unspecified; .)
+ [ "(" "==" ")" (. eqSupport = TypeParameter.EqualitySupportValue.Required; .)
+ ] (. typeArgs.Add(new TypeParameter(id, id.val, eqSupport)); .)
+ { "," NoUSIdent<out id> (. eqSupport = TypeParameter.EqualitySupportValue.Unspecified; .)
+ [ "(" "==" ")" (. eqSupport = TypeParameter.EqualitySupportValue.Required; .)
+ ] (. typeArgs.Add(new TypeParameter(id, id.val, eqSupport)); .)
+ }
+ ">"
+ .
+/*------------------------------------------------------------------------*/
+MethodDecl<MemberModifiers mmod, bool allowConstructor, out Method/*!*/ m>
+= (. Contract.Ensures(Contract.ValueAtReturn(out m) !=null);
+ IToken/*!*/ id;
+ Attributes attrs = null;
+ List<TypeParameter/*!*/>/*!*/ typeArgs = new List<TypeParameter/*!*/>();
+ IToken openParen;
+ List<Formal/*!*/> ins = new List<Formal/*!*/>();
+ List<Formal/*!*/> outs = new List<Formal/*!*/>();
+ List<MaybeFreeExpression/*!*/> req = new List<MaybeFreeExpression/*!*/>();
+ List<FrameExpression/*!*/> mod = new List<FrameExpression/*!*/>();
+ List<MaybeFreeExpression/*!*/> ens = new List<MaybeFreeExpression/*!*/>();
+ List<Expression/*!*/> dec = new List<Expression/*!*/>();
+ Attributes decAttrs = null;
+ Attributes modAttrs = null;
+ BlockStmt body = null;
+ bool isConstructor = false;
+ bool signatureOmitted = false;
+ IToken bodyStart = Token.NoToken;
+ IToken bodyEnd = Token.NoToken;
+ .)
+ SYNC
+ ( "method"
+ | "constructor" (. if (allowConstructor) {
+ isConstructor = true;
+ } else {
+ SemErr(t, "constructors are only allowed in classes");
+ }
+ .)
+ )
+ (. if (isConstructor) {
+ if (mmod.IsGhost) {
+ SemErr(t, "constructors cannot be declared 'ghost'");
+ }
+ if (mmod.IsStatic) {
+ SemErr(t, "constructors cannot be declared 'static'");
+ }
+ }
+ .)
+ { Attribute<ref attrs> }
+ NoUSIdent<out id>
+ (
+ [ GenericParameters<typeArgs> ]
+ Formals<true, !mmod.IsGhost, ins, out openParen>
+ [ "returns" (. if (isConstructor) { SemErr(t, "constructors cannot have out-parameters"); } .)
+ Formals<false, !mmod.IsGhost, outs, out openParen>
+ ]
+ | "..." (. signatureOmitted = true; openParen = Token.NoToken; .)
+ )
+ { MethodSpec<req, mod, ens, dec, ref decAttrs, ref modAttrs> }
+ [ BlockStmt<out body, out bodyStart, out bodyEnd>
+ ]
+ (. if (isConstructor) {
+ m = new Constructor(id, id.val, typeArgs, ins,
+ req, new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, attrs, signatureOmitted);
+ } else {
+ m = new Method(id, id.val, mmod.IsStatic, mmod.IsGhost, typeArgs, ins, outs,
+ req, new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, attrs, signatureOmitted);
+ }
+ m.BodyStartTok = bodyStart;
+ m.BodyEndTok = bodyEnd;
+ .)
+ .
+MethodSpec<.List<MaybeFreeExpression/*!*/>/*!*/ req, List<FrameExpression/*!*/>/*!*/ mod, List<MaybeFreeExpression/*!*/>/*!*/ ens,
+ List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes modAttrs.>
+= (. 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;
+ .)
+ SYNC
+ ( "modifies" { IF(IsAttribute()) Attribute<ref modAttrs> }
+ [ FrameExpression<out fe> (. mod.Add(fe); .)
+ { "," FrameExpression<out fe> (. mod.Add(fe); .)
+ }
+ ] SYNC ";"
+ | [ "free" (. isFree = true; .)
+ ]
+ ( "requires" Expression<out e> SYNC ";" (. req.Add(new MaybeFreeExpression(e, isFree)); .)
+ | "ensures" { IF(IsAttribute()) Attribute<ref ensAttrs> } Expression<out e> SYNC ";" (. ens.Add(new MaybeFreeExpression(e, isFree, ensAttrs)); .)
+ )
+ | "decreases" { IF(IsAttribute()) Attribute<ref decAttrs> } DecreasesList<decreases, true> SYNC ";"
+ )
+ .
+IteratorSpec<.List<FrameExpression/*!*/>/*!*/ reads, List<FrameExpression/*!*/>/*!*/ mod, List<Expression/*!*/> decreases,
+ List<MaybeFreeExpression/*!*/>/*!*/ req, List<MaybeFreeExpression/*!*/>/*!*/ ens,
+ List<MaybeFreeExpression/*!*/>/*!*/ yieldReq, List<MaybeFreeExpression/*!*/>/*!*/ yieldEns,
+ ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs.>
+= (. Expression/*!*/ e; FrameExpression/*!*/ fe; bool isFree = false; bool isYield = false; Attributes ensAttrs = null;
+ .)
+ SYNC
+ ( "reads" { IF(IsAttribute()) Attribute<ref readsAttrs> }
+ [ FrameExpression<out fe> (. reads.Add(fe); .)
+ { "," FrameExpression<out fe> (. reads.Add(fe); .)
+ }
+ ] SYNC ";"
+ | "modifies" { IF(IsAttribute()) Attribute<ref modAttrs> }
+ [ FrameExpression<out fe> (. mod.Add(fe); .)
+ { "," FrameExpression<out fe> (. mod.Add(fe); .)
+ }
+ ] SYNC ";"
+ | [ "free" (. isFree = true; .)
+ ]
+ [ "yield" (. isYield = true; .)
+ ]
+ ( "requires" Expression<out e> SYNC ";" (. if (isYield) {
+ yieldReq.Add(new MaybeFreeExpression(e, isFree));
+ } else {
+ req.Add(new MaybeFreeExpression(e, isFree));
+ }
+ .)
+ | "ensures" { IF(IsAttribute()) Attribute<ref ensAttrs> }
+ Expression<out e> SYNC ";" (. if (isYield) {
+ yieldEns.Add(new MaybeFreeExpression(e, isFree, ensAttrs));
+ } else {
+ ens.Add(new MaybeFreeExpression(e, isFree, ensAttrs));
+ }
+ .)
+ )
+ | "decreases" { IF(IsAttribute()) Attribute<ref decrAttrs> } DecreasesList<decreases, false> SYNC ";"
+ )
+ .
+Formals<.bool incoming, bool allowGhostKeyword, List<Formal/*!*/>/*!*/ formals, out IToken openParen.>
+= (. Contract.Requires(cce.NonNullElements(formals)); IToken/*!*/ id; Type/*!*/ ty; bool isGhost; .)
+ "(" (. openParen = t; .)
+ [
+ GIdentType<allowGhostKeyword, out id, out ty, out isGhost> (. formals.Add(new Formal(id, id.val, ty, incoming, isGhost)); .)
+ { "," GIdentType<allowGhostKeyword, out id, out ty, out isGhost> (. formals.Add(new Formal(id, id.val, ty, incoming, isGhost)); .)
+ }
+ ]
+ ")"
+ .
+FormalsOptionalIds<.List<Formal/*!*/>/*!*/ formals.>
+= (. Contract.Requires(cce.NonNullElements(formals)); IToken/*!*/ id; Type/*!*/ ty; string/*!*/ name; bool isGhost; .)
+ "("
+ [
+ TypeIdentOptional<out id, out name, out ty, out isGhost> (. formals.Add(new Formal(id, name, ty, true, isGhost)); .)
+ { "," TypeIdentOptional<out id, out name, out ty, out isGhost> (. formals.Add(new Formal(id, name, ty, true, isGhost)); .)
+ }
+ ]
+ ")"
+ .
+/*------------------------------------------------------------------------*/
+Type<out Type/*!*/ ty>
+= (. Contract.Ensures(Contract.ValueAtReturn(out ty) != null); IToken/*!*/ tok; .)
+ TypeAndToken<out tok, out ty>
+ .
+TypeAndToken<out IToken/*!*/ tok, out Type/*!*/ ty>
+= (. Contract.Ensures(Contract.ValueAtReturn(out tok)!=null); Contract.Ensures(Contract.ValueAtReturn(out ty) != null); tok = Token.NoToken; ty = new BoolType(); /*keep compiler happy*/
+ List<Type/*!*/>/*!*/ gt;
+ .)
+ ( "bool" (. tok = t; .)
+ | "nat" (. tok = t; ty = new NatType(); .)
+ | "int" (. tok = t; ty = new IntType(); .)
+ | "set" (. tok = t; gt = new List<Type/*!*/>(); .)
+ GenericInstantiation<gt> (. if (gt.Count != 1) {
+ SemErr("set type expects exactly one type argument");
+ }
+ ty = new SetType(gt[0]);
+ .)
+ | "multiset" (. tok = t; gt = new List<Type/*!*/>(); .)
+ GenericInstantiation<gt> (. if (gt.Count != 1) {
+ SemErr("multiset type expects exactly one type argument");
+ }
+ ty = new MultiSetType(gt[0]);
+ .)
+ | "seq" (. tok = t; gt = new List<Type/*!*/>(); .)
+ GenericInstantiation<gt> (. if (gt.Count != 1) {
+ SemErr("seq type expects exactly one type argument");
+ }
+ ty = new SeqType(gt[0]);
+ .)
+ | "map" (. tok = t; gt = new List<Type/*!*/>(); .)
+ GenericInstantiation<gt> (. if (gt.Count != 2) {
+ SemErr("map type expects exactly two type arguments");
+ }
+ else { ty = new MapType(gt[0], gt[1]); }
+ .)
+ | ReferenceType<out tok, out ty>
+ )
+ .
+ReferenceType<out IToken/*!*/ tok, out Type/*!*/ ty>
+= (. Contract.Ensures(Contract.ValueAtReturn(out tok) != null); Contract.Ensures(Contract.ValueAtReturn(out ty) != null);
+ tok = Token.NoToken; ty = new BoolType(); /*keep compiler happy*/
+ List<Type/*!*/>/*!*/ gt;
+ List<IToken> path;
+ .)
+ ( "object" (. tok = t; ty = new ObjectType(); .)
+ | arrayToken (. tok = t; gt = new List<Type/*!*/>(); .)
+ GenericInstantiation<gt> (. if (gt.Count != 1) {
+ SemErr("array type expects exactly one type argument");
+ }
+ int dims = 1;
+ if (tok.val.Length != 5) {
+ dims = int.Parse(tok.val.Substring(5));
+ }
+ ty = theBuiltIns.ArrayType(tok, dims, gt[0], true);
+ .)
+ | Ident<out tok> (. gt = new List<Type/*!*/>();
+ path = new List<IToken>(); .)
+ { (. path.Add(tok); .)
+ "." Ident<out tok>
+ }
+ [ GenericInstantiation<gt> ] (. ty = new UserDefinedType(tok, tok.val, gt, path); .)
+ )
+ .
+GenericInstantiation<.List<Type/*!*/>/*!*/ gt.>
+= (. Contract.Requires(cce.NonNullElements(gt)); Type/*!*/ ty; .)
+ "<"
+ Type<out ty> (. gt.Add(ty); .)
+ { "," Type<out ty> (. gt.Add(ty); .)
+ }
+ ">"
+ .
+/*------------------------------------------------------------------------*/
+FunctionDecl<MemberModifiers mmod, out Function/*!*/ f>
+= (. Contract.Ensures(Contract.ValueAtReturn(out f)!=null);
+ Attributes attrs = null;
+ IToken/*!*/ id = Token.NoToken; // to please compiler
+ List<TypeParameter/*!*/> typeArgs = new List<TypeParameter/*!*/>();
+ List<Formal/*!*/> formals = new List<Formal/*!*/>();
+ Type/*!*/ returnType = new BoolType();
+ List<Expression/*!*/> reqs = new List<Expression/*!*/>();
+ List<Expression/*!*/> ens = new List<Expression/*!*/>();
+ List<FrameExpression/*!*/> reads = new List<FrameExpression/*!*/>();
+ List<Expression/*!*/> decreases;
+ Expression body = null;
+ bool isPredicate = false; bool isCoPredicate = false;
+ bool isFunctionMethod = false;
+ IToken openParen = null;
+ IToken bodyStart = Token.NoToken;
+ IToken bodyEnd = Token.NoToken;
+ bool signatureOmitted = false;
+ .)
+ /* ----- function ----- */
+ ( "function"
+ [ "method" (. isFunctionMethod = true; .)
+ ]
+ (. if (mmod.IsGhost) { SemErr(t, "functions cannot be declared 'ghost' (they are ghost by default)"); }
+ .)
+ { Attribute<ref attrs> }
+ NoUSIdent<out id>
+ (
+ [ GenericParameters<typeArgs> ]
+ Formals<true, isFunctionMethod, formals, out openParen>
+ ":"
+ Type<out returnType>
+ | "..." (. signatureOmitted = true;
+ openParen = Token.NoToken; .)
+ )
+
+ /* ----- predicate ----- */
+ | "predicate" (. isPredicate = true; .)
+ [ "method" (. isFunctionMethod = true; .)
+ ]
+ (. if (mmod.IsGhost) { SemErr(t, "predicates cannot be declared 'ghost' (they are ghost by default)"); }
+ .)
+ { Attribute<ref attrs> }
+ NoUSIdent<out id>
+ (
+ [ GenericParameters<typeArgs> ]
+ [ Formals<true, isFunctionMethod, formals, out openParen>
+ [ ":" (. SemErr(t, "predicates do not have an explicitly declared return type; it is always bool"); .)
+ ]
+ ]
+ | "..." (. signatureOmitted = true;
+ openParen = Token.NoToken; .)
+ )
+
+ /* ----- copredicate ----- */
+ | "copredicate" (. isCoPredicate = true; .)
+ (. if (mmod.IsGhost) { SemErr(t, "copredicates cannot be declared 'ghost' (they are ghost by default)"); }
+ .)
+ { Attribute<ref attrs> }
+ NoUSIdent<out id>
+ (
+ [ GenericParameters<typeArgs> ]
+ [ Formals<true, isFunctionMethod, formals, out openParen>
+ [ ":" (. SemErr(t, "copredicates do not have an explicitly declared return type; it is always bool"); .)
+ ]
+ ]
+ | "..." (. signatureOmitted = true;
+ openParen = Token.NoToken; .)
+ )
+ )
+
+ (. decreases = isCoPredicate ? null : new List<Expression/*!*/>(); .)
+ { FunctionSpec<reqs, reads, ens, decreases> }
+ [ FunctionBody<out body, out bodyStart, out bodyEnd>
+ ]
+ (. if (isPredicate) {
+ f = new Predicate(id, id.val, mmod.IsStatic, !isFunctionMethod, typeArgs, openParen, formals,
+ reqs, reads, ens, new Specification<Expression>(decreases, null), body, Predicate.BodyOriginKind.OriginalOrInherited, attrs, signatureOmitted);
+ } else if (isCoPredicate) {
+ f = new CoPredicate(id, id.val, mmod.IsStatic, typeArgs, openParen, formals,
+ reqs, reads, ens, body, attrs, signatureOmitted);
+ } else {
+ f = new Function(id, id.val, mmod.IsStatic, !isFunctionMethod, typeArgs, openParen, formals, returnType,
+ reqs, reads, ens, new Specification<Expression>(decreases, null), body, attrs, signatureOmitted);
+ }
+ f.BodyStartTok = bodyStart;
+ f.BodyEndTok = bodyEnd;
+ .)
+ .
+FunctionSpec<.List<Expression/*!*/>/*!*/ reqs, List<FrameExpression/*!*/>/*!*/ reads, List<Expression/*!*/>/*!*/ ens, List<Expression/*!*/> decreases.>
+= (. Contract.Requires(cce.NonNullElements(reqs));
+ Contract.Requires(cce.NonNullElements(reads));
+ Contract.Requires(decreases == null || cce.NonNullElements(decreases));
+ Expression/*!*/ e; FrameExpression/*!*/ fe; .)
+ (
+ SYNC
+ "requires" Expression<out e> SYNC ";" (. reqs.Add(e); .)
+ | "reads" [ PossiblyWildFrameExpression<out fe> (. reads.Add(fe); .)
+ { "," PossiblyWildFrameExpression<out fe> (. reads.Add(fe); .)
+ }
+ ] SYNC ";"
+ | "ensures" Expression<out e> SYNC ";" (. ens.Add(e); .)
+ | "decreases" (. if (decreases == null) {
+ SemErr(t, "'decreases' clauses are meaningless for copredicates, so they are not allowed");
+ decreases = new List<Expression/*!*/>();
+ }
+ .)
+ DecreasesList<decreases, false> SYNC ";"
+ )
+ .
+PossiblyWildExpression<out Expression/*!*/ e>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e)!=null);
+ e = dummyExpr; .)
+ /* A decreases clause on a loop asks that no termination check be performed.
+ * Use of this feature is sound only with respect to partial correctness.
+ */
+ ( "*" (. e = new WildcardExpr(t); .)
+ | Expression<out e>
+ )
+ .
+PossiblyWildFrameExpression<out FrameExpression/*!*/ fe>
+= (. Contract.Ensures(Contract.ValueAtReturn(out fe) != null); fe = dummyFrameExpr; .)
+ /* 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).
+ */
+ ( "*" (. fe = new FrameExpression(t, new WildcardExpr(t), null); .)
+ | FrameExpression<out fe>
+ )
+ .
+FrameExpression<out FrameExpression/*!*/ fe>
+= (. Contract.Ensures(Contract.ValueAtReturn(out fe) != null);
+ Expression/*!*/ e;
+ IToken/*!*/ id;
+ string fieldName = null; IToken feTok = null;
+ fe = null;
+ .)
+ (( Expression<out e> (. feTok = e.tok; .)
+ [ "`" Ident<out id> (. fieldName = id.val; feTok = id; .)
+ ]
+ (. fe = new FrameExpression(feTok, e, fieldName); .)
+ ) |
+ ( "`" Ident<out id> (. fieldName = id.val; .)
+ (. fe = new FrameExpression(id, new ImplicitThisExpr(id), fieldName); .)
+ ))
+ .
+FunctionBody<out Expression/*!*/ e, out IToken bodyStart, out IToken bodyEnd>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null); e = dummyExpr; .)
+ "{" (. bodyStart = t; .)
+ Expression<out e>
+ "}" (. bodyEnd = t; .)
+ .
+/*------------------------------------------------------------------------*/
+BlockStmt<out BlockStmt/*!*/ block, out IToken bodyStart, out IToken bodyEnd>
+= (. Contract.Ensures(Contract.ValueAtReturn(out block) != null);
+ List<Statement/*!*/> body = new List<Statement/*!*/>();
+ .)
+ "{" (. bodyStart = t; .)
+ { Stmt<body>
+ }
+ "}" (. bodyEnd = t;
+ block = new BlockStmt(bodyStart, body); .)
+ .
+Stmt<.List<Statement/*!*/>/*!*/ ss.>
+= (. Statement/*!*/ s;
+ .)
+ OneStmt<out s> (. ss.Add(s); .)
+ .
+OneStmt<out Statement/*!*/ s>
+= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null); IToken/*!*/ x; IToken/*!*/ id; string label = null;
+ s = dummyStmt; /* to please the compiler */
+ BlockStmt bs;
+ IToken bodyStart, bodyEnd;
+ int breakCount;
+ .)
+ SYNC
+ ( BlockStmt<out bs, out bodyStart, out bodyEnd> (. s = bs; .)
+ | AssertStmt<out s>
+ | AssumeStmt<out s>
+ | PrintStmt<out s>
+ | UpdateStmt<out s>
+ | VarDeclStatement<out s>
+ | IfStmt<out s>
+ | WhileStmt<out s>
+ | MatchStmt<out s>
+ | ParallelStmt<out s>
+ | CalcStmt<out s>
+ | "label" (. x = t; .)
+ NoUSIdent<out id> ":"
+ OneStmt<out s> (. s.Labels = new LList<Label>(new Label(x, id.val), s.Labels); .)
+ | "break" (. x = t; breakCount = 1; label = null; .)
+ ( NoUSIdent<out id> (. label = id.val; .)
+ | { "break" (. breakCount++; .)
+ }
+ )
+ SYNC
+ ";" (. s = label != null ? new BreakStmt(x, label) : new BreakStmt(x, breakCount); .)
+ | ReturnStmt<out s>
+ | SkeletonStmt<out s>
+ ";"
+ )
+ .
+
+SkeletonStmt<out Statement s>
+= (. List<IToken> names = null;
+ List<Expression> exprs = null;
+ IToken tok, dotdotdot, whereTok;
+ Expression e; .)
+ "..." (. dotdotdot = t; .)
+ ["where" (. names = new List<IToken>(); exprs = new List<Expression>(); whereTok = t;.)
+ Ident<out tok> (. names.Add(tok); .)
+ {"," Ident<out tok> (. names.Add(tok); .)
+ }
+ ":="
+ Expression<out e> (. exprs.Add(e); .)
+ {"," Expression<out e> (. exprs.Add(e); .)
+ }
+ (. if (exprs.Count != names.Count) {
+ SemErr(whereTok, exprs.Count < names.Count ? "not enough expressions" : "too many expressions");
+ names = null; exprs = null;
+ }
+ .)
+ ]
+ (. s = new SkeletonStatement(dotdotdot, names, exprs); .)
+ .
+ReturnStmt<out Statement/*!*/ s>
+= (.
+ IToken returnTok = null;
+ List<AssignmentRhs> rhss = null;
+ AssignmentRhs r;
+ bool isYield = false;
+ .)
+ ( "return" (. returnTok = t; .)
+ | "yield" (. returnTok = t; isYield = true; .)
+ )
+ [
+ Rhs<out r, null> (. rhss = new List<AssignmentRhs>(); rhss.Add(r); .)
+ { "," Rhs<out r, null> (. rhss.Add(r); .)
+ }
+ ]
+ ";" (. if (isYield) {
+ s = new YieldStmt(returnTok, rhss);
+ } else {
+ s = new ReturnStmt(returnTok, rhss);
+ }
+ .)
+ .
+UpdateStmt<out Statement/*!*/ s>
+= (. List<Expression> lhss = new List<Expression>();
+ List<AssignmentRhs> rhss = new List<AssignmentRhs>();
+ Expression e; AssignmentRhs r;
+ Expression lhs0;
+ IToken x;
+ Attributes attrs = null;
+ IToken suchThatAssume = null;
+ Expression suchThat = null;
+ .)
+ Lhs<out e> (. x = e.tok; .)
+ ( { Attribute<ref attrs> }
+ ";" (. rhss.Add(new ExprRhs(e, attrs)); .)
+ | (. lhss.Add(e); lhs0 = e; .)
+ { "," Lhs<out e> (. lhss.Add(e); .)
+ }
+ ( ":=" (. x = t; .)
+ Rhs<out r, lhs0> (. rhss.Add(r); .)
+ { "," Rhs<out r, lhs0> (. rhss.Add(r); .)
+ }
+ | ":|" (. x = t; .)
+ [ "assume" (. suchThatAssume = t; .)
+ ]
+ Expression<out suchThat>
+ )
+ ";"
+ | ":" (. SemErr(t, "invalid statement (did you forget the 'label' keyword?)"); .)
+ )
+ (. if (suchThat != null) {
+ s = new AssignSuchThatStmt(x, lhss, suchThat, suchThatAssume);
+ } else {
+ if (lhss.Count == 0 && rhss.Count == 0) {
+ s = new BlockStmt(x, new List<Statement>()); // error, give empty statement
+ } else {
+ s = new UpdateStmt(x, lhss, rhss);
+ }
+ }
+ .)
+ .
+Rhs<out AssignmentRhs r, Expression receiverForInitCall>
+= (. Contract.Ensures(Contract.ValueAtReturn<AssignmentRhs>(out r) != null);
+ IToken/*!*/ x, newToken; Expression/*!*/ e;
+ List<Expression> ee = null;
+ Type ty = null;
+ CallStmt initCall = null;
+ List<Expression> args;
+ r = dummyRhs; // to please compiler
+ Attributes attrs = null;
+ .)
+ ( "new" (. newToken = t; .)
+ TypeAndToken<out x, out ty>
+ [ "[" (. ee = new List<Expression>(); .)
+ Expressions<ee>
+ "]" (. // make sure an array class with this dimensionality exists
+ UserDefinedType tmp = theBuiltIns.ArrayType(x, ee.Count, new IntType(), true);
+ .)
+ | "." Ident<out x>
+ "(" (. // This case happens when we have type<typeargs>.Constructor(args)
+ // There is no ambiguity about where the constructor is or whether one exists.
+ args = new List<Expression/*!*/>(); .)
+ [ Expressions<args> ]
+ ")" (. initCall = new CallStmt(x, new List<Expression>(), receiverForInitCall, x.val, args); .)
+ | "(" (. var udf = ty as UserDefinedType;
+ if (udf != null && 0 < udf.Path.Count && udf.TypeArgs.Count == 0) {
+ // The parsed name had the form "A.B.Ctr", so treat "A.B" as the name of the type and "Ctr" as
+ // the name of the constructor that's being invoked.
+ x = udf.tok;
+ ty = new UserDefinedType(udf.Path[0], udf.Path[udf.Path.Count-1].val, new List<Type>(), udf.Path.GetRange(0,udf.Path.Count-1));
+ } else {
+ SemErr(t, "expected '.'");
+ x = null;
+ }
+ args = new List<Expression/*!*/>(); .)
+ [ Expressions<args> ]
+ ")" (. if (x != null) {
+ initCall = new CallStmt(x, new List<Expression>(), receiverForInitCall, x.val, args);
+ }
+ .)
+ ]
+ (. if (ee != null) {
+ r = new TypeRhs(newToken, ty, ee);
+ } else {
+ r = new TypeRhs(newToken, ty, initCall);
+ }
+ .)
+ /* One day, the choose expression should be treated just as a special case of a method call. */
+ | "choose" (. x = t; .)
+ Expression<out e> (. r = new ExprRhs(new UnaryExpr(x, UnaryExpr.Opcode.SetChoose, e)); .)
+ | "*" (. r = new HavocRhs(t); .)
+ | Expression<out e> (. r = new ExprRhs(e); .)
+ )
+ { Attribute<ref attrs> } (. r.Attributes = attrs; .)
+ .
+VarDeclStatement<.out Statement/*!*/ s.>
+= (. IToken x = null, assignTok = null; bool isGhost = false;
+ VarDecl/*!*/ d;
+ AssignmentRhs r; IdentifierExpr lhs0;
+ List<VarDecl> lhss = new List<VarDecl>();
+ List<AssignmentRhs> rhss = new List<AssignmentRhs>();
+ IToken suchThatAssume = null;
+ Expression suchThat = null;
+ .)
+ [ "ghost" (. isGhost = true; x = t; .)
+ ]
+ "var" (. if (!isGhost) { x = t; } .)
+ LocalIdentTypeOptional<out d, isGhost> (. lhss.Add(d); .)
+ { ","
+ LocalIdentTypeOptional<out d, isGhost> (. lhss.Add(d); .)
+ }
+ [ ":=" (. assignTok = t;
+ lhs0 = new IdentifierExpr(lhss[0].Tok, lhss[0].Name);
+ lhs0.Var = lhss[0]; lhs0.Type = lhss[0].OptionalType; // resolve here
+ .)
+ Rhs<out r, lhs0> (. rhss.Add(r); .)
+ { "," Rhs<out r, lhs0> (. rhss.Add(r); .)
+ }
+ | ":|" (. assignTok = t; .)
+ [ "assume" (. suchThatAssume = t; .)
+ ]
+ Expression<out suchThat>
+ ]
+ ";"
+ (. 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, ies, suchThat, suchThatAssume);
+ } 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, ies, rhss);
+ }
+ s = new VarDeclStmt(x, lhss, update);
+ .)
+ .
+IfStmt<out Statement/*!*/ ifStmt>
+= (. Contract.Ensures(Contract.ValueAtReturn(out ifStmt) != null); IToken/*!*/ x;
+ Expression guard = null; bool guardOmitted = false;
+ BlockStmt/*!*/ thn;
+ BlockStmt/*!*/ bs;
+ Statement/*!*/ s;
+ Statement els = null;
+ IToken bodyStart, bodyEnd;
+ List<GuardedAlternative> alternatives;
+ ifStmt = dummyStmt; // to please the compiler
+ .)
+ "if" (. x = t; .)
+ (
+ ( Guard<out guard>
+ | "..." (. guardOmitted = true; .)
+ )
+ BlockStmt<out thn, out bodyStart, out bodyEnd>
+ [ "else"
+ ( IfStmt<out s> (. els = s; .)
+ | BlockStmt<out bs, out bodyStart, out bodyEnd> (. els = bs; .)
+ )
+ ]
+ (. if (guardOmitted) {
+ ifStmt = new SkeletonStatement(new IfStmt(x, guard, thn, els), true, false);
+ } else {
+ ifStmt = new IfStmt(x, guard, thn, els);
+ }
+ .)
+ |
+ AlternativeBlock<out alternatives>
+ (. ifStmt = new AlternativeStmt(x, alternatives); .)
+ )
+ .
+AlternativeBlock<.out List<GuardedAlternative> alternatives.>
+= (. alternatives = new List<GuardedAlternative>();
+ IToken x;
+ Expression e;
+ List<Statement> body;
+ .)
+ "{"
+ { "case" (. x = t; .)
+ Expression<out e>
+ "=>"
+ (. body = new List<Statement>(); .)
+ { Stmt<body> }
+ (. alternatives.Add(new GuardedAlternative(x, e, body)); .)
+ }
+ "}"
+ .
+WhileStmt<out Statement/*!*/ stmt>
+= (. Contract.Ensures(Contract.ValueAtReturn(out stmt) != null); IToken/*!*/ x;
+ Expression guard = null; bool guardOmitted = false;
+ List<MaybeFreeExpression/*!*/> invariants = new List<MaybeFreeExpression/*!*/>();
+ List<Expression/*!*/> decreases = new List<Expression/*!*/>();
+ Attributes decAttrs = null;
+ Attributes modAttrs = null;
+ List<FrameExpression/*!*/> mod = null;
+ BlockStmt/*!*/ body = null; bool bodyOmitted = false;
+ IToken bodyStart = null, bodyEnd = null;
+ List<GuardedAlternative> alternatives;
+ stmt = dummyStmt; // to please the compiler
+ .)
+ "while" (. x = t; .)
+ (
+ ( Guard<out guard> (. Contract.Assume(guard == null || cce.Owner.None(guard)); .)
+ | "..." (. guardOmitted = true; .)
+ )
+ LoopSpec<out invariants, out decreases, out mod, ref decAttrs, ref modAttrs>
+ ( BlockStmt<out body, out bodyStart, out bodyEnd>
+ | "..." (. bodyOmitted = true; .)
+ )
+ (.
+ if (guardOmitted || bodyOmitted) {
+ if (mod != null) {
+ SemErr(mod[0].E.tok, "'modifies' clauses are not allowed on refining loops");
+ }
+ if (body == null) {
+ body = new BlockStmt(x, new List<Statement>());
+ }
+ stmt = new WhileStmt(x, guard, invariants, new Specification<Expression>(decreases, decAttrs), new Specification<FrameExpression>(null, null), body);
+ stmt = new SkeletonStatement(stmt, guardOmitted, bodyOmitted);
+ } else {
+ // The following statement protects against crashes in case of parsing errors
+ body = body ?? new BlockStmt(x, new List<Statement>());
+ stmt = new WhileStmt(x, guard, invariants, new Specification<Expression>(decreases, decAttrs), new Specification<FrameExpression>(mod, modAttrs), body);
+ }
+ .)
+ |
+ LoopSpec<out invariants, out decreases, out mod, ref decAttrs, ref modAttrs>
+ AlternativeBlock<out alternatives>
+ (. stmt = new AlternativeLoopStmt(x, invariants, new Specification<Expression>(decreases, decAttrs), new Specification<FrameExpression>(mod, modAttrs), alternatives); .)
+ )
+ .
+LoopSpec<.out List<MaybeFreeExpression/*!*/> invariants, out List<Expression/*!*/> decreases, out List<FrameExpression/*!*/> mod, ref Attributes decAttrs, ref Attributes modAttrs.>
+= (. FrameExpression/*!*/ fe;
+ invariants = new List<MaybeFreeExpression/*!*/>();
+ MaybeFreeExpression invariant = null;
+ decreases = new List<Expression/*!*/>();
+ mod = null;
+ .)
+ {
+ Invariant<out invariant> SYNC ";" (. invariants.Add(invariant); .)
+ | SYNC "decreases"
+ { IF(IsAttribute()) Attribute<ref decAttrs> }
+ DecreasesList<decreases, true> SYNC ";"
+ | SYNC "modifies"
+ { IF(IsAttribute()) Attribute<ref modAttrs> } (. mod = mod ?? new List<FrameExpression>(); .)
+ [ FrameExpression<out fe> (. mod.Add(fe); .)
+ { "," FrameExpression<out fe> (. mod.Add(fe); .)
+ }
+ ] SYNC ";"
+ }
+ .
+Invariant<out MaybeFreeExpression/*!*/ invariant>
+= (. bool isFree = false; Expression/*!*/ e; List<string> ids = new List<string>(); invariant = null; Attributes attrs = null; .)
+ SYNC
+ ["free" (. isFree = true; .)
+ ]
+ "invariant" { IF(IsAttribute()) Attribute<ref attrs> } Expression<out e> (. invariant = new MaybeFreeExpression(e, isFree, attrs); .)
+ .
+DecreasesList<.List<Expression/*!*/> decreases, bool allowWildcard.>
+= (. Expression/*!*/ e; .)
+ PossiblyWildExpression<out e> (. if (!allowWildcard && e is WildcardExpr) {
+ SemErr(e.tok, "'decreases *' is only allowed on loops and tail-recursive methods");
+ } else {
+ decreases.Add(e);
+ }
+ .)
+ { "," PossiblyWildExpression<out e> (. if (!allowWildcard && e is WildcardExpr) {
+ SemErr(e.tok, "'decreases *' is only allowed on loops and tail-recursive methods");
+ } else {
+ decreases.Add(e);
+ }
+ .)
+ }
+ .
+Guard<out Expression e> /* null represents demonic-choice */
+= (. Expression/*!*/ ee; e = null; .)
+ "("
+ ( "*" (. e = null; .)
+ | Expression<out ee> (. e = ee; .)
+ )
+ ")"
+ .
+MatchStmt<out Statement/*!*/ s>
+= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null);
+ Token x; Expression/*!*/ e; MatchCaseStmt/*!*/ c;
+ List<MatchCaseStmt/*!*/> cases = new List<MatchCaseStmt/*!*/>(); .)
+ "match" (. x = t; .)
+ Expression<out e>
+ "{"
+ { CaseStatement<out c> (. cases.Add(c); .)
+ }
+ "}"
+ (. s = new MatchStmt(x, e, cases); .)
+ .
+CaseStatement<out MatchCaseStmt/*!*/ c>
+= (. Contract.Ensures(Contract.ValueAtReturn(out c) != null);
+ IToken/*!*/ x, id;
+ List<BoundVar/*!*/> arguments = new List<BoundVar/*!*/>();
+ BoundVar/*!*/ bv;
+ List<Statement/*!*/> body = new List<Statement/*!*/>();
+ .)
+ "case" (. x = t; .)
+ Ident<out id>
+ [ "("
+ IdentTypeOptional<out bv> (. arguments.Add(bv); .)
+ { "," IdentTypeOptional<out bv> (. arguments.Add(bv); .)
+ }
+ ")" ]
+ "=>"
+ { Stmt<body> }
+ (. c = new MatchCaseStmt(x, id.val, arguments, body); .)
+ .
+/*------------------------------------------------------------------------*/
+AssertStmt<out Statement/*!*/ s>
+= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null); IToken/*!*/ x;
+ Expression e = null; Attributes attrs = null;
+ .)
+ "assert" (. x = t; .)
+ { IF(IsAttribute()) Attribute<ref attrs> }
+ ( Expression<out e>
+ | "..."
+ )
+ ";"
+ (. if (e == null) {
+ s = new SkeletonStatement(new AssertStmt(x, new LiteralExpr(x, true), attrs), true, false);
+ } else {
+ s = new AssertStmt(x, e, attrs);
+ }
+ .)
+ .
+AssumeStmt<out Statement/*!*/ s>
+= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null); IToken/*!*/ x;
+ Expression e = null; Attributes attrs = null;
+ .)
+ "assume" (. x = t; .)
+ { IF(IsAttribute()) Attribute<ref attrs> }
+ ( Expression<out e>
+ | "..."
+ )
+ (. if (e == null) {
+ s = new SkeletonStatement(new AssumeStmt(x, new LiteralExpr(x, true), attrs), true, false);
+ } else {
+ s = new AssumeStmt(x, e, attrs);
+ }
+ .)
+ ";"
+ .
+PrintStmt<out Statement/*!*/ s>
+= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null); IToken/*!*/ x; Attributes.Argument/*!*/ arg;
+ List<Attributes.Argument/*!*/> args = new List<Attributes.Argument/*!*/>();
+ .)
+ "print" (. x = t; .)
+ AttributeArg<out arg> (. args.Add(arg); .)
+ { "," AttributeArg<out arg> (. args.Add(arg); .)
+ }
+ ";" (. s = new PrintStmt(x, args); .)
+ .
+
+ParallelStmt<out Statement/*!*/ s>
+= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null);
+ IToken/*!*/ x;
+ List<BoundVar/*!*/> bvars = null;
+ Attributes attrs = null;
+ Expression range = null;
+ var ens = new List<MaybeFreeExpression/*!*/>();
+ bool isFree;
+ Expression/*!*/ e;
+ BlockStmt/*!*/ block;
+ IToken bodyStart, bodyEnd;
+ .)
+ "parallel" (. x = t; .)
+ "("
+ [ (. List<BoundVar/*!*/> bvarsX; Attributes attrsX; Expression rangeX; .)
+ QuantifierDomain<out bvarsX, out attrsX, out rangeX>
+ (. bvars = bvarsX; attrs = attrsX; range = rangeX;
+ .)
+ ]
+ (. if (bvars == null) { bvars = new List<BoundVar>(); }
+ if (range == null) { range = new LiteralExpr(x, true); }
+ .)
+ ")"
+ { (. isFree = false; .)
+ [ "free" (. isFree = true; .)
+ ]
+ "ensures" Expression<out e> ";" (. ens.Add(new MaybeFreeExpression(e, isFree)); .)
+ }
+ BlockStmt<out block, out bodyStart, out bodyEnd>
+ (. s = new ParallelStmt(x, bvars, attrs, range, ens, block); .)
+ .
+
+CalcStmt<out Statement/*!*/ s>
+= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null);
+ Token x;
+ BinaryExpr.Opcode op, calcOp = BinaryExpr.Opcode.Eq, resOp = BinaryExpr.Opcode.Eq;
+ var lines = new List<Expression/*!*/>();
+ var hints = new List<BlockStmt/*!*/>();
+ var customOps = new List<BinaryExpr.Opcode?>();
+ BinaryExpr.Opcode? maybeOp;
+ Expression/*!*/ e;
+ BlockStmt/*!*/ h;
+ IToken opTok;
+ .)
+ "calc" (. x = t; .)
+ [ CalcOp<out opTok, out calcOp> (. maybeOp = Microsoft.Dafny.CalcStmt.ResultOp(calcOp, calcOp); // guard against non-trasitive calcOp (like !=)
+ if (maybeOp == null) {
+ SemErr(opTok, "the main operator of a calculation must be transitive");
+ }
+ resOp = calcOp;
+ .)
+ ]
+ "{"
+ [ Expression<out e> (. lines.Add(e); .)
+ ";"
+ {
+ Hint<out h> (. hints.Add(h); .)
+ ( CalcOp<out opTok, out op> (. maybeOp = Microsoft.Dafny.CalcStmt.ResultOp(resOp, op);
+ if (maybeOp == null) {
+ customOps.Add(null); // pretend the operator was not there to satisfy the precondition of the CalcStmt contructor
+ SemErr(opTok, "this operator cannot continue this calculation");
+ } else {
+ customOps.Add(op);
+ resOp = (BinaryExpr.Opcode)maybeOp;
+ }
+ .)
+ | (. customOps.Add(null); .)
+ )
+ Expression<out e> (. lines.Add(e); .)
+ ";"
+ }
+ ]
+ "}"
+ (. s = new CalcStmt(x, calcOp, lines, hints, customOps); .)
+ .
+CalcOp<out IToken x, out BinaryExpr.Opcode/*!*/ op>
+= (. Contract.Ensures(Microsoft.Dafny.CalcStmt.ValidOp(Contract.ValueAtReturn(out op)));
+ op = BinaryExpr.Opcode.Eq; // Returns Eq if parsing fails because it is compatible with any other operator
+ x = null;
+ .)
+ ( "==" (. x = t; op = BinaryExpr.Opcode.Eq; .)
+ | "<" (. x = t; op = BinaryExpr.Opcode.Lt; .)
+ | ">" (. x = t; op = BinaryExpr.Opcode.Gt; .)
+ | "<=" (. x = t; op = BinaryExpr.Opcode.Le; .)
+ | ">=" (. x = t; op = BinaryExpr.Opcode.Ge; .)
+ | "!=" (. x = t; op = BinaryExpr.Opcode.Neq; .)
+ | '\u2260' (. x = t; op = BinaryExpr.Opcode.Neq; .)
+ | '\u2264' (. x = t; op = BinaryExpr.Opcode.Le; .)
+ | '\u2265' (. x = t; op = BinaryExpr.Opcode.Ge; .)
+ | EquivOp (. x = t; op = BinaryExpr.Opcode.Iff; .)
+ | ImpliesOp (. x = t; op = BinaryExpr.Opcode.Imp; .)
+ )
+ .
+Hint<out BlockStmt s>
+= (. Contract.Ensures(Contract.ValueAtReturn(out s) != null); // returns an empty block statement if the hint is empty
+ var subhints = new List<Statement/*!*/>();
+ IToken bodyStart, bodyEnd;
+ BlockStmt/*!*/ block;
+ Statement/*!*/ calc;
+ Token x = la;
+ .)
+ { BlockStmt<out block, out bodyStart, out bodyEnd> (. subhints.Add(block); .)
+ | CalcStmt<out calc> (. subhints.Add(calc); .)
+ }
+ (. s = new BlockStmt(x, subhints); // if the hint is empty x is the first token of the next line, but it doesn't matter cause the block statement is just used as a container
+ .)
+ .
+/*------------------------------------------------------------------------*/
+Expression<out Expression/*!*/ e>
+=
+ EquivExpression<out e>
+ .
+/*------------------------------------------------------------------------*/
+EquivExpression<out Expression/*!*/ e0>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e0) != null); IToken/*!*/ x; Expression/*!*/ e1; .)
+ ImpliesExpression<out e0>
+ { EquivOp (. x = t; .)
+ ImpliesExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Iff, e0, e1); .)
+ }
+ .
+EquivOp = "<==>" | '\u21d4'.
+/*------------------------------------------------------------------------*/
+ImpliesExpression<out Expression/*!*/ e0>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e0) != null); IToken/*!*/ x; Expression/*!*/ e1; .)
+ LogicalExpression<out e0>
+ [ ImpliesOp (. x = t; .)
+ ImpliesExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Imp, e0, e1); .)
+ ]
+ .
+ImpliesOp = "==>" | '\u21d2'.
+/*------------------------------------------------------------------------*/
+LogicalExpression<out Expression/*!*/ e0>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e0) != null); IToken/*!*/ x; Expression/*!*/ e1; .)
+ RelationalExpression<out e0>
+ [ AndOp (. x = t; .)
+ RelationalExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1); .)
+ { AndOp (. x = t; .)
+ RelationalExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1); .)
+ }
+ | OrOp (. x = t; .)
+ RelationalExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1); .)
+ { OrOp (. x = t; .)
+ RelationalExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1); .)
+ }
+ ]
+ .
+AndOp = "&&" | '\u2227'.
+OrOp = "||" | '\u2228'.
+/*------------------------------------------------------------------------*/
+RelationalExpression<out Expression/*!*/ e>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null);
+ IToken x, firstOpTok = null; Expression e0, e1, acc = null; BinaryExpr.Opcode op;
+ List<Expression> chain = null;
+ List<BinaryExpr.Opcode> ops = null;
+ int kind = 0; // 0 ("uncommitted") indicates chain of ==, possibly with one !=
+ // 1 ("ascending") indicates chain of ==, <, <=, possibly with one !=
+ // 2 ("descending") indicates chain of ==, >, >=, possibly with one !=
+ // 3 ("illegal") indicates illegal chain
+ // 4 ("disjoint") indicates chain of disjoint set operators
+ bool hasSeenNeq = false;
+ .)
+ Term<out e0> (. e = e0; .)
+ [ RelOp<out x, out op> (. firstOpTok = x; .)
+ Term<out e1> (. e = new BinaryExpr(x, op, e0, e1);
+ if (op == BinaryExpr.Opcode.Disjoint)
+ acc = new BinaryExpr(x, BinaryExpr.Opcode.Add, e0, e1); // accumulate first two operands.
+ .)
+ { (. if (chain == null) {
+ chain = new List<Expression>();
+ ops = new List<BinaryExpr.Opcode>();
+ chain.Add(e0); ops.Add(op); chain.Add(e1);
+ switch (op) {
+ case BinaryExpr.Opcode.Eq:
+ kind = 0; break;
+ case BinaryExpr.Opcode.Neq:
+ kind = 0; hasSeenNeq = true; break;
+ case BinaryExpr.Opcode.Lt:
+ case BinaryExpr.Opcode.Le:
+ kind = 1; break;
+ case BinaryExpr.Opcode.Gt:
+ case BinaryExpr.Opcode.Ge:
+ kind = 2; break;
+ case BinaryExpr.Opcode.Disjoint:
+ kind = 4; break;
+ default:
+ kind = 3; break;
+ }
+ }
+ e0 = e1;
+ .)
+ RelOp<out x, out op> (. switch (op) {
+ case BinaryExpr.Opcode.Eq:
+ if (kind != 0 && kind != 1 && kind != 2) { SemErr(x, "chaining not allowed from the previous operator"); }
+ break;
+ case BinaryExpr.Opcode.Neq:
+ if (hasSeenNeq) { SemErr(x, "a chain cannot have more than one != operator"); }
+ if (kind != 0 && kind != 1 && kind != 2) { SemErr(x, "this operator cannot continue this chain"); }
+ hasSeenNeq = true; break;
+ case BinaryExpr.Opcode.Lt:
+ case BinaryExpr.Opcode.Le:
+ if (kind == 0) { kind = 1; }
+ else if (kind != 1) { SemErr(x, "this operator chain cannot continue with an ascending operator"); }
+ break;
+ case BinaryExpr.Opcode.Gt:
+ case BinaryExpr.Opcode.Ge:
+ if (kind == 0) { kind = 2; }
+ else if (kind != 2) { SemErr(x, "this operator chain cannot continue with a descending operator"); }
+ break;
+ case BinaryExpr.Opcode.Disjoint:
+ if (kind != 4) { SemErr(x, "can only chain disjoint (!!) with itself."); kind = 3; }
+ break;
+ default:
+ SemErr(x, "this operator cannot be part of a chain");
+ kind = 3; break;
+ }
+ .)
+ Term<out e1> (. ops.Add(op); chain.Add(e1);
+ if (op == BinaryExpr.Opcode.Disjoint) {
+ e = new BinaryExpr(x, BinaryExpr.Opcode.And, e, new BinaryExpr(x, op, acc, e1));
+ acc = new BinaryExpr(x, BinaryExpr.Opcode.Add, acc, e1); //e0 has already been added.
+ }
+ else
+ e = new BinaryExpr(x, BinaryExpr.Opcode.And, e, new BinaryExpr(x, op, e0, e1));
+ .)
+ }
+ ]
+ (. if (chain != null) {
+ e = new ChainingExpression(firstOpTok, chain, ops, e);
+ }
+ .)
+ .
+RelOp<out IToken/*!*/ x, out BinaryExpr.Opcode op>
+= (. Contract.Ensures(Contract.ValueAtReturn(out x) != null);
+ x = Token.NoToken; op = BinaryExpr.Opcode.Add/*(dummy)*/;
+ IToken y;
+ .)
+ ( "==" (. x = t; op = BinaryExpr.Opcode.Eq; .)
+ | "<" (. x = t; op = BinaryExpr.Opcode.Lt; .)
+ | ">" (. x = t; op = BinaryExpr.Opcode.Gt; .)
+ | "<=" (. x = t; op = BinaryExpr.Opcode.Le; .)
+ | ">=" (. x = t; op = BinaryExpr.Opcode.Ge; .)
+ | "!=" (. x = t; op = BinaryExpr.Opcode.Neq; .)
+ | "!!" (. x = t; op = BinaryExpr.Opcode.Disjoint; .)
+ | "in" (. x = t; op = BinaryExpr.Opcode.In; .)
+ | /* The next operator is "!in", but Coco evidently parses "!in" even when it is a prefix of, say, "!initialized".
+ * The reason for this seems to be that the first character of "!in" is not a letter. So, here is the workaround:
+ */
+ "!" (. x = t; y = Token.NoToken; .)
+ [ "in" (. y = t; .)
+ ] (. if (y == Token.NoToken) {
+ SemErr(x, "invalid RelOp");
+ } else if (y.pos != x.pos + 1) {
+ SemErr(x, "invalid RelOp (perhaps you intended \"!in\" with no intervening whitespace?)");
+ } else {
+ x.val = "!in";
+ op = BinaryExpr.Opcode.NotIn;
+ }
+ .)
+ | '\u2260' (. x = t; op = BinaryExpr.Opcode.Neq; .)
+ | '\u2264' (. x = t; op = BinaryExpr.Opcode.Le; .)
+ | '\u2265' (. x = t; op = BinaryExpr.Opcode.Ge; .)
+ )
+ .
+/*------------------------------------------------------------------------*/
+Term<out Expression/*!*/ e0>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e0) != null); IToken/*!*/ x; Expression/*!*/ e1; BinaryExpr.Opcode op; .)
+ Factor<out e0>
+ { AddOp<out x, out op>
+ Factor<out e1> (. e0 = new BinaryExpr(x, op, e0, e1); .)
+ }
+ .
+AddOp<out IToken/*!*/ x, out BinaryExpr.Opcode op>
+= (. Contract.Ensures(Contract.ValueAtReturn(out x) != null); x = Token.NoToken; op=BinaryExpr.Opcode.Add/*(dummy)*/; .)
+ ( "+" (. x = t; op = BinaryExpr.Opcode.Add; .)
+ | "-" (. x = t; op = BinaryExpr.Opcode.Sub; .)
+ )
+ .
+/*------------------------------------------------------------------------*/
+Factor<out Expression/*!*/ e0>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e0) != null); IToken/*!*/ x; Expression/*!*/ e1; BinaryExpr.Opcode op; .)
+ UnaryExpression<out e0>
+ { MulOp<out x, out op>
+ UnaryExpression<out e1> (. e0 = new BinaryExpr(x, op, e0, e1); .)
+ }
+ .
+MulOp<out IToken/*!*/ x, out BinaryExpr.Opcode op>
+= (. Contract.Ensures(Contract.ValueAtReturn(out x) != null); x = Token.NoToken; op = BinaryExpr.Opcode.Add/*(dummy)*/; .)
+ ( "*" (. x = t; op = BinaryExpr.Opcode.Mul; .)
+ | "/" (. x = t; op = BinaryExpr.Opcode.Div; .)
+ | "%" (. x = t; op = BinaryExpr.Opcode.Mod; .)
+ )
+ .
+/*------------------------------------------------------------------------*/
+UnaryExpression<out Expression/*!*/ e>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null); IToken/*!*/ x; e = dummyExpr; .)
+ ( "-" (. x = t; .)
+ UnaryExpression<out e> (. e = new BinaryExpr(x, BinaryExpr.Opcode.Sub, new LiteralExpr(x, 0), e); .)
+ | NegOp (. x = t; .)
+ UnaryExpression<out e> (. e = new UnaryExpr(x, UnaryExpr.Opcode.Not, e); .)
+ | EndlessExpression<out e> /* these have no further suffix */
+ | DottedIdentifiersAndFunction<out e>
+ { Suffix<ref e> }
+ | DisplayExpr<out e>
+ { Suffix<ref e> }
+ | MultiSetExpr<out e>
+ { Suffix<ref e> }
+ | "map" (. x = t; .)
+ ( MapDisplayExpr<x, out e>
+ { Suffix<ref e> }
+ | MapComprehensionExpr<x, out e>
+ | (. SemErr("map must be followed by literal in brackets or comprehension."); .)
+ )
+ | ConstAtomExpression<out e>
+ { Suffix<ref e> }
+ )
+ .
+Lhs<out Expression e>
+= (. e = dummyExpr; // the assignment is to please the compiler, the dummy value to satisfy contracts in the event of a parse error
+ .)
+ ( DottedIdentifiersAndFunction<out e>
+ { Suffix<ref e> }
+ | ConstAtomExpression<out e>
+ Suffix<ref e>
+ { Suffix<ref e> }
+ )
+ .
+NegOp = "!" | '\u00ac'.
+/* A ConstAtomExpression is never an l-value. Also, a ConstAtomExpression is never followed by
+ * an open paren (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.)
+ */
+ConstAtomExpression<out Expression/*!*/ e>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null);
+ IToken/*!*/ x; BigInteger n;
+ e = dummyExpr;
+ .)
+ ( "false" (. e = new LiteralExpr(t, false); .)
+ | "true" (. e = new LiteralExpr(t, true); .)
+ | "null" (. e = new LiteralExpr(t); .)
+ | Nat<out n> (. e = new LiteralExpr(t, n); .)
+ | "this" (. e = new ThisExpr(t); .)
+ | "fresh" (. x = t; .)
+ "(" Expression<out e> ")" (. e = new FreshExpr(x, e); .)
+ | "old" (. x = t; .)
+ "(" Expression<out e> ")" (. e = new OldExpr(x, e); .)
+ | "|" (. x = t; .)
+ Expression<out e> (. e = new UnaryExpr(x, UnaryExpr.Opcode.SeqLength, e); .)
+ "|"
+ | "(" (. x = t; .)
+ Expression<out e> (. e = new ParensExpression(x, e); .)
+ ")"
+ )
+ .
+DisplayExpr<out Expression e>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null);
+ IToken/*!*/ x = null; List<Expression/*!*/>/*!*/ elements;
+ e = dummyExpr;
+ .)
+ ( "{" (. x = t; elements = new List<Expression/*!*/>(); .)
+ [ Expressions<elements> ] (. e = new SetDisplayExpr(x, elements);.)
+ "}"
+ | "[" (. x = t; elements = new List<Expression/*!*/>(); .)
+ [ Expressions<elements> ] (. e = new SeqDisplayExpr(x, elements); .)
+ "]"
+ )
+ .
+MultiSetExpr<out Expression e>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null);
+ IToken/*!*/ x = null; List<Expression/*!*/>/*!*/ elements;
+ e = dummyExpr;
+ .)
+ "multiset" (. x = t; .)
+ ( "{" (. elements = new List<Expression/*!*/>(); .)
+ [ Expressions<elements> ] (. e = new MultiSetDisplayExpr(x, elements);.)
+ "}"
+ | "(" (. x = t; elements = new List<Expression/*!*/>(); .)
+ Expression<out e> (. e = new MultiSetFormingExpr(x, e); .)
+ ")"
+ | (. SemErr("multiset must be followed by multiset literal or expression to coerce in parentheses."); .)
+ )
+ .
+MapDisplayExpr<IToken/*!*/ mapToken, out Expression e>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null);
+ List<ExpressionPair/*!*/>/*!*/ elements= new List<ExpressionPair/*!*/>() ;
+ e = dummyExpr;
+ .)
+ "["
+ [ MapLiteralExpressions<out elements> ] (. e = new MapDisplayExpr(mapToken, elements);.)
+ "]"
+ .
+MapLiteralExpressions<.out List<ExpressionPair> elements.>
+= (. Expression/*!*/ d, r;
+ elements = new List<ExpressionPair/*!*/>(); .)
+ Expression<out d> ":=" Expression<out r> (. elements.Add(new ExpressionPair(d,r)); .)
+ { "," Expression<out d> ":=" Expression<out r>(. elements.Add(new ExpressionPair(d,r)); .)
+ }
+ .
+MapComprehensionExpr<IToken/*!*/ mapToken, out Expression e>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null);
+ BoundVar/*!*/ bv;
+ List<BoundVar/*!*/> bvars = new List<BoundVar/*!*/>();
+ Expression range = null;
+ Expression body;
+ .)
+ IdentTypeOptional<out bv> (. bvars.Add(bv); .)
+ [ "|" Expression<out range> ]
+ QSep
+ Expression<out body>
+ (. e = new MapComprehension(mapToken, bvars, range ?? new LiteralExpr(mapToken, true), body);
+ .)
+ .
+EndlessExpression<out Expression e>
+= (. IToken/*!*/ x;
+ Expression e0, e1;
+ e = dummyExpr;
+ .)
+ ( "if" (. x = t; .)
+ Expression<out e>
+ "then" Expression<out e0>
+ "else" Expression<out e1> (. e = new ITEExpr(x, e, e0, e1); .)
+ | MatchExpression<out e>
+ | QuantifierGuts<out e>
+ | ComprehensionExpr<out e>
+ | "assert" (. x = t; .)
+ Expression<out e0> ";"
+ Expression<out e1> (. e = new AssertExpr(x, e0, e1); .)
+ | "assume" (. x = t; .)
+ Expression<out e0> ";"
+ Expression<out e1> (. e = new AssumeExpr(x, e0, e1); .)
+ | LetExpr<out e>
+ | NamedExpr<out e>
+ )
+ .
+
+LetExpr<out Expression e>
+= (. IToken/*!*/ x;
+ e = dummyExpr;
+ BoundVar d;
+ List<BoundVar> letVars; List<Expression> letRHSs;
+ .)
+ "var" (. x = t;
+ letVars = new List<BoundVar>();
+ letRHSs = new List<Expression>(); .)
+ IdentTypeOptional<out d> (. letVars.Add(d); .)
+ { "," IdentTypeOptional<out d> (. letVars.Add(d); .)
+ }
+ ":="
+ Expression<out e> (. letRHSs.Add(e); .)
+ { "," Expression<out e> (. letRHSs.Add(e); .)
+ }
+ ";"
+ Expression<out e> (. e = new LetExpr(x, letVars, letRHSs, e); .)
+ .
+
+NamedExpr<out Expression e>
+= (. IToken/*!*/ x, d;
+ e = dummyExpr;
+ Expression expr;
+ .)
+ "label" (. x = t; .)
+ NoUSIdent<out d>
+ ":"
+ Expression<out e> (. expr = e;
+ e = new NamedExpr(x, d.val, expr); .)
+ .
+
+MatchExpression<out Expression/*!*/ e>
+= (. Contract.Ensures(Contract.ValueAtReturn(out e) != null); IToken/*!*/ x; MatchCaseExpr/*!*/ c;
+ List<MatchCaseExpr/*!*/> cases = new List<MatchCaseExpr/*!*/>();
+ .)
+ "match" (. x = t; .)
+ Expression<out e>
+ /* Note: The following gives rise to a '"case" is start & successor of deletable structure' error,
+ but it's okay, because we want this closer match expression to bind as much as possible--use
+ parens around it to limit its scope. */
+ { CaseExpression<out c> (. cases.Add(c); .)
+ }
+ (. e = new MatchExpr(x, e, cases); .)
+ .
+CaseExpression<out MatchCaseExpr/*!*/ c>
+= (. Contract.Ensures(Contract.ValueAtReturn(out c) != null); IToken/*!*/ x, id;
+ List<BoundVar/*!*/> arguments = new List<BoundVar/*!*/>();
+ BoundVar/*!*/ bv;
+ Expression/*!*/ body;
+ .)
+ "case" (. x = t; .)
+ Ident<out id>
+ [ "("
+ IdentTypeOptional<out bv> (. arguments.Add(bv); .)
+ { "," IdentTypeOptional<out bv> (. arguments.Add(bv); .)
+ }
+ ")" ]
+ "=>"
+ Expression<out body> (. c = new MatchCaseExpr(x, id.val, arguments, body); .)
+ .
+/*------------------------------------------------------------------------*/
+DottedIdentifiersAndFunction<out Expression e>
+= (. IToken id; IToken openParen = null;
+ List<Expression> args = null;
+ List<IToken> idents = new List<IToken>();
+ .)
+ Ident<out id> (. idents.Add(id); .)
+ { "."
+ Ident<out id> (. idents.Add(id); .)
+ }
+ [ "(" (. openParen = t; args = new List<Expression>(); .)
+ [ Expressions<args> ]
+ ")"
+ ]
+ (. e = new IdentifierSequence(idents, openParen, args); .)
+ .
+Suffix<ref Expression/*!*/ e>
+= (. Contract.Requires(e != null); Contract.Ensures(e!=null); IToken/*!*/ id, x; List<Expression/*!*/>/*!*/ args;
+ Expression e0 = null; Expression e1 = null; Expression/*!*/ ee; bool anyDots = false;
+ List<Expression> multipleIndices = null;
+ bool func = false;
+ .)
+ ( "."
+ Ident<out id>
+ [ "(" (. IToken openParen = t; args = new List<Expression/*!*/>(); func = true; .)
+ [ Expressions<args> ]
+ ")" (. e = new FunctionCallExpr(id, id.val, e, openParen, args); .)
+ ] (. if (!func) { e = new ExprDotName(id, e, id.val); } .)
+ | "[" (. x = t; .)
+ ( Expression<out ee> (. e0 = ee; .)
+ ( ".." (. anyDots = true; .)
+ [ Expression<out ee> (. e1 = ee; .)
+ ]
+ | ":="
+ Expression<out ee> (. e1 = ee; .)
+ | { "," Expression<out ee> (. if (multipleIndices == null) {
+ multipleIndices = new List<Expression>();
+ multipleIndices.Add(e0);
+ }
+ multipleIndices.Add(ee);
+ .)
+ }
+ )
+ | ".." (. anyDots = true; .)
+ [ Expression<out ee> (. e1 = ee; .)
+ ]
+ )
+ (. if (multipleIndices != null) {
+ e = new MultiSelectExpr(x, e, multipleIndices);
+ // make sure an array class with this dimensionality exists
+ UserDefinedType tmp = theBuiltIns.ArrayType(x, multipleIndices.Count, new IntType(), true);
+ } else {
+ if (!anyDots && e0 == null) {
+ /* a parsing error occurred */
+ e0 = dummyExpr;
+ }
+ Contract.Assert(anyDots || e0 != null);
+ if (anyDots) {
+ //Contract.Assert(e0 != null || e1 != null);
+ e = new SeqSelectExpr(x, false, e, e0, e1);
+ } else if (e1 == null) {
+ Contract.Assert(e0 != null);
+ e = new SeqSelectExpr(x, true, e, e0, null);
+ } else {
+ Contract.Assert(e0 != null);
+ e = new SeqUpdateExpr(x, e, e0, e1);
+ }
+ }
+ .)
+ "]"
+ )
+ .
+/*------------------------------------------------------------------------*/
+QuantifierGuts<out Expression/*!*/ q>
+= (. Contract.Ensures(Contract.ValueAtReturn(out q) != null); IToken/*!*/ x = Token.NoToken;
+ bool univ = false;
+ List<BoundVar/*!*/> bvars;
+ Attributes attrs;
+ Expression range;
+ Expression/*!*/ body;
+ .)
+ ( Forall (. x = t; univ = true; .)
+ | Exists (. x = t; .)
+ )
+ QuantifierDomain<out bvars, out attrs, out range>
+ QSep
+ Expression<out body>
+ (. if (univ) {
+ q = new ForallExpr(x, bvars, range, body, attrs);
+ } else {
+ q = new ExistsExpr(x, bvars, range, body, attrs);
+ }
+ .)
+ .
+
+Forall = "forall" | '\u2200'.
+Exists = "exists" | '\u2203'.
+QSep = "::" | '\u2022'.
+
+QuantifierDomain<.out List<BoundVar/*!*/> bvars, out Attributes attrs, out Expression range.>
+= (.
+ bvars = new List<BoundVar/*!*/>();
+ BoundVar/*!*/ bv;
+ attrs = null;
+ range = null;
+ .)
+ IdentTypeOptional<out bv> (. bvars.Add(bv); .)
+ { ","
+ IdentTypeOptional<out bv> (. bvars.Add(bv); .)
+ }
+ { Attribute<ref attrs> }
+ [ "|"
+ Expression<out range>
+ ]
+ .
+
+ComprehensionExpr<out Expression/*!*/ q>
+= (. Contract.Ensures(Contract.ValueAtReturn(out q) != null);
+ IToken/*!*/ x = Token.NoToken;
+ BoundVar/*!*/ bv;
+ List<BoundVar/*!*/> bvars = new List<BoundVar/*!*/>();
+ Expression/*!*/ range;
+ Expression body = null;
+ .)
+ "set" (. x = t; .)
+ IdentTypeOptional<out bv> (. bvars.Add(bv); .)
+ { ","
+ IdentTypeOptional<out bv> (. bvars.Add(bv); .)
+ }
+ "|" Expression<out range>
+ [
+ QSep
+ Expression<out body>
+ ]
+ (. 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);
+ .)
+ .
+Expressions<.List<Expression/*!*/>/*!*/ args.>
+= (. Contract.Requires(cce.NonNullElements(args)); Expression/*!*/ e; .)
+ Expression<out e> (. args.Add(e); .)
+ { "," Expression<out e> (. args.Add(e); .)
+ }
+ .
+/*------------------------------------------------------------------------*/
+Attribute<ref Attributes attrs>
+= "{"
+ AttributeBody<ref attrs>
+ "}"
+ .
+AttributeBody<ref Attributes attrs>
+= (. string aName;
+ List<Attributes.Argument/*!*/> aArgs = new List<Attributes.Argument/*!*/>();
+ Attributes.Argument/*!*/ aArg;
+ .)
+ ":" ident (. aName = t.val; .)
+ [ AttributeArg<out aArg> (. aArgs.Add(aArg); .)
+ { "," AttributeArg<out aArg> (. aArgs.Add(aArg); .)
+ }
+ ] (. attrs = new Attributes(aName, aArgs, attrs); .)
+ .
+AttributeArg<out Attributes.Argument/*!*/ arg>
+= (. Contract.Ensures(Contract.ValueAtReturn(out arg) != null); Expression/*!*/ e; arg = dummyAttrArg; .)
+ ( string (. arg = new Attributes.Argument(t, t.val.Substring(1, t.val.Length-2)); .)
+ | Expression<out e> (. arg = new Attributes.Argument(t, e); .)
+ )
+ .
+/*------------------------------------------------------------------------*/
+Ident<out IToken/*!*/ x>
+= (. Contract.Ensures(Contract.ValueAtReturn(out x) != null); .)
+ ident (. x = t; .)
+ .
+// Identifier, disallowing leading underscores
+NoUSIdent<out IToken/*!*/ x>
+= (. Contract.Ensures(Contract.ValueAtReturn(out x) != null); .)
+ ident (. x = t;
+ if (x.val.StartsWith("_")) {
+ SemErr("cannot declare identifier beginning with underscore");
+ }
+ .)
+ .
+
+// Identifier, disallowing leading underscores, except possibly the "wildcard" identifier "_"
+WildIdent<out IToken/*!*/ x, bool allowWildcardId>
+= (. Contract.Ensures(Contract.ValueAtReturn(out x) != null); .)
+ ident (. x = t;
+ if (x.val.StartsWith("_")) {
+ if (allowWildcardId && x.val.Length == 1) {
+ t.val = "_v" + anonymousIds++;
+ } else {
+ SemErr("cannot declare identifier beginning with underscore");
+ }
+ }
+ .)
+ .
+
+Nat<out BigInteger n>
+=
+ digits
+ (. try {
+ n = BigInteger.Parse(t.val);
+ } catch (System.FormatException) {
+ SemErr("incorrectly formatted number");
+ n = BigInteger.Zero;
+ }
+ .)
+ .
+END Dafny.
diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs
new file mode 100644
index 00000000..345fc6fa
--- /dev/null
+++ b/Source/Dafny/DafnyAst.cs
@@ -0,0 +1,4472 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Text;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Numerics;
+using Microsoft.Boogie;
+
+namespace Microsoft.Dafny {
+ public class Program {
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Name != null);
+ Contract.Invariant(DefaultModule != null);
+ }
+
+ public readonly string Name;
+ public List<ModuleDefinition/*!*/>/*!*/ Modules; // filled in during resolution.
+ // Resolution essentially flattens the module hierarchy, for
+ // purposes of translation and compilation.
+ public List<ModuleDefinition> CompileModules; // filled in during resolution.
+ // Contains the definitions to be used for compilation.
+
+ 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) {
+ Contract.Requires(name != null);
+ Contract.Requires(module != null);
+ Contract.Requires(module is LiteralModuleDecl);
+ Name = name;
+ DefaultModule = module;
+ DefaultModuleDef = (DefaultModuleDecl)((LiteralModuleDecl)module).ModuleDef;
+ BuiltIns = builtIns;
+ Modules = new List<ModuleDefinition>();
+ CompileModules = new List<ModuleDefinition>();
+ TranslationTasks = new List<TranslationTask>();
+ }
+ }
+
+ public class BuiltIns
+ {
+ public readonly ModuleDefinition SystemModule = new ModuleDefinition(Token.NoToken, "_System", false, false, null, null, true);
+ Dictionary<int, ClassDecl/*!*/> arrayTypeDecls = new Dictionary<int, ClassDecl>();
+ public readonly ClassDecl ObjectDecl;
+ public BuiltIns() {
+ // create class 'object'
+ ObjectDecl = new ClassDecl(Token.NoToken, "object", SystemModule, new List<TypeParameter>(), new List<MemberDecl>(), null);
+ SystemModule.TopLevelDecls.Add(ObjectDecl);
+ // add one-dimensional arrays, since they may arise during type checking
+ UserDefinedType tmp = ArrayType(Token.NoToken, 1, Type.Int, true);
+ }
+
+ public UserDefinedType ArrayType(int dims, Type arg) {
+ return ArrayType(Token.NoToken, dims, arg, false);
+ }
+ public UserDefinedType ArrayType(IToken tok, int dims, Type arg, bool allowCreationOfNewClass) {
+ Contract.Requires(tok != null);
+ Contract.Requires(1 <= dims);
+ Contract.Requires(arg != null);
+ Contract.Ensures(Contract.Result<UserDefinedType>() != null);
+
+ List<Type/*!*/> typeArgs = new List<Type/*!*/>();
+ typeArgs.Add(arg);
+ UserDefinedType udt = new UserDefinedType(tok, ArrayClassName(dims), typeArgs, null);
+ if (allowCreationOfNewClass && !arrayTypeDecls.ContainsKey(dims)) {
+ ArrayClassDecl arrayClass = new ArrayClassDecl(dims, SystemModule);
+ for (int d = 0; d < dims; d++) {
+ string name = dims == 1 ? "Length" : "Length" + d;
+ string compiledName = dims == 1 ? "Length" : "GetLength(" + d + ")";
+ Field len = new SpecialField(Token.NoToken, name, compiledName, "new BigInteger(", ")", false, false, false, Type.Int, null);
+ len.EnclosingClass = arrayClass; // resolve here
+ arrayClass.Members.Add(len);
+ }
+ arrayTypeDecls.Add(dims, arrayClass);
+ SystemModule.TopLevelDecls.Add(arrayClass);
+ }
+ udt.ResolvedClass = arrayTypeDecls[dims];
+ return udt;
+ }
+
+ public static string ArrayClassName(int dims) {
+ Contract.Requires(1 <= dims);
+ if (dims == 1) {
+ return "array";
+ } else {
+ return "array" + dims;
+ }
+ }
+ }
+
+ public class Attributes {
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Name != null);
+ Contract.Invariant(cce.NonNullElements(Args));
+ }
+
+ public readonly string Name;
+ /*Frozen*/
+ public readonly List<Argument/*!*/>/*!*/ Args;
+ public readonly Attributes Prev;
+
+ public Attributes(string name, [Captured] List<Argument/*!*/>/*!*/ args, Attributes prev) {
+ Contract.Requires(name != null);
+ Contract.Requires(cce.NonNullElements(args));
+ Name = name;
+ Args = args;
+ Prev = prev;
+ }
+
+ 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;
+ }
+
+ /// <summary>
+ /// Returns true if "nm" is a specified attribute. If it is, then:
+ /// - if the attribute is {:nm true}, then value==true
+ /// - if the attribute is {:nm false}, then value==false
+ /// - if the attribute is anything else, then value returns as whatever it was passed in as.
+ /// </summary>
+ 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].E as LiteralExpr;
+ if (arg != null && arg.Value is bool) {
+ value = (bool)arg.Value;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public class Argument
+ {
+ public readonly IToken Tok;
+ public readonly string S;
+ public readonly Expression E;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Tok != null);
+ Contract.Invariant((S == null) != (E == null));
+ }
+
+ public Argument(IToken tok, string s) {
+ Contract.Requires(tok != null);
+ Contract.Requires(s != null);
+ Tok = tok;
+ S = s;
+ }
+ public Argument(IToken tok, Expression e) {
+ Contract.Requires(tok != null);
+ Contract.Requires(e != null);
+ Tok = tok;
+ E = e;
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------------
+
+ public abstract class Type {
+ public static readonly BoolType Bool = new BoolType();
+ public static readonly IntType Int = new IntType();
+ /// <summary>
+ /// Used in error situations in order to reduce further error messages.
+ /// </summary>
+ //[Pure(false)]
+ public static Type Flexible {
+ get {
+ Contract.Ensures(Contract.Result<Type>() != null);
+ return new InferredTypeProxy();
+ }
+ }
+
+ [Pure]
+ public abstract string TypeName(ModuleDefinition/*?*/ context);
+ [Pure]
+ public override string ToString() {
+ return TypeName(null);
+ }
+
+ /// <summary>
+ /// Return the most constrained version of "this".
+ /// </summary>
+ /// <returns></returns>
+ public Type Normalize() {
+ Contract.Ensures(Contract.Result<Type>() != null);
+ Type type = this;
+ while (true) {
+ TypeProxy pt = type as TypeProxy;
+ if (pt != null && pt.T != null) {
+ type = pt.T;
+ } else {
+ return type;
+ }
+ }
+ }
+
+ public bool IsSubrangeType {
+ get { return this is NatType; }
+ }
+
+ public bool IsRefType {
+ get {
+ if (this is ObjectType) {
+ return true;
+ } else {
+ UserDefinedType udt = this as UserDefinedType;
+ return udt != null && udt.ResolvedParam == null && udt.ResolvedClass is ClassDecl;
+ }
+ }
+ }
+ public bool IsArrayType {
+ get {
+ return AsArrayType != null;
+ }
+ }
+ public ArrayClassDecl/*?*/ AsArrayType {
+ get {
+ UserDefinedType udt = UserDefinedType.DenotesClass(this);
+ return udt == null ? null : udt.ResolvedClass as ArrayClassDecl;
+ }
+ }
+ public bool IsDatatype {
+ get {
+ return AsDatatype != null;
+ }
+ }
+ public DatatypeDecl AsDatatype {
+ get {
+ UserDefinedType udt = this as UserDefinedType;
+ if (udt == null) {
+ return null;
+ } else {
+ return udt.ResolvedClass as DatatypeDecl;
+ }
+ }
+ }
+ public bool IsIndDatatype {
+ get {
+ return AsIndDatatype != null;
+ }
+ }
+ public IndDatatypeDecl AsIndDatatype {
+ get {
+ UserDefinedType udt = this as UserDefinedType;
+ if (udt == null) {
+ return null;
+ } else {
+ return udt.ResolvedClass as IndDatatypeDecl;
+ }
+ }
+ }
+ public bool IsCoDatatype {
+ get {
+ return AsCoDatatype != null;
+ }
+ }
+ public CoDatatypeDecl AsCoDatatype {
+ get {
+ UserDefinedType udt = this as UserDefinedType;
+ if (udt == null) {
+ return null;
+ } else {
+ return udt.ResolvedClass as CoDatatypeDecl;
+ }
+ }
+ }
+ public bool InvolvesCoDatatype {
+ get {
+ return IsCoDatatype; // TODO: should really check structure of the type recursively
+ }
+ }
+ public bool IsTypeParameter {
+ get {
+ return AsTypeParameter != null;
+ }
+ }
+ public TypeParameter AsTypeParameter {
+ get {
+ UserDefinedType ct = this as UserDefinedType;
+ return ct == null ? null : ct.ResolvedParam;
+ }
+ }
+ public virtual bool SupportsEquality {
+ get {
+ return true;
+ }
+ }
+ }
+
+ /// <summary>
+ /// A NonProxy type is a fully constrained type. It may contain members.
+ /// </summary>
+ public abstract class NonProxyType : Type
+ {
+ }
+
+ public abstract class BasicType : NonProxyType
+ {
+ }
+
+ public class BoolType : BasicType {
+ [Pure]
+ public override string TypeName(ModuleDefinition context) {
+ return "bool";
+ }
+ }
+
+ public class IntType : BasicType {
+ [Pure]
+ public override string TypeName(ModuleDefinition context) {
+ return "int";
+ }
+ }
+
+ public class NatType : IntType
+ {
+ [Pure]
+ public override string TypeName(ModuleDefinition context) {
+ return "nat";
+ }
+ }
+
+ public class ObjectType : BasicType
+ {
+ [Pure]
+ public override string TypeName(ModuleDefinition context) {
+ return "object";
+ }
+ }
+
+ public abstract class CollectionType : NonProxyType
+ {
+ public readonly Type Arg; // denotes the Domain type for a Map
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Arg != null);
+ }
+ public CollectionType(Type arg) {
+ Contract.Requires(arg != null);
+ this.Arg = arg;
+ }
+ public override bool SupportsEquality {
+ get {
+ return Arg.SupportsEquality;
+ }
+ }
+ }
+
+ public class SetType : CollectionType {
+ public SetType(Type arg) : base(arg) {
+ Contract.Requires(arg != null);
+ }
+ [Pure]
+ public override string TypeName(ModuleDefinition context) {
+ Contract.Ensures(Contract.Result<string>() != null);
+ Contract.Assume(cce.IsPeerConsistent(Arg));
+ return "set<" + base.Arg.TypeName(context) + ">";
+ }
+ }
+
+ public class MultiSetType : CollectionType
+ {
+ public MultiSetType(Type arg) : base(arg) {
+ Contract.Requires(arg != null);
+ }
+ [Pure]
+ public override string TypeName(ModuleDefinition context) {
+ Contract.Ensures(Contract.Result<string>() != null);
+ Contract.Assume(cce.IsPeerConsistent(Arg));
+ return "multiset<" + base.Arg.TypeName(context) + ">";
+ }
+ }
+
+ public class SeqType : CollectionType {
+ public SeqType(Type arg) : base(arg) {
+ Contract.Requires(arg != null);
+
+ }
+ [Pure]
+ public override string TypeName(ModuleDefinition context) {
+ Contract.Ensures(Contract.Result<string>() != null);
+ Contract.Assume(cce.IsPeerConsistent(Arg));
+ return "seq<" + base.Arg.TypeName(context) + ">";
+ }
+ }
+ public class MapType : CollectionType
+ {
+ public Type Range;
+ public MapType(Type domain, Type range) : base(domain) {
+ Contract.Requires(domain != null && range != null);
+ Range = range;
+ }
+ public Type Domain {
+ get { return Arg; }
+ }
+ [Pure]
+ public override string TypeName(ModuleDefinition context) {
+ Contract.Ensures(Contract.Result<string>() != null);
+ Contract.Assume(cce.IsPeerConsistent(Domain));
+ Contract.Assume(cce.IsPeerConsistent(Range));
+ return "map<" + Domain.TypeName(context) + ", " + Range.TypeName(context) + ">";
+ }
+ }
+
+ public class UserDefinedType : NonProxyType
+ {
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(tok != null);
+ Contract.Invariant(Name != null);
+ Contract.Invariant(cce.NonNullElements(TypeArgs));
+ Contract.Invariant(cce.NonNullElements(Path));
+ }
+
+ public readonly List<IToken> Path; // may be null
+ public readonly IToken tok; // token of the Name
+ public readonly string Name;
+ [Rep]
+ public readonly List<Type/*!*/>/*!*/ TypeArgs;
+
+ public string FullName {
+ get {
+ if (ResolvedClass != null && !ResolvedClass.Module.IsDefaultModule) {
+ return ResolvedClass.Module.Name + "." + Name;
+ } else {
+ return Name;
+ }
+ }
+ }
+
+ string compileName;
+ public string CompileName {
+ get {
+ if (compileName == null) {
+ compileName = NonglobalVariable.CompilerizeName(Name);
+ }
+ return compileName;
+ }
+ }
+ public string FullCompileName {
+ get {
+ if (ResolvedClass != null && !ResolvedClass.Module.IsDefaultModule) {
+ return ResolvedClass.Module.CompileName + "." + CompileName;
+ } else {
+ return CompileName;
+ }
+ }
+ }
+
+ public TopLevelDecl ResolvedClass; // filled in by resolution, if Name denotes a class/datatype/iterator and TypeArgs match the type parameters of that class/datatype/iterator
+ public TypeParameter ResolvedParam; // filled in by resolution, if Name denotes an enclosing type parameter and TypeArgs is the empty list
+
+ public UserDefinedType(IToken/*!*/ tok, string/*!*/ name, [Captured] List<Type/*!*/>/*!*/ typeArgs, List<IToken> moduleName) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(cce.NonNullElements(typeArgs));
+ Contract.Requires(moduleName == null || cce.NonNullElements(moduleName));
+ if (moduleName != null) this.Path = moduleName;
+ else this.Path = new List<IToken>();
+ this.tok = tok;
+ this.Name = name;
+ this.TypeArgs = typeArgs;
+ }
+
+ /// <summary>
+ /// This constructor constructs a resolved class/datatype/iterator type
+ /// </summary>
+ public UserDefinedType(IToken/*!*/ tok, string/*!*/ name, TopLevelDecl/*!*/ cd, [Captured] List<Type/*!*/>/*!*/ typeArgs) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(cd != null);
+ Contract.Requires(cce.NonNullElements(typeArgs));
+ this.tok = tok;
+ this.Name = name;
+ this.TypeArgs = typeArgs;
+ this.ResolvedClass = cd;
+ this.Path = new List<IToken>();
+ }
+
+ /// <summary>
+ /// This constructor constructs a resolved type parameter
+ /// </summary>
+ public UserDefinedType(IToken/*!*/ tok, string/*!*/ name, TypeParameter/*!*/ tp) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(tp != null);
+ this.tok = tok;
+ this.Name = name;
+ this.TypeArgs = new List<Type/*!*/>();
+ this.ResolvedParam = tp;
+ this.Path = new List<IToken>();
+ }
+
+ /// <summary>
+ /// If type denotes a resolved class type, then return that class type.
+ /// Otherwise, return null.
+ /// </summary>
+ public static UserDefinedType DenotesClass(Type/*!*/ type) {
+ Contract.Requires(type != null);
+ Contract.Ensures(Contract.Result<UserDefinedType>() == null || Contract.Result<UserDefinedType>().ResolvedClass is ClassDecl);
+ type = type.Normalize();
+ UserDefinedType ct = type as UserDefinedType;
+ if (ct != null && ct.ResolvedClass is ClassDecl) {
+ return ct;
+ } else {
+ return null;
+ }
+ }
+
+ public static Type ArrayElementType(Type type) {
+ Contract.Requires(type.IsArrayType);
+
+ Contract.Requires(type != null);
+ Contract.Ensures(Contract.Result<Type>() != null);
+
+ UserDefinedType udt = DenotesClass(type);
+ Contract.Assert(udt != null);
+ Contract.Assert(udt.TypeArgs.Count == 1); // holds true of all array types
+ return udt.TypeArgs[0];
+ }
+
+ [Pure]
+ public override string TypeName(ModuleDefinition context) {
+ Contract.Ensures(Contract.Result<string>() != null);
+ string s = "";
+ foreach (var t in Path) {
+ if (context != null && t == context.tok) {
+ // drop the prefix up to here
+ s = "";
+ } else {
+ s += t.val + ".";
+ }
+ }
+ s += Name;
+ if (TypeArgs.Count != 0) {
+ s += "<" + Util.Comma(",", TypeArgs, ty => ty.TypeName(context)) + ">";
+ }
+ return s;
+ }
+
+ public override bool SupportsEquality {
+ get {
+ if (ResolvedClass is ClassDecl) {
+ return true;
+ } else if (ResolvedClass is CoDatatypeDecl) {
+ return false;
+ } else if (ResolvedClass is IndDatatypeDecl) {
+ var dt = (IndDatatypeDecl)ResolvedClass;
+ Contract.Assume(dt.EqualitySupport != IndDatatypeDecl.ES.NotYetComputed);
+ if (dt.EqualitySupport == IndDatatypeDecl.ES.Never) {
+ return false;
+ }
+ Contract.Assert(dt.TypeArgs.Count == TypeArgs.Count);
+ var i = 0;
+ foreach (var tp in dt.TypeArgs) {
+ if (tp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype && !TypeArgs[i].SupportsEquality) {
+ return false;
+ }
+ i++;
+ }
+ return true;
+ } else if (ResolvedParam != null) {
+ return ResolvedParam.MustSupportEquality;
+ }
+ Contract.Assume(false); // the SupportsEquality getter requires the Type to have been successfully resolved
+ return true;
+ }
+ }
+ }
+
+ public abstract class TypeProxy : Type {
+ public Type T; // filled in during resolution
+ internal TypeProxy() {
+ }
+
+ [Pure]
+ public override string TypeName(ModuleDefinition context) {
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ Contract.Assume(T == null || cce.IsPeerConsistent(T));
+ return T == null ? "?" : T.TypeName(context);
+ }
+ public override bool SupportsEquality {
+ get {
+ if (T != null) {
+ return T.SupportsEquality;
+ } else {
+ return base.SupportsEquality;
+ }
+ }
+ }
+ }
+
+ public abstract class UnrestrictedTypeProxy : TypeProxy {
+ }
+
+ /// <summary>
+ /// This proxy stands for any type.
+ /// </summary>
+ public class InferredTypeProxy : UnrestrictedTypeProxy {
+ }
+
+ /// <summary>
+ /// This proxy stands for any type, but it originates from an instantiated type parameter.
+ /// </summary>
+ public class ParamTypeProxy : UnrestrictedTypeProxy {
+ public TypeParameter orig;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(orig != null);
+ }
+
+ public ParamTypeProxy(TypeParameter orig) {
+ Contract.Requires(orig != null);
+ this.orig = orig;
+ }
+ }
+
+ public abstract class RestrictedTypeProxy : TypeProxy {
+ /// <summary>
+ /// The OrderID is used to simplify the unification code. Each restricted type proxy should use its
+ /// own OrderID.
+ /// </summary>
+ public abstract int OrderID {
+ get;
+ }
+ }
+
+ /// <summary>
+ /// This proxy stands for any datatype.
+ /// </summary>
+ public class DatatypeProxy : RestrictedTypeProxy {
+ public override int OrderID {
+ get {
+ return 0;
+ }
+ }
+ }
+
+ /// <summary>
+ /// This proxy stands for object or any class/array type.
+ /// </summary>
+ public class ObjectTypeProxy : RestrictedTypeProxy {
+ public override int OrderID {
+ get {
+ return 1;
+ }
+ }
+ }
+
+ /// <summary>
+ /// This proxy stands for:
+ /// set(Arg) or seq(Arg) or map(Arg, Range)
+ /// </summary>
+ public class CollectionTypeProxy : RestrictedTypeProxy {
+ public readonly Type Arg;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Arg != null);
+ }
+
+ public CollectionTypeProxy(Type arg) {
+ Contract.Requires(arg != null);
+ Arg = arg;
+ }
+ public override int OrderID {
+ get {
+ return 2;
+ }
+ }
+ }
+
+ /// <summary>
+ /// This proxy stands for either:
+ /// int or set or multiset or seq
+ /// if AllowSeq, or:
+ /// int or set or multiset
+ /// if !AllowSeq.
+ /// </summary>
+ public class OperationTypeProxy : RestrictedTypeProxy {
+ public readonly bool AllowSeq;
+ public OperationTypeProxy(bool allowSeq) {
+ AllowSeq = allowSeq;
+ }
+ public override int OrderID {
+ get {
+ return 3;
+ }
+ }
+ }
+
+ /// <summary>
+ /// This proxy stands for:
+ /// seq(Arg) or array(Arg) or map(Arg, Range)
+ /// </summary>
+ public class IndexableTypeProxy : RestrictedTypeProxy {
+ public readonly Type Arg, Domain;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Arg != null);
+ }
+
+ public IndexableTypeProxy(Type arg, Type domain) {
+ Contract.Requires(arg != null);
+ Arg = arg;
+ Domain = domain;
+ }
+ public override int OrderID {
+ get {
+ return 4;
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------------
+
+ public abstract class Declaration {
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(tok != null);
+ Contract.Invariant(Name != null);
+ }
+
+ public IToken/*!*/ tok;
+ public IToken BodyStartTok = Token.NoToken;
+ public IToken BodyEndTok = Token.NoToken;
+ public readonly string/*!*/ Name;
+ string compileName;
+ public virtual string CompileName {
+ get {
+ if (compileName == null) {
+ compileName = NonglobalVariable.CompilerizeName(Name);
+ }
+ return compileName;
+ }
+ }
+ public readonly Attributes Attributes;
+
+ public Declaration(IToken tok, string name, Attributes attributes) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ this.tok = tok;
+ this.Name = name;
+ this.Attributes = attributes;
+ }
+
+ [Pure]
+ public override string ToString() {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return Name;
+ }
+ }
+
+ public class TypeParameter : Declaration {
+ public interface ParentType {
+ }
+ [Peer]
+ ParentType parent;
+ public ParentType Parent {
+ get {
+ return parent;
+ }
+ [param: Captured]
+ 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);
+ //modifies parent;
+ parent = value;
+ }
+ }
+ public enum EqualitySupportValue { Required, InferredRequired, Unspecified }
+ public EqualitySupportValue EqualitySupport; // the resolver may change this value from Unspecified to InferredRequired (for some signatures that may immediately imply that equality support is required)
+ public bool MustSupportEquality {
+ get { return EqualitySupport != EqualitySupportValue.Unspecified; }
+ }
+
+ public bool NecessaryForEqualitySupportOfSurroundingInductiveDatatype = false; // computed during resolution; relevant only when Parent denotes an IndDatatypeDecl
+
+ public bool IsAbstractTypeDeclaration { // true if this type parameter represents t in type t;
+ get { return parent == null; }
+ }
+ public bool IsToplevelScope { // true if this type parameter is on a toplevel (ie. class C<T>), and false if it is on a member (ie. method m<T>(...))
+ get { return parent is TopLevelDecl; }
+ }
+ 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) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ EqualitySupport = equalitySupport;
+ }
+ }
+
+ // Represents a submodule declaration at module level scope
+ abstract public class ModuleDecl : TopLevelDecl
+ {
+ public ModuleSignature Signature; // filled in by resolution, in topological order.
+ public int Height;
+ public readonly bool Opened;
+ public ModuleDecl(IToken tok, string name, ModuleDefinition parent, bool opened)
+ : base(tok, name, parent, new List<TypeParameter>(), null) {
+ Height = -1;
+ Signature = null;
+ Opened = opened;
+ }
+ }
+ // Represents module X { ... }
+ public class LiteralModuleDecl : ModuleDecl
+ {
+ public readonly ModuleDefinition ModuleDef;
+ public LiteralModuleDecl(ModuleDefinition module, ModuleDefinition parent)
+ : base(module.tok, module.Name, parent, false) {
+ ModuleDef = module;
+ }
+ }
+ // Represents "module name = path;", where name is a identifier and path is a possibly qualified name.
+ public class AliasModuleDecl : ModuleDecl
+ {
+ public ModuleDecl ModuleReference; // should refer to another declaration somewhere. NOTE: cyclicity is possible, and should
+ // be detected and warned.
+ public readonly List<IToken> Path; // generated by the parser, this is looked up
+ public ModuleDecl Root; // the moduleDecl that Path[0] refers to.
+ public AliasModuleDecl(List<IToken> path, IToken name, ModuleDefinition parent, bool opened)
+ : base(name, name.val, parent, opened) {
+ Contract.Requires(path != null && path.Count > 0);
+ Path = path;
+ ModuleReference = null;
+ }
+ }
+ // Represents "module name as path [ = compilePath];", where name is a identifier and path is a possibly qualified name.
+ public class AbstractModuleDecl : ModuleDecl
+ {
+ public ModuleDecl Root;
+ public readonly List<IToken> Path;
+ public ModuleDecl CompileRoot;
+ public readonly List<IToken> CompilePath;
+ public ModuleSignature OriginalSignature;
+
+ public AbstractModuleDecl(List<IToken> path, IToken name, ModuleDefinition parent, List<IToken> compilePath, bool opened)
+ : base(name, name.val, parent, opened) {
+ Path = path;
+ Root = null;
+ CompilePath = compilePath;
+ }
+ }
+
+ public class ModuleSignature {
+
+ 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>();
+ public ModuleDefinition ModuleDef = null; // Note: this is null if this signature does not correspond to a specific definition (i.e.
+ // 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 ModuleSignature() {}
+
+ public bool FindSubmodule(string name, out ModuleSignature pp) {
+ TopLevelDecl top;
+ pp = null;
+ if (TopLevels.TryGetValue(name, out top)) {
+ if (top is ModuleDecl) {
+ pp = ((ModuleDecl)top).Signature;
+ return true;
+ } else return false;
+ } else return false;
+ }
+
+
+ }
+ public class ModuleDefinition : Declaration {
+ 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 readonly List<TopLevelDecl/*!*/> TopLevelDecls = new List<TopLevelDecl/*!*/>(); // filled in by the parser; readonly after that
+ public readonly Graph<MemberDecl/*!*/> CallGraph = new Graph<MemberDecl/*!*/>(); // filled in during resolution
+ public int Height; // height in the topological sorting of modules; filled in during resolution
+ public readonly bool IsGhost;
+ public readonly bool IsAbstract; // True iff this module represents an abstract interface
+ private readonly bool IsBuiltinName; // true if this is something like _System that shouldn't have it's name mangled.
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(cce.NonNullElements(TopLevelDecls));
+ Contract.Invariant(CallGraph != null);
+ }
+
+ public ModuleDefinition(IToken tok, string name, bool isGhost, bool isAbstract, List<IToken> refinementBase, Attributes attributes, bool isBuiltinName)
+ : base(tok, name, attributes) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ RefinementBaseName = refinementBase;
+ IsGhost = isGhost;
+ IsAbstract = isAbstract;
+ RefinementBaseRoot = null;
+ RefinementBase = null;
+ IsBuiltinName = isBuiltinName;
+ }
+ public virtual bool IsDefaultModule {
+ get {
+ return false;
+ }
+ }
+ string compileName;
+ new public string CompileName {
+ get {
+ if (compileName == null) {
+ if (IsBuiltinName)
+ compileName = Name;
+ else
+ compileName = "_" + Height.ToString() + "_" + NonglobalVariable.CompilerizeName(Name);
+ }
+ return compileName;
+ }
+ }
+
+ public static IEnumerable<Function> AllFunctions(List<TopLevelDecl> declarations) {
+ foreach (var d in declarations) {
+ var cl = d as ClassDecl;
+ if (cl != null) {
+ foreach (var member in cl.Members) {
+ var fn = member as Function;
+ if (fn != null) {
+ yield return fn;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public class DefaultModuleDecl : ModuleDefinition {
+ public DefaultModuleDecl() : base(Token.NoToken, "_module", false, false, null, null, true) {
+ }
+ public override bool IsDefaultModule {
+ get {
+ return true;
+ }
+ }
+ }
+
+ public abstract class TopLevelDecl : Declaration, TypeParameter.ParentType {
+ public readonly ModuleDefinition Module;
+ public readonly List<TypeParameter/*!*/>/*!*/ TypeArgs;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(cce.NonNullElements(TypeArgs));
+ }
+
+ public TopLevelDecl(IToken/*!*/ tok, string/*!*/ name, ModuleDefinition module, List<TypeParameter/*!*/>/*!*/ typeArgs, Attributes attributes)
+ : base(tok, name, attributes) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(cce.NonNullElements(typeArgs));
+ Module = module;
+ TypeArgs = typeArgs;
+ }
+
+ public string FullName {
+ get {
+ return Module.Name + "." + Name;
+ }
+ }
+ public string FullNameInContext(ModuleDefinition context) {
+ if (Module == context) {
+ return Name;
+ } else {
+ return Module.Name + "." + Name;
+ }
+ }
+ public string FullCompileName {
+ get {
+ return Module.CompileName + "." + CompileName;
+ }
+ }
+ }
+
+ public class ClassDecl : TopLevelDecl {
+ public readonly List<MemberDecl/*!*/>/*!*/ Members;
+ public bool HasConstructor; // filled in (early) during resolution; true iff there exists a member that is a Constructor
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(cce.NonNullElements(Members));
+ }
+
+ public ClassDecl(IToken/*!*/ tok, string/*!*/ name, ModuleDefinition/*!*/ module,
+ List<TypeParameter/*!*/>/*!*/ typeArgs, [Captured] List<MemberDecl/*!*/>/*!*/ members, Attributes attributes)
+ : base(tok, name, module, typeArgs, attributes) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(module != null);
+ Contract.Requires(cce.NonNullElements(typeArgs));
+ Contract.Requires(cce.NonNullElements(members));
+ Members = members;
+ }
+ public virtual bool IsDefaultClass {
+ get {
+ return false;
+ }
+ }
+ }
+
+ public class DefaultClassDecl : ClassDecl {
+ public DefaultClassDecl(ModuleDefinition/*!*/ module, [Captured] List<MemberDecl/*!*/>/*!*/ members)
+ : base(Token.NoToken, "_default", module, new List<TypeParameter/*!*/>(), members, null) {
+ Contract.Requires(module != null);
+ Contract.Requires(cce.NonNullElements(members));
+ }
+ public override bool IsDefaultClass {
+ get {
+ return true;
+ }
+ }
+ }
+
+ public class ArrayClassDecl : ClassDecl {
+ public readonly int Dims;
+ public ArrayClassDecl(int dims, ModuleDefinition module)
+ : base(Token.NoToken, BuiltIns.ArrayClassName(dims), module,
+ new List<TypeParameter>(new TypeParameter[]{new TypeParameter(Token.NoToken, "arg")}),
+ new List<MemberDecl>(), null)
+ {
+ Contract.Requires(1 <= dims);
+ Contract.Requires(module != null);
+
+ Dims = dims;
+ }
+ }
+
+ public abstract class DatatypeDecl : TopLevelDecl {
+ public readonly List<DatatypeCtor/*!*/>/*!*/ Ctors;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(cce.NonNullElements(Ctors));
+ Contract.Invariant(1 <= Ctors.Count);
+ }
+
+ public DatatypeDecl(IToken/*!*/ tok, string/*!*/ name, ModuleDefinition/*!*/ module, List<TypeParameter/*!*/>/*!*/ typeArgs,
+ [Captured] List<DatatypeCtor/*!*/>/*!*/ ctors, Attributes attributes)
+ : base(tok, name, module, typeArgs, attributes) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(module != null);
+ Contract.Requires(cce.NonNullElements(typeArgs));
+ Contract.Requires(cce.NonNullElements(ctors));
+ Contract.Requires(1 <= ctors.Count);
+ Ctors = ctors;
+ }
+ public bool HasFinitePossibleValues {
+ get {
+ return (TypeArgs.Count == 0 && Ctors.TrueForAll(ctr => ctr.Formals.Count == 0));
+ }
+ }
+ }
+
+ public class IndDatatypeDecl : DatatypeDecl
+ {
+ public DatatypeCtor DefaultCtor; // set during resolution
+ public bool[] TypeParametersUsedInConstructionByDefaultCtor; // set during resolution; has same length as the number of type arguments
+
+ public enum ES { NotYetComputed, Never, ConsultTypeArguments }
+ 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) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(module != null);
+ Contract.Requires(cce.NonNullElements(typeArgs));
+ Contract.Requires(cce.NonNullElements(ctors));
+ Contract.Requires(1 <= ctors.Count);
+ }
+ }
+
+ public class CoDatatypeDecl : DatatypeDecl
+ {
+ public CoDatatypeDecl(IToken/*!*/ tok, string/*!*/ name, ModuleDefinition/*!*/ module, List<TypeParameter/*!*/>/*!*/ typeArgs,
+ [Captured] List<DatatypeCtor/*!*/>/*!*/ ctors, Attributes attributes)
+ : base(tok, name, module, typeArgs, ctors, attributes) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(module != null);
+ Contract.Requires(cce.NonNullElements(typeArgs));
+ Contract.Requires(cce.NonNullElements(ctors));
+ Contract.Requires(1 <= ctors.Count);
+ }
+ }
+
+ public class DatatypeCtor : Declaration, TypeParameter.ParentType
+ {
+ public readonly List<Formal/*!*/>/*!*/ Formals;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(cce.NonNullElements(Formals));
+ Contract.Invariant(Destructors != null);
+ Contract.Invariant(
+ Destructors.Count == 0 || // this is until resolution
+ Destructors.Count == Formals.Count); // after resolution
+ }
+
+ // TODO: One could imagine having a precondition on datatype constructors
+ public DatatypeDecl EnclosingDatatype; // filled in during resolution
+ public SpecialField QueryField; // filled in during resolution
+ public List<SpecialField/*may be null*/> Destructors = new List<SpecialField/*may be null*/>(); // contents filled in during resolution
+
+ public DatatypeCtor(IToken/*!*/ tok, string/*!*/ name, [Captured] List<Formal/*!*/>/*!*/ formals, Attributes attributes)
+ : base(tok, name, attributes) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(cce.NonNullElements(formals));
+ this.Formals = formals;
+ }
+
+ public string FullName {
+ get {
+ Contract.Requires(EnclosingDatatype != null);
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ return "#" + EnclosingDatatype.FullCompileName + "." + Name;
+ }
+ }
+ }
+
+ public interface ICodeContext : ICallable
+ {
+ bool IsGhost { get; }
+ bool IsStatic { get; }
+ List<TypeParameter> TypeArgs { get; }
+ List<Formal> Ins { get ; }
+ List<Formal> Outs { get; }
+ Specification<FrameExpression> Modifies { get; }
+ Specification<Expression> Decreases { get; }
+ ModuleDefinition EnclosingModule { get; } // to be called only after signature-resolution is complete
+ bool MustReverify { get; }
+ }
+
+ public class IteratorDecl : ClassDecl, ICodeContext
+ {
+ public readonly List<Formal> Ins;
+ public readonly List<Formal> Outs;
+ public readonly Specification<FrameExpression> Reads;
+ public readonly Specification<FrameExpression> Modifies;
+ public readonly Specification<Expression> Decreases;
+ public bool InferredDecreases; // fill in during resolution/registration
+ public readonly List<MaybeFreeExpression> Requires;
+ public readonly List<MaybeFreeExpression> Ensures;
+ public readonly List<MaybeFreeExpression> YieldRequires;
+ public readonly List<MaybeFreeExpression> YieldEnsures;
+ public readonly BlockStmt Body;
+ public readonly bool SignatureIsOmitted;
+ public readonly List<Field> OutsFields;
+ public readonly List<Field> OutsHistoryFields; // these are the 'xs' variables
+ public readonly List<Field> DecreasesFields; // filled in during resolution
+ public SpecialField Member_Modifies; // filled in during resolution
+ public SpecialField Member_Reads; // filled in during resolution
+ public SpecialField Member_New; // filled in during resolution
+ public Constructor Member_Init; // created during registration phase of resolution; its specification is filled in during resolution
+ 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 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,
+ List<MaybeFreeExpression> requires,
+ List<MaybeFreeExpression> ensures,
+ List<MaybeFreeExpression> yieldRequires,
+ List<MaybeFreeExpression> yieldEnsures,
+ BlockStmt body, Attributes attributes, bool signatureIsOmitted)
+ : base(tok, name, module, typeArgs, new List<MemberDecl>(), attributes)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(module != null);
+ Contract.Requires(typeArgs != null);
+ Contract.Requires(ins != null);
+ Contract.Requires(outs != null);
+ Contract.Requires(reads != null);
+ Contract.Requires(mod != null);
+ Contract.Requires(decreases != null);
+ Contract.Requires(requires != null);
+ Contract.Requires(ensures != null);
+ Contract.Requires(yieldRequires != null);
+ Contract.Requires(yieldEnsures != null);
+ Ins = ins;
+ Outs = outs;
+ Reads = reads;
+ Modifies = mod;
+ Decreases = decreases;
+ Requires = requires;
+ Ensures = ensures;
+ YieldRequires = yieldRequires;
+ YieldEnsures = yieldEnsures;
+ Body = body;
+ SignatureIsOmitted = signatureIsOmitted;
+
+ OutsFields = new List<Field>();
+ OutsHistoryFields = new List<Field>();
+ DecreasesFields = new List<Field>();
+ }
+
+ bool ICodeContext.IsGhost { get { return false; } }
+ bool ICodeContext.IsStatic { get { return true; } }
+ List<TypeParameter> ICodeContext.TypeArgs { get { return this.TypeArgs; } }
+ List<Formal> ICodeContext.Ins { get { return this.Ins; } }
+ List<Formal> ICodeContext.Outs { get { return this.Outs; } }
+ Specification<FrameExpression> ICodeContext.Modifies { get { return this.Modifies; } }
+ Specification<Expression> ICodeContext.Decreases { get { return this.Decreases; } }
+ ModuleDefinition ICodeContext.EnclosingModule { get { return this.Module; } }
+ bool ICodeContext.MustReverify { get { return false; } }
+ }
+
+ /// <summary>
+ /// An "ICallable" is a function, method, or iterator.
+ /// </summary>
+ public interface ICallable { }
+
+ public abstract class MemberDecl : Declaration {
+ public readonly bool IsStatic;
+ public readonly bool IsGhost;
+ public TopLevelDecl EnclosingClass; // filled in during resolution
+
+ public MemberDecl(IToken tok, string name, bool isStatic, bool isGhost, Attributes attributes)
+ : base(tok, name, attributes) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ IsStatic = isStatic;
+ IsGhost = isGhost;
+ }
+ /// <summary>
+ /// Returns className+"."+memberName. Available only after resolution.
+ /// </summary>
+ public string FullName {
+ get {
+ Contract.Requires(EnclosingClass != null);
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ return EnclosingClass.FullName + "." + Name;
+ }
+ }
+ public string FullNameInContext(ModuleDefinition context) {
+ Contract.Requires(EnclosingClass != null);
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ return EnclosingClass.FullNameInContext(context) + "." + Name;
+ }
+ public override string CompileName {
+ get {
+ var nm = base.CompileName;
+ if (this.Name == EnclosingClass.Name) {
+ nm = "_" + nm;
+ }
+ return nm;
+ }
+ }
+ public string FullCompileName {
+ get {
+ Contract.Requires(EnclosingClass != null);
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ return EnclosingClass.FullCompileName + "." + CompileName;
+ }
+ }
+ }
+
+ public class Field : MemberDecl {
+ public readonly bool IsMutable; // says whether or not the field can ever change values
+ public readonly bool IsUserMutable; // says whether or not code is allowed to assign to the field (IsUserMutable implies IsMutable)
+ public readonly Type Type;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Type != null);
+ Contract.Invariant(!IsUserMutable || IsMutable); // IsUserMutable ==> IsMutable
+ }
+
+ public Field(IToken tok, string name, bool isGhost, Type type, Attributes attributes)
+ : this(tok, name, isGhost, true, true, type, attributes) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(type != null);
+ }
+
+ public Field(IToken tok, string name, bool isGhost, bool isMutable, bool isUserMutable, Type type, Attributes attributes)
+ : base(tok, name, false, isGhost, attributes) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(type != null);
+ Contract.Requires(!isUserMutable || isMutable);
+ IsMutable = isMutable;
+ IsUserMutable = isUserMutable;
+ Type = type;
+ }
+ }
+
+ public class SpecialField : Field
+ {
+ public readonly string CompiledName;
+ public readonly string PreString;
+ public readonly string PostString;
+ public SpecialField(IToken tok, string name, string compiledName, string preString, string postString, bool isGhost, bool isMutable, bool isUserMutable, Type type, Attributes attributes)
+ : base(tok, name, isGhost, isMutable, isUserMutable, type, attributes) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(compiledName != null);
+ Contract.Requires(preString != null);
+ Contract.Requires(postString != null);
+ Contract.Requires(!isUserMutable || isMutable);
+ Contract.Requires(type != null);
+
+ CompiledName = compiledName;
+ PreString = preString;
+ PostString = postString;
+ }
+ }
+
+ public class DatatypeDestructor : SpecialField
+ {
+ public readonly DatatypeCtor EnclosingCtor;
+ public readonly Formal CorrespondingFormal;
+
+ public DatatypeDestructor(IToken tok, DatatypeCtor enclosingCtor, Formal correspondingFormal, string name, string compiledName, string preString, string postString, bool isGhost, Type type, Attributes attributes)
+ : base(tok, name, compiledName, preString, postString, isGhost, false, false, type, attributes)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(enclosingCtor != null);
+ Contract.Requires(correspondingFormal != null);
+ Contract.Requires(name != null);
+ Contract.Requires(compiledName != null);
+ Contract.Requires(preString != null);
+ Contract.Requires(postString != null);
+ Contract.Requires(type != null);
+ EnclosingCtor = enclosingCtor;
+ CorrespondingFormal = correspondingFormal;
+ }
+ }
+
+ public class ArbitraryTypeDecl : TopLevelDecl, TypeParameter.ParentType
+ {
+ public readonly TypeParameter TheType;
+ public TypeParameter.EqualitySupportValue EqualitySupport {
+ get { return TheType.EqualitySupport; }
+ }
+ public bool MustSupportEquality {
+ get { return TheType.MustSupportEquality; }
+ }
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(TheType != null && Name == TheType.Name);
+ }
+
+ public ArbitraryTypeDecl(IToken/*!*/ tok, string/*!*/ name, ModuleDefinition/*!*/ module, TypeParameter.EqualitySupportValue equalitySupport, Attributes attributes)
+ : base(tok, name, module, new List<TypeParameter>(), attributes) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(module != null);
+ TheType = new TypeParameter(tok, name, equalitySupport);
+ }
+ }
+
+ [ContractClass(typeof(IVariableContracts))]
+ public interface IVariable {
+ string/*!*/ Name {
+ get;
+ }
+ string/*!*/ DisplayName { // what the user thinks he wrote
+ get;
+ }
+ string/*!*/ UniqueName {
+ get;
+ }
+ string/*!*/ CompileName {
+ get;
+ }
+ Type/*!*/ Type {
+ get;
+ }
+ bool IsMutable {
+ get;
+ }
+ bool IsGhost {
+ get;
+ }
+ }
+ [ContractClassFor(typeof(IVariable))]
+ public abstract class IVariableContracts : IVariable {
+ public string Name {
+ get {
+ Contract.Ensures(Contract.Result<string>() != null);
+ throw new NotImplementedException(); // this getter implementation is here only so that the Ensures contract can be given here
+ }
+ }
+ public string DisplayName {
+ get {
+ Contract.Ensures(Contract.Result<string>() != null);
+ throw new NotImplementedException(); // this getter implementation is here only so that the Ensures contract can be given here
+ }
+ }
+ public string UniqueName {
+ get {
+ Contract.Ensures(Contract.Result<string>() != null);
+ throw new NotImplementedException(); // this getter implementation is here only so that the Ensures contract can be given here
+ }
+ }
+ public string CompileName {
+ get {
+ Contract.Ensures(Contract.Result<string>() != null);
+ throw new NotImplementedException(); // this getter implementation is here only so that the Ensures contract can be given here
+ }
+ }
+ public Type Type {
+ get {
+ Contract.Ensures(Contract.Result<Type>() != null);
+ throw new NotImplementedException(); // this getter implementation is here only so that the Ensures contract can be given here
+ }
+ }
+ public bool IsMutable {
+ get {
+ throw new NotImplementedException();
+ }
+ }
+ public bool IsGhost {
+ get {
+ throw new NotImplementedException();
+ }
+ }
+ }
+
+ public abstract class NonglobalVariable : IVariable {
+ public readonly IToken tok;
+ readonly string name;
+
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(tok != null);
+ Contract.Invariant(name != null);
+ Contract.Invariant(type != null);
+ }
+
+ public string Name {
+ get {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return name;
+ }
+ }
+ public string/*!*/ DisplayName {
+ get { return VarDecl.DisplayNameHelper(this); }
+ }
+ readonly int varId = varIdCount++;
+ public string UniqueName {
+ get {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return name + "#" + varId;
+ }
+ }
+ static char[] specialChars = new char[] { '\'', '_', '?', '\\' };
+ public static string CompilerizeName(string nm) {
+ string name = null;
+ int i = 0;
+ while (true) {
+ int j = nm.IndexOfAny(specialChars, i);
+ if (j == -1) {
+ if (i == 0) {
+ return nm; // this is the common case
+ } else {
+ return name + nm.Substring(i);
+ }
+ } else {
+ string nxt = nm.Substring(i, j - i);
+ name = name == null ? nxt : name + nxt;
+ switch (nm[j]) {
+ case '\'': name += "_k"; break;
+ case '_': name += "__"; break;
+ case '?': name += "_q"; break;
+ case '\\': name += "_b"; break;
+ default:
+ Contract.Assume(false); // unexpected character
+ break;
+ }
+ i = j + 1;
+ if (i == nm.Length) {
+ return name;
+ }
+ }
+ }
+ }
+ protected string compileName;
+ public virtual string CompileName {
+ get {
+ if (compileName == null) {
+ compileName = string.Format("_{0}_{1}", varId, CompilerizeName(name));
+ }
+ return compileName;
+ }
+ }
+ Type type;
+ //[Pure(false)] // TODO: if Type gets the status of [Frozen], then this attribute is not needed
+ public Type/*!*/ Type {
+ get {
+ Contract.Ensures(Contract.Result<Type>() != null);
+ return type.Normalize();
+ }
+ }
+ public abstract bool IsMutable {
+ get;
+ }
+ bool isGhost; // readonly, except for BoundVar's of match expressions/statements during resolution
+ public bool IsGhost {
+ get {
+ return isGhost;
+ }
+ set {
+ isGhost = value;
+ }
+ }
+
+ public NonglobalVariable(IToken tok, string name, Type type, bool isGhost) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(type != null);
+ this.tok = tok;
+ this.name = name;
+ this.type = type;
+ this.isGhost = isGhost;
+ }
+
+ internal static int varIdCount; // this varIdCount is used for both NonglobalVariable's and VarDecl's.
+ }
+
+ public class Formal : NonglobalVariable {
+ public readonly bool InParam; // true to in-parameter, false for out-parameter
+ public override bool IsMutable {
+ get {
+ return !InParam;
+ }
+ }
+
+ public Formal(IToken/*!*/ tok, string/*!*/ name, Type/*!*/ type, bool inParam, bool isGhost)
+ : base(tok, name, type, isGhost) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(type != null);
+ InParam = inParam;
+ }
+
+ public bool HasName {
+ get {
+ return !Name.StartsWith("#");
+ }
+ }
+ public override string CompileName {
+ get {
+ if (compileName == null) {
+ compileName = CompilerizeName(Name);
+ }
+ return compileName;
+ }
+ }
+ }
+
+ /// <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);
+ }
+ }
+
+ public class BoundVar : NonglobalVariable {
+ public override bool IsMutable {
+ get {
+ return false;
+ }
+ }
+
+ public BoundVar(IToken/*!*/ tok, string/*!*/ name, Type/*!*/ type)
+ : base(tok, name, type, false) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(type != null);
+ }
+ }
+
+ public class Function : MemberDecl, TypeParameter.ParentType, ICallable {
+ public bool IsRecursive; // filled in during resolution
+ public readonly List<TypeParameter/*!*/>/*!*/ TypeArgs;
+ public readonly IToken OpenParen; // can be null (for predicates), if there are no formals
+ public readonly List<Formal/*!*/>/*!*/ Formals;
+ public readonly Type/*!*/ ResultType;
+ public readonly List<Expression/*!*/>/*!*/ Req;
+ public readonly List<FrameExpression/*!*/>/*!*/ Reads;
+ public readonly List<Expression/*!*/>/*!*/ Ens;
+ public readonly Specification<Expression>/*!*/ Decreases;
+ public Expression Body; // an extended expression; Body is readonly after construction, except for any kind of rewrite that may take place around the time of resolution
+ public readonly bool SignatureIsOmitted; // is "false" for all Function objects that survive into resolution
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(cce.NonNullElements(TypeArgs));
+ Contract.Invariant(cce.NonNullElements(Formals));
+ Contract.Invariant(ResultType != null);
+ Contract.Invariant(cce.NonNullElements(Req));
+ Contract.Invariant(cce.NonNullElements(Reads));
+ Contract.Invariant(cce.NonNullElements(Ens));
+ Contract.Invariant(Decreases != null);
+ }
+
+ /// <summary>
+ /// Note, functions are "ghost" by default; a non-ghost function is called a "function method".
+ /// </summary>
+ public Function(IToken tok, string name, bool isStatic, bool isGhost,
+ List<TypeParameter> typeArgs, IToken openParen, List<Formal> formals, Type resultType,
+ List<Expression> req, List<FrameExpression> reads, List<Expression> ens, Specification<Expression> decreases,
+ Expression body, Attributes attributes, bool signatureOmitted)
+ : base(tok, name, isStatic, isGhost, attributes) {
+
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(cce.NonNullElements(typeArgs));
+ Contract.Requires(cce.NonNullElements(formals));
+ Contract.Requires(resultType != null);
+ Contract.Requires(cce.NonNullElements(req));
+ Contract.Requires(cce.NonNullElements(reads));
+ Contract.Requires(cce.NonNullElements(ens));
+ Contract.Requires(decreases != null);
+ this.TypeArgs = typeArgs;
+ this.OpenParen = openParen;
+ this.Formals = formals;
+ this.ResultType = resultType;
+ this.Req = req;
+ this.Reads = reads;
+ this.Ens = ens;
+ this.Decreases = decreases;
+ this.Body = body;
+ this.SignatureIsOmitted = signatureOmitted;
+ }
+ }
+
+ public class Predicate : Function
+ {
+ public enum BodyOriginKind
+ {
+ OriginalOrInherited, // this predicate definition is new (and the predicate may or may not have a body), or the predicate's body (whether or not it exists) is being inherited unmodified (from the previous refinement--it may be that the inherited body was itself an extension, for example)
+ DelayedDefinition, // this predicate declaration provides, for the first time, a body--the declaration refines a previously declared predicate, but the previous one had no body
+ Extension // this predicate extends the definition of a predicate with a body in a module being refined
+ }
+ public readonly BodyOriginKind BodyOrigin;
+ public Predicate(IToken tok, string name, bool isStatic, bool isGhost,
+ List<TypeParameter> typeArgs, IToken openParen, List<Formal> formals,
+ List<Expression> req, List<FrameExpression> reads, List<Expression> ens, Specification<Expression> decreases,
+ Expression body, BodyOriginKind bodyOrigin, Attributes attributes, bool signatureOmitted)
+ : base(tok, name, isStatic, isGhost, typeArgs, openParen, formals, new BoolType(), req, reads, ens, decreases, body, attributes, signatureOmitted) {
+ Contract.Requires(bodyOrigin == Predicate.BodyOriginKind.OriginalOrInherited || body != null);
+ BodyOrigin = bodyOrigin;
+ }
+ }
+
+ public class CoPredicate : Function
+ {
+ public readonly List<FunctionCallExpr> Uses = new List<FunctionCallExpr>(); // filled in during resolution, used by verifier
+
+ public CoPredicate(IToken tok, string name, bool isStatic,
+ List<TypeParameter> typeArgs, IToken openParen, List<Formal> formals,
+ List<Expression> req, List<FrameExpression> reads, List<Expression> ens,
+ Expression body, Attributes attributes, bool signatureOmitted)
+ : base(tok, name, isStatic, true, typeArgs, openParen, formals, new BoolType(),
+ req, reads, ens, new Specification<Expression>(new List<Expression>(), null), body, attributes, signatureOmitted) {
+ }
+ }
+
+ public class Method : MemberDecl, TypeParameter.ParentType, ICodeContext
+ {
+ public readonly bool SignatureIsOmitted;
+ public bool MustReverify;
+ public readonly List<TypeParameter/*!*/>/*!*/ TypeArgs;
+ public readonly List<Formal/*!*/>/*!*/ Ins;
+ public readonly List<Formal/*!*/>/*!*/ Outs;
+ public readonly List<MaybeFreeExpression/*!*/>/*!*/ Req;
+ public readonly Specification<FrameExpression>/*!*/ Mod;
+ public readonly List<MaybeFreeExpression/*!*/>/*!*/ Ens;
+ public readonly Specification<Expression>/*!*/ Decreases;
+ public BlockStmt Body; // Body is readonly after construction, except for any kind of rewrite that may take place around the time of resolution
+ public bool IsTailRecursive; // filled in during resolution
+
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(cce.NonNullElements(TypeArgs));
+ Contract.Invariant(cce.NonNullElements(Ins));
+ Contract.Invariant(cce.NonNullElements(Outs));
+ Contract.Invariant(cce.NonNullElements(Req));
+ Contract.Invariant(Mod != null);
+ Contract.Invariant(cce.NonNullElements(Ens));
+ Contract.Invariant(Decreases != null);
+ }
+
+ public Method(IToken tok, string name,
+ bool isStatic, bool isGhost,
+ [Captured] List<TypeParameter/*!*/>/*!*/ typeArgs,
+ [Captured] List<Formal/*!*/>/*!*/ ins, [Captured] List<Formal/*!*/>/*!*/ outs,
+ [Captured] List<MaybeFreeExpression/*!*/>/*!*/ req, [Captured] Specification<FrameExpression>/*!*/ mod,
+ [Captured] List<MaybeFreeExpression/*!*/>/*!*/ ens,
+ [Captured] Specification<Expression>/*!*/ decreases,
+ [Captured] BlockStmt body,
+ Attributes attributes, bool signatureOmitted)
+ : base(tok, name, isStatic, isGhost, attributes) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(cce.NonNullElements(typeArgs));
+ Contract.Requires(cce.NonNullElements(ins));
+ Contract.Requires(cce.NonNullElements(outs));
+ Contract.Requires(cce.NonNullElements(req));
+ Contract.Requires(mod != null);
+ Contract.Requires(cce.NonNullElements(ens));
+ Contract.Requires(decreases != null);
+ this.TypeArgs = typeArgs;
+ this.Ins = ins;
+ this.Outs = outs;
+ this.Req = req;
+ this.Mod = mod;
+ this.Ens = ens;
+ this.Decreases = decreases;
+ this.Body = body;
+ this.SignatureIsOmitted = signatureOmitted;
+ MustReverify = false;
+ }
+
+ bool ICodeContext.IsGhost { get { return this.IsGhost; } }
+ bool ICodeContext.IsStatic { get { return this.IsStatic; } }
+ List<TypeParameter> ICodeContext.TypeArgs { get { return this.TypeArgs; } }
+ List<Formal> ICodeContext.Ins { get { return this.Ins; } }
+ List<Formal> ICodeContext.Outs { get { return this.Outs; } }
+ Specification<FrameExpression> ICodeContext.Modifies { get { return Mod; } }
+ Specification<Expression> ICodeContext.Decreases { get { return this.Decreases; } }
+ ModuleDefinition ICodeContext.EnclosingModule {
+ get {
+ Contract.Assert(this.EnclosingClass != null); // this getter is supposed to be called only after signature-resolution is complete
+ return this.EnclosingClass.Module;
+ }
+ }
+ bool ICodeContext.MustReverify { get { return this.MustReverify; } }
+ }
+
+ public class Constructor : Method
+ {
+ public Constructor(IToken tok, string name,
+ [Captured] List<TypeParameter/*!*/>/*!*/ typeArgs,
+ [Captured] List<Formal/*!*/>/*!*/ ins,
+ [Captured] List<MaybeFreeExpression/*!*/>/*!*/ req, [Captured] Specification<FrameExpression>/*!*/ mod,
+ [Captured] List<MaybeFreeExpression/*!*/>/*!*/ ens,
+ [Captured] Specification<Expression>/*!*/ decreases,
+ [Captured] BlockStmt body,
+ Attributes attributes, bool signatureOmitted)
+ : base(tok, name, false, false, typeArgs, ins, new List<Formal>(), req, mod, ens, decreases, body, attributes, signatureOmitted) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(cce.NonNullElements(typeArgs));
+ Contract.Requires(cce.NonNullElements(ins));
+ Contract.Requires(cce.NonNullElements(req));
+ Contract.Requires(mod != null);
+ Contract.Requires(cce.NonNullElements(ens));
+ Contract.Requires(decreases != null);
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------------
+
+ public abstract class Statement {
+ public readonly IToken Tok;
+ public LList<Label> Labels; // mutable during resolution
+
+ private Attributes attributes;
+ public Attributes Attributes {
+ get {
+ return attributes;
+ }
+ set {
+ attributes = value;
+ }
+ }
+
+ public bool HasAttributes() {
+ return Attributes != null;
+ }
+
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Tok != null);
+ }
+
+ public bool IsGhost; // filled in by resolution
+
+ public Statement(IToken tok, Attributes attrs) {
+ Contract.Requires(tok != null);
+ this.Tok = tok;
+ this.attributes = attrs;
+ }
+
+ public Statement(IToken tok)
+ : this(tok, null) {
+ Contract.Requires(tok != null);
+ this.Tok = tok;
+ }
+
+ /// <summary>
+ /// Returns the non-null substatements of the Statements.
+ /// </summary>
+ public virtual IEnumerable<Statement> SubStatements {
+ get { yield break; }
+ }
+
+ /// <summary>
+ /// Returns the non-null expressions of this statement proper (that is, do not include the expressions of substatements).
+ /// </summary>
+ public virtual IEnumerable<Expression> SubExpressions {
+ get { yield break; }
+ }
+ }
+
+ public class LList<T>
+ {
+ public readonly T Data;
+ public readonly LList<T> Next;
+ const LList<T> Empty = null;
+
+ public LList(T d, LList<T> next) {
+ Data = d;
+ Next = next;
+ }
+
+ public static LList<T> Append(LList<T> a, LList<T> b) {
+ if (a == null) return b;
+ return new LList<T>(a.Data, Append(a.Next, b));
+ // pretend this is ML
+ }
+ public static int Count(LList<T> n) {
+ int count = 0;
+ while (n != null) {
+ count++;
+ n = n.Next;
+ }
+ return count;
+ }
+ }
+
+ public class Label
+ {
+ public readonly IToken Tok;
+ public readonly string Name;
+ public readonly int UniqueId;
+ static int nodes = 0;
+
+ public Label(IToken tok, string label) {
+ Contract.Requires(tok != null);
+ Tok = tok;
+ Name = label;
+ UniqueId = nodes++;
+ }
+ }
+
+ public abstract class PredicateStmt : Statement
+ {
+ public readonly Expression Expr;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Expr != null);
+ }
+
+ public PredicateStmt(IToken tok, Expression expr, Attributes attrs)
+ : base(tok, attrs) {
+ Contract.Requires(tok != null);
+ Contract.Requires(expr != null);
+ this.Expr = expr;
+ }
+
+ public PredicateStmt(IToken tok, Expression expr)
+ : this(tok, expr, null) {
+ Contract.Requires(tok != null);
+ Contract.Requires(expr != null);
+ this.Expr = expr;
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return Expr;
+ }
+ }
+ }
+
+ public class AssertStmt : PredicateStmt {
+ public AssertStmt(IToken/*!*/ tok, Expression/*!*/ expr, Attributes attrs)
+ : base(tok, expr, attrs) {
+ Contract.Requires(tok != null);
+ Contract.Requires(expr != null);
+ }
+ }
+
+ public class AssumeStmt : PredicateStmt {
+ public AssumeStmt(IToken/*!*/ tok, Expression/*!*/ expr, Attributes attrs)
+ : base(tok, expr, attrs) {
+ Contract.Requires(tok != null);
+ Contract.Requires(expr != null);
+ }
+ }
+
+ public class PrintStmt : Statement {
+ public readonly List<Attributes.Argument/*!*/>/*!*/ Args;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(cce.NonNullElements(Args));
+ }
+
+ public PrintStmt(IToken tok, List<Attributes.Argument/*!*/>/*!*/ args)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(args));
+
+ Args = args;
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ foreach (var arg in Args) {
+ if (arg.E != null) {
+ yield return arg.E;
+ }
+ }
+ }
+ }
+ }
+
+ public class BreakStmt : Statement {
+ public readonly string TargetLabel;
+ public readonly int BreakCount;
+ public Statement TargetStmt; // filled in during resolution
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(TargetLabel != null || 1 <= BreakCount);
+ }
+
+ public BreakStmt(IToken tok, string targetLabel)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(targetLabel != null);
+ this.TargetLabel = targetLabel;
+ }
+ public BreakStmt(IToken tok, int breakCount)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(1 <= breakCount);
+ this.BreakCount = breakCount;
+ }
+ }
+
+ public abstract class ProduceStmt : Statement
+ {
+ public List<AssignmentRhs> rhss;
+ public UpdateStmt hiddenUpdate;
+ public ProduceStmt(IToken tok, List<AssignmentRhs> rhss)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ this.rhss = rhss;
+ hiddenUpdate = null;
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ if (rhss != null) {
+ foreach (var rhs in rhss) {
+ foreach (var ee in rhs.SubExpressions) {
+ yield return ee;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public class ReturnStmt : ProduceStmt
+ {
+ public ReturnStmt(IToken tok, List<AssignmentRhs> rhss)
+ : base(tok, rhss) {
+ Contract.Requires(tok != null);
+ }
+ }
+
+ public class YieldStmt : ProduceStmt
+ {
+ public YieldStmt(IToken tok, List<AssignmentRhs> rhss)
+ : base(tok, rhss) {
+ Contract.Requires(tok != null);
+ }
+ }
+
+ public abstract class AssignmentRhs
+ {
+ public readonly IToken Tok;
+
+ private Attributes attributes;
+ public Attributes Attributes
+ {
+ get
+ {
+ return attributes;
+ }
+ set
+ {
+ attributes = value;
+ }
+ }
+
+ public bool HasAttributes()
+ {
+ return Attributes != null;
+ }
+
+ internal AssignmentRhs(IToken tok, Attributes attrs = null) {
+ Tok = tok;
+ Attributes = attrs;
+ }
+ public abstract bool CanAffectPreviouslyKnownExpressions { get; }
+ /// <summary>
+ /// Returns the non-null subexpressions of the AssignmentRhs.
+ /// </summary>
+ public virtual IEnumerable<Expression> SubExpressions {
+ get { yield break; }
+ }
+ /// <summary>
+ /// Returns the non-null sub-statements of the AssignmentRhs.
+ /// </summary>
+ public virtual IEnumerable<Statement> SubStatements{
+ get { yield break; }
+ }
+ }
+
+ public class ExprRhs : AssignmentRhs
+ {
+ public readonly Expression Expr;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Expr != null);
+ }
+
+ public ExprRhs(Expression expr, Attributes attrs = null)
+ : base(expr.tok, attrs)
+ {
+ Contract.Requires(expr != null);
+ Expr = expr;
+ }
+ public override bool CanAffectPreviouslyKnownExpressions { get { return false; } }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return Expr;
+ }
+ }
+ }
+
+ public class TypeRhs : AssignmentRhs
+ {
+ public readonly Type EType;
+ public readonly List<Expression> ArrayDimensions;
+ public CallStmt InitCall; // may be null (and is definitely null for arrays)
+ public Type Type; // filled in during resolution
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(EType != null);
+ Contract.Invariant(ArrayDimensions == null || 1 <= ArrayDimensions.Count);
+ Contract.Invariant(ArrayDimensions == null || InitCall == null);
+ }
+
+ public TypeRhs(IToken tok, Type type)
+ : base(tok)
+ {
+ Contract.Requires(type != null);
+ EType = type;
+ }
+ public TypeRhs(IToken tok, Type type, CallStmt initCall)
+ : base(tok)
+ {
+ Contract.Requires(type != null);
+ EType = type;
+ InitCall = initCall;
+ }
+ public TypeRhs(IToken tok, Type type, List<Expression> arrayDimensions)
+ : base(tok)
+ {
+ Contract.Requires(type != null);
+ Contract.Requires(arrayDimensions != null && 1 <= arrayDimensions.Count);
+ EType = type;
+ ArrayDimensions = arrayDimensions;
+ }
+ public override bool CanAffectPreviouslyKnownExpressions {
+ get {
+ if (InitCall != null) {
+ foreach (var mod in InitCall.Method.Mod.Expressions) {
+ if (!(mod.E is ThisExpr)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ if (ArrayDimensions != null) {
+ foreach (var e in ArrayDimensions) {
+ yield return e;
+ }
+ }
+ }
+ }
+ public override IEnumerable<Statement> SubStatements {
+ get {
+ if (InitCall != null) {
+ yield return InitCall;
+ }
+ }
+ }
+ }
+
+ public class CallRhs : AssignmentRhs
+ {
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Receiver != null);
+ Contract.Invariant(MethodName != null);
+ Contract.Invariant(cce.NonNullElements(Args));
+ }
+
+ public readonly Expression/*!*/ Receiver;
+ public readonly string/*!*/ MethodName;
+ public readonly List<Expression/*!*/>/*!*/ Args;
+ public Method Method; // filled in by resolution
+
+ public CallRhs(IToken tok, Expression/*!*/ receiver, string/*!*/ methodName, List<Expression/*!*/>/*!*/ args)
+ : base(tok)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(receiver != null);
+ Contract.Requires(methodName != null);
+ Contract.Requires(cce.NonNullElements(args));
+
+ this.Receiver = receiver;
+ this.MethodName = methodName;
+ this.Args = args;
+ }
+ // TODO: Investigate this. For an initialization, this is true. But for existing objects, this is not true.
+ public override bool CanAffectPreviouslyKnownExpressions {
+ get {
+ foreach (var mod in Method.Mod.Expressions) {
+ if (!(mod.E is ThisExpr)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return Receiver;
+ foreach (var e in Args) {
+ yield return e;
+ }
+ }
+ }
+ }
+
+ public class HavocRhs : AssignmentRhs {
+ public HavocRhs(IToken tok)
+ : base(tok)
+ {
+ }
+ public override bool CanAffectPreviouslyKnownExpressions { get { return false; } }
+ }
+
+ public abstract class ConcreteSyntaxStatement : Statement
+ {
+ public List<Statement> ResolvedStatements = new List<Statement>(); // contents filled in during resolution
+ public ConcreteSyntaxStatement(IToken tok)
+ : base(tok) {
+ }
+
+ public override IEnumerable<Statement> SubStatements {
+ get { return ResolvedStatements; }
+ }
+ }
+
+ public class VarDeclStmt : ConcreteSyntaxStatement
+ {
+ public readonly List<VarDecl> Lhss;
+ public readonly ConcreteUpdateStatement Update;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(cce.NonNullElements(Lhss));
+ }
+
+ public VarDeclStmt(IToken tok, List<VarDecl> lhss, ConcreteUpdateStatement update)
+ : base(tok)
+ {
+ Contract.Requires(lhss != null);
+
+ Lhss = lhss;
+ Update = update;
+ }
+ }
+
+ /// <summary>
+ /// Common superclass of UpdateStmt and AssignSuchThatStmt.
+ /// </summary>
+ public abstract class ConcreteUpdateStatement : ConcreteSyntaxStatement
+ {
+ public readonly List<Expression> Lhss;
+ public ConcreteUpdateStatement(IToken tok, List<Expression> lhss)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(lhss));
+ Lhss = lhss;
+ }
+ }
+
+ public class AssignSuchThatStmt : ConcreteUpdateStatement
+ {
+ public readonly Expression Expr;
+ public readonly IToken AssumeToken;
+ /// <summary>
+ /// "assumeToken" is allowed to be "null", in which case the verifier will check that a RHS value exists.
+ /// If "assumeToken" is non-null, then it should denote the "assume" keyword used in the statement.
+ /// </summary>
+ public AssignSuchThatStmt(IToken tok, List<Expression> lhss, Expression expr, IToken assumeToken)
+ : base(tok, lhss) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(lhss));
+ Contract.Requires(lhss.Count != 0);
+ Contract.Requires(expr != null);
+ Expr = expr;
+ if (assumeToken != null) {
+ AssumeToken = assumeToken;
+ }
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return Expr;
+ foreach (var lhs in Lhss) {
+ yield return lhs;
+ }
+ }
+ }
+ }
+
+ public class UpdateStmt : ConcreteUpdateStatement
+ {
+ public readonly List<AssignmentRhs> Rhss;
+ public readonly bool CanMutateKnownState;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(cce.NonNullElements(Lhss));
+ Contract.Invariant(cce.NonNullElements(Rhss));
+ }
+ public UpdateStmt(IToken tok, List<Expression> lhss, List<AssignmentRhs> rhss)
+ : base(tok, lhss)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(lhss));
+ Contract.Requires(cce.NonNullElements(rhss));
+ Contract.Requires(lhss.Count != 0 || rhss.Count == 1);
+ Rhss = rhss;
+ CanMutateKnownState = false;
+ }
+ public UpdateStmt(IToken tok, List<Expression> lhss, List<AssignmentRhs> rhss, bool mutate)
+ : base(tok, lhss)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(lhss));
+ Contract.Requires(cce.NonNullElements(rhss));
+ Contract.Requires(lhss.Count != 0 || rhss.Count == 1);
+ Rhss = rhss;
+ CanMutateKnownState = mutate;
+ }
+ }
+
+ public class AssignStmt : Statement {
+ public readonly Expression/*!*/ Lhs;
+ public readonly AssignmentRhs/*!*/ Rhs;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Lhs != null);
+ Contract.Invariant(Rhs != null);
+ }
+
+ public AssignStmt(IToken tok, Expression lhs, AssignmentRhs rhs)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(lhs != null);
+ Contract.Requires(rhs != null);
+ this.Lhs = lhs;
+ this.Rhs = rhs;
+ }
+
+ public override IEnumerable<Statement> SubStatements {
+ get {
+ var trhs = Rhs as TypeRhs;
+ if (trhs != null && trhs.InitCall != null) {
+ yield return trhs.InitCall;
+ }
+ }
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return Lhs;
+ foreach (var ee in Rhs.SubExpressions) {
+ yield return ee;
+ }
+ }
+ }
+
+ /// <summary>
+ /// This method assumes "lhs" has been successfully resolved.
+ /// </summary>
+ public static bool LhsIsToGhost(Expression lhs) {
+ Contract.Requires(lhs != null);
+ lhs = lhs.Resolved;
+ if (lhs is IdentifierExpr) {
+ var x = (IdentifierExpr)lhs;
+ return x.Var.IsGhost;
+ } else if (lhs is FieldSelectExpr) {
+ var x = (FieldSelectExpr)lhs;
+ return x.Field.IsGhost;
+ } else {
+ // LHS denotes an array element, which is always non-ghost
+ return false;
+ }
+ }
+ }
+
+ public class VarDecl : Statement, IVariable {
+ readonly string/*!*/ name;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(name != null);
+ Contract.Invariant(OptionalType != null);
+ }
+
+ public VarDecl(IToken tok, string name, Type type, bool isGhost)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(type != null); // can be a proxy, though
+
+ this.name = name;
+ this.OptionalType = type;
+ this.IsGhost = isGhost;
+ }
+
+ public string/*!*/ Name {
+ get {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return name;
+ }
+ }
+ public static bool HasWildcardName(IVariable v) {
+ Contract.Requires(v != null);
+ return v.Name.StartsWith("_");
+ }
+ public static string DisplayNameHelper(IVariable v) {
+ Contract.Requires(v != null);
+ return HasWildcardName(v) ? "_" : v.Name;
+ }
+ public string/*!*/ DisplayName {
+ get { return DisplayNameHelper(this); }
+ }
+ readonly int varId = NonglobalVariable.varIdCount++;
+ public string/*!*/ UniqueName {
+ get {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return name + "#" + varId;
+ }
+ }
+ string compileName;
+ public string CompileName {
+ get {
+ if (compileName == null) {
+ compileName = string.Format("_{0}_{1}", varId, NonglobalVariable.CompilerizeName(name));
+ }
+ return compileName;
+ }
+ }
+ public readonly Type OptionalType; // this is the type mentioned in the declaration, if any
+ internal Type type; // this is the declared or inferred type of the variable; it is non-null after resolution (even if resolution fails)
+ //[Pure(false)]
+ public Type Type {
+ get {
+ Contract.Ensures(Contract.Result<Type>() != null);
+
+ Contract.Assume(type != null); /* we assume object has been resolved */
+ return type.Normalize();
+ }
+ }
+ public bool IsMutable {
+ get {
+ return true;
+ }
+ }
+ bool IVariable.IsGhost {
+ get {
+ return base.IsGhost;
+ }
+ }
+ /// <summary>
+ /// This method retrospectively makes the VarDecl a ghost. It is to be used only during resolution.
+ /// </summary>
+ public void MakeGhost() {
+ base.IsGhost = true;
+ }
+ }
+
+ public class CallStmt : Statement {
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ //Contract.Invariant(Receiver != null);
+ Contract.Invariant(MethodName != null);
+ Contract.Invariant(cce.NonNullElements(Lhs));
+ Contract.Invariant(cce.NonNullElements(Args));
+ }
+
+ public readonly List<Expression/*!*/>/*!*/ Lhs;
+ public Expression/*!*/ Receiver;
+ public readonly string/*!*/ MethodName;
+ public readonly List<Expression/*!*/>/*!*/ Args;
+ public Dictionary<TypeParameter, Type> TypeArgumentSubstitutions; // create, initialized, and used by resolution (could be deleted once all of resolution is done)
+ public Method Method; // filled in by resolution
+
+ public CallStmt(IToken tok, List<Expression/*!*/>/*!*/ lhs, Expression/*!*/ receiver,
+ string/*!*/ methodName, List<Expression/*!*/>/*!*/ args)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(lhs));
+ Contract.Requires(methodName != null);
+ Contract.Requires(cce.NonNullElements(args));
+
+ this.Lhs = lhs;
+ this.Receiver = receiver;
+ this.MethodName = methodName;
+ this.Args = args;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ foreach (var ee in Lhs) {
+ yield return ee;
+ }
+ yield return Receiver;
+ foreach (var ee in Args) {
+ yield return ee;
+ }
+ }
+ }
+ }
+
+ public class BlockStmt : Statement {
+ public readonly List<Statement/*!*/>/*!*/ Body;
+ public BlockStmt(IToken/*!*/ tok, [Captured] List<Statement/*!*/>/*!*/ body)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(body));
+ this.Body = body;
+ }
+
+ public override IEnumerable<Statement> SubStatements {
+ get { return Body; }
+ }
+ }
+
+ public class IfStmt : Statement {
+ public readonly Expression Guard;
+ public readonly BlockStmt Thn;
+ public readonly Statement Els;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Thn != null);
+ Contract.Invariant(Els == null || Els is BlockStmt || Els is IfStmt || Els is SkeletonStatement);
+ }
+ public IfStmt(IToken tok, Expression guard, BlockStmt thn, Statement els)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(thn != null);
+ Contract.Requires(els == null || els is BlockStmt || els is IfStmt || els is SkeletonStatement);
+ this.Guard = guard;
+ this.Thn = thn;
+ this.Els = els;
+ }
+ public override IEnumerable<Statement> SubStatements {
+ get {
+ yield return Thn;
+ if (Els != null) {
+ yield return Els;
+ }
+ }
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ if (Guard != null) {
+ yield return Guard;
+ }
+ }
+ }
+ }
+
+ public class GuardedAlternative
+ {
+ public readonly IToken Tok;
+ public readonly Expression Guard;
+ public readonly List<Statement> Body;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Tok != null);
+ Contract.Invariant(Guard != null);
+ Contract.Invariant(Body != null);
+ }
+ public GuardedAlternative(IToken tok, Expression guard, List<Statement> body)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(guard != null);
+ Contract.Requires(body != null);
+ this.Tok = tok;
+ this.Guard = guard;
+ this.Body = body;
+ }
+ }
+
+ public class AlternativeStmt : Statement
+ {
+ public readonly List<GuardedAlternative> Alternatives;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Alternatives != null);
+ }
+ public AlternativeStmt(IToken tok, List<GuardedAlternative> alternatives)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(alternatives != null);
+ this.Alternatives = alternatives;
+ }
+ public override IEnumerable<Statement> SubStatements {
+ get {
+ foreach (var alt in Alternatives) {
+ foreach (var s in alt.Body) {
+ yield return s;
+ }
+ }
+ }
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ foreach (var alt in Alternatives) {
+ yield return alt.Guard;
+ }
+ }
+ }
+ }
+
+ public abstract class LoopStmt : Statement
+ {
+ public readonly List<MaybeFreeExpression/*!*/>/*!*/ Invariants;
+ public readonly Specification<Expression>/*!*/ Decreases;
+ public readonly Specification<FrameExpression>/*!*/ Mod;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(cce.NonNullElements(Invariants));
+ Contract.Invariant(Decreases != null);
+ Contract.Invariant(Mod != null);
+ }
+ public LoopStmt(IToken tok, List<MaybeFreeExpression/*!*/>/*!*/ invariants, Specification<Expression>/*!*/ decreases, Specification<FrameExpression>/*!*/ mod)
+ : base(tok)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(invariants));
+ Contract.Requires(decreases != null);
+ Contract.Requires(mod != null);
+
+ this.Invariants = invariants;
+ this.Decreases = decreases;
+ this.Mod = mod;
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ foreach (var mfe in Invariants) {
+ yield return mfe.E;
+ }
+ if (Decreases.Expressions != null) {
+ foreach (var e in Decreases.Expressions) {
+ yield return e;
+ }
+ }
+ if (Mod.Expressions != null) {
+ foreach (var fe in Mod.Expressions) {
+ yield return fe.E;
+ }
+ }
+ }
+ }
+ }
+
+ public class WhileStmt : LoopStmt
+ {
+ public readonly Expression Guard;
+ public readonly BlockStmt/*!*/ Body;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Body != null);
+ }
+
+ public WhileStmt(IToken tok, Expression guard,
+ List<MaybeFreeExpression/*!*/>/*!*/ invariants, Specification<Expression>/*!*/ decreases, Specification<FrameExpression>/*!*/ mod,
+ BlockStmt/*!*/ body)
+ : base(tok, invariants, decreases, mod) {
+ Contract.Requires(tok != null);
+ Contract.Requires(body != null);
+ this.Guard = guard;
+ this.Body = body;
+ }
+
+ public override IEnumerable<Statement> SubStatements {
+ get {
+ yield return Body;
+ }
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ if (Guard != null) {
+ yield return Guard;
+ }
+ foreach (var e in base.SubExpressions) {
+ yield return e;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// This class is really just a WhileStmt, except that it serves the purpose of remembering if the object was created as the result of a refinement
+ /// merge.
+ /// </summary>
+ public class RefinedWhileStmt : WhileStmt
+ {
+ public RefinedWhileStmt(IToken tok, Expression guard,
+ List<MaybeFreeExpression/*!*/>/*!*/ invariants, Specification<Expression>/*!*/ decreases, Specification<FrameExpression>/*!*/ mod,
+ BlockStmt/*!*/ body)
+ : base(tok, guard, invariants, decreases, mod, body) {
+ Contract.Requires(tok != null);
+ Contract.Requires(body != null);
+ }
+ }
+
+ public class AlternativeLoopStmt : LoopStmt
+ {
+ public readonly List<GuardedAlternative> Alternatives;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Alternatives != null);
+ }
+ public AlternativeLoopStmt(IToken tok,
+ List<MaybeFreeExpression/*!*/>/*!*/ invariants, Specification<Expression>/*!*/ decreases, Specification<FrameExpression>/*!*/ mod,
+ List<GuardedAlternative> alternatives)
+ : base(tok, invariants, decreases, mod) {
+ Contract.Requires(tok != null);
+ Contract.Requires(alternatives != null);
+ this.Alternatives = alternatives;
+ }
+ public override IEnumerable<Statement> SubStatements {
+ get {
+ foreach (var alt in Alternatives) {
+ foreach (var s in alt.Body) {
+ yield return s;
+ }
+ }
+ }
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ foreach (var alt in Alternatives) {
+ yield return alt.Guard;
+ }
+ foreach (var e in base.SubExpressions) {
+ yield return e;
+ }
+ }
+ }
+ }
+
+ public class ParallelStmt : Statement
+ {
+ public readonly List<BoundVar/*!*/> BoundVars; // note, can be the empty list, in which case Range denotes "true"
+ public readonly Expression/*!*/ Range;
+ public readonly List<MaybeFreeExpression/*!*/>/*!*/ Ens;
+ public readonly Statement Body; // used only until resolution; afterwards, use BodyAssign
+
+ public List<ComprehensionExpr.BoundedPool> Bounds; // initialized and filled in by resolver
+ // invariant: if successfully resolved, Bounds.Count == BoundVars.Count;
+
+ /// <summary>
+ /// Assign means there are no ensures clauses and the body consists of one update statement,
+ /// either to an object field or to an array.
+ /// Call means there are no ensures clauses and the body consists of a single call to a (presumably
+ /// ghost, but non-ghost is also allowed) method with no out-parameters and an empty modifies
+ /// clause.
+ /// Proof means there is at least one ensures clause, and the body consists of any (presumably ghost,
+ /// but non-ghost is also allowed) code without side effects on variables (including fields and array
+ /// elements) declared outside the body itself.
+ /// Notes:
+ /// * More kinds may be allowed in the future.
+ /// * One could also allow Call to call non-ghost methods without side effects. However, that
+ /// would seem pointless in the program, so they are disallowed (to avoid any confusion that
+ /// such use of the parallel statement might actually have a point).
+ /// * One could allow Proof even without ensures clauses that "export" what was learned.
+ /// However, that might give the false impression that the body is nevertheless exported.
+ /// </summary>
+ public enum ParBodyKind { Assign, Call, Proof }
+ public ParBodyKind Kind; // filled in during resolution
+
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(BoundVars != null);
+ Contract.Invariant(Range != null);
+ Contract.Invariant(BoundVars.Count != 0 || LiteralExpr.IsTrue(Range));
+ Contract.Invariant(Ens != null);
+ Contract.Invariant(Body != null);
+ }
+
+ public ParallelStmt(IToken tok, List<BoundVar> boundVars, Attributes attrs, Expression range, List<MaybeFreeExpression/*!*/>/*!*/ ens, Statement body)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(boundVars));
+ Contract.Requires(range != null);
+ Contract.Requires(boundVars.Count != 0 || LiteralExpr.IsTrue(range));
+ Contract.Requires(cce.NonNullElements(ens));
+ Contract.Requires(body != null);
+ this.BoundVars = boundVars;
+ this.Attributes = attrs;
+ this.Range = range;
+ this.Ens = ens;
+ this.Body = body;
+ }
+
+ public Statement S0 {
+ get {
+ // dig into Body to find a single statement
+ Statement s = this.Body;
+ while (true) {
+ var block = s as BlockStmt;
+ if (block != null && block.Body.Count == 1) {
+ s = block.Body[0];
+ } else {
+ var conc = s as ConcreteSyntaxStatement;
+ if (conc != null && conc.ResolvedStatements.Count == 1) {
+ s = conc.ResolvedStatements[0];
+ } else {
+ return s;
+ }
+ }
+ }
+ }
+ }
+
+ public override IEnumerable<Statement> SubStatements {
+ get {
+ yield return Body;
+ }
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return Range;
+ foreach (var ee in Ens) {
+ yield return ee.E;
+ }
+ }
+ }
+ }
+
+ public class CalcStmt : Statement
+ {
+ public readonly BinaryExpr.Opcode/*!*/ Op; // main operator of the calculation
+ public readonly List<Expression/*!*/> Lines;
+ public readonly List<BlockStmt/*!*/> Hints; // Hints[i] comes after line i; block statement is used as a container for multiple sub-hints
+ public readonly List<BinaryExpr.Opcode?> CustomOps; // CustomOps[i] comes after line i; null denotes the absence of a custom operator
+ public readonly List<BinaryExpr/*!*/> Steps; // expressions li op l<i + 1>, filled in during resolution in order to get the correct op
+ public BinaryExpr Result; // expression l0 op ln, filled in during resolution in order to get the correct op
+
+ public static readonly BinaryExpr.Opcode/*!*/ DefaultOp = BinaryExpr.Opcode.Eq;
+
+ [ContractInvariantMethod]
+ void ObjectInvariant()
+ {
+ Contract.Invariant(ValidOp(Op));
+ Contract.Invariant(Lines != null);
+ Contract.Invariant(Hints != null);
+ Contract.Invariant(CustomOps != null);
+ Contract.Invariant(Steps != null);
+ Contract.Invariant(Hints.Count == Math.Max(Lines.Count - 1, 0));
+ Contract.Invariant(CustomOps.Count == Hints.Count);
+ }
+
+ public CalcStmt(IToken tok, BinaryExpr.Opcode/*!*/ op, List<Expression/*!*/> lines, List<BlockStmt/*!*/> hints, List<BinaryExpr.Opcode?> customOps)
+ : base(tok)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(ValidOp(op));
+ Contract.Requires(lines != null);
+ Contract.Requires(hints != null);
+ Contract.Requires(customOps != null);
+ Contract.Requires(cce.NonNullElements(lines));
+ Contract.Requires(cce.NonNullElements(hints));
+ Contract.Requires(hints.Count == Math.Max(lines.Count - 1, 0));
+ Contract.Requires(customOps.Count == hints.Count);
+ this.Op = op;
+ this.Lines = lines;
+ this.Hints = hints;
+ this.CustomOps = customOps;
+ this.Steps = new List<BinaryExpr>();
+ this.Result = null;
+ }
+
+ public override IEnumerable<Statement> SubStatements
+ {
+ get {
+ foreach (var h in Hints) {
+ yield return h;
+ }
+ }
+ }
+ public override IEnumerable<Expression> SubExpressions
+ {
+ get {
+ foreach (var l in Lines) {
+ yield return l;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Is op a valid calculation operator (i.e. a transitive relational operator)?
+ /// </summary>
+ [Pure]
+ public static bool ValidOp(BinaryExpr.Opcode op) {
+ return op == BinaryExpr.Opcode.Eq || op == BinaryExpr.Opcode.Lt || op == BinaryExpr.Opcode.Le || op == BinaryExpr.Opcode.Gt || op == BinaryExpr.Opcode.Ge
+ || op == BinaryExpr.Opcode.Neq
+ || op == BinaryExpr.Opcode.Iff || op == BinaryExpr.Opcode.Imp;
+ }
+
+ /// <summary>
+ /// Does op1 subsume op2 (i.e. forall x, y, z :: (x op1 y op2 z) || (x op2 y op1 z) ==> x op1 z)?
+ /// </summary>
+ [Pure]
+ private static bool Subsumes(BinaryExpr.Opcode op1, BinaryExpr.Opcode op2) {
+ Contract.Requires(ValidOp(op1) && ValidOp(op2));
+ if (op1 == BinaryExpr.Opcode.Neq || op2 == BinaryExpr.Opcode.Neq)
+ return op2 == BinaryExpr.Opcode.Eq;
+ if (op1 == op2)
+ return true;
+ if (op1 == BinaryExpr.Opcode.Iff || op1 == BinaryExpr.Opcode.Imp || op2 == BinaryExpr.Opcode.Iff || op2 == BinaryExpr.Opcode.Imp)
+ return op2 == BinaryExpr.Opcode.Eq ||
+ (op1 == BinaryExpr.Opcode.Imp && op2 == BinaryExpr.Opcode.Iff) ||
+ (op1 == BinaryExpr.Opcode.Eq && op2 == BinaryExpr.Opcode.Iff);
+ return op2 == BinaryExpr.Opcode.Eq ||
+ (op1 == BinaryExpr.Opcode.Lt && op2 == BinaryExpr.Opcode.Le) ||
+ (op1 == BinaryExpr.Opcode.Gt && op2 == BinaryExpr.Opcode.Ge);
+ }
+
+ /// <summary>
+ /// Resulting operator x op z if x op1 y op2 z.
+ /// (Least upper bound in the Subsumes order).
+ /// Returns null if neither of op1 or op2 subsumes the other.
+ /// </summary>
+ [Pure]
+ public static BinaryExpr.Opcode? ResultOp(BinaryExpr.Opcode op1, BinaryExpr.Opcode op2) {
+ Contract.Requires(ValidOp(op1) && ValidOp(op2));
+ Contract.Ensures(Contract.Result<BinaryExpr.Opcode?>() == null || ValidOp((BinaryExpr.Opcode)Contract.Result<BinaryExpr.Opcode?>()));
+ if (Subsumes(op1, op2)) {
+ return op1;
+ } else if (Subsumes(op2, op1)) {
+ return op2;
+ }
+ return null;
+ }
+ }
+
+ public class MatchStmt : Statement
+ {
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Source != null);
+ Contract.Invariant(cce.NonNullElements(Cases));
+ Contract.Invariant(cce.NonNullElements(MissingCases));
+ }
+
+ public readonly Expression Source;
+ public readonly List<MatchCaseStmt/*!*/>/*!*/ Cases;
+ public readonly List<DatatypeCtor/*!*/> MissingCases = new List<DatatypeCtor>(); // filled in during resolution
+
+ public MatchStmt(IToken tok, Expression source, [Captured] List<MatchCaseStmt/*!*/>/*!*/ cases)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(source != null);
+ Contract.Requires(cce.NonNullElements(cases));
+ this.Source = source;
+ this.Cases = cases;
+ }
+
+ public override IEnumerable<Statement> SubStatements {
+ get {
+ foreach (var kase in Cases) {
+ foreach (var s in kase.Body) {
+ yield return s;
+ }
+ }
+ }
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return Source;
+ }
+ }
+ }
+
+ public class MatchCaseStmt : MatchCase
+ {
+ public readonly List<Statement/*!*/>/*!*/ Body;
+
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(cce.NonNullElements(Body));
+ }
+
+ public MatchCaseStmt(IToken tok, string id, [Captured] List<BoundVar/*!*/>/*!*/ arguments, [Captured] List<Statement/*!*/>/*!*/ body)
+ : base(tok, id, arguments)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(id != null);
+ Contract.Requires(cce.NonNullElements(arguments));
+ Contract.Requires(cce.NonNullElements(body));
+ this.Body = body;
+ }
+ }
+
+ /// <summary>
+ /// The class represents several possible scenarios:
+ /// * ...;
+ /// S == null
+ /// * assert ...
+ /// ConditionOmitted == true
+ /// * assume ...
+ /// ConditionOmitted == true
+ /// * if ... { Stmt }
+ /// if ... { Stmt } else ElseStmt
+ /// ConditionOmitted == true
+ /// * while ... invariant J;
+ /// ConditionOmitted == true && BodyOmitted == true
+ /// * while ... invariant J; { Stmt }
+ /// ConditionOmitted == true && BodyOmitted == false
+ /// </summary>
+ public class SkeletonStatement : Statement
+ {
+ public readonly Statement S;
+ public readonly bool ConditionOmitted;
+ public readonly bool BodyOmitted;
+ public readonly List<IToken> NameReplacements;
+ public readonly List<Expression> ExprReplacements;
+ public SkeletonStatement(IToken tok)
+ : base(tok)
+ {
+ Contract.Requires(tok != null);
+ S = null;
+ }
+ public SkeletonStatement(Statement s, bool conditionOmitted, bool bodyOmitted)
+ : base(s.Tok)
+ {
+ Contract.Requires(s != null);
+ S = s;
+ ConditionOmitted = conditionOmitted;
+ BodyOmitted = bodyOmitted;
+ }
+ public SkeletonStatement(IToken tok, List<IToken> nameReplacements, List<Expression> exprReplacements)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ NameReplacements = nameReplacements;
+ ExprReplacements = exprReplacements;
+
+ }
+ public override IEnumerable<Statement> SubStatements {
+ get {
+ // The SkeletonStatement is really a modification of its inner statement S. Therefore,
+ // we don't consider S to be a substatement. Instead, the substatements of S are the
+ // substatements of the SkeletonStatement. In the case the SkeletonStatement modifies
+ // S by omitting its body (which is true only for loops), there are no substatements.
+ if (!BodyOmitted) {
+ foreach (var s in S.SubStatements) {
+ yield return s;
+ }
+ }
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------------
+
+ public abstract class TokenWrapper : IToken
+ {
+ protected readonly IToken WrappedToken;
+ protected TokenWrapper(IToken wrappedToken) {
+ Contract.Requires(wrappedToken != null);
+ WrappedToken = wrappedToken;
+ }
+
+ public int col {
+ get { return WrappedToken.col; }
+ set { throw new NotSupportedException(); }
+ }
+ public virtual string filename {
+ get { return WrappedToken.filename; }
+ set { throw new NotSupportedException(); }
+ }
+ public bool IsValid {
+ get { return WrappedToken.IsValid; }
+ }
+ public int kind {
+ get { return WrappedToken.kind; }
+ set { throw new NotSupportedException(); }
+ }
+ public int line {
+ get { return WrappedToken.line; }
+ set { throw new NotSupportedException(); }
+ }
+ public int pos {
+ get { return WrappedToken.pos; }
+ set { throw new NotSupportedException(); }
+ }
+ public string val {
+ get { return WrappedToken.val; }
+ set { throw new NotSupportedException(); }
+ }
+ }
+
+ public class NestedToken : TokenWrapper
+ {
+ public NestedToken(IToken outer, IToken inner)
+ : base(outer)
+ {
+ Contract.Requires(outer != null);
+ Contract.Requires(inner != null);
+ Inner = inner;
+ }
+ public IToken Outer { get { return WrappedToken; } }
+ public readonly IToken Inner;
+ }
+
+ // ------------------------------------------------------------------------------------------------------
+
+ public abstract class Expression
+ {
+ public readonly IToken tok;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(tok != null);
+ }
+
+ [Pure]
+ public bool WasResolved()
+ {
+ return Type != null;
+ }
+
+ public Expression Resolved {
+ get {
+ Contract.Requires(WasResolved()); // should be called only on resolved expressions; this approximates that precondition
+ Expression r = this;
+ while (true) {
+ var rr = r as ConcreteSyntaxExpression;
+ if (rr == null) { break; }
+ r = rr.ResolvedExpression;
+ }
+ return r;
+ }
+ }
+
+ protected Type type;
+ public Type Type { // filled in during resolution
+ [Verify(false)] // TODO: how do we allow Type.get to modify type and still be [Pure]?
+ [Additive] // validity of proper subclasses is not required
+ get {
+ Contract.Ensures(type != null || Contract.Result<Type>() == null); // useful in conjunction with postcondition of constructor
+ return type == null ? null : type.Normalize();
+ }
+ [NoDefaultContract] // no particular validity of 'this' is required, except that it not be committed
+ set {
+ Contract.Requires(cce.IsValid(this));
+ Contract.Requires(!WasResolved()); // set it only once
+ Contract.Requires(value != null);
+ //modifies type;
+ type = value.Normalize();
+ }
+ }
+
+ public Expression(IToken tok) {
+ Contract.Requires(tok != null);
+ Contract.Ensures(type == null); // we would have liked to have written Type==null, but that's not admissible or provable
+
+ this.tok = tok;
+ }
+
+ /// <summary>
+ /// Returns the non-null subexpressions of the Expression.
+ /// </summary>
+ public virtual IEnumerable<Expression> SubExpressions {
+ get { yield break; }
+ }
+ }
+
+ /// <summary>
+ /// Instances of this class are introduced during resolution to indicate that a static method or function has
+ /// been invoked without specifying a receiver (that is, by just giving the name of the enclosing class).
+ /// </summary>
+ public class StaticReceiverExpr : LiteralExpr
+ {
+ public StaticReceiverExpr(IToken tok, ClassDecl cl)
+ : base(tok) // constructs a LiteralExpr representing the 'null' literal
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(cl != null);
+ var typeArgs = new List<Type>();
+ foreach (var ta in cl.TypeArgs) {
+ typeArgs.Add(new InferredTypeProxy());
+ }
+ Type = new UserDefinedType(tok, cl.Name, cl, typeArgs);
+ }
+ }
+
+ public class LiteralExpr : Expression {
+ public readonly object Value;
+
+ [Pure]
+ public static bool IsTrue(Expression e) {
+ Contract.Requires(e != null);
+ if (e is LiteralExpr) {
+ LiteralExpr le = (LiteralExpr)e;
+ return le.Value is bool && (bool)le.Value;
+ } else {
+ return false;
+ }
+ }
+
+ public LiteralExpr(IToken tok)
+ : base(tok) { // represents the Dafny literal "null"
+ Contract.Requires(tok != null);
+ this.Value = null;
+ }
+
+ public LiteralExpr(IToken tok, BigInteger n)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(0 <= n.Sign);
+
+ this.Value = n;
+ }
+
+ public LiteralExpr(IToken tok, int n) :base(tok){
+ Contract.Requires(tok != null);
+ Contract.Requires(0 <= n);
+
+ this.Value = new BigInteger(n);
+ }
+
+ public LiteralExpr(IToken tok, bool b)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ this.Value = b;
+
+ }
+ }
+
+ public class DatatypeValue : Expression {
+ public readonly string DatatypeName;
+ public readonly string MemberName;
+ public readonly List<Expression/*!*/>/*!*/ Arguments;
+ public DatatypeCtor Ctor; // filled in by resolution
+ public List<Type/*!*/> InferredTypeArgs = new List<Type>(); // filled in by resolution
+ public bool IsCoCall; // filled in by resolution
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(DatatypeName != null);
+ Contract.Invariant(MemberName != null);
+ Contract.Invariant(cce.NonNullElements(Arguments));
+ Contract.Invariant(cce.NonNullElements(InferredTypeArgs));
+ }
+
+ public DatatypeValue(IToken tok, string datatypeName, string memberName, [Captured] List<Expression/*!*/>/*!*/ arguments)
+ : base(tok) {
+ Contract.Requires(cce.NonNullElements(arguments));
+ Contract.Requires(tok != null);
+ Contract.Requires(datatypeName != null);
+ Contract.Requires(memberName != null);
+ this.DatatypeName = datatypeName;
+ this.MemberName = memberName;
+ this.Arguments = arguments;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get { return Arguments; }
+ }
+ }
+
+ public class ThisExpr : Expression {
+ public ThisExpr(IToken tok)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ }
+ }
+ public class ExpressionPair {
+ public Expression A, B;
+ public ExpressionPair(Expression a, Expression b) {
+ Contract.Requires(a != null);
+ Contract.Requires(b != null);
+ A = a;
+ B = b;
+ }
+ }
+
+ public class ImplicitThisExpr : ThisExpr {
+ public ImplicitThisExpr(IToken tok)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ }
+ }
+
+ public class IdentifierExpr : Expression
+ {
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Name != null);
+ }
+
+ public readonly string Name;
+ public IVariable Var; // filled in by resolution
+
+ public IdentifierExpr(IToken tok, string name)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Name = name;
+ }
+ }
+
+ /// <summary>
+ /// If an "AutoGhostIdentifierExpr" is used as the out-parameter of a ghost method or
+ /// a method with a ghost parameter, resolution will change the .Var's .IsGhost to true
+ /// automatically. This class is intended to be used only as a communicate between the
+ /// parser and parts of the resolver.
+ /// </summary>
+ public class AutoGhostIdentifierExpr : IdentifierExpr
+ {
+ public AutoGhostIdentifierExpr(IToken tok, string name)
+ : base(tok, name) { }
+ }
+
+ public abstract class DisplayExpression : Expression {
+ public readonly List<Expression/*!*/>/*!*/ Elements;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(cce.NonNullElements(Elements));
+ }
+
+ public DisplayExpression(IToken tok, List<Expression/*!*/>/*!*/ elements)
+ : base(tok) {
+ Contract.Requires(cce.NonNullElements(elements));
+ Elements = elements;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get { return Elements; }
+ }
+ }
+
+ public class SetDisplayExpr : DisplayExpression {
+ public SetDisplayExpr(IToken tok, List<Expression/*!*/>/*!*/ elements)
+ : base(tok, elements) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(elements));
+ }
+ }
+
+ public class MultiSetDisplayExpr : DisplayExpression {
+ public MultiSetDisplayExpr(IToken tok, List<Expression/*!*/>/*!*/ elements) : base(tok, elements) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(elements));
+ }
+ }
+
+ public class MapDisplayExpr : Expression {
+ public List<ExpressionPair/*!*/>/*!*/ Elements;
+ public MapDisplayExpr(IToken tok, List<ExpressionPair/*!*/>/*!*/ elements)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(elements));
+ Elements = elements;
+ }
+ }
+ public class SeqDisplayExpr : DisplayExpression {
+ public SeqDisplayExpr(IToken tok, List<Expression/*!*/>/*!*/ elements)
+ : base(tok, elements) {
+ Contract.Requires(cce.NonNullElements(elements));
+ Contract.Requires(tok != null);
+ }
+ }
+
+ public class FieldSelectExpr : Expression {
+ public readonly Expression Obj;
+ public readonly string FieldName;
+ public Field Field; // filled in by resolution
+
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Obj != null);
+ Contract.Invariant(FieldName != null);
+ }
+
+ public FieldSelectExpr(IToken tok, Expression obj, string fieldName)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(obj != null);
+ Contract.Requires(fieldName != null);
+ this.Obj = obj;
+ this.FieldName = fieldName;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get { yield return Obj; }
+ }
+ }
+
+ public class SeqSelectExpr : Expression {
+ public readonly bool SelectOne; // false means select a range
+ public readonly Expression Seq;
+ public readonly Expression E0;
+ public readonly Expression E1;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Seq != null);
+ Contract.Invariant(!SelectOne || E1 == null);
+ }
+
+ public SeqSelectExpr(IToken tok, bool selectOne, Expression seq, Expression e0, Expression e1)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(seq != null);
+ Contract.Requires(!selectOne || e1 == null);
+
+ SelectOne = selectOne;
+ Seq = seq;
+ E0 = e0;
+ E1 = e1;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return Seq;
+ if (E0 != null) yield return E0;
+ if (E1 != null) yield return E1;
+ }
+ }
+ }
+
+ public class MultiSelectExpr : Expression {
+ public readonly Expression Array;
+ public readonly List<Expression> Indices;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Array != null);
+ Contract.Invariant(cce.NonNullElements(Indices));
+ Contract.Invariant(1 <= Indices.Count);
+ }
+
+ public MultiSelectExpr(IToken tok, Expression array, List<Expression> indices)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(array != null);
+ Contract.Requires(cce.NonNullElements(indices) && 1 <= indices.Count);
+
+ Array = array;
+ Indices = indices;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return Array;
+ foreach (var e in Indices) {
+ yield return e;
+ }
+ }
+ }
+ }
+
+ public class SeqUpdateExpr : Expression {
+ public readonly Expression Seq;
+ public readonly Expression Index;
+ public readonly Expression Value;
+
+ public SeqUpdateExpr(IToken tok, Expression seq, Expression index, Expression val)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(seq != null);
+ Contract.Requires(index != null);
+ Contract.Requires(val != null);
+ Seq = seq;
+ Index = index;
+ Value = val;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return Seq;
+ yield return Index;
+ yield return Value;
+ }
+ }
+ }
+
+ public class FunctionCallExpr : Expression {
+ public readonly string/*!*/ Name;
+ public readonly Expression/*!*/ Receiver;
+ public readonly IToken OpenParen; // can be null if Args.Count == 0
+ public readonly List<Expression/*!*/>/*!*/ Args;
+ public Dictionary<TypeParameter, Type> TypeArgumentSubstitutions; // created, initialized, and used by resolution (could be deleted once all of resolution is done)
+ public enum CoCallResolution { No, Yes, NoBecauseFunctionHasSideEffects, NoBecauseRecursiveCallsAreNotAllowedInThisContext, NoBecauseIsNotGuarded }
+ public CoCallResolution CoCall = CoCallResolution.No; // indicates whether or not the call is a co-recursive call; filled in by resolution
+
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Name != null);
+ Contract.Invariant(Receiver != null);
+ Contract.Invariant(cce.NonNullElements(Args));
+ }
+
+ public Function Function; // filled in by resolution
+
+ [Captured]
+ public FunctionCallExpr(IToken tok, string fn, Expression receiver, IToken openParen, [Captured] List<Expression/*!*/>/*!*/ args)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(fn != null);
+ Contract.Requires(receiver != null);
+ Contract.Requires(cce.NonNullElements(args));
+ Contract.Requires(openParen != null || args.Count == 0);
+ Contract.Ensures(type == null);
+ Contract.Ensures(cce.Owner.Same(this, receiver));
+
+ this.Name = fn;
+ cce.Owner.AssignSame(this, receiver);
+ this.Receiver = receiver;
+ this.OpenParen = openParen;
+ this.Args = args;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ if (!Function.IsStatic) {
+ yield return Receiver;
+ }
+ foreach (var e in Args) {
+ yield return e;
+ }
+ }
+ }
+ }
+
+ public class OldExpr : Expression {
+ [Peer]
+ public readonly Expression E;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(E != null);
+ }
+
+ [Captured]
+ public OldExpr(IToken tok, Expression expr)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(expr != null);
+ cce.Owner.AssignSame(this, expr);
+ E = expr;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get { yield return E; }
+ }
+ }
+
+ public class MultiSetFormingExpr : Expression
+ {
+ [Peer]
+ public readonly Expression E;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(E != null);
+ }
+
+ [Captured]
+ public MultiSetFormingExpr(IToken tok, Expression expr)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(expr != null);
+ cce.Owner.AssignSame(this, expr);
+ E = expr;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get { yield return E; }
+ }
+ }
+ public class FreshExpr : Expression {
+ public readonly Expression E;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(E != null);
+ }
+
+ public FreshExpr(IToken tok, Expression expr)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(expr != null);
+ E = expr;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get { yield return E; }
+ }
+ }
+
+ public class UnaryExpr : Expression
+ {
+ public enum Opcode {
+ Not,
+ SetChoose, // Important: SetChoose is not a function, so it can only be used in a statement context (in particular, the RHS of an assignment)
+ SeqLength
+ }
+ public readonly Opcode Op;
+ public readonly Expression E;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(E != null);
+ }
+
+ public UnaryExpr(IToken tok, Opcode op, Expression e)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(e != null);
+ this.Op = op;
+ this.E = e;
+
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get { yield return E; }
+ }
+ }
+
+ public class BinaryExpr : Expression {
+ public enum Opcode {
+ Iff,
+ Imp,
+ And,
+ Or,
+ Eq,
+ Neq,
+ Lt,
+ Le,
+ Ge,
+ Gt,
+ Disjoint,
+ In,
+ NotIn,
+ Add,
+ Sub,
+ Mul,
+ Div,
+ Mod
+ }
+ public readonly Opcode Op;
+ public enum ResolvedOpcode {
+ // logical operators
+ Iff,
+ Imp,
+ And,
+ Or,
+ // non-collection types
+ EqCommon,
+ NeqCommon,
+ // integers
+ Lt,
+ Le,
+ Ge,
+ Gt,
+ Add,
+ Sub,
+ Mul,
+ Div,
+ Mod,
+ // sets
+ SetEq,
+ SetNeq,
+ ProperSubset,
+ Subset,
+ Superset,
+ ProperSuperset,
+ Disjoint,
+ InSet,
+ NotInSet,
+ Union,
+ Intersection,
+ SetDifference,
+ // multi-sets
+ MultiSetEq,
+ MultiSetNeq,
+ MultiSubset,
+ MultiSuperset,
+ ProperMultiSubset,
+ ProperMultiSuperset,
+ MultiSetDisjoint,
+ InMultiSet,
+ NotInMultiSet,
+ MultiSetUnion,
+ MultiSetIntersection,
+ MultiSetDifference,
+ // Sequences
+ SeqEq,
+ SeqNeq,
+ ProperPrefix,
+ Prefix,
+ Concat,
+ InSeq,
+ NotInSeq,
+ // Maps
+ MapEq,
+ MapNeq,
+ InMap,
+ NotInMap,
+ MapDisjoint,
+ MapUnion,
+ // datatypes
+ RankLt,
+ RankGt
+ }
+ public ResolvedOpcode ResolvedOp; // filled in by resolution
+
+ public static Opcode ResolvedOp2SyntacticOp(ResolvedOpcode rop) {
+ switch (rop) {
+ case ResolvedOpcode.Iff: return Opcode.Iff;
+ case ResolvedOpcode.Imp: return Opcode.Imp;
+ case ResolvedOpcode.And: return Opcode.And;
+ case ResolvedOpcode.Or: return Opcode.Or;
+
+ case ResolvedOpcode.EqCommon:
+ case ResolvedOpcode.SetEq:
+ case ResolvedOpcode.MultiSetEq:
+ case ResolvedOpcode.SeqEq:
+ case ResolvedOpcode.MapEq:
+ return Opcode.Eq;
+
+ case ResolvedOpcode.NeqCommon:
+ case ResolvedOpcode.SetNeq:
+ case ResolvedOpcode.MultiSetNeq:
+ case ResolvedOpcode.SeqNeq:
+ case ResolvedOpcode.MapNeq:
+ return Opcode.Neq;
+
+ case ResolvedOpcode.Lt:
+ case ResolvedOpcode.ProperSubset:
+ case ResolvedOpcode.ProperMultiSuperset:
+ case ResolvedOpcode.ProperPrefix:
+ case ResolvedOpcode.RankLt:
+ return Opcode.Lt;
+
+ case ResolvedOpcode.Le:
+ case ResolvedOpcode.Subset:
+ case ResolvedOpcode.MultiSubset:
+ case ResolvedOpcode.Prefix:
+ return Opcode.Le;
+
+ case ResolvedOpcode.Ge:
+ case ResolvedOpcode.Superset:
+ case ResolvedOpcode.MultiSuperset:
+ return Opcode.Ge;
+
+ case ResolvedOpcode.Gt:
+ case ResolvedOpcode.ProperSuperset:
+ case ResolvedOpcode.ProperMultiSubset:
+ case ResolvedOpcode.RankGt:
+ return Opcode.Gt;
+
+ case ResolvedOpcode.Add:
+ case ResolvedOpcode.Union:
+ case ResolvedOpcode.MultiSetUnion:
+ case ResolvedOpcode.MapUnion:
+ case ResolvedOpcode.Concat:
+ return Opcode.Add;
+
+ case ResolvedOpcode.Sub:
+ case ResolvedOpcode.SetDifference:
+ case ResolvedOpcode.MultiSetDifference:
+ return Opcode.Sub;
+
+ case ResolvedOpcode.Mul:
+ case ResolvedOpcode.Intersection:
+ case ResolvedOpcode.MultiSetIntersection:
+ return Opcode.Mul;
+
+ case ResolvedOpcode.Div: return Opcode.Div;
+ case ResolvedOpcode.Mod: return Opcode.Mod;
+
+ case ResolvedOpcode.Disjoint:
+ case ResolvedOpcode.MultiSetDisjoint:
+ case ResolvedOpcode.MapDisjoint:
+ return Opcode.Disjoint;
+
+ case ResolvedOpcode.InSet:
+ case ResolvedOpcode.InMultiSet:
+ case ResolvedOpcode.InSeq:
+ case ResolvedOpcode.InMap:
+ return Opcode.In;
+
+ case ResolvedOpcode.NotInSet:
+ case ResolvedOpcode.NotInMultiSet:
+ case ResolvedOpcode.NotInSeq:
+ case ResolvedOpcode.NotInMap:
+ return Opcode.NotIn;
+
+ default:
+ Contract.Assert(false); // unexpected ResolvedOpcode
+ return Opcode.Add; // please compiler
+ }
+ }
+
+ public static string OpcodeString(Opcode op) {
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ switch (op) {
+ case Opcode.Iff:
+ return "<==>";
+ case Opcode.Imp:
+ return "==>";
+ case Opcode.And:
+ return "&&";
+ case Opcode.Or:
+ return "||";
+ case Opcode.Eq:
+ return "==";
+ case Opcode.Lt:
+ return "<";
+ case Opcode.Gt:
+ return ">";
+ case Opcode.Le:
+ return "<=";
+ case Opcode.Ge:
+ return ">=";
+ case Opcode.Neq:
+ return "!=";
+ case Opcode.Disjoint:
+ return "!!";
+ case Opcode.In:
+ return "in";
+ case Opcode.NotIn:
+ return "!in";
+ case Opcode.Add:
+ return "+";
+ case Opcode.Sub:
+ return "-";
+ case Opcode.Mul:
+ return "*";
+ case Opcode.Div:
+ return "/";
+ case Opcode.Mod:
+ return "%";
+ default:
+ Contract.Assert(false);
+ throw new cce.UnreachableException(); // unexpected operator
+ }
+ }
+ public readonly Expression E0;
+ public readonly Expression E1;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(E0 != null);
+ Contract.Invariant(E1 != null);
+ }
+
+
+ public BinaryExpr(IToken tok, Opcode op, Expression e0, Expression e1)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(e0 != null);
+ Contract.Requires(e1 != null);
+ this.Op = op;
+ this.E0 = e0;
+ this.E1 = e1;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return E0;
+ yield return E1;
+ }
+ }
+ }
+
+ public class LetExpr : Expression
+ {
+ public readonly List<BoundVar> Vars;
+ public readonly List<Expression> RHSs;
+ public readonly Expression Body;
+ public LetExpr(IToken tok, List<BoundVar> vars, List<Expression> rhss, Expression body)
+ : base(tok) {
+ Vars = vars;
+ RHSs = rhss;
+ Body = body;
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ foreach (var rhs in RHSs) {
+ yield return rhs;
+ }
+ yield return Body;
+ }
+ }
+ }
+ // Represents expr Name: Body
+ // or expr Name: (assert Body == Contract; Body)
+ public class NamedExpr : Expression
+ {
+ public readonly string Name;
+ public readonly Expression Body;
+ public readonly Expression Contract;
+ public readonly IToken ReplacerToken;
+
+ public NamedExpr(IToken tok, string p, Expression body)
+ : base(tok) {
+ Name = p;
+ Body = body;
+ }
+ public NamedExpr(IToken tok, string p, Expression body, Expression contract, IToken token)
+ : base(tok) {
+ Name = p;
+ Body = body;
+ Contract = contract;
+ ReplacerToken = token;
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return Body;
+ if (Contract != null) yield return Contract;
+ }
+ }
+ }
+
+ /// <summary>
+ /// A ComprehensionExpr has the form:
+ /// BINDER x Attributes | Range(x) :: Term(x)
+ /// When BINDER is "forall" or "exists", the range may be "null" (which stands for the logical value "true").
+ /// For other BINDERs (currently, "set"), the range is non-null.
+ /// where "Attributes" is optional, and "| Range(x)" is optional and defaults to "true".
+ /// Currently, BINDER is one of the logical quantifiers "exists" or "forall".
+ /// </summary>
+ public abstract class ComprehensionExpr : Expression {
+ public readonly List<BoundVar/*!*/>/*!*/ BoundVars;
+ public readonly Expression Range;
+ public readonly Expression/*!*/ Term;
+
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(BoundVars != null);
+ Contract.Invariant(Term != null);
+ }
+
+ public readonly Attributes Attributes;
+
+ public abstract class BoundedPool { }
+ public class IntBoundedPool : BoundedPool
+ {
+ public readonly Expression LowerBound;
+ public readonly Expression UpperBound;
+ public IntBoundedPool(Expression lowerBound, Expression upperBound) {
+ LowerBound = lowerBound;
+ UpperBound = upperBound;
+ }
+ }
+ public class SetBoundedPool : BoundedPool
+ {
+ public readonly Expression Set;
+ public SetBoundedPool(Expression set) { Set = set; }
+ }
+ public class SuperSetBoundedPool : BoundedPool
+ {
+ public readonly Expression LowerBound;
+ public SuperSetBoundedPool(Expression set) { LowerBound = set; }
+ }
+ public class MapBoundedPool : BoundedPool
+ {
+ public readonly Expression Map;
+ public MapBoundedPool(Expression map) { Map = map; }
+ }
+ public class SeqBoundedPool : BoundedPool
+ {
+ public readonly Expression Seq;
+ public SeqBoundedPool(Expression seq) { Seq = seq; }
+ }
+ public class BoolBoundedPool : BoundedPool
+ {
+ }
+ public class DatatypeBoundedPool : BoundedPool
+ {
+ public readonly DatatypeDecl Decl;
+ public DatatypeBoundedPool(DatatypeDecl d) { Decl = d; }
+ }
+
+ public List<BoundedPool> Bounds; // initialized and filled in by resolver
+ // invariant Bounds == null || Bounds.Count == BoundVars.Count;
+ public List<BoundVar> MissingBounds; // filled in during resolution; remains "null" if bounds can be found
+ // invariant Bounds == null || MissingBounds == null;
+
+ public ComprehensionExpr(IToken/*!*/ tok, List<BoundVar/*!*/>/*!*/ bvars, Expression range, Expression/*!*/ term, Attributes attrs)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(bvars));
+ Contract.Requires(term != null);
+
+ this.BoundVars = bvars;
+ this.Range = range;
+ this.Term = term;
+ this.Attributes = attrs;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ if (Range != null) { yield return Range; }
+ yield return Term;
+ }
+ }
+ }
+
+ public abstract class QuantifierExpr : ComprehensionExpr {
+ public QuantifierExpr(IToken/*!*/ tok, List<BoundVar/*!*/>/*!*/ bvars, Expression range, Expression/*!*/ term, Attributes attrs)
+ : base(tok, bvars, range, term, attrs) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(bvars));
+ Contract.Requires(term != null);
+ }
+ public abstract Expression/*!*/ LogicalBody();
+ }
+
+ public class ForallExpr : QuantifierExpr {
+ public ForallExpr(IToken tok, List<BoundVar/*!*/>/*!*/ bvars, Expression range, Expression term, Attributes attrs)
+ : base(tok, bvars, range, term, attrs) {
+ Contract.Requires(cce.NonNullElements(bvars));
+ Contract.Requires(tok != null);
+ Contract.Requires(term != null);
+ }
+ public override Expression/*!*/ LogicalBody() {
+ if (Range == null) {
+ return Term;
+ }
+ var body = new BinaryExpr(Term.tok, BinaryExpr.Opcode.Imp, Range, Term);
+ body.ResolvedOp = BinaryExpr.ResolvedOpcode.Imp;
+ body.Type = Term.Type;
+ return body;
+ }
+ }
+
+ public class ExistsExpr : QuantifierExpr {
+ public ExistsExpr(IToken tok, List<BoundVar/*!*/>/*!*/ bvars, Expression range, Expression term, Attributes attrs)
+ : base(tok, bvars, range, term, attrs) {
+ Contract.Requires(cce.NonNullElements(bvars));
+ Contract.Requires(tok != null);
+ Contract.Requires(term != null);
+ }
+ public override Expression/*!*/ LogicalBody() {
+ if (Range == null) {
+ return Term;
+ }
+ var body = new BinaryExpr(Term.tok, BinaryExpr.Opcode.And, Range, Term);
+ body.ResolvedOp = BinaryExpr.ResolvedOpcode.And;
+ body.Type = Term.Type;
+ return body;
+ }
+ }
+
+ public class SetComprehension : ComprehensionExpr
+ {
+ public readonly bool TermIsImplicit;
+
+ public SetComprehension(IToken/*!*/ tok, List<BoundVar/*!*/>/*!*/ bvars, Expression/*!*/ range, Expression term)
+ : base(tok, bvars, range, term ?? new IdentifierExpr(tok, bvars[0].Name), null) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(bvars));
+ Contract.Requires(1 <= bvars.Count);
+ Contract.Requires(range != null);
+
+ TermIsImplicit = term == null;
+ }
+ }
+ public class MapComprehension : ComprehensionExpr
+ {
+ public MapComprehension(IToken/*!*/ tok, List<BoundVar/*!*/>/*!*/ bvars, Expression/*!*/ range, Expression term)
+ : base(tok, bvars, range, term, null) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(bvars));
+ Contract.Requires(1 <= bvars.Count);
+ Contract.Requires(range != null);
+ Contract.Requires(term != null);
+ }
+ }
+
+ public class WildcardExpr : Expression
+ { // a WildcardExpr can occur only in reads clauses and a loop's decreases clauses (with different meanings)
+ public WildcardExpr(IToken tok)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ }
+ }
+
+ public abstract class PredicateExpr : Expression
+ {
+ public readonly Expression Guard;
+ public readonly Expression Body;
+ public PredicateExpr(IToken tok, Expression guard, Expression body)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(guard != null);
+ Contract.Requires(body != null);
+ Guard = guard;
+ Body = body;
+ }
+ public abstract string Kind { get; }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return Guard;
+ yield return Body;
+ }
+ }
+ }
+
+ public class AssertExpr : PredicateExpr
+ {
+ public AssertExpr(IToken tok, Expression guard, Expression body)
+ : base(tok, guard, body) {
+ Contract.Requires(tok != null);
+ Contract.Requires(guard != null);
+ Contract.Requires(body != null);
+ }
+ public override string Kind { get { return "assert"; } }
+ }
+
+ public class AssumeExpr : PredicateExpr
+ {
+ public AssumeExpr(IToken tok, Expression guard, Expression body)
+ : base(tok, guard, body) {
+ Contract.Requires(tok != null);
+ Contract.Requires(guard != null);
+ Contract.Requires(body != null);
+ }
+ public override string Kind { get { return "assume"; } }
+ }
+
+ public class ITEExpr : Expression
+ {
+ public readonly Expression Test;
+ public readonly Expression Thn;
+ public readonly Expression Els;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Test != null);
+ Contract.Invariant(Thn != null);
+ Contract.Invariant(Els != null);
+ }
+
+ public ITEExpr(IToken tok, Expression test, Expression thn, Expression els)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(test != null);
+ Contract.Requires(thn != null);
+ Contract.Requires(els != null);
+ this.Test = test;
+ this.Thn = thn;
+ this.Els = els;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return Test;
+ yield return Thn;
+ yield return Els;
+ }
+ }
+ }
+
+ 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;
+ public readonly List<DatatypeCtor/*!*/> MissingCases = new List<DatatypeCtor>(); // filled in during resolution
+
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Source != null);
+ Contract.Invariant(cce.NonNullElements(Cases));
+ Contract.Invariant(cce.NonNullElements(MissingCases));
+ }
+
+ public MatchExpr(IToken tok, Expression source, [Captured] List<MatchCaseExpr/*!*/>/*!*/ cases)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(source != null);
+ Contract.Requires(cce.NonNullElements(cases));
+ this.Source = source;
+ this.Cases = cases;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ yield return Source;
+ foreach (var mc in Cases) {
+ yield return mc.Body;
+ }
+ }
+ }
+ }
+
+ public abstract class MatchCase
+ {
+ public readonly IToken tok;
+ public readonly string Id;
+ public DatatypeCtor Ctor; // filled in by resolution
+ public readonly List<BoundVar/*!*/>/*!*/ Arguments;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(tok != null);
+ Contract.Invariant(Id != null);
+ Contract.Invariant(cce.NonNullElements(Arguments));
+ }
+
+ public MatchCase(IToken tok, string id, [Captured] List<BoundVar/*!*/>/*!*/ arguments) {
+ Contract.Requires(tok != null);
+ Contract.Requires(id != null);
+ Contract.Requires(cce.NonNullElements(arguments));
+ this.tok = tok;
+ this.Id = id;
+ this.Arguments = arguments;
+ }
+ }
+
+ public class MatchCaseExpr : MatchCase
+ {
+ public readonly Expression/*!*/ Body;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Body != null);
+ }
+
+ public MatchCaseExpr(IToken tok, string id, [Captured] List<BoundVar/*!*/>/*!*/ arguments, Expression body)
+ : base(tok, id, arguments)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(id != null);
+ Contract.Requires(cce.NonNullElements(arguments));
+ Contract.Requires(body != null);
+ this.Body = body;
+ }
+ }
+
+ public class BoxingCastExpr : Expression { // a BoxingCastExpr is used only as a temporary placeholding during translation
+ public readonly Expression E;
+ public readonly Type FromType;
+ public readonly Type ToType;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(E != null);
+ Contract.Invariant(FromType != null);
+ Contract.Invariant(ToType != null);
+ }
+
+ public BoxingCastExpr(Expression e, Type fromType, Type toType)
+ : base(e.tok) {
+ Contract.Requires(e != null);
+ Contract.Requires(fromType != null);
+ Contract.Requires(toType != null);
+
+ E = e;
+ FromType = fromType;
+ ToType = toType;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get { yield return E; }
+ }
+ }
+
+ public class UnboxingCastExpr : Expression { // an UnboxingCastExpr is used only as a temporary placeholding during translation
+ public readonly Expression E;
+ public readonly Type FromType;
+ public readonly Type ToType;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(E != null);
+ Contract.Invariant(FromType != null);
+ Contract.Invariant(ToType != null);
+ }
+
+ public UnboxingCastExpr(Expression e, Type fromType, Type toType)
+ : base(e.tok) {
+ Contract.Requires(e != null);
+ Contract.Requires(fromType != null);
+ Contract.Requires(toType != null);
+
+ E = e;
+ FromType = fromType;
+ ToType = toType;
+ }
+
+ public override IEnumerable<Expression> SubExpressions {
+ get { yield return E; }
+ }
+ }
+
+
+ public class MaybeFreeExpression {
+ public readonly Expression E;
+ public readonly bool IsFree;
+
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(E != null);
+ }
+
+ private Attributes attributes;
+ public Attributes Attributes {
+ get {
+ return attributes;
+ }
+ set {
+ attributes = value;
+ }
+ }
+
+ public bool HasAttributes() {
+ return Attributes != null;
+ }
+
+ public MaybeFreeExpression(Expression e)
+ : this(e, false, null)
+ {
+ Contract.Requires(e != null);
+ }
+
+ public MaybeFreeExpression(Expression e, bool isFree)
+ : this(e, isFree, null)
+ {
+ Contract.Requires(e != null);
+ }
+
+ public MaybeFreeExpression(Expression e, bool isFree, Attributes attrs) {
+ Contract.Requires(e != null);
+ E = e;
+ IsFree = isFree;
+ Attributes = attrs;
+ }
+ }
+
+
+ public class FrameExpression {
+ public readonly IToken tok;
+ public readonly Expression E; // may be a WildcardExpr
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(E != null);
+ Contract.Invariant(!(E is WildcardExpr) || FieldName == null && Field == null);
+ }
+
+ public readonly string FieldName;
+ public Field Field; // filled in during resolution (but is null if FieldName is)
+
+ /// <summary>
+ /// If a "fieldName" is given, then "tok" denotes its source location. Otherwise, "tok"
+ /// denotes the source location of "e".
+ /// </summary>
+ public FrameExpression(IToken tok, Expression e, string fieldName) {
+ Contract.Requires(tok != null);
+ Contract.Requires(e != null);
+ Contract.Requires(!(e is WildcardExpr) || fieldName == null);
+ this.tok = tok;
+ E = e;
+ FieldName = fieldName;
+ }
+ }
+
+ /// <summary>
+ /// This class represents a piece of concrete syntax in the parse tree. During resolution,
+ /// it gets "replaced" by the expression in "ResolvedExpression".
+ /// </summary>
+ public abstract class ConcreteSyntaxExpression : Expression
+ {
+ public Expression ResolvedExpression; // filled in during resolution; after resolution, manipulation of "this" should proceed as with manipulating "this.ResolvedExpression"
+ public ConcreteSyntaxExpression(IToken tok)
+ : base(tok) {
+ }
+ public override IEnumerable<Expression> SubExpressions {
+ get {
+ if (ResolvedExpression != null) {
+ yield return ResolvedExpression;
+ }
+ }
+ }
+ }
+
+ public class ParensExpression : ConcreteSyntaxExpression
+ {
+ public readonly Expression E;
+ public ParensExpression(IToken tok, Expression e)
+ : base(tok) {
+ E = e;
+ }
+ }
+
+ public class ChainingExpression : ConcreteSyntaxExpression
+ {
+ public readonly List<Expression> Operands;
+ public readonly List<BinaryExpr.Opcode> Operators;
+ public readonly Expression E;
+ public ChainingExpression(IToken tok, List<Expression> operands, List<BinaryExpr.Opcode> operators, Expression desugaring)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(operands != null);
+ Contract.Requires(operators != null);
+ Contract.Requires(desugaring != null);
+ Contract.Requires(operands.Count == operators.Count + 1);
+
+ Operands = operands;
+ Operators = operators;
+ E = desugaring;
+ }
+ }
+
+ /// <summary>
+ /// An ExprDotName desugars into either a FieldSelectExpr or a FunctionCallExpr (with a parameterless predicate function).
+ /// </summary>
+ public class ExprDotName : ConcreteSyntaxExpression
+ {
+ public readonly Expression Obj;
+ public readonly string SuffixName;
+
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(Obj != null);
+ Contract.Invariant(SuffixName != null);
+ }
+
+ public ExprDotName(IToken tok, Expression obj, string suffixName)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(obj != null);
+ Contract.Requires(suffixName != null);
+ this.Obj = obj;
+ this.SuffixName = suffixName;
+ }
+ }
+
+ public class IdentifierSequence : ConcreteSyntaxExpression
+ {
+ public readonly List<IToken> Tokens;
+ public readonly IToken OpenParen;
+ public readonly List<Expression> Arguments;
+ public IdentifierSequence(List<IToken> tokens, IToken openParen, List<Expression> args)
+ : base(tokens[0]) {
+ Contract.Requires(tokens != null && 1 <= tokens.Count);
+ /* "args" is null to indicate the absence of a parenthesized suffix */
+ Contract.Requires(args == null || openParen != null);
+
+ Tokens = tokens;
+ OpenParen = openParen;
+ Arguments = args;
+ }
+ }
+
+
+ public class Specification<T> where T : class
+ {
+ public readonly List<T> Expressions;
+
+ [ContractInvariantMethod]
+ private void ObjectInvariant()
+ {
+ Contract.Invariant(Expressions == null || cce.NonNullElements<T>(Expressions));
+ }
+
+
+ public Specification(List<T> exprs, Attributes attrs)
+ {
+ Contract.Requires(exprs == null || cce.NonNullElements<T>(exprs));
+ Expressions = exprs;
+ Attributes = attrs;
+ }
+
+ private Attributes attributes;
+ public Attributes Attributes
+ {
+ get
+ {
+ return attributes;
+ }
+ set
+ {
+ attributes = value;
+ }
+ }
+
+ public bool HasAttributes()
+ {
+ return Attributes != null;
+ }
+ }
+ public abstract class TranslationTask
+ {
+
+ }
+ public class MethodCheck : TranslationTask
+ {
+ public readonly Method Refined;
+ public readonly Method Refining;
+ public MethodCheck(Method a, Method b) {
+ Refined = b;
+ Refining = a;
+ }
+ }
+ public class FunctionCheck : TranslationTask
+ {
+ public readonly Function Refined;
+ public readonly Function Refining;
+ public FunctionCheck(Function a, Function b) {
+ Refined = b;
+ Refining = a;
+ }
+ }
+}
diff --git a/Source/Dafny/DafnyMain.cs b/Source/Dafny/DafnyMain.cs
new file mode 100644
index 00000000..6091e522
--- /dev/null
+++ b/Source/Dafny/DafnyMain.cs
@@ -0,0 +1,88 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using Bpl = Microsoft.Boogie;
+
+namespace Microsoft.Dafny {
+ public class Main {
+ /// <summary>
+ /// Returns null on success, or an error string otherwise.
+ /// </summary>
+ public static string ParseCheck(List<string/*!*/>/*!*/ fileNames, string/*!*/ programName, out Program program)
+ //modifies Bpl.CommandLineOptions.Clo.XmlSink.*;
+ {
+ Contract.Requires(programName != null);
+ Contract.Requires(fileNames != null);
+ program = null;
+ ModuleDecl module = new LiteralModuleDecl(new DefaultModuleDecl(), null);
+ BuiltIns builtIns = new BuiltIns();
+ foreach (string dafnyFileName in fileNames){
+ Contract.Assert(dafnyFileName != null);
+ if (Bpl.CommandLineOptions.Clo.XmlSink != null && Bpl.CommandLineOptions.Clo.XmlSink.IsOpen) {
+ Bpl.CommandLineOptions.Clo.XmlSink.WriteFileFragment(dafnyFileName);
+ }
+ if (Bpl.CommandLineOptions.Clo.Trace)
+ {
+ Console.WriteLine("Parsing " + dafnyFileName);
+ }
+
+ int errorCount;
+ try
+ {
+ errorCount = Dafny.Parser.Parse(dafnyFileName, module, builtIns);
+ if (errorCount != 0)
+ {
+ return string.Format("{0} parse errors detected in {1}", errorCount, dafnyFileName);
+ }
+ }
+ catch (IOException e)
+ {
+ return string.Format("Error opening file \"{0}\": {1}", dafnyFileName, e.Message);
+ }
+ }
+
+ program = new Program(programName, module, builtIns);
+
+ if (DafnyOptions.O.DafnyPrintFile != null) {
+ string filename = DafnyOptions.O.DafnyPrintFile;
+ if (filename == "-") {
+ Printer pr = new Printer(System.Console.Out);
+ pr.PrintProgram(program);
+ } else {
+ using (TextWriter writer = new System.IO.StreamWriter(filename)) {
+ Printer pr = new Printer(writer);
+ pr.PrintProgram(program);
+ }
+ }
+ }
+
+ if (Bpl.CommandLineOptions.Clo.NoResolve || Bpl.CommandLineOptions.Clo.NoTypecheck) { return null; }
+
+ Dafny.Resolver r = new Dafny.Resolver(program);
+ r.ResolveProgram(program);
+ if (DafnyOptions.O.DafnyPrintResolvedFile != null) {
+ string filename = DafnyOptions.O.DafnyPrintResolvedFile;
+ if (filename == "-") {
+ Printer pr = new Printer(System.Console.Out);
+ pr.PrintProgram(program);
+ } else {
+ using (TextWriter writer = new System.IO.StreamWriter(filename)) {
+ Printer pr = new Printer(writer);
+ pr.PrintProgram(program);
+ }
+ }
+ }
+ if (r.ErrorCount != 0) {
+ return string.Format("{0} resolution/type errors detected in {1}", r.ErrorCount, programName);
+ }
+
+ return null; // success
+ }
+ }
+}
diff --git a/Source/Dafny/DafnyOptions.cs b/Source/Dafny/DafnyOptions.cs
new file mode 100644
index 00000000..d5057017
--- /dev/null
+++ b/Source/Dafny/DafnyOptions.cs
@@ -0,0 +1,155 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Diagnostics.Contracts;
+using Bpl = Microsoft.Boogie;
+
+namespace Microsoft.Dafny
+{
+ public class DafnyOptions : Bpl.CommandLineOptions
+ {
+ public DafnyOptions()
+ : base("Dafny", "Dafny program verifier") {
+ }
+
+ private static DafnyOptions clo;
+ public static DafnyOptions O {
+ get { return clo; }
+ }
+
+ public static void Install(DafnyOptions options) {
+ Contract.Requires(options != null);
+ clo = options;
+ Bpl.CommandLineOptions.Install(options);
+ }
+
+ public bool DisallowSoundnessCheating = false;
+ public int Induction = 3;
+ public int InductionHeuristic = 6;
+ public string DafnyPrelude = null;
+ public string DafnyPrintFile = null;
+ public string DafnyPrintResolvedFile = null;
+ public bool Compile = true;
+ public bool ForceCompile = false;
+ public bool SpillTargetCode = false;
+
+ protected override bool ParseOption(string name, Bpl.CommandLineOptionEngine.CommandLineParseState ps) {
+ var args = ps.args; // convenient synonym
+
+ switch (name) {
+ case "dprelude":
+ if (ps.ConfirmArgumentCount(1)) {
+ DafnyPrelude = args[ps.i];
+ }
+ return true;
+
+ case "dprint":
+ if (ps.ConfirmArgumentCount(1)) {
+ DafnyPrintFile = args[ps.i];
+ }
+ return true;
+
+ case "rprint":
+ if (ps.ConfirmArgumentCount(1)) {
+ DafnyPrintResolvedFile = args[ps.i];
+ }
+ return true;
+
+ case "compile": {
+ int compile = 0;
+ if (ps.GetNumericArgument(ref compile, 3)) {
+ // convert option to two booleans
+ Compile = compile == 1 || compile == 2;
+ ForceCompile = compile == 2;
+ }
+ return true;
+ }
+
+ case "spillTargetCode": {
+ int spill = 0;
+ if (ps.GetNumericArgument(ref spill, 2)) {
+ SpillTargetCode = spill != 0; // convert to a boolean
+ }
+ return true;
+ }
+
+ case "noCheating": {
+ int cheat = 0; // 0 is default, allows cheating
+ if (ps.GetNumericArgument(ref cheat, 2)) {
+ DisallowSoundnessCheating = cheat == 1;
+ }
+ return true;
+ }
+
+ case "induction":
+ ps.GetNumericArgument(ref Induction, 4);
+ return true;
+
+ case "inductionHeuristic":
+ ps.GetNumericArgument(ref InductionHeuristic, 7);
+ return true;
+
+ default:
+ break;
+ }
+ // not a Dafny-specific option, so defer to superclass
+ return base.ParseOption(name, ps);
+ }
+
+ public override void ApplyDefaultOptions() {
+ base.ApplyDefaultOptions();
+
+ // expand macros in filenames, now that LogPrefix is fully determined
+ ExpandFilename(ref DafnyPrelude, LogPrefix, FileTimestamp);
+ ExpandFilename(ref DafnyPrintFile, LogPrefix, FileTimestamp);
+ }
+
+ public override void AttributeUsage() {
+ // TODO: provide attribute help here
+ }
+
+ public override void Usage() {
+ Console.WriteLine(@" ---- 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)
+ /rprint:<file>
+ print Dafny program after resolving it
+ (use - as <file> to print to console)
+ /compile:<n> 0 - do not compile Dafny program
+ 1 (default) - upon successful verification of the Dafny
+ program, compile Dafny program to C# program out.cs
+ 2 - always attempt to compile Dafny program to C# program
+ out.cs, regardless of verification outcome
+ /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
+ /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
+");
+ base.Usage(); // also print the Boogie options
+ }
+ }
+}
diff --git a/Source/Dafny/DafnyPipeline.csproj b/Source/Dafny/DafnyPipeline.csproj
new file mode 100644
index 00000000..b4c2ae1e
--- /dev/null
+++ b/Source/Dafny/DafnyPipeline.csproj
@@ -0,0 +1,199 @@
+<?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>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{FE44674A-1633-4917-99F4-57635E6FA740}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>DafnyPipeline</RootNamespace>
+ <AssemblyName>DafnyPipeline</AssemblyName>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <CodeContractsAssemblyMode>0</CodeContractsAssemblyMode>
+ <SignAssembly>true</SignAssembly>
+ <AssemblyOriginatorKeyFile>..\InterimKey.snk</AssemblyOriginatorKeyFile>
+ <FileUpgradeFlags>
+ </FileUpgradeFlags>
+ <OldToolsVersion>3.5</OldToolsVersion>
+ <UpgradeBackupLocation />
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <ApplicationRevision>0</ApplicationRevision>
+ <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ <TargetFrameworkProfile>
+ </TargetFrameworkProfile>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CodeContractsEnableRuntimeChecking>False</CodeContractsEnableRuntimeChecking>
+ <CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
+ <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
+ <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
+ <CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis>
+ <CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
+ <CodeContractsBoundsObligations>False</CodeContractsBoundsObligations>
+ <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
+ <CodeContractsPointerObligations>False</CodeContractsPointerObligations>
+ <CodeContractsContainerAnalysis>False</CodeContractsContainerAnalysis>
+ <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions>
+ <CodeContractsRunInBackground>True</CodeContractsRunInBackground>
+ <CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
+ <CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
+ <CodeContractsEmitXMLDocs>False</CodeContractsEmitXMLDocs>
+ <CodeContractsCustomRewriterAssembly>
+ </CodeContractsCustomRewriterAssembly>
+ <CodeContractsCustomRewriterClass>
+ </CodeContractsCustomRewriterClass>
+ <CodeContractsLibPaths>
+ </CodeContractsLibPaths>
+ <CodeContractsExtraRewriteOptions>
+ </CodeContractsExtraRewriteOptions>
+ <CodeContractsExtraAnalysisOptions>
+ </CodeContractsExtraAnalysisOptions>
+ <CodeContractsBaseLineFile>
+ </CodeContractsBaseLineFile>
+ <CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
+ <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeContractsRuntimeSkipQuantifiers>False</CodeContractsRuntimeSkipQuantifiers>
+ <CodeContractsEnumObligations>False</CodeContractsEnumObligations>
+ <CodeContractsCacheAnalysisResults>False</CodeContractsCacheAnalysisResults>
+ <CodeContractsAnalysisWarningLevel>0</CodeContractsAnalysisWarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Checked|AnyCPU'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\Checked\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <DebugType>full</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisIgnoreBuiltInRuleSets>true</CodeAnalysisIgnoreBuiltInRuleSets>
+ <CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
+ <CodeAnalysisFailOnMissingRules>false</CodeAnalysisFailOnMissingRules>
+ <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="Basetypes, Version=2.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\Binaries\Basetypes.dll</HintPath>
+ </Reference>
+ <Reference Include="Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\Binaries\Core.dll</HintPath>
+ </Reference>
+ <Reference Include="ParserHelper">
+ <HintPath>..\..\Binaries\ParserHelper.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Numerics" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Cloner.cs" />
+ <Compile Include="Util.cs" />
+ <Compile Include="Compiler.cs" />
+ <Compile Include="DafnyAst.cs" />
+ <Compile Include="DafnyMain.cs" />
+ <Compile Include="DafnyOptions.cs" />
+ <Compile Include="Printer.cs" />
+ <Compile Include="RefinementTransformer.cs" />
+ <Compile Include="Resolver.cs" />
+ <Compile Include="Rewriter.cs" />
+ <Compile Include="SccGraph.cs" />
+ <Compile Include="Translator.cs" />
+ <Compile Include="..\version.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="cce.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Dafny.atg" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Parser.cs" />
+ <Compile Include="Scanner.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
+ <Visible>False</Visible>
+ <ProductName>Windows Installer 3.1</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ </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/Dafny/Makefile b/Source/Dafny/Makefile
new file mode 100644
index 00000000..2013b4f9
--- /dev/null
+++ b/Source/Dafny/Makefile
@@ -0,0 +1,22 @@
+COCO = Coco.exe
+
+# ###############################################################################
+# The frame files are no longer in this directory. They must be downloaded
+# from http://boogiepartners.codeplex.com/. Update the FRAME_DIR variable to
+# point to whatever directory you install that into.
+# ###############################################################################
+FRAME_DIR = ..\..\..\boogiepartners\CocoR\Modified
+
+# "all" depends on 2 files, really (Parser.cs and Scanner.cs), but they
+# are both generated in one go and I don't know a better way to tell
+# nmake that. --KRML
+all: Parser.cs
+
+Parser.cs: $(FRAME_DIR)\Scanner.frame $(FRAME_DIR)\Parser.frame Dafny.atg
+ $(COCO) Dafny.atg -namespace Microsoft.Dafny -frames $(FRAME_DIR)
+
+clean:
+ if exist Scanner.cs del Scanner.cs
+ if exist Scanner.cs.old del Scanner.cs.old
+ if exist Parser.cs del Parser.cs
+ if exist Parser.cs.old del Parser.cs.old
diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs
new file mode 100644
index 00000000..032b8a10
--- /dev/null
+++ b/Source/Dafny/Parser.cs
@@ -0,0 +1,3470 @@
+using System.Collections.Generic;
+using System.Numerics;
+using Microsoft.Boogie;
+using System.IO;
+using System.Text;
+
+
+using System;
+using System.Diagnostics.Contracts;
+
+namespace Microsoft.Dafny {
+
+
+
+public class Parser {
+ public const int _EOF = 0;
+ public const int _ident = 1;
+ public const int _digits = 2;
+ public const int _arrayToken = 3;
+ public const int _string = 4;
+ public const int _colon = 5;
+ public const int _lbrace = 6;
+ public const int _rbrace = 7;
+ public const int maxT = 115;
+
+ const bool T = true;
+ const bool x = false;
+ const int minErrDist = 2;
+
+ public Scanner/*!*/ scanner;
+ public Errors/*!*/ errors;
+
+ public Token/*!*/ t; // last recognized token
+ public Token/*!*/ la; // lookahead token
+ int errDist = minErrDist;
+
+readonly Expression/*!*/ dummyExpr;
+readonly AssignmentRhs/*!*/ dummyRhs;
+readonly FrameExpression/*!*/ dummyFrameExpr;
+readonly Statement/*!*/ dummyStmt;
+readonly Attributes.Argument/*!*/ dummyAttrArg;
+readonly ModuleDecl theModule;
+readonly BuiltIns theBuiltIns;
+int anonymousIds = 0;
+
+struct MemberModifiers {
+ public bool IsGhost;
+ public bool IsStatic;
+}
+// helper routine for parsing call statements
+///<summary>
+/// Parses top-level things (modules, classes, datatypes, class members) from "filename"
+/// and appends them in appropriate form to "module".
+/// Returns the number of parsing errors encountered.
+/// Note: first initialize the Scanner.
+///</summary>
+public static int Parse (string/*!*/ filename, ModuleDecl module, BuiltIns builtIns) /* throws System.IO.IOException */ {
+ Contract.Requires(filename != null);
+ Contract.Requires(module != null);
+ string s;
+ if (filename == "stdin.dfy") {
+ s = Microsoft.Boogie.ParserHelper.Fill(System.Console.In, new List<string>());
+ return Parse(s, filename, module, builtIns);
+ } else {
+ using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) {
+ s = Microsoft.Boogie.ParserHelper.Fill(reader, new List<string>());
+ return Parse(s, filename, module, builtIns);
+ }
+ }
+}
+///<summary>
+/// Parses top-level things (modules, classes, datatypes, class members)
+/// and appends them in appropriate form to "module".
+/// 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) {
+ Contract.Requires(s != null);
+ Contract.Requires(filename != null);
+ Contract.Requires(module != null);
+ Errors errors = new Errors();
+ return Parse(s, filename, module, builtIns, errors);
+}
+///<summary>
+/// Parses top-level things (modules, classes, datatypes, class members)
+/// and appends them in appropriate form to "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) {
+ 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);
+ Parser parser = new Parser(scanner, errors, module, builtIns);
+ parser.Parse();
+ return parser.errors.count;
+}
+public Parser(Scanner/*!*/ scanner, Errors/*!*/ errors, ModuleDecl module, BuiltIns builtIns)
+ : this(scanner, errors) // the real work
+{
+ // initialize readonly fields
+ dummyExpr = new LiteralExpr(Token.NoToken);
+ dummyRhs = new ExprRhs(dummyExpr, null);
+ dummyFrameExpr = new FrameExpression(dummyExpr.tok, dummyExpr, null);
+ dummyStmt = new ReturnStmt(Token.NoToken, null);
+ dummyAttrArg = new Attributes.Argument(Token.NoToken, "dummyAttrArg");
+ theModule = module;
+ theBuiltIns = builtIns;
+}
+
+bool IsAttribute() {
+ Token x = scanner.Peek();
+ return la.kind == _lbrace && x.kind == _colon;
+}
+/*--------------------------------------------------------------------------*/
+
+
+ public Parser(Scanner/*!*/ scanner, Errors/*!*/ errors) {
+ this.scanner = scanner;
+ this.errors = errors;
+ Token/*!*/ tok = new Token();
+ tok.val = "";
+ this.la = tok;
+ this.t = new Token(); // just to satisfy its non-null constraint
+ }
+
+ void SynErr (int n) {
+ if (errDist >= minErrDist) errors.SynErr(la.filename, la.line, la.col, n);
+ errDist = 0;
+ }
+
+ 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) {
+ Contract.Requires(tok != null);
+ Contract.Requires(msg != null);
+ errors.SemErr(tok, msg);
+ }
+
+ void Get () {
+ for (;;) {
+ t = la;
+ la = scanner.Scan();
+ if (la.kind <= maxT) { ++errDist; break; }
+
+ la = t;
+ }
+ }
+
+ void Expect (int n) {
+ if (la.kind==n) Get(); else { SynErr(n); }
+ }
+
+ bool StartOf (int s) {
+ return set[s, la.kind];
+ }
+
+ void ExpectWeak (int n, int follow) {
+ if (la.kind == n) Get();
+ else {
+ SynErr(n);
+ while (!StartOf(follow)) Get();
+ }
+ }
+
+
+ bool WeakSeparator(int n, int syFol, int repFol) {
+ int kind = la.kind;
+ if (kind == n) {Get(); return true;}
+ else if (StartOf(repFol)) {return false;}
+ else {
+ SynErr(n);
+ while (!(set[syFol, kind] || set[repFol, kind] || set[0, kind])) {
+ Get();
+ kind = la.kind;
+ }
+ return StartOf(syFol);
+ }
+ }
+
+
+ void Dafny() {
+ ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; ArbitraryTypeDecl at; 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)
+ Contract.Assert(defaultModule != null);
+ bool isGhost;
+
+ while (StartOf(1)) {
+ isGhost = false;
+ if (la.kind == 8) {
+ Get();
+ isGhost = true;
+ }
+ switch (la.kind) {
+ case 9: case 11: {
+ SubModuleDecl(defaultModule, isGhost, out submodule);
+ defaultModule.TopLevelDecls.Add(submodule);
+ break;
+ }
+ case 18: {
+ if (isGhost) { SemErr(t, "a class is not allowed to be declared as 'ghost'"); }
+ ClassDecl(defaultModule, out c);
+ defaultModule.TopLevelDecls.Add(c);
+ break;
+ }
+ case 20: case 21: {
+ if (isGhost) { SemErr(t, "a datatype/codatatype is not allowed to be declared as 'ghost'"); }
+ DatatypeDecl(defaultModule, out dt);
+ defaultModule.TopLevelDecls.Add(dt);
+ break;
+ }
+ case 25: {
+ if (isGhost) { SemErr(t, "a type is not allowed to be declared as 'ghost'"); }
+ ArbitraryTypeDecl(defaultModule, out at);
+ defaultModule.TopLevelDecls.Add(at);
+ break;
+ }
+ case 29: {
+ if (isGhost) { SemErr(t, "an iterator is not allowed to be declared as 'ghost'"); }
+ IteratorDecl(defaultModule, out iter);
+ defaultModule.TopLevelDecls.Add(iter);
+ break;
+ }
+ case 8: case 19: case 23: case 34: case 35: case 52: case 53: case 54: {
+ ClassMemberDecl(membersDefaultClass, isGhost, false);
+ break;
+ }
+ default: SynErr(116); break;
+ }
+ }
+ DefaultClassDecl defaultClass = null;
+ foreach (TopLevelDecl topleveldecl in defaultModule.TopLevelDecls) {
+ defaultClass = topleveldecl as DefaultClassDecl;
+ if (defaultClass != null) {
+ defaultClass.Members.AddRange(membersDefaultClass);
+ break;
+ }
+ }
+ if (defaultClass == null) { // create the default class here, because it wasn't found
+ defaultClass = new DefaultClassDecl(defaultModule, membersDefaultClass);
+ defaultModule.TopLevelDecls.Add(defaultClass);
+ }
+ Expect(0);
+ }
+
+ void SubModuleDecl(ModuleDefinition parent, bool isOverallModuleGhost, out ModuleDecl submodule) {
+ ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; ArbitraryTypeDecl at; IteratorDecl iter;
+ Attributes attrs = null; IToken/*!*/ id;
+ List<MemberDecl/*!*/> namedModuleDefaultClassMembers = new List<MemberDecl>();;
+ List<IToken> idRefined = null, idPath = null, idAssignment = null;
+ bool isGhost = false;
+ ModuleDefinition module;
+ ModuleDecl sm;
+ submodule = null; // appease compiler
+ bool opened = false;
+
+ if (la.kind == 9) {
+ Get();
+ while (la.kind == 6) {
+ Attribute(ref attrs);
+ }
+ NoUSIdent(out id);
+ if (la.kind == 10) {
+ Get();
+ QualifiedName(out idRefined);
+ }
+ module = new ModuleDefinition(id, id.val, isOverallModuleGhost, false, idRefined == null ? null : idRefined, attrs, false);
+ Expect(6);
+ module.BodyStartTok = t;
+ while (StartOf(1)) {
+ isGhost = false;
+ if (la.kind == 8) {
+ Get();
+ isGhost = true;
+ }
+ switch (la.kind) {
+ case 9: case 11: {
+ SubModuleDecl(module, isGhost, out sm);
+ module.TopLevelDecls.Add(sm);
+ break;
+ }
+ case 18: {
+ if (isGhost) { SemErr(t, "a class is not allowed to be declared as 'ghost'"); }
+ ClassDecl(module, out c);
+ module.TopLevelDecls.Add(c);
+ break;
+ }
+ case 20: case 21: {
+ if (isGhost) { SemErr(t, "a datatype/codatatype is not allowed to be declared as 'ghost'"); }
+ DatatypeDecl(module, out dt);
+ module.TopLevelDecls.Add(dt);
+ break;
+ }
+ case 25: {
+ if (isGhost) { SemErr(t, "a type is not allowed to be declared as 'ghost'"); }
+ ArbitraryTypeDecl(module, out at);
+ module.TopLevelDecls.Add(at);
+ break;
+ }
+ case 29: {
+ if (isGhost) { SemErr(t, "an iterator is not allowed to be declared as 'ghost'"); }
+ IteratorDecl(module, out iter);
+ module.TopLevelDecls.Add(iter);
+ break;
+ }
+ case 8: case 19: case 23: case 34: case 35: case 52: case 53: case 54: {
+ ClassMemberDecl(namedModuleDefaultClassMembers, isGhost, false);
+ break;
+ }
+ default: SynErr(117); break;
+ }
+ }
+ Expect(7);
+ module.BodyEndTok = t;
+ module.TopLevelDecls.Add(new DefaultClassDecl(module, namedModuleDefaultClassMembers));
+ submodule = new LiteralModuleDecl(module, parent);
+ } else if (la.kind == 11) {
+ Get();
+ if (la.kind == 12) {
+ Get();
+ opened = true;
+ }
+ NoUSIdent(out id);
+ if (la.kind == 13) {
+ Get();
+ QualifiedName(out idPath);
+ Expect(14);
+ submodule = new AliasModuleDecl(idPath, id, parent, opened);
+ } else if (la.kind == 14) {
+ Get();
+ idPath = new List<IToken>(); idPath.Add(id); submodule = new AliasModuleDecl(idPath, id, parent, opened);
+ } else if (la.kind == 15) {
+ Get();
+ QualifiedName(out idPath);
+ if (la.kind == 16) {
+ Get();
+ QualifiedName(out idAssignment);
+ }
+ Expect(14);
+ submodule = new AbstractModuleDecl(idPath, id, parent, idAssignment, opened);
+ } else SynErr(118);
+ } else SynErr(119);
+ }
+
+ void ClassDecl(ModuleDefinition/*!*/ module, out ClassDecl/*!*/ c) {
+ Contract.Requires(module != null);
+ Contract.Ensures(Contract.ValueAtReturn(out c) != null);
+ IToken/*!*/ id;
+ Attributes attrs = null;
+ List<TypeParameter/*!*/> typeArgs = new List<TypeParameter/*!*/>();
+ List<MemberDecl/*!*/> members = new List<MemberDecl/*!*/>();
+ IToken bodyStart;
+
+ while (!(la.kind == 0 || la.kind == 18)) {SynErr(120); Get();}
+ Expect(18);
+ while (la.kind == 6) {
+ Attribute(ref attrs);
+ }
+ NoUSIdent(out id);
+ if (la.kind == 32) {
+ GenericParameters(typeArgs);
+ }
+ Expect(6);
+ bodyStart = t;
+ while (StartOf(2)) {
+ ClassMemberDecl(members, false, true);
+ }
+ Expect(7);
+ c = new ClassDecl(id, id.val, module, typeArgs, members, attrs);
+ c.BodyStartTok = bodyStart;
+ c.BodyEndTok = t;
+
+ }
+
+ void DatatypeDecl(ModuleDefinition/*!*/ module, out DatatypeDecl/*!*/ dt) {
+ Contract.Requires(module != null);
+ Contract.Ensures(Contract.ValueAtReturn(out dt)!=null);
+ IToken/*!*/ id;
+ Attributes attrs = null;
+ List<TypeParameter/*!*/> typeArgs = new List<TypeParameter/*!*/>();
+ List<DatatypeCtor/*!*/> ctors = new List<DatatypeCtor/*!*/>();
+ IToken bodyStart = Token.NoToken; // dummy assignment
+ bool co = false;
+
+ while (!(la.kind == 0 || la.kind == 20 || la.kind == 21)) {SynErr(121); Get();}
+ if (la.kind == 20) {
+ Get();
+ } else if (la.kind == 21) {
+ Get();
+ co = true;
+ } else SynErr(122);
+ while (la.kind == 6) {
+ Attribute(ref attrs);
+ }
+ NoUSIdent(out id);
+ if (la.kind == 32) {
+ GenericParameters(typeArgs);
+ }
+ Expect(13);
+ bodyStart = t;
+ DatatypeMemberDecl(ctors);
+ while (la.kind == 22) {
+ Get();
+ DatatypeMemberDecl(ctors);
+ }
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(123); Get();}
+ Expect(14);
+ if (co) {
+ dt = new CoDatatypeDecl(id, id.val, module, typeArgs, ctors, attrs);
+ } else {
+ dt = new IndDatatypeDecl(id, id.val, module, typeArgs, ctors, attrs);
+ }
+ dt.BodyStartTok = bodyStart;
+ dt.BodyEndTok = t;
+
+ }
+
+ void ArbitraryTypeDecl(ModuleDefinition/*!*/ module, out ArbitraryTypeDecl at) {
+ IToken/*!*/ id;
+ Attributes attrs = null;
+ var eqSupport = TypeParameter.EqualitySupportValue.Unspecified;
+
+ Expect(25);
+ while (la.kind == 6) {
+ Attribute(ref attrs);
+ }
+ NoUSIdent(out id);
+ if (la.kind == 26) {
+ Get();
+ Expect(27);
+ Expect(28);
+ eqSupport = TypeParameter.EqualitySupportValue.Required;
+ }
+ at = new ArbitraryTypeDecl(id, id.val, module, eqSupport, attrs);
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(124); Get();}
+ Expect(14);
+ }
+
+ void IteratorDecl(ModuleDefinition module, out IteratorDecl/*!*/ iter) {
+ Contract.Ensures(Contract.ValueAtReturn(out iter) != null);
+ IToken/*!*/ id;
+ Attributes attrs = null;
+ List<TypeParameter/*!*/>/*!*/ typeArgs = new List<TypeParameter/*!*/>();
+ IToken openParen;
+ List<Formal/*!*/> ins = new List<Formal/*!*/>();
+ List<Formal/*!*/> outs = new List<Formal/*!*/>();
+ List<FrameExpression/*!*/> reads = new List<FrameExpression/*!*/>();
+ List<FrameExpression/*!*/> mod = new List<FrameExpression/*!*/>();
+ List<Expression/*!*/> decreases = new List<Expression>();
+ List<MaybeFreeExpression/*!*/> req = new List<MaybeFreeExpression/*!*/>();
+ List<MaybeFreeExpression/*!*/> ens = new List<MaybeFreeExpression/*!*/>();
+ List<MaybeFreeExpression/*!*/> yieldReq = new List<MaybeFreeExpression/*!*/>();
+ List<MaybeFreeExpression/*!*/> yieldEns = new List<MaybeFreeExpression/*!*/>();
+ List<Expression/*!*/> dec = new List<Expression/*!*/>();
+ Attributes readsAttrs = null;
+ Attributes modAttrs = null;
+ Attributes decrAttrs = null;
+ BlockStmt body = null;
+ bool signatureOmitted = false;
+ IToken bodyStart = Token.NoToken;
+ IToken bodyEnd = Token.NoToken;
+
+ while (!(la.kind == 0 || la.kind == 29)) {SynErr(125); Get();}
+ Expect(29);
+ while (la.kind == 6) {
+ Attribute(ref attrs);
+ }
+ NoUSIdent(out id);
+ if (la.kind == 26 || la.kind == 32) {
+ if (la.kind == 32) {
+ GenericParameters(typeArgs);
+ }
+ Formals(true, true, ins, out openParen);
+ if (la.kind == 30) {
+ Get();
+ Formals(false, true, outs, out openParen);
+ }
+ } else if (la.kind == 31) {
+ Get();
+ signatureOmitted = true; openParen = Token.NoToken;
+ } else SynErr(126);
+ while (StartOf(3)) {
+ IteratorSpec(reads, mod, decreases, req, ens, yieldReq, yieldEns, ref readsAttrs, ref modAttrs, ref decrAttrs);
+ }
+ if (la.kind == 6) {
+ BlockStmt(out body, out bodyStart, out bodyEnd);
+ }
+ iter = new IteratorDecl(id, id.val, module, typeArgs, ins, outs,
+ new Specification<FrameExpression>(reads, readsAttrs),
+ new Specification<FrameExpression>(mod, modAttrs),
+ new Specification<Expression>(decreases, decrAttrs),
+ req, ens, yieldReq, yieldEns,
+ body, attrs, signatureOmitted);
+ iter.BodyStartTok = bodyStart;
+ iter.BodyEndTok = bodyEnd;
+
+ }
+
+ void ClassMemberDecl(List<MemberDecl/*!*/>/*!*/ mm, bool isAlreadyGhost, bool allowConstructors) {
+ Contract.Requires(cce.NonNullElements(mm));
+ Method/*!*/ m;
+ Function/*!*/ f;
+ MemberModifiers mmod = new MemberModifiers();
+ mmod.IsGhost = isAlreadyGhost;
+
+ while (la.kind == 8 || la.kind == 19) {
+ if (la.kind == 8) {
+ Get();
+ mmod.IsGhost = true;
+ } else {
+ Get();
+ mmod.IsStatic = true;
+ }
+ }
+ if (la.kind == 23) {
+ FieldDecl(mmod, mm);
+ } else if (la.kind == 52 || la.kind == 53 || la.kind == 54) {
+ FunctionDecl(mmod, out f);
+ mm.Add(f);
+ } else if (la.kind == 34 || la.kind == 35) {
+ MethodDecl(mmod, allowConstructors, out m);
+ mm.Add(m);
+ } else SynErr(127);
+ }
+
+ void Attribute(ref Attributes attrs) {
+ Expect(6);
+ AttributeBody(ref attrs);
+ Expect(7);
+ }
+
+ void NoUSIdent(out IToken/*!*/ x) {
+ Contract.Ensures(Contract.ValueAtReturn(out x) != null);
+ Expect(1);
+ x = t;
+ if (x.val.StartsWith("_")) {
+ SemErr("cannot declare identifier beginning with underscore");
+ }
+
+ }
+
+ void QualifiedName(out List<IToken> ids) {
+ IToken id; ids = new List<IToken>();
+ Ident(out id);
+ ids.Add(id);
+ while (la.kind == 17) {
+ Get();
+ Ident(out id);
+ ids.Add(id);
+ }
+ }
+
+ void Ident(out IToken/*!*/ x) {
+ Contract.Ensures(Contract.ValueAtReturn(out x) != null);
+ Expect(1);
+ x = t;
+ }
+
+ void GenericParameters(List<TypeParameter/*!*/>/*!*/ typeArgs) {
+ Contract.Requires(cce.NonNullElements(typeArgs));
+ IToken/*!*/ id;
+ TypeParameter.EqualitySupportValue eqSupport;
+
+ Expect(32);
+ NoUSIdent(out id);
+ eqSupport = TypeParameter.EqualitySupportValue.Unspecified;
+ if (la.kind == 26) {
+ Get();
+ Expect(27);
+ Expect(28);
+ eqSupport = TypeParameter.EqualitySupportValue.Required;
+ }
+ typeArgs.Add(new TypeParameter(id, id.val, eqSupport));
+ while (la.kind == 24) {
+ Get();
+ NoUSIdent(out id);
+ eqSupport = TypeParameter.EqualitySupportValue.Unspecified;
+ if (la.kind == 26) {
+ Get();
+ Expect(27);
+ Expect(28);
+ eqSupport = TypeParameter.EqualitySupportValue.Required;
+ }
+ typeArgs.Add(new TypeParameter(id, id.val, eqSupport));
+ }
+ Expect(33);
+ }
+
+ void FieldDecl(MemberModifiers mmod, List<MemberDecl/*!*/>/*!*/ mm) {
+ Contract.Requires(cce.NonNullElements(mm));
+ Attributes attrs = null;
+ IToken/*!*/ id; Type/*!*/ ty;
+
+ while (!(la.kind == 0 || la.kind == 23)) {SynErr(128); Get();}
+ Expect(23);
+ if (mmod.IsStatic) { SemErr(t, "fields cannot be declared 'static'"); }
+
+ while (la.kind == 6) {
+ Attribute(ref attrs);
+ }
+ IdentType(out id, out ty, false);
+ mm.Add(new Field(id, id.val, mmod.IsGhost, ty, attrs));
+ while (la.kind == 24) {
+ Get();
+ IdentType(out id, out ty, false);
+ mm.Add(new Field(id, id.val, mmod.IsGhost, ty, attrs));
+ }
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(129); Get();}
+ Expect(14);
+ }
+
+ void FunctionDecl(MemberModifiers mmod, out Function/*!*/ f) {
+ Contract.Ensures(Contract.ValueAtReturn(out f)!=null);
+ Attributes attrs = null;
+ IToken/*!*/ id = Token.NoToken; // to please compiler
+ List<TypeParameter/*!*/> typeArgs = new List<TypeParameter/*!*/>();
+ List<Formal/*!*/> formals = new List<Formal/*!*/>();
+ Type/*!*/ returnType = new BoolType();
+ List<Expression/*!*/> reqs = new List<Expression/*!*/>();
+ List<Expression/*!*/> ens = new List<Expression/*!*/>();
+ List<FrameExpression/*!*/> reads = new List<FrameExpression/*!*/>();
+ List<Expression/*!*/> decreases;
+ Expression body = null;
+ bool isPredicate = false; bool isCoPredicate = false;
+ bool isFunctionMethod = false;
+ IToken openParen = null;
+ IToken bodyStart = Token.NoToken;
+ IToken bodyEnd = Token.NoToken;
+ bool signatureOmitted = false;
+
+ if (la.kind == 52) {
+ Get();
+ if (la.kind == 34) {
+ Get();
+ isFunctionMethod = true;
+ }
+ if (mmod.IsGhost) { SemErr(t, "functions cannot be declared 'ghost' (they are ghost by default)"); }
+
+ while (la.kind == 6) {
+ Attribute(ref attrs);
+ }
+ NoUSIdent(out id);
+ if (la.kind == 26 || la.kind == 32) {
+ if (la.kind == 32) {
+ GenericParameters(typeArgs);
+ }
+ Formals(true, isFunctionMethod, formals, out openParen);
+ Expect(5);
+ Type(out returnType);
+ } else if (la.kind == 31) {
+ Get();
+ signatureOmitted = true;
+ openParen = Token.NoToken;
+ } else SynErr(130);
+ } else if (la.kind == 53) {
+ Get();
+ isPredicate = true;
+ if (la.kind == 34) {
+ Get();
+ isFunctionMethod = true;
+ }
+ if (mmod.IsGhost) { SemErr(t, "predicates cannot be declared 'ghost' (they are ghost by default)"); }
+
+ while (la.kind == 6) {
+ Attribute(ref attrs);
+ }
+ NoUSIdent(out id);
+ if (StartOf(4)) {
+ if (la.kind == 32) {
+ GenericParameters(typeArgs);
+ }
+ if (la.kind == 26) {
+ Formals(true, isFunctionMethod, formals, out openParen);
+ if (la.kind == 5) {
+ Get();
+ SemErr(t, "predicates do not have an explicitly declared return type; it is always bool");
+ }
+ }
+ } else if (la.kind == 31) {
+ Get();
+ signatureOmitted = true;
+ openParen = Token.NoToken;
+ } else SynErr(131);
+ } else if (la.kind == 54) {
+ Get();
+ isCoPredicate = true;
+ if (mmod.IsGhost) { SemErr(t, "copredicates cannot be declared 'ghost' (they are ghost by default)"); }
+
+ while (la.kind == 6) {
+ Attribute(ref attrs);
+ }
+ NoUSIdent(out id);
+ if (StartOf(4)) {
+ if (la.kind == 32) {
+ GenericParameters(typeArgs);
+ }
+ if (la.kind == 26) {
+ Formals(true, isFunctionMethod, formals, out openParen);
+ if (la.kind == 5) {
+ Get();
+ SemErr(t, "copredicates do not have an explicitly declared return type; it is always bool");
+ }
+ }
+ } else if (la.kind == 31) {
+ Get();
+ signatureOmitted = true;
+ openParen = Token.NoToken;
+ } else SynErr(132);
+ } else SynErr(133);
+ decreases = isCoPredicate ? null : new List<Expression/*!*/>();
+ while (StartOf(5)) {
+ FunctionSpec(reqs, reads, ens, decreases);
+ }
+ if (la.kind == 6) {
+ FunctionBody(out body, out bodyStart, out bodyEnd);
+ }
+ if (isPredicate) {
+ f = new Predicate(id, id.val, mmod.IsStatic, !isFunctionMethod, typeArgs, openParen, formals,
+ reqs, reads, ens, new Specification<Expression>(decreases, null), body, Predicate.BodyOriginKind.OriginalOrInherited, attrs, signatureOmitted);
+ } else if (isCoPredicate) {
+ f = new CoPredicate(id, id.val, mmod.IsStatic, typeArgs, openParen, formals,
+ reqs, reads, ens, body, attrs, signatureOmitted);
+ } else {
+ f = new Function(id, id.val, mmod.IsStatic, !isFunctionMethod, typeArgs, openParen, formals, returnType,
+ reqs, reads, ens, new Specification<Expression>(decreases, null), body, attrs, signatureOmitted);
+ }
+ f.BodyStartTok = bodyStart;
+ f.BodyEndTok = bodyEnd;
+
+ }
+
+ void MethodDecl(MemberModifiers mmod, bool allowConstructor, out Method/*!*/ m) {
+ Contract.Ensures(Contract.ValueAtReturn(out m) !=null);
+ IToken/*!*/ id;
+ Attributes attrs = null;
+ List<TypeParameter/*!*/>/*!*/ typeArgs = new List<TypeParameter/*!*/>();
+ IToken openParen;
+ List<Formal/*!*/> ins = new List<Formal/*!*/>();
+ List<Formal/*!*/> outs = new List<Formal/*!*/>();
+ List<MaybeFreeExpression/*!*/> req = new List<MaybeFreeExpression/*!*/>();
+ List<FrameExpression/*!*/> mod = new List<FrameExpression/*!*/>();
+ List<MaybeFreeExpression/*!*/> ens = new List<MaybeFreeExpression/*!*/>();
+ List<Expression/*!*/> dec = new List<Expression/*!*/>();
+ Attributes decAttrs = null;
+ Attributes modAttrs = null;
+ BlockStmt body = null;
+ bool isConstructor = false;
+ bool signatureOmitted = false;
+ IToken bodyStart = Token.NoToken;
+ IToken bodyEnd = Token.NoToken;
+
+ while (!(la.kind == 0 || la.kind == 34 || la.kind == 35)) {SynErr(134); Get();}
+ if (la.kind == 34) {
+ Get();
+ } else if (la.kind == 35) {
+ Get();
+ if (allowConstructor) {
+ isConstructor = true;
+ } else {
+ SemErr(t, "constructors are only allowed in classes");
+ }
+
+ } else SynErr(135);
+ if (isConstructor) {
+ if (mmod.IsGhost) {
+ SemErr(t, "constructors cannot be declared 'ghost'");
+ }
+ if (mmod.IsStatic) {
+ SemErr(t, "constructors cannot be declared 'static'");
+ }
+ }
+
+ while (la.kind == 6) {
+ Attribute(ref attrs);
+ }
+ NoUSIdent(out id);
+ if (la.kind == 26 || la.kind == 32) {
+ if (la.kind == 32) {
+ GenericParameters(typeArgs);
+ }
+ Formals(true, !mmod.IsGhost, ins, out openParen);
+ if (la.kind == 36) {
+ Get();
+ if (isConstructor) { SemErr(t, "constructors cannot have out-parameters"); }
+ Formals(false, !mmod.IsGhost, outs, out openParen);
+ }
+ } else if (la.kind == 31) {
+ Get();
+ signatureOmitted = true; openParen = Token.NoToken;
+ } else SynErr(136);
+ while (StartOf(6)) {
+ MethodSpec(req, mod, ens, dec, ref decAttrs, ref modAttrs);
+ }
+ if (la.kind == 6) {
+ BlockStmt(out body, out bodyStart, out bodyEnd);
+ }
+ if (isConstructor) {
+ m = new Constructor(id, id.val, typeArgs, ins,
+ req, new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, attrs, signatureOmitted);
+ } else {
+ m = new Method(id, id.val, mmod.IsStatic, mmod.IsGhost, typeArgs, ins, outs,
+ req, new Specification<FrameExpression>(mod, modAttrs), ens, new Specification<Expression>(dec, decAttrs), body, attrs, signatureOmitted);
+ }
+ m.BodyStartTok = bodyStart;
+ m.BodyEndTok = bodyEnd;
+
+ }
+
+ void DatatypeMemberDecl(List<DatatypeCtor/*!*/>/*!*/ ctors) {
+ Contract.Requires(cce.NonNullElements(ctors));
+ Attributes attrs = null;
+ IToken/*!*/ id;
+ List<Formal/*!*/> formals = new List<Formal/*!*/>();
+
+ while (la.kind == 6) {
+ Attribute(ref attrs);
+ }
+ NoUSIdent(out id);
+ if (la.kind == 26) {
+ FormalsOptionalIds(formals);
+ }
+ ctors.Add(new DatatypeCtor(id, id.val, formals, attrs));
+ }
+
+ void FormalsOptionalIds(List<Formal/*!*/>/*!*/ formals) {
+ Contract.Requires(cce.NonNullElements(formals)); IToken/*!*/ id; Type/*!*/ ty; string/*!*/ name; bool isGhost;
+ Expect(26);
+ if (StartOf(7)) {
+ TypeIdentOptional(out id, out name, out ty, out isGhost);
+ formals.Add(new Formal(id, name, ty, true, isGhost));
+ while (la.kind == 24) {
+ Get();
+ TypeIdentOptional(out id, out name, out ty, out isGhost);
+ formals.Add(new Formal(id, name, ty, true, isGhost));
+ }
+ }
+ Expect(28);
+ }
+
+ 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(5);
+ Type(out ty);
+ }
+
+ void GIdentType(bool allowGhostKeyword, out IToken/*!*/ id, out Type/*!*/ ty, out bool isGhost) {
+ Contract.Ensures(Contract.ValueAtReturn(out id)!=null);
+ Contract.Ensures(Contract.ValueAtReturn(out ty)!=null);
+ isGhost = false;
+ if (la.kind == 8) {
+ Get();
+ if (allowGhostKeyword) { isGhost = true; } else { SemErr(t, "formal cannot be declared 'ghost' in this context"); }
+ }
+ IdentType(out id, out ty, true);
+ }
+
+ void WildIdent(out IToken/*!*/ x, bool allowWildcardId) {
+ Contract.Ensures(Contract.ValueAtReturn(out x) != null);
+ Expect(1);
+ x = t;
+ if (x.val.StartsWith("_")) {
+ if (allowWildcardId && x.val.Length == 1) {
+ t.val = "_v" + anonymousIds++;
+ } else {
+ SemErr("cannot declare identifier beginning with underscore");
+ }
+ }
+
+ }
+
+ void Type(out Type/*!*/ ty) {
+ Contract.Ensures(Contract.ValueAtReturn(out ty) != null); IToken/*!*/ tok;
+ TypeAndToken(out tok, out ty);
+ }
+
+ void LocalIdentTypeOptional(out VarDecl/*!*/ var, bool isGhost) {
+ IToken/*!*/ id; Type/*!*/ ty; Type optType = null;
+
+ WildIdent(out id, true);
+ if (la.kind == 5) {
+ Get();
+ Type(out ty);
+ optType = ty;
+ }
+ var = new VarDecl(id, id.val, optType == null ? new InferredTypeProxy() : optType, isGhost);
+ }
+
+ void IdentTypeOptional(out BoundVar/*!*/ var) {
+ Contract.Ensures(Contract.ValueAtReturn(out var)!=null); IToken/*!*/ id; Type/*!*/ ty; Type optType = null;
+
+ WildIdent(out id, true);
+ if (la.kind == 5) {
+ Get();
+ Type(out ty);
+ optType = ty;
+ }
+ var = new BoundVar(id, id.val, optType == null ? new InferredTypeProxy() : optType);
+ }
+
+ void TypeIdentOptional(out IToken/*!*/ id, out string/*!*/ identName, out Type/*!*/ ty, out bool isGhost) {
+ Contract.Ensures(Contract.ValueAtReturn(out id)!=null);
+ Contract.Ensures(Contract.ValueAtReturn(out ty)!=null);
+ Contract.Ensures(Contract.ValueAtReturn(out identName)!=null);
+ string name = null; isGhost = false;
+ if (la.kind == 8) {
+ Get();
+ isGhost = true;
+ }
+ TypeAndToken(out id, out ty);
+ if (la.kind == 5) {
+ Get();
+ UserDefinedType udt = ty as UserDefinedType;
+ if (udt != null && udt.TypeArgs.Count == 0) {
+ name = udt.Name;
+ } else {
+ SemErr(id, "invalid formal-parameter name in datatype constructor");
+ }
+
+ Type(out ty);
+ }
+ if (name != null) {
+ identName = name;
+ } else {
+ identName = "#" + anonymousIds++;
+ }
+
+ }
+
+ void TypeAndToken(out IToken/*!*/ tok, out Type/*!*/ ty) {
+ Contract.Ensures(Contract.ValueAtReturn(out tok)!=null); Contract.Ensures(Contract.ValueAtReturn(out ty) != null); tok = Token.NoToken; ty = new BoolType(); /*keep compiler happy*/
+ List<Type/*!*/>/*!*/ gt;
+
+ switch (la.kind) {
+ case 44: {
+ Get();
+ tok = t;
+ break;
+ }
+ case 45: {
+ Get();
+ tok = t; ty = new NatType();
+ break;
+ }
+ case 46: {
+ Get();
+ tok = t; ty = new IntType();
+ break;
+ }
+ case 47: {
+ Get();
+ tok = t; gt = new List<Type/*!*/>();
+ GenericInstantiation(gt);
+ if (gt.Count != 1) {
+ SemErr("set type expects exactly one type argument");
+ }
+ ty = new SetType(gt[0]);
+
+ break;
+ }
+ case 48: {
+ Get();
+ tok = t; gt = new List<Type/*!*/>();
+ GenericInstantiation(gt);
+ if (gt.Count != 1) {
+ SemErr("multiset type expects exactly one type argument");
+ }
+ ty = new MultiSetType(gt[0]);
+
+ break;
+ }
+ case 49: {
+ Get();
+ tok = t; gt = new List<Type/*!*/>();
+ GenericInstantiation(gt);
+ if (gt.Count != 1) {
+ SemErr("seq type expects exactly one type argument");
+ }
+ ty = new SeqType(gt[0]);
+
+ break;
+ }
+ case 50: {
+ Get();
+ tok = t; gt = new List<Type/*!*/>();
+ GenericInstantiation(gt);
+ if (gt.Count != 2) {
+ SemErr("map type expects exactly two type arguments");
+ }
+ else { ty = new MapType(gt[0], gt[1]); }
+
+ break;
+ }
+ case 1: case 3: case 51: {
+ ReferenceType(out tok, out ty);
+ break;
+ }
+ default: SynErr(137); break;
+ }
+ }
+
+ void Formals(bool incoming, bool allowGhostKeyword, List<Formal/*!*/>/*!*/ formals, out IToken openParen) {
+ Contract.Requires(cce.NonNullElements(formals)); IToken/*!*/ id; Type/*!*/ ty; bool isGhost;
+ Expect(26);
+ openParen = t;
+ if (la.kind == 1 || la.kind == 8) {
+ GIdentType(allowGhostKeyword, out id, out ty, out isGhost);
+ formals.Add(new Formal(id, id.val, ty, incoming, isGhost));
+ while (la.kind == 24) {
+ Get();
+ GIdentType(allowGhostKeyword, out id, out ty, out isGhost);
+ formals.Add(new Formal(id, id.val, ty, incoming, isGhost));
+ }
+ }
+ Expect(28);
+ }
+
+ void IteratorSpec(List<FrameExpression/*!*/>/*!*/ reads, List<FrameExpression/*!*/>/*!*/ mod, List<Expression/*!*/> decreases,
+List<MaybeFreeExpression/*!*/>/*!*/ req, List<MaybeFreeExpression/*!*/>/*!*/ ens,
+List<MaybeFreeExpression/*!*/>/*!*/ yieldReq, List<MaybeFreeExpression/*!*/>/*!*/ yieldEns,
+ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) {
+ Expression/*!*/ e; FrameExpression/*!*/ fe; bool isFree = false; bool isYield = false; Attributes ensAttrs = null;
+
+ while (!(StartOf(8))) {SynErr(138); Get();}
+ if (la.kind == 42) {
+ Get();
+ while (IsAttribute()) {
+ Attribute(ref readsAttrs);
+ }
+ if (StartOf(9)) {
+ FrameExpression(out fe);
+ reads.Add(fe);
+ while (la.kind == 24) {
+ Get();
+ FrameExpression(out fe);
+ reads.Add(fe);
+ }
+ }
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(139); Get();}
+ Expect(14);
+ } else if (la.kind == 37) {
+ Get();
+ while (IsAttribute()) {
+ Attribute(ref modAttrs);
+ }
+ if (StartOf(9)) {
+ FrameExpression(out fe);
+ mod.Add(fe);
+ while (la.kind == 24) {
+ Get();
+ FrameExpression(out fe);
+ mod.Add(fe);
+ }
+ }
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(140); Get();}
+ Expect(14);
+ } else if (StartOf(10)) {
+ if (la.kind == 38) {
+ Get();
+ isFree = true;
+ }
+ if (la.kind == 43) {
+ Get();
+ isYield = true;
+ }
+ if (la.kind == 39) {
+ Get();
+ Expression(out e);
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(141); Get();}
+ Expect(14);
+ if (isYield) {
+ yieldReq.Add(new MaybeFreeExpression(e, isFree));
+ } else {
+ req.Add(new MaybeFreeExpression(e, isFree));
+ }
+
+ } else if (la.kind == 40) {
+ Get();
+ while (IsAttribute()) {
+ Attribute(ref ensAttrs);
+ }
+ Expression(out e);
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(142); Get();}
+ Expect(14);
+ if (isYield) {
+ yieldEns.Add(new MaybeFreeExpression(e, isFree, ensAttrs));
+ } else {
+ ens.Add(new MaybeFreeExpression(e, isFree, ensAttrs));
+ }
+
+ } else SynErr(143);
+ } else if (la.kind == 41) {
+ Get();
+ while (IsAttribute()) {
+ Attribute(ref decrAttrs);
+ }
+ DecreasesList(decreases, false);
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(144); Get();}
+ Expect(14);
+ } else SynErr(145);
+ }
+
+ 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(6);
+ bodyStart = t;
+ while (StartOf(11)) {
+ Stmt(body);
+ }
+ Expect(7);
+ bodyEnd = t;
+ block = new BlockStmt(bodyStart, body);
+ }
+
+ void MethodSpec(List<MaybeFreeExpression/*!*/>/*!*/ req, List<FrameExpression/*!*/>/*!*/ mod, List<MaybeFreeExpression/*!*/>/*!*/ ens,
+List<Expression/*!*/>/*!*/ decreases, ref Attributes decAttrs, ref Attributes modAttrs) {
+ 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(12))) {SynErr(146); Get();}
+ if (la.kind == 37) {
+ Get();
+ while (IsAttribute()) {
+ Attribute(ref modAttrs);
+ }
+ if (StartOf(9)) {
+ FrameExpression(out fe);
+ mod.Add(fe);
+ while (la.kind == 24) {
+ Get();
+ FrameExpression(out fe);
+ mod.Add(fe);
+ }
+ }
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(147); Get();}
+ Expect(14);
+ } else if (la.kind == 38 || la.kind == 39 || la.kind == 40) {
+ if (la.kind == 38) {
+ Get();
+ isFree = true;
+ }
+ if (la.kind == 39) {
+ Get();
+ Expression(out e);
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(148); Get();}
+ Expect(14);
+ req.Add(new MaybeFreeExpression(e, isFree));
+ } else if (la.kind == 40) {
+ Get();
+ while (IsAttribute()) {
+ Attribute(ref ensAttrs);
+ }
+ Expression(out e);
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(149); Get();}
+ Expect(14);
+ ens.Add(new MaybeFreeExpression(e, isFree, ensAttrs));
+ } else SynErr(150);
+ } else if (la.kind == 41) {
+ Get();
+ while (IsAttribute()) {
+ Attribute(ref decAttrs);
+ }
+ DecreasesList(decreases, true);
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(151); Get();}
+ Expect(14);
+ } else SynErr(152);
+ }
+
+ void FrameExpression(out FrameExpression/*!*/ fe) {
+ Contract.Ensures(Contract.ValueAtReturn(out fe) != null);
+ Expression/*!*/ e;
+ IToken/*!*/ id;
+ string fieldName = null; IToken feTok = null;
+ fe = null;
+
+ if (StartOf(13)) {
+ Expression(out e);
+ feTok = e.tok;
+ if (la.kind == 56) {
+ Get();
+ Ident(out id);
+ fieldName = id.val; feTok = id;
+ }
+ fe = new FrameExpression(feTok, e, fieldName);
+ } else if (la.kind == 56) {
+ Get();
+ Ident(out id);
+ fieldName = id.val;
+ fe = new FrameExpression(id, new ImplicitThisExpr(id), fieldName);
+ } else SynErr(153);
+ }
+
+ void Expression(out Expression/*!*/ e) {
+ EquivExpression(out e);
+ }
+
+ void DecreasesList(List<Expression/*!*/> decreases, bool allowWildcard) {
+ Expression/*!*/ e;
+ PossiblyWildExpression(out e);
+ if (!allowWildcard && e is WildcardExpr) {
+ SemErr(e.tok, "'decreases *' is only allowed on loops and tail-recursive methods");
+ } else {
+ decreases.Add(e);
+ }
+
+ while (la.kind == 24) {
+ Get();
+ PossiblyWildExpression(out e);
+ if (!allowWildcard && e is WildcardExpr) {
+ SemErr(e.tok, "'decreases *' is only allowed on loops and tail-recursive methods");
+ } else {
+ decreases.Add(e);
+ }
+
+ }
+ }
+
+ void GenericInstantiation(List<Type/*!*/>/*!*/ gt) {
+ Contract.Requires(cce.NonNullElements(gt)); Type/*!*/ ty;
+ Expect(32);
+ Type(out ty);
+ gt.Add(ty);
+ while (la.kind == 24) {
+ Get();
+ Type(out ty);
+ gt.Add(ty);
+ }
+ Expect(33);
+ }
+
+ void ReferenceType(out IToken/*!*/ tok, out Type/*!*/ ty) {
+ Contract.Ensures(Contract.ValueAtReturn(out tok) != null); Contract.Ensures(Contract.ValueAtReturn(out ty) != null);
+ tok = Token.NoToken; ty = new BoolType(); /*keep compiler happy*/
+ List<Type/*!*/>/*!*/ gt;
+ List<IToken> path;
+
+ if (la.kind == 51) {
+ Get();
+ tok = t; ty = new ObjectType();
+ } else if (la.kind == 3) {
+ Get();
+ tok = t; gt = new List<Type/*!*/>();
+ GenericInstantiation(gt);
+ if (gt.Count != 1) {
+ SemErr("array type expects exactly one type argument");
+ }
+ int dims = 1;
+ if (tok.val.Length != 5) {
+ dims = int.Parse(tok.val.Substring(5));
+ }
+ ty = theBuiltIns.ArrayType(tok, dims, gt[0], true);
+
+ } else if (la.kind == 1) {
+ Ident(out tok);
+ gt = new List<Type/*!*/>();
+ path = new List<IToken>();
+ while (la.kind == 17) {
+ path.Add(tok);
+ Get();
+ Ident(out tok);
+ }
+ if (la.kind == 32) {
+ GenericInstantiation(gt);
+ }
+ ty = new UserDefinedType(tok, tok.val, gt, path);
+ } else SynErr(154);
+ }
+
+ void FunctionSpec(List<Expression/*!*/>/*!*/ reqs, List<FrameExpression/*!*/>/*!*/ reads, List<Expression/*!*/>/*!*/ ens, List<Expression/*!*/> decreases) {
+ Contract.Requires(cce.NonNullElements(reqs));
+ Contract.Requires(cce.NonNullElements(reads));
+ Contract.Requires(decreases == null || cce.NonNullElements(decreases));
+ Expression/*!*/ e; FrameExpression/*!*/ fe;
+ if (la.kind == 39) {
+ while (!(la.kind == 0 || la.kind == 39)) {SynErr(155); Get();}
+ Get();
+ Expression(out e);
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(156); Get();}
+ Expect(14);
+ reqs.Add(e);
+ } else if (la.kind == 42) {
+ Get();
+ if (StartOf(14)) {
+ PossiblyWildFrameExpression(out fe);
+ reads.Add(fe);
+ while (la.kind == 24) {
+ Get();
+ PossiblyWildFrameExpression(out fe);
+ reads.Add(fe);
+ }
+ }
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(157); Get();}
+ Expect(14);
+ } else if (la.kind == 40) {
+ Get();
+ Expression(out e);
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(158); Get();}
+ Expect(14);
+ ens.Add(e);
+ } else if (la.kind == 41) {
+ Get();
+ if (decreases == null) {
+ SemErr(t, "'decreases' clauses are meaningless for copredicates, so they are not allowed");
+ decreases = new List<Expression/*!*/>();
+ }
+
+ DecreasesList(decreases, false);
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(159); Get();}
+ Expect(14);
+ } else SynErr(160);
+ }
+
+ void FunctionBody(out Expression/*!*/ e, out IToken bodyStart, out IToken bodyEnd) {
+ Contract.Ensures(Contract.ValueAtReturn(out e) != null); e = dummyExpr;
+ Expect(6);
+ bodyStart = t;
+ Expression(out e);
+ Expect(7);
+ bodyEnd = t;
+ }
+
+ void PossiblyWildFrameExpression(out FrameExpression/*!*/ fe) {
+ Contract.Ensures(Contract.ValueAtReturn(out fe) != null); fe = dummyFrameExpr;
+ if (la.kind == 55) {
+ Get();
+ fe = new FrameExpression(t, new WildcardExpr(t), null);
+ } else if (StartOf(9)) {
+ FrameExpression(out fe);
+ } else SynErr(161);
+ }
+
+ void PossiblyWildExpression(out Expression/*!*/ e) {
+ Contract.Ensures(Contract.ValueAtReturn(out e)!=null);
+ e = dummyExpr;
+ if (la.kind == 55) {
+ Get();
+ e = new WildcardExpr(t);
+ } else if (StartOf(13)) {
+ Expression(out e);
+ } else SynErr(162);
+ }
+
+ void Stmt(List<Statement/*!*/>/*!*/ ss) {
+ Statement/*!*/ s;
+
+ OneStmt(out s);
+ ss.Add(s);
+ }
+
+ void OneStmt(out Statement/*!*/ s) {
+ Contract.Ensures(Contract.ValueAtReturn(out s) != null); IToken/*!*/ x; IToken/*!*/ id; string label = null;
+ s = dummyStmt; /* to please the compiler */
+ BlockStmt bs;
+ IToken bodyStart, bodyEnd;
+ int breakCount;
+
+ while (!(StartOf(15))) {SynErr(163); Get();}
+ switch (la.kind) {
+ case 6: {
+ BlockStmt(out bs, out bodyStart, out bodyEnd);
+ s = bs;
+ break;
+ }
+ case 75: {
+ AssertStmt(out s);
+ break;
+ }
+ case 63: {
+ AssumeStmt(out s);
+ break;
+ }
+ case 76: {
+ PrintStmt(out s);
+ break;
+ }
+ case 1: case 2: case 22: case 26: case 101: case 102: case 103: case 104: case 105: case 106: {
+ UpdateStmt(out s);
+ break;
+ }
+ case 8: case 23: {
+ VarDeclStatement(out s);
+ break;
+ }
+ case 68: {
+ IfStmt(out s);
+ break;
+ }
+ case 72: {
+ WhileStmt(out s);
+ break;
+ }
+ case 74: {
+ MatchStmt(out s);
+ break;
+ }
+ case 77: {
+ ParallelStmt(out s);
+ break;
+ }
+ case 78: {
+ CalcStmt(out s);
+ break;
+ }
+ case 57: {
+ Get();
+ x = t;
+ NoUSIdent(out id);
+ Expect(5);
+ OneStmt(out s);
+ s.Labels = new LList<Label>(new Label(x, id.val), s.Labels);
+ break;
+ }
+ case 58: {
+ Get();
+ x = t; breakCount = 1; label = null;
+ if (la.kind == 1) {
+ NoUSIdent(out id);
+ label = id.val;
+ } else if (la.kind == 14 || la.kind == 58) {
+ while (la.kind == 58) {
+ Get();
+ breakCount++;
+ }
+ } else SynErr(164);
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(165); Get();}
+ Expect(14);
+ s = label != null ? new BreakStmt(x, label) : new BreakStmt(x, breakCount);
+ break;
+ }
+ case 43: case 61: {
+ ReturnStmt(out s);
+ break;
+ }
+ case 31: {
+ SkeletonStmt(out s);
+ Expect(14);
+ break;
+ }
+ default: SynErr(166); break;
+ }
+ }
+
+ void AssertStmt(out Statement/*!*/ s) {
+ Contract.Ensures(Contract.ValueAtReturn(out s) != null); IToken/*!*/ x;
+ Expression e = null; Attributes attrs = null;
+
+ Expect(75);
+ x = t;
+ while (IsAttribute()) {
+ Attribute(ref attrs);
+ }
+ if (StartOf(13)) {
+ Expression(out e);
+ } else if (la.kind == 31) {
+ Get();
+ } else SynErr(167);
+ Expect(14);
+ if (e == null) {
+ s = new SkeletonStatement(new AssertStmt(x, new LiteralExpr(x, true), attrs), true, false);
+ } else {
+ s = new AssertStmt(x, e, attrs);
+ }
+
+ }
+
+ void AssumeStmt(out Statement/*!*/ s) {
+ Contract.Ensures(Contract.ValueAtReturn(out s) != null); IToken/*!*/ x;
+ Expression e = null; Attributes attrs = null;
+
+ Expect(63);
+ x = t;
+ while (IsAttribute()) {
+ Attribute(ref attrs);
+ }
+ if (StartOf(13)) {
+ Expression(out e);
+ } else if (la.kind == 31) {
+ Get();
+ } else SynErr(168);
+ if (e == null) {
+ s = new SkeletonStatement(new AssumeStmt(x, new LiteralExpr(x, true), attrs), true, false);
+ } else {
+ s = new AssumeStmt(x, e, attrs);
+ }
+
+ Expect(14);
+ }
+
+ void PrintStmt(out Statement/*!*/ s) {
+ Contract.Ensures(Contract.ValueAtReturn(out s) != null); IToken/*!*/ x; Attributes.Argument/*!*/ arg;
+ List<Attributes.Argument/*!*/> args = new List<Attributes.Argument/*!*/>();
+
+ Expect(76);
+ x = t;
+ AttributeArg(out arg);
+ args.Add(arg);
+ while (la.kind == 24) {
+ Get();
+ AttributeArg(out arg);
+ args.Add(arg);
+ }
+ Expect(14);
+ s = new PrintStmt(x, args);
+ }
+
+ void UpdateStmt(out Statement/*!*/ s) {
+ List<Expression> lhss = new List<Expression>();
+ List<AssignmentRhs> rhss = new List<AssignmentRhs>();
+ Expression e; AssignmentRhs r;
+ Expression lhs0;
+ IToken x;
+ Attributes attrs = null;
+ IToken suchThatAssume = null;
+ Expression suchThat = null;
+
+ Lhs(out e);
+ x = e.tok;
+ if (la.kind == 6 || la.kind == 14) {
+ while (la.kind == 6) {
+ Attribute(ref attrs);
+ }
+ Expect(14);
+ rhss.Add(new ExprRhs(e, attrs));
+ } else if (la.kind == 24 || la.kind == 60 || la.kind == 62) {
+ lhss.Add(e); lhs0 = e;
+ while (la.kind == 24) {
+ Get();
+ Lhs(out e);
+ lhss.Add(e);
+ }
+ if (la.kind == 60) {
+ Get();
+ x = t;
+ Rhs(out r, lhs0);
+ rhss.Add(r);
+ while (la.kind == 24) {
+ Get();
+ Rhs(out r, lhs0);
+ rhss.Add(r);
+ }
+ } else if (la.kind == 62) {
+ Get();
+ x = t;
+ if (la.kind == 63) {
+ Get();
+ suchThatAssume = t;
+ }
+ Expression(out suchThat);
+ } else SynErr(169);
+ Expect(14);
+ } else if (la.kind == 5) {
+ Get();
+ SemErr(t, "invalid statement (did you forget the 'label' keyword?)");
+ } else SynErr(170);
+ if (suchThat != null) {
+ s = new AssignSuchThatStmt(x, lhss, suchThat, suchThatAssume);
+ } else {
+ if (lhss.Count == 0 && rhss.Count == 0) {
+ s = new BlockStmt(x, new List<Statement>()); // error, give empty statement
+ } else {
+ s = new UpdateStmt(x, lhss, rhss);
+ }
+ }
+
+ }
+
+ void VarDeclStatement(out Statement/*!*/ s) {
+ IToken x = null, assignTok = null; bool isGhost = false;
+ VarDecl/*!*/ d;
+ AssignmentRhs r; IdentifierExpr lhs0;
+ List<VarDecl> lhss = new List<VarDecl>();
+ List<AssignmentRhs> rhss = new List<AssignmentRhs>();
+ IToken suchThatAssume = null;
+ Expression suchThat = null;
+
+ if (la.kind == 8) {
+ Get();
+ isGhost = true; x = t;
+ }
+ Expect(23);
+ if (!isGhost) { x = t; }
+ LocalIdentTypeOptional(out d, isGhost);
+ lhss.Add(d);
+ while (la.kind == 24) {
+ Get();
+ LocalIdentTypeOptional(out d, isGhost);
+ lhss.Add(d);
+ }
+ if (la.kind == 60 || la.kind == 62) {
+ if (la.kind == 60) {
+ Get();
+ assignTok = t;
+ lhs0 = new IdentifierExpr(lhss[0].Tok, lhss[0].Name);
+ lhs0.Var = lhss[0]; lhs0.Type = lhss[0].OptionalType; // resolve here
+
+ Rhs(out r, lhs0);
+ rhss.Add(r);
+ while (la.kind == 24) {
+ Get();
+ Rhs(out r, lhs0);
+ rhss.Add(r);
+ }
+ } else {
+ Get();
+ assignTok = t;
+ if (la.kind == 63) {
+ Get();
+ suchThatAssume = t;
+ }
+ Expression(out suchThat);
+ }
+ }
+ Expect(14);
+ 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, ies, suchThat, suchThatAssume);
+ } 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, ies, rhss);
+ }
+ s = new VarDeclStmt(x, lhss, update);
+
+ }
+
+ void IfStmt(out Statement/*!*/ ifStmt) {
+ Contract.Ensures(Contract.ValueAtReturn(out ifStmt) != null); IToken/*!*/ x;
+ Expression guard = null; bool guardOmitted = false;
+ BlockStmt/*!*/ thn;
+ BlockStmt/*!*/ bs;
+ Statement/*!*/ s;
+ Statement els = null;
+ IToken bodyStart, bodyEnd;
+ List<GuardedAlternative> alternatives;
+ ifStmt = dummyStmt; // to please the compiler
+
+ Expect(68);
+ x = t;
+ if (la.kind == 26 || la.kind == 31) {
+ if (la.kind == 26) {
+ Guard(out guard);
+ } else {
+ Get();
+ guardOmitted = true;
+ }
+ BlockStmt(out thn, out bodyStart, out bodyEnd);
+ if (la.kind == 69) {
+ Get();
+ if (la.kind == 68) {
+ IfStmt(out s);
+ els = s;
+ } else if (la.kind == 6) {
+ BlockStmt(out bs, out bodyStart, out bodyEnd);
+ els = bs;
+ } else SynErr(171);
+ }
+ if (guardOmitted) {
+ ifStmt = new SkeletonStatement(new IfStmt(x, guard, thn, els), true, false);
+ } else {
+ ifStmt = new IfStmt(x, guard, thn, els);
+ }
+
+ } else if (la.kind == 6) {
+ AlternativeBlock(out alternatives);
+ ifStmt = new AlternativeStmt(x, alternatives);
+ } else SynErr(172);
+ }
+
+ void WhileStmt(out Statement/*!*/ stmt) {
+ Contract.Ensures(Contract.ValueAtReturn(out stmt) != null); IToken/*!*/ x;
+ Expression guard = null; bool guardOmitted = false;
+ List<MaybeFreeExpression/*!*/> invariants = new List<MaybeFreeExpression/*!*/>();
+ List<Expression/*!*/> decreases = new List<Expression/*!*/>();
+ Attributes decAttrs = null;
+ Attributes modAttrs = null;
+ List<FrameExpression/*!*/> mod = null;
+ BlockStmt/*!*/ body = null; bool bodyOmitted = false;
+ IToken bodyStart = null, bodyEnd = null;
+ List<GuardedAlternative> alternatives;
+ stmt = dummyStmt; // to please the compiler
+
+ Expect(72);
+ x = t;
+ if (la.kind == 26 || la.kind == 31) {
+ if (la.kind == 26) {
+ Guard(out guard);
+ Contract.Assume(guard == null || cce.Owner.None(guard));
+ } else {
+ Get();
+ guardOmitted = true;
+ }
+ LoopSpec(out invariants, out decreases, out mod, ref decAttrs, ref modAttrs);
+ if (la.kind == 6) {
+ BlockStmt(out body, out bodyStart, out bodyEnd);
+ } else if (la.kind == 31) {
+ Get();
+ bodyOmitted = true;
+ } else SynErr(173);
+ if (guardOmitted || bodyOmitted) {
+ if (mod != null) {
+ SemErr(mod[0].E.tok, "'modifies' clauses are not allowed on refining loops");
+ }
+ if (body == null) {
+ body = new BlockStmt(x, new List<Statement>());
+ }
+ stmt = new WhileStmt(x, guard, invariants, new Specification<Expression>(decreases, decAttrs), new Specification<FrameExpression>(null, null), body);
+ stmt = new SkeletonStatement(stmt, guardOmitted, bodyOmitted);
+ } else {
+ // The following statement protects against crashes in case of parsing errors
+ body = body ?? new BlockStmt(x, new List<Statement>());
+ stmt = new WhileStmt(x, guard, invariants, new Specification<Expression>(decreases, decAttrs), new Specification<FrameExpression>(mod, modAttrs), body);
+ }
+
+ } else if (StartOf(16)) {
+ LoopSpec(out invariants, out decreases, out mod, ref decAttrs, ref modAttrs);
+ AlternativeBlock(out alternatives);
+ stmt = new AlternativeLoopStmt(x, invariants, new Specification<Expression>(decreases, decAttrs), new Specification<FrameExpression>(mod, modAttrs), alternatives);
+ } else SynErr(174);
+ }
+
+ void MatchStmt(out Statement/*!*/ s) {
+ Contract.Ensures(Contract.ValueAtReturn(out s) != null);
+ Token x; Expression/*!*/ e; MatchCaseStmt/*!*/ c;
+ List<MatchCaseStmt/*!*/> cases = new List<MatchCaseStmt/*!*/>();
+ Expect(74);
+ x = t;
+ Expression(out e);
+ Expect(6);
+ while (la.kind == 70) {
+ CaseStatement(out c);
+ cases.Add(c);
+ }
+ Expect(7);
+ s = new MatchStmt(x, e, cases);
+ }
+
+ void ParallelStmt(out Statement/*!*/ s) {
+ Contract.Ensures(Contract.ValueAtReturn(out s) != null);
+ IToken/*!*/ x;
+ List<BoundVar/*!*/> bvars = null;
+ Attributes attrs = null;
+ Expression range = null;
+ var ens = new List<MaybeFreeExpression/*!*/>();
+ bool isFree;
+ Expression/*!*/ e;
+ BlockStmt/*!*/ block;
+ IToken bodyStart, bodyEnd;
+
+ Expect(77);
+ x = t;
+ Expect(26);
+ if (la.kind == 1) {
+ List<BoundVar/*!*/> bvarsX; Attributes attrsX; Expression rangeX;
+ QuantifierDomain(out bvarsX, out attrsX, out rangeX);
+ bvars = bvarsX; attrs = attrsX; range = rangeX;
+
+ }
+ if (bvars == null) { bvars = new List<BoundVar>(); }
+ if (range == null) { range = new LiteralExpr(x, true); }
+
+ Expect(28);
+ while (la.kind == 38 || la.kind == 40) {
+ isFree = false;
+ if (la.kind == 38) {
+ Get();
+ isFree = true;
+ }
+ Expect(40);
+ Expression(out e);
+ Expect(14);
+ ens.Add(new MaybeFreeExpression(e, isFree));
+ }
+ BlockStmt(out block, out bodyStart, out bodyEnd);
+ s = new ParallelStmt(x, bvars, attrs, range, ens, block);
+ }
+
+ void CalcStmt(out Statement/*!*/ s) {
+ Contract.Ensures(Contract.ValueAtReturn(out s) != null);
+ Token x;
+ BinaryExpr.Opcode op, calcOp = BinaryExpr.Opcode.Eq, resOp = BinaryExpr.Opcode.Eq;
+ var lines = new List<Expression/*!*/>();
+ var hints = new List<BlockStmt/*!*/>();
+ var customOps = new List<BinaryExpr.Opcode?>();
+ BinaryExpr.Opcode? maybeOp;
+ Expression/*!*/ e;
+ BlockStmt/*!*/ h;
+ IToken opTok;
+
+ Expect(78);
+ x = t;
+ if (StartOf(17)) {
+ CalcOp(out opTok, out calcOp);
+ maybeOp = Microsoft.Dafny.CalcStmt.ResultOp(calcOp, calcOp); // guard against non-trasitive calcOp (like !=)
+ if (maybeOp == null) {
+ SemErr(opTok, "the main operator of a calculation must be transitive");
+ }
+ resOp = calcOp;
+
+ }
+ Expect(6);
+ if (StartOf(13)) {
+ Expression(out e);
+ lines.Add(e);
+ Expect(14);
+ while (StartOf(18)) {
+ Hint(out h);
+ hints.Add(h);
+ if (StartOf(17)) {
+ CalcOp(out opTok, out op);
+ maybeOp = Microsoft.Dafny.CalcStmt.ResultOp(resOp, op);
+ if (maybeOp == null) {
+ customOps.Add(null); // pretend the operator was not there to satisfy the precondition of the CalcStmt contructor
+ SemErr(opTok, "this operator cannot continue this calculation");
+ } else {
+ customOps.Add(op);
+ resOp = (BinaryExpr.Opcode)maybeOp;
+ }
+
+ } else if (StartOf(13)) {
+ customOps.Add(null);
+ } else SynErr(175);
+ Expression(out e);
+ lines.Add(e);
+ Expect(14);
+ }
+ }
+ Expect(7);
+ s = new CalcStmt(x, calcOp, lines, hints, customOps);
+ }
+
+ void ReturnStmt(out Statement/*!*/ s) {
+ IToken returnTok = null;
+ List<AssignmentRhs> rhss = null;
+ AssignmentRhs r;
+ bool isYield = false;
+
+ if (la.kind == 61) {
+ Get();
+ returnTok = t;
+ } else if (la.kind == 43) {
+ Get();
+ returnTok = t; isYield = true;
+ } else SynErr(176);
+ if (StartOf(19)) {
+ Rhs(out r, null);
+ rhss = new List<AssignmentRhs>(); rhss.Add(r);
+ while (la.kind == 24) {
+ Get();
+ Rhs(out r, null);
+ rhss.Add(r);
+ }
+ }
+ Expect(14);
+ if (isYield) {
+ s = new YieldStmt(returnTok, rhss);
+ } else {
+ s = new ReturnStmt(returnTok, rhss);
+ }
+
+ }
+
+ void SkeletonStmt(out Statement s) {
+ List<IToken> names = null;
+ List<Expression> exprs = null;
+ IToken tok, dotdotdot, whereTok;
+ Expression e;
+ Expect(31);
+ dotdotdot = t;
+ if (la.kind == 59) {
+ Get();
+ names = new List<IToken>(); exprs = new List<Expression>(); whereTok = t;
+ Ident(out tok);
+ names.Add(tok);
+ while (la.kind == 24) {
+ Get();
+ Ident(out tok);
+ names.Add(tok);
+ }
+ Expect(60);
+ Expression(out e);
+ exprs.Add(e);
+ while (la.kind == 24) {
+ Get();
+ Expression(out e);
+ exprs.Add(e);
+ }
+ if (exprs.Count != names.Count) {
+ SemErr(whereTok, exprs.Count < names.Count ? "not enough expressions" : "too many expressions");
+ names = null; exprs = null;
+ }
+
+ }
+ s = new SkeletonStatement(dotdotdot, names, exprs);
+ }
+
+ void Rhs(out AssignmentRhs r, Expression receiverForInitCall) {
+ Contract.Ensures(Contract.ValueAtReturn<AssignmentRhs>(out r) != null);
+ IToken/*!*/ x, newToken; Expression/*!*/ e;
+ List<Expression> ee = null;
+ Type ty = null;
+ CallStmt initCall = null;
+ List<Expression> args;
+ r = dummyRhs; // to please compiler
+ Attributes attrs = null;
+
+ if (la.kind == 64) {
+ Get();
+ newToken = t;
+ TypeAndToken(out x, out ty);
+ if (la.kind == 17 || la.kind == 26 || la.kind == 65) {
+ if (la.kind == 65) {
+ Get();
+ ee = new List<Expression>();
+ Expressions(ee);
+ Expect(66);
+ UserDefinedType tmp = theBuiltIns.ArrayType(x, ee.Count, new IntType(), true);
+
+ } else if (la.kind == 17) {
+ Get();
+ Ident(out x);
+ Expect(26);
+ args = new List<Expression/*!*/>();
+ if (StartOf(13)) {
+ Expressions(args);
+ }
+ Expect(28);
+ initCall = new CallStmt(x, new List<Expression>(), receiverForInitCall, x.val, args);
+ } else {
+ Get();
+ var udf = ty as UserDefinedType;
+ if (udf != null && 0 < udf.Path.Count && udf.TypeArgs.Count == 0) {
+ // The parsed name had the form "A.B.Ctr", so treat "A.B" as the name of the type and "Ctr" as
+ // the name of the constructor that's being invoked.
+ x = udf.tok;
+ ty = new UserDefinedType(udf.Path[0], udf.Path[udf.Path.Count-1].val, new List<Type>(), udf.Path.GetRange(0,udf.Path.Count-1));
+ } else {
+ SemErr(t, "expected '.'");
+ x = null;
+ }
+ args = new List<Expression/*!*/>();
+ if (StartOf(13)) {
+ Expressions(args);
+ }
+ Expect(28);
+ if (x != null) {
+ initCall = new CallStmt(x, new List<Expression>(), receiverForInitCall, x.val, args);
+ }
+
+ }
+ }
+ if (ee != null) {
+ r = new TypeRhs(newToken, ty, ee);
+ } else {
+ r = new TypeRhs(newToken, ty, initCall);
+ }
+
+ } else if (la.kind == 67) {
+ Get();
+ x = t;
+ Expression(out e);
+ r = new ExprRhs(new UnaryExpr(x, UnaryExpr.Opcode.SetChoose, e));
+ } else if (la.kind == 55) {
+ Get();
+ r = new HavocRhs(t);
+ } else if (StartOf(13)) {
+ Expression(out e);
+ r = new ExprRhs(e);
+ } else SynErr(177);
+ while (la.kind == 6) {
+ Attribute(ref attrs);
+ }
+ r.Attributes = attrs;
+ }
+
+ void Lhs(out Expression e) {
+ e = dummyExpr; // the assignment is to please the compiler, the dummy value to satisfy contracts in the event of a parse error
+
+ if (la.kind == 1) {
+ DottedIdentifiersAndFunction(out e);
+ while (la.kind == 17 || la.kind == 65) {
+ Suffix(ref e);
+ }
+ } else if (StartOf(20)) {
+ ConstAtomExpression(out e);
+ Suffix(ref e);
+ while (la.kind == 17 || la.kind == 65) {
+ Suffix(ref e);
+ }
+ } else SynErr(178);
+ }
+
+ void Expressions(List<Expression/*!*/>/*!*/ args) {
+ Contract.Requires(cce.NonNullElements(args)); Expression/*!*/ e;
+ Expression(out e);
+ args.Add(e);
+ while (la.kind == 24) {
+ Get();
+ Expression(out e);
+ args.Add(e);
+ }
+ }
+
+ void Guard(out Expression e) {
+ Expression/*!*/ ee; e = null;
+ Expect(26);
+ if (la.kind == 55) {
+ Get();
+ e = null;
+ } else if (StartOf(13)) {
+ Expression(out ee);
+ e = ee;
+ } else SynErr(179);
+ Expect(28);
+ }
+
+ void AlternativeBlock(out List<GuardedAlternative> alternatives) {
+ alternatives = new List<GuardedAlternative>();
+ IToken x;
+ Expression e;
+ List<Statement> body;
+
+ Expect(6);
+ while (la.kind == 70) {
+ Get();
+ x = t;
+ Expression(out e);
+ Expect(71);
+ body = new List<Statement>();
+ while (StartOf(11)) {
+ Stmt(body);
+ }
+ alternatives.Add(new GuardedAlternative(x, e, body));
+ }
+ Expect(7);
+ }
+
+ void LoopSpec(out List<MaybeFreeExpression/*!*/> invariants, out List<Expression/*!*/> decreases, out List<FrameExpression/*!*/> mod, ref Attributes decAttrs, ref Attributes modAttrs) {
+ FrameExpression/*!*/ fe;
+ invariants = new List<MaybeFreeExpression/*!*/>();
+ MaybeFreeExpression invariant = null;
+ decreases = new List<Expression/*!*/>();
+ mod = null;
+
+ while (StartOf(21)) {
+ if (la.kind == 38 || la.kind == 73) {
+ Invariant(out invariant);
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(180); Get();}
+ Expect(14);
+ invariants.Add(invariant);
+ } else if (la.kind == 41) {
+ while (!(la.kind == 0 || la.kind == 41)) {SynErr(181); Get();}
+ Get();
+ while (IsAttribute()) {
+ Attribute(ref decAttrs);
+ }
+ DecreasesList(decreases, true);
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(182); Get();}
+ Expect(14);
+ } else {
+ while (!(la.kind == 0 || la.kind == 37)) {SynErr(183); Get();}
+ Get();
+ while (IsAttribute()) {
+ Attribute(ref modAttrs);
+ }
+ mod = mod ?? new List<FrameExpression>();
+ if (StartOf(9)) {
+ FrameExpression(out fe);
+ mod.Add(fe);
+ while (la.kind == 24) {
+ Get();
+ FrameExpression(out fe);
+ mod.Add(fe);
+ }
+ }
+ while (!(la.kind == 0 || la.kind == 14)) {SynErr(184); Get();}
+ Expect(14);
+ }
+ }
+ }
+
+ void Invariant(out MaybeFreeExpression/*!*/ invariant) {
+ bool isFree = false; Expression/*!*/ e; List<string> ids = new List<string>(); invariant = null; Attributes attrs = null;
+ while (!(la.kind == 0 || la.kind == 38 || la.kind == 73)) {SynErr(185); Get();}
+ if (la.kind == 38) {
+ Get();
+ isFree = true;
+ }
+ Expect(73);
+ while (IsAttribute()) {
+ Attribute(ref attrs);
+ }
+ Expression(out e);
+ invariant = new MaybeFreeExpression(e, isFree, attrs);
+ }
+
+ void CaseStatement(out MatchCaseStmt/*!*/ c) {
+ Contract.Ensures(Contract.ValueAtReturn(out c) != null);
+ IToken/*!*/ x, id;
+ List<BoundVar/*!*/> arguments = new List<BoundVar/*!*/>();
+ BoundVar/*!*/ bv;
+ List<Statement/*!*/> body = new List<Statement/*!*/>();
+
+ Expect(70);
+ x = t;
+ Ident(out id);
+ if (la.kind == 26) {
+ Get();
+ IdentTypeOptional(out bv);
+ arguments.Add(bv);
+ while (la.kind == 24) {
+ Get();
+ IdentTypeOptional(out bv);
+ arguments.Add(bv);
+ }
+ Expect(28);
+ }
+ Expect(71);
+ while (StartOf(11)) {
+ Stmt(body);
+ }
+ c = new MatchCaseStmt(x, id.val, arguments, body);
+ }
+
+ void AttributeArg(out Attributes.Argument/*!*/ arg) {
+ Contract.Ensures(Contract.ValueAtReturn(out arg) != null); Expression/*!*/ e; arg = dummyAttrArg;
+ if (la.kind == 4) {
+ Get();
+ arg = new Attributes.Argument(t, t.val.Substring(1, t.val.Length-2));
+ } else if (StartOf(13)) {
+ Expression(out e);
+ arg = new Attributes.Argument(t, e);
+ } else SynErr(186);
+ }
+
+ void QuantifierDomain(out List<BoundVar/*!*/> bvars, out Attributes attrs, out Expression range) {
+ bvars = new List<BoundVar/*!*/>();
+ BoundVar/*!*/ bv;
+ attrs = null;
+ range = null;
+
+ IdentTypeOptional(out bv);
+ bvars.Add(bv);
+ while (la.kind == 24) {
+ Get();
+ IdentTypeOptional(out bv);
+ bvars.Add(bv);
+ }
+ while (la.kind == 6) {
+ Attribute(ref attrs);
+ }
+ if (la.kind == 22) {
+ Get();
+ Expression(out range);
+ }
+ }
+
+ void CalcOp(out IToken x, out BinaryExpr.Opcode/*!*/ op) {
+ Contract.Ensures(Microsoft.Dafny.CalcStmt.ValidOp(Contract.ValueAtReturn(out op)));
+ op = BinaryExpr.Opcode.Eq; // Returns Eq if parsing fails because it is compatible with any other operator
+ x = null;
+
+ switch (la.kind) {
+ case 27: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Eq;
+ break;
+ }
+ case 32: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Lt;
+ break;
+ }
+ case 33: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Gt;
+ break;
+ }
+ case 79: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Le;
+ break;
+ }
+ case 80: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Ge;
+ break;
+ }
+ case 81: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Neq;
+ break;
+ }
+ case 82: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Neq;
+ break;
+ }
+ case 83: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Le;
+ break;
+ }
+ case 84: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Ge;
+ break;
+ }
+ case 85: case 86: {
+ EquivOp();
+ x = t; op = BinaryExpr.Opcode.Iff;
+ break;
+ }
+ case 87: case 88: {
+ ImpliesOp();
+ x = t; op = BinaryExpr.Opcode.Imp;
+ break;
+ }
+ default: SynErr(187); break;
+ }
+ }
+
+ void Hint(out BlockStmt s) {
+ Contract.Ensures(Contract.ValueAtReturn(out s) != null); // returns an empty block statement if the hint is empty
+ var subhints = new List<Statement/*!*/>();
+ IToken bodyStart, bodyEnd;
+ BlockStmt/*!*/ block;
+ Statement/*!*/ calc;
+ Token x = la;
+
+ while (la.kind == 6 || la.kind == 78) {
+ if (la.kind == 6) {
+ BlockStmt(out block, out bodyStart, out bodyEnd);
+ subhints.Add(block);
+ } else {
+ CalcStmt(out calc);
+ subhints.Add(calc);
+ }
+ }
+ s = new BlockStmt(x, subhints); // if the hint is empty x is the first token of the next line, but it doesn't matter cause the block statement is just used as a container
+
+ }
+
+ void EquivOp() {
+ if (la.kind == 85) {
+ Get();
+ } else if (la.kind == 86) {
+ Get();
+ } else SynErr(188);
+ }
+
+ void ImpliesOp() {
+ if (la.kind == 87) {
+ Get();
+ } else if (la.kind == 88) {
+ Get();
+ } else SynErr(189);
+ }
+
+ void EquivExpression(out Expression/*!*/ e0) {
+ Contract.Ensures(Contract.ValueAtReturn(out e0) != null); IToken/*!*/ x; Expression/*!*/ e1;
+ ImpliesExpression(out e0);
+ while (la.kind == 85 || la.kind == 86) {
+ EquivOp();
+ x = t;
+ ImpliesExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Iff, e0, e1);
+ }
+ }
+
+ void ImpliesExpression(out Expression/*!*/ e0) {
+ Contract.Ensures(Contract.ValueAtReturn(out e0) != null); IToken/*!*/ x; Expression/*!*/ e1;
+ LogicalExpression(out e0);
+ if (la.kind == 87 || la.kind == 88) {
+ ImpliesOp();
+ x = t;
+ ImpliesExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Imp, e0, e1);
+ }
+ }
+
+ void LogicalExpression(out Expression/*!*/ e0) {
+ Contract.Ensures(Contract.ValueAtReturn(out e0) != null); IToken/*!*/ x; Expression/*!*/ e1;
+ RelationalExpression(out e0);
+ if (StartOf(22)) {
+ if (la.kind == 89 || la.kind == 90) {
+ AndOp();
+ x = t;
+ RelationalExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1);
+ while (la.kind == 89 || la.kind == 90) {
+ AndOp();
+ x = t;
+ RelationalExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1);
+ }
+ } else {
+ OrOp();
+ x = t;
+ RelationalExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1);
+ while (la.kind == 91 || la.kind == 92) {
+ OrOp();
+ x = t;
+ RelationalExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1);
+ }
+ }
+ }
+ }
+
+ void RelationalExpression(out Expression/*!*/ e) {
+ Contract.Ensures(Contract.ValueAtReturn(out e) != null);
+ IToken x, firstOpTok = null; Expression e0, e1, acc = null; BinaryExpr.Opcode op;
+ List<Expression> chain = null;
+ List<BinaryExpr.Opcode> ops = null;
+ int kind = 0; // 0 ("uncommitted") indicates chain of ==, possibly with one !=
+ // 1 ("ascending") indicates chain of ==, <, <=, possibly with one !=
+ // 2 ("descending") indicates chain of ==, >, >=, possibly with one !=
+ // 3 ("illegal") indicates illegal chain
+ // 4 ("disjoint") indicates chain of disjoint set operators
+ bool hasSeenNeq = false;
+
+ Term(out e0);
+ e = e0;
+ if (StartOf(23)) {
+ RelOp(out x, out op);
+ firstOpTok = x;
+ Term(out e1);
+ e = new BinaryExpr(x, op, e0, e1);
+ if (op == BinaryExpr.Opcode.Disjoint)
+ acc = new BinaryExpr(x, BinaryExpr.Opcode.Add, e0, e1); // accumulate first two operands.
+
+ while (StartOf(23)) {
+ if (chain == null) {
+ chain = new List<Expression>();
+ ops = new List<BinaryExpr.Opcode>();
+ chain.Add(e0); ops.Add(op); chain.Add(e1);
+ switch (op) {
+ case BinaryExpr.Opcode.Eq:
+ kind = 0; break;
+ case BinaryExpr.Opcode.Neq:
+ kind = 0; hasSeenNeq = true; break;
+ case BinaryExpr.Opcode.Lt:
+ case BinaryExpr.Opcode.Le:
+ kind = 1; break;
+ case BinaryExpr.Opcode.Gt:
+ case BinaryExpr.Opcode.Ge:
+ kind = 2; break;
+ case BinaryExpr.Opcode.Disjoint:
+ kind = 4; break;
+ default:
+ kind = 3; break;
+ }
+ }
+ e0 = e1;
+
+ RelOp(out x, out op);
+ switch (op) {
+ case BinaryExpr.Opcode.Eq:
+ if (kind != 0 && kind != 1 && kind != 2) { SemErr(x, "chaining not allowed from the previous operator"); }
+ break;
+ case BinaryExpr.Opcode.Neq:
+ if (hasSeenNeq) { SemErr(x, "a chain cannot have more than one != operator"); }
+ if (kind != 0 && kind != 1 && kind != 2) { SemErr(x, "this operator cannot continue this chain"); }
+ hasSeenNeq = true; break;
+ case BinaryExpr.Opcode.Lt:
+ case BinaryExpr.Opcode.Le:
+ if (kind == 0) { kind = 1; }
+ else if (kind != 1) { SemErr(x, "this operator chain cannot continue with an ascending operator"); }
+ break;
+ case BinaryExpr.Opcode.Gt:
+ case BinaryExpr.Opcode.Ge:
+ if (kind == 0) { kind = 2; }
+ else if (kind != 2) { SemErr(x, "this operator chain cannot continue with a descending operator"); }
+ break;
+ case BinaryExpr.Opcode.Disjoint:
+ if (kind != 4) { SemErr(x, "can only chain disjoint (!!) with itself."); kind = 3; }
+ break;
+ default:
+ SemErr(x, "this operator cannot be part of a chain");
+ kind = 3; break;
+ }
+
+ Term(out e1);
+ ops.Add(op); chain.Add(e1);
+ if (op == BinaryExpr.Opcode.Disjoint) {
+ e = new BinaryExpr(x, BinaryExpr.Opcode.And, e, new BinaryExpr(x, op, acc, e1));
+ acc = new BinaryExpr(x, BinaryExpr.Opcode.Add, acc, e1); //e0 has already been added.
+ }
+ else
+ e = new BinaryExpr(x, BinaryExpr.Opcode.And, e, new BinaryExpr(x, op, e0, e1));
+
+ }
+ }
+ if (chain != null) {
+ e = new ChainingExpression(firstOpTok, chain, ops, e);
+ }
+
+ }
+
+ void AndOp() {
+ if (la.kind == 89) {
+ Get();
+ } else if (la.kind == 90) {
+ Get();
+ } else SynErr(190);
+ }
+
+ void OrOp() {
+ if (la.kind == 91) {
+ Get();
+ } else if (la.kind == 92) {
+ Get();
+ } else SynErr(191);
+ }
+
+ void Term(out Expression/*!*/ e0) {
+ Contract.Ensures(Contract.ValueAtReturn(out e0) != null); IToken/*!*/ x; Expression/*!*/ e1; BinaryExpr.Opcode op;
+ Factor(out e0);
+ while (la.kind == 96 || la.kind == 97) {
+ AddOp(out x, out op);
+ Factor(out e1);
+ e0 = new BinaryExpr(x, op, e0, e1);
+ }
+ }
+
+ void RelOp(out IToken/*!*/ x, out BinaryExpr.Opcode op) {
+ Contract.Ensures(Contract.ValueAtReturn(out x) != null);
+ x = Token.NoToken; op = BinaryExpr.Opcode.Add/*(dummy)*/;
+ IToken y;
+
+ switch (la.kind) {
+ case 27: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Eq;
+ break;
+ }
+ case 32: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Lt;
+ break;
+ }
+ case 33: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Gt;
+ break;
+ }
+ case 79: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Le;
+ break;
+ }
+ case 80: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Ge;
+ break;
+ }
+ case 81: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Neq;
+ break;
+ }
+ case 93: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Disjoint;
+ break;
+ }
+ case 94: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.In;
+ break;
+ }
+ case 95: {
+ Get();
+ x = t; y = Token.NoToken;
+ if (la.kind == 94) {
+ Get();
+ y = t;
+ }
+ if (y == Token.NoToken) {
+ SemErr(x, "invalid RelOp");
+ } else if (y.pos != x.pos + 1) {
+ SemErr(x, "invalid RelOp (perhaps you intended \"!in\" with no intervening whitespace?)");
+ } else {
+ x.val = "!in";
+ op = BinaryExpr.Opcode.NotIn;
+ }
+
+ break;
+ }
+ case 82: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Neq;
+ break;
+ }
+ case 83: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Le;
+ break;
+ }
+ case 84: {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Ge;
+ break;
+ }
+ default: SynErr(192); break;
+ }
+ }
+
+ void Factor(out Expression/*!*/ e0) {
+ Contract.Ensures(Contract.ValueAtReturn(out e0) != null); IToken/*!*/ x; Expression/*!*/ e1; BinaryExpr.Opcode op;
+ UnaryExpression(out e0);
+ while (la.kind == 55 || la.kind == 98 || la.kind == 99) {
+ MulOp(out x, out op);
+ UnaryExpression(out e1);
+ e0 = new BinaryExpr(x, op, e0, e1);
+ }
+ }
+
+ 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 == 96) {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Add;
+ } else if (la.kind == 97) {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Sub;
+ } else SynErr(193);
+ }
+
+ void UnaryExpression(out Expression/*!*/ e) {
+ Contract.Ensures(Contract.ValueAtReturn(out e) != null); IToken/*!*/ x; e = dummyExpr;
+ switch (la.kind) {
+ case 97: {
+ Get();
+ x = t;
+ UnaryExpression(out e);
+ e = new BinaryExpr(x, BinaryExpr.Opcode.Sub, new LiteralExpr(x, 0), e);
+ break;
+ }
+ case 95: case 100: {
+ NegOp();
+ x = t;
+ UnaryExpression(out e);
+ e = new UnaryExpr(x, UnaryExpr.Opcode.Not, e);
+ break;
+ }
+ case 23: case 47: case 57: case 63: case 68: case 74: case 75: case 109: case 110: case 111: case 112: {
+ EndlessExpression(out e);
+ break;
+ }
+ case 1: {
+ DottedIdentifiersAndFunction(out e);
+ while (la.kind == 17 || la.kind == 65) {
+ Suffix(ref e);
+ }
+ break;
+ }
+ case 6: case 65: {
+ DisplayExpr(out e);
+ while (la.kind == 17 || la.kind == 65) {
+ Suffix(ref e);
+ }
+ break;
+ }
+ case 48: {
+ MultiSetExpr(out e);
+ while (la.kind == 17 || la.kind == 65) {
+ Suffix(ref e);
+ }
+ break;
+ }
+ case 50: {
+ Get();
+ x = t;
+ if (la.kind == 65) {
+ MapDisplayExpr(x, out e);
+ while (la.kind == 17 || la.kind == 65) {
+ Suffix(ref e);
+ }
+ } else if (la.kind == 1) {
+ MapComprehensionExpr(x, out e);
+ } else if (StartOf(24)) {
+ SemErr("map must be followed by literal in brackets or comprehension.");
+ } else SynErr(194);
+ break;
+ }
+ case 2: case 22: case 26: case 101: case 102: case 103: case 104: case 105: case 106: {
+ ConstAtomExpression(out e);
+ while (la.kind == 17 || la.kind == 65) {
+ Suffix(ref e);
+ }
+ break;
+ }
+ default: SynErr(195); break;
+ }
+ }
+
+ 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) {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Mul;
+ } else if (la.kind == 98) {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Div;
+ } else if (la.kind == 99) {
+ Get();
+ x = t; op = BinaryExpr.Opcode.Mod;
+ } else SynErr(196);
+ }
+
+ void NegOp() {
+ if (la.kind == 95) {
+ Get();
+ } else if (la.kind == 100) {
+ Get();
+ } else SynErr(197);
+ }
+
+ void EndlessExpression(out Expression e) {
+ IToken/*!*/ x;
+ Expression e0, e1;
+ e = dummyExpr;
+
+ switch (la.kind) {
+ case 68: {
+ Get();
+ x = t;
+ Expression(out e);
+ Expect(107);
+ Expression(out e0);
+ Expect(69);
+ Expression(out e1);
+ e = new ITEExpr(x, e, e0, e1);
+ break;
+ }
+ case 74: {
+ MatchExpression(out e);
+ break;
+ }
+ case 109: case 110: case 111: case 112: {
+ QuantifierGuts(out e);
+ break;
+ }
+ case 47: {
+ ComprehensionExpr(out e);
+ break;
+ }
+ case 75: {
+ Get();
+ x = t;
+ Expression(out e0);
+ Expect(14);
+ Expression(out e1);
+ e = new AssertExpr(x, e0, e1);
+ break;
+ }
+ case 63: {
+ Get();
+ x = t;
+ Expression(out e0);
+ Expect(14);
+ Expression(out e1);
+ e = new AssumeExpr(x, e0, e1);
+ break;
+ }
+ case 23: {
+ LetExpr(out e);
+ break;
+ }
+ case 57: {
+ NamedExpr(out e);
+ break;
+ }
+ default: SynErr(198); break;
+ }
+ }
+
+ void DottedIdentifiersAndFunction(out Expression e) {
+ IToken id; IToken openParen = null;
+ List<Expression> args = null;
+ List<IToken> idents = new List<IToken>();
+
+ Ident(out id);
+ idents.Add(id);
+ while (la.kind == 17) {
+ Get();
+ Ident(out id);
+ idents.Add(id);
+ }
+ if (la.kind == 26) {
+ Get();
+ openParen = t; args = new List<Expression>();
+ if (StartOf(13)) {
+ Expressions(args);
+ }
+ Expect(28);
+ }
+ e = new IdentifierSequence(idents, openParen, args);
+ }
+
+ void Suffix(ref Expression/*!*/ e) {
+ Contract.Requires(e != null); Contract.Ensures(e!=null); IToken/*!*/ id, x; List<Expression/*!*/>/*!*/ args;
+ Expression e0 = null; Expression e1 = null; Expression/*!*/ ee; bool anyDots = false;
+ List<Expression> multipleIndices = null;
+ bool func = false;
+
+ if (la.kind == 17) {
+ Get();
+ Ident(out id);
+ if (la.kind == 26) {
+ Get();
+ IToken openParen = t; args = new List<Expression/*!*/>(); func = true;
+ if (StartOf(13)) {
+ Expressions(args);
+ }
+ Expect(28);
+ e = new FunctionCallExpr(id, id.val, e, openParen, args);
+ }
+ if (!func) { e = new ExprDotName(id, e, id.val); }
+ } else if (la.kind == 65) {
+ Get();
+ x = t;
+ if (StartOf(13)) {
+ Expression(out ee);
+ e0 = ee;
+ if (la.kind == 108) {
+ Get();
+ anyDots = true;
+ if (StartOf(13)) {
+ Expression(out ee);
+ e1 = ee;
+ }
+ } else if (la.kind == 60) {
+ Get();
+ Expression(out ee);
+ e1 = ee;
+ } else if (la.kind == 24 || la.kind == 66) {
+ while (la.kind == 24) {
+ Get();
+ Expression(out ee);
+ if (multipleIndices == null) {
+ multipleIndices = new List<Expression>();
+ multipleIndices.Add(e0);
+ }
+ multipleIndices.Add(ee);
+
+ }
+ } else SynErr(199);
+ } else if (la.kind == 108) {
+ Get();
+ anyDots = true;
+ if (StartOf(13)) {
+ Expression(out ee);
+ e1 = ee;
+ }
+ } else SynErr(200);
+ if (multipleIndices != null) {
+ e = new MultiSelectExpr(x, e, multipleIndices);
+ // make sure an array class with this dimensionality exists
+ UserDefinedType tmp = theBuiltIns.ArrayType(x, multipleIndices.Count, new IntType(), true);
+ } else {
+ if (!anyDots && e0 == null) {
+ /* a parsing error occurred */
+ e0 = dummyExpr;
+ }
+ Contract.Assert(anyDots || e0 != null);
+ if (anyDots) {
+ //Contract.Assert(e0 != null || e1 != null);
+ e = new SeqSelectExpr(x, false, e, e0, e1);
+ } else if (e1 == null) {
+ Contract.Assert(e0 != null);
+ e = new SeqSelectExpr(x, true, e, e0, null);
+ } else {
+ Contract.Assert(e0 != null);
+ e = new SeqUpdateExpr(x, e, e0, e1);
+ }
+ }
+
+ Expect(66);
+ } else SynErr(201);
+ }
+
+ void DisplayExpr(out Expression e) {
+ Contract.Ensures(Contract.ValueAtReturn(out e) != null);
+ IToken/*!*/ x = null; List<Expression/*!*/>/*!*/ elements;
+ e = dummyExpr;
+
+ if (la.kind == 6) {
+ Get();
+ x = t; elements = new List<Expression/*!*/>();
+ if (StartOf(13)) {
+ Expressions(elements);
+ }
+ e = new SetDisplayExpr(x, elements);
+ Expect(7);
+ } else if (la.kind == 65) {
+ Get();
+ x = t; elements = new List<Expression/*!*/>();
+ if (StartOf(13)) {
+ Expressions(elements);
+ }
+ e = new SeqDisplayExpr(x, elements);
+ Expect(66);
+ } else SynErr(202);
+ }
+
+ void MultiSetExpr(out Expression e) {
+ Contract.Ensures(Contract.ValueAtReturn(out e) != null);
+ IToken/*!*/ x = null; List<Expression/*!*/>/*!*/ elements;
+ e = dummyExpr;
+
+ Expect(48);
+ x = t;
+ if (la.kind == 6) {
+ Get();
+ elements = new List<Expression/*!*/>();
+ if (StartOf(13)) {
+ Expressions(elements);
+ }
+ e = new MultiSetDisplayExpr(x, elements);
+ Expect(7);
+ } else if (la.kind == 26) {
+ Get();
+ x = t; elements = new List<Expression/*!*/>();
+ Expression(out e);
+ e = new MultiSetFormingExpr(x, e);
+ Expect(28);
+ } else if (StartOf(25)) {
+ SemErr("multiset must be followed by multiset literal or expression to coerce in parentheses.");
+ } else SynErr(203);
+ }
+
+ void MapDisplayExpr(IToken/*!*/ mapToken, out Expression e) {
+ Contract.Ensures(Contract.ValueAtReturn(out e) != null);
+ List<ExpressionPair/*!*/>/*!*/ elements= new List<ExpressionPair/*!*/>() ;
+ e = dummyExpr;
+
+ Expect(65);
+ if (StartOf(13)) {
+ MapLiteralExpressions(out elements);
+ }
+ e = new MapDisplayExpr(mapToken, elements);
+ Expect(66);
+ }
+
+ void MapComprehensionExpr(IToken/*!*/ mapToken, out Expression e) {
+ Contract.Ensures(Contract.ValueAtReturn(out e) != null);
+ BoundVar/*!*/ bv;
+ List<BoundVar/*!*/> bvars = new List<BoundVar/*!*/>();
+ Expression range = null;
+ Expression body;
+
+ IdentTypeOptional(out bv);
+ bvars.Add(bv);
+ if (la.kind == 22) {
+ Get();
+ Expression(out range);
+ }
+ QSep();
+ Expression(out body);
+ e = new MapComprehension(mapToken, bvars, range ?? new LiteralExpr(mapToken, true), body);
+
+ }
+
+ void ConstAtomExpression(out Expression/*!*/ e) {
+ Contract.Ensures(Contract.ValueAtReturn(out e) != null);
+ IToken/*!*/ x; BigInteger n;
+ e = dummyExpr;
+
+ switch (la.kind) {
+ case 101: {
+ Get();
+ e = new LiteralExpr(t, false);
+ break;
+ }
+ case 102: {
+ Get();
+ e = new LiteralExpr(t, true);
+ break;
+ }
+ case 103: {
+ Get();
+ e = new LiteralExpr(t);
+ break;
+ }
+ case 2: {
+ Nat(out n);
+ e = new LiteralExpr(t, n);
+ break;
+ }
+ case 104: {
+ Get();
+ e = new ThisExpr(t);
+ break;
+ }
+ case 105: {
+ Get();
+ x = t;
+ Expect(26);
+ Expression(out e);
+ Expect(28);
+ e = new FreshExpr(x, e);
+ break;
+ }
+ case 106: {
+ Get();
+ x = t;
+ Expect(26);
+ Expression(out e);
+ Expect(28);
+ e = new OldExpr(x, e);
+ break;
+ }
+ case 22: {
+ Get();
+ x = t;
+ Expression(out e);
+ e = new UnaryExpr(x, UnaryExpr.Opcode.SeqLength, e);
+ Expect(22);
+ break;
+ }
+ case 26: {
+ Get();
+ x = t;
+ Expression(out e);
+ e = new ParensExpression(x, e);
+ Expect(28);
+ break;
+ }
+ default: SynErr(204); break;
+ }
+ }
+
+ void Nat(out BigInteger n) {
+ Expect(2);
+ try {
+ n = BigInteger.Parse(t.val);
+ } catch (System.FormatException) {
+ SemErr("incorrectly formatted number");
+ n = BigInteger.Zero;
+ }
+
+ }
+
+ void MapLiteralExpressions(out List<ExpressionPair> elements) {
+ Expression/*!*/ d, r;
+ elements = new List<ExpressionPair/*!*/>();
+ Expression(out d);
+ Expect(60);
+ Expression(out r);
+ elements.Add(new ExpressionPair(d,r));
+ while (la.kind == 24) {
+ Get();
+ Expression(out d);
+ Expect(60);
+ Expression(out r);
+ elements.Add(new ExpressionPair(d,r));
+ }
+ }
+
+ void QSep() {
+ if (la.kind == 113) {
+ Get();
+ } else if (la.kind == 114) {
+ Get();
+ } else SynErr(205);
+ }
+
+ void MatchExpression(out Expression/*!*/ e) {
+ Contract.Ensures(Contract.ValueAtReturn(out e) != null); IToken/*!*/ x; MatchCaseExpr/*!*/ c;
+ List<MatchCaseExpr/*!*/> cases = new List<MatchCaseExpr/*!*/>();
+
+ Expect(74);
+ x = t;
+ Expression(out e);
+ while (la.kind == 70) {
+ CaseExpression(out c);
+ cases.Add(c);
+ }
+ e = new MatchExpr(x, e, cases);
+ }
+
+ void QuantifierGuts(out Expression/*!*/ q) {
+ Contract.Ensures(Contract.ValueAtReturn(out q) != null); IToken/*!*/ x = Token.NoToken;
+ bool univ = false;
+ List<BoundVar/*!*/> bvars;
+ Attributes attrs;
+ Expression range;
+ Expression/*!*/ body;
+
+ if (la.kind == 109 || la.kind == 110) {
+ Forall();
+ x = t; univ = true;
+ } else if (la.kind == 111 || la.kind == 112) {
+ Exists();
+ x = t;
+ } else SynErr(206);
+ QuantifierDomain(out bvars, out attrs, out range);
+ QSep();
+ Expression(out body);
+ if (univ) {
+ q = new ForallExpr(x, bvars, range, body, attrs);
+ } else {
+ q = new ExistsExpr(x, bvars, range, body, attrs);
+ }
+
+ }
+
+ void ComprehensionExpr(out Expression/*!*/ q) {
+ Contract.Ensures(Contract.ValueAtReturn(out q) != null);
+ IToken/*!*/ x = Token.NoToken;
+ BoundVar/*!*/ bv;
+ List<BoundVar/*!*/> bvars = new List<BoundVar/*!*/>();
+ Expression/*!*/ range;
+ Expression body = null;
+
+ Expect(47);
+ x = t;
+ IdentTypeOptional(out bv);
+ bvars.Add(bv);
+ while (la.kind == 24) {
+ Get();
+ IdentTypeOptional(out bv);
+ bvars.Add(bv);
+ }
+ Expect(22);
+ Expression(out range);
+ if (la.kind == 113 || la.kind == 114) {
+ QSep();
+ Expression(out body);
+ }
+ 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);
+
+ }
+
+ void LetExpr(out Expression e) {
+ IToken/*!*/ x;
+ e = dummyExpr;
+ BoundVar d;
+ List<BoundVar> letVars; List<Expression> letRHSs;
+
+ Expect(23);
+ x = t;
+ letVars = new List<BoundVar>();
+ letRHSs = new List<Expression>();
+ IdentTypeOptional(out d);
+ letVars.Add(d);
+ while (la.kind == 24) {
+ Get();
+ IdentTypeOptional(out d);
+ letVars.Add(d);
+ }
+ Expect(60);
+ Expression(out e);
+ letRHSs.Add(e);
+ while (la.kind == 24) {
+ Get();
+ Expression(out e);
+ letRHSs.Add(e);
+ }
+ Expect(14);
+ Expression(out e);
+ e = new LetExpr(x, letVars, letRHSs, e);
+ }
+
+ void NamedExpr(out Expression e) {
+ IToken/*!*/ x, d;
+ e = dummyExpr;
+ Expression expr;
+
+ Expect(57);
+ x = t;
+ NoUSIdent(out d);
+ Expect(5);
+ Expression(out e);
+ expr = e;
+ e = new NamedExpr(x, d.val, expr);
+ }
+
+ void CaseExpression(out MatchCaseExpr/*!*/ c) {
+ Contract.Ensures(Contract.ValueAtReturn(out c) != null); IToken/*!*/ x, id;
+ List<BoundVar/*!*/> arguments = new List<BoundVar/*!*/>();
+ BoundVar/*!*/ bv;
+ Expression/*!*/ body;
+
+ Expect(70);
+ x = t;
+ Ident(out id);
+ if (la.kind == 26) {
+ Get();
+ IdentTypeOptional(out bv);
+ arguments.Add(bv);
+ while (la.kind == 24) {
+ Get();
+ IdentTypeOptional(out bv);
+ arguments.Add(bv);
+ }
+ Expect(28);
+ }
+ Expect(71);
+ Expression(out body);
+ c = new MatchCaseExpr(x, id.val, arguments, body);
+ }
+
+ void Forall() {
+ if (la.kind == 109) {
+ Get();
+ } else if (la.kind == 110) {
+ Get();
+ } else SynErr(207);
+ }
+
+ void Exists() {
+ if (la.kind == 111) {
+ Get();
+ } else if (la.kind == 112) {
+ Get();
+ } else SynErr(208);
+ }
+
+ void AttributeBody(ref Attributes attrs) {
+ string aName;
+ List<Attributes.Argument/*!*/> aArgs = new List<Attributes.Argument/*!*/>();
+ Attributes.Argument/*!*/ aArg;
+
+ Expect(5);
+ Expect(1);
+ aName = t.val;
+ if (StartOf(26)) {
+ AttributeArg(out aArg);
+ aArgs.Add(aArg);
+ while (la.kind == 24) {
+ Get();
+ AttributeArg(out aArg);
+ aArgs.Add(aArg);
+ }
+ }
+ attrs = new Attributes(aName, aArgs, attrs);
+ }
+
+
+
+ public void Parse() {
+ la = new Token();
+ la.val = "";
+ Get();
+ Dafny();
+ Expect(0);
+
+ Expect(0);
+ }
+
+ static readonly bool[,]/*!*/ set = {
+ {T,T,T,x, x,x,T,x, T,x,x,x, x,x,T,x, x,x,T,x, T,T,T,T, x,x,T,x, x,T,x,T, x,x,T,T, x,T,T,T, T,T,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,x, x,T,x,T, x,x,x,x, T,x,x,x, 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,T,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, x,x,T,T, T,T,x,T, x,T,x,x, x,T,x,x, x,x,T,T, 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, T,x,x,x, x,x,x,x, x,x,x,T, x,x,x,T, 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, 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,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,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,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,T, T,T,x,T, x,x,x,x, x,x,T,T, T,T,x,T, x,T,T,x, x,T,x,x, T,x,T,T, x,x,x,T, T,T,T,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,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,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,T,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,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, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,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,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,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,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,T,x, x,x,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,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,T,x, x,x,x,x, T,T,x,x, x,x,x,T, x,T,x,x, T,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,T, x,T,x,x, 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,T,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,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,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,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, x,x,T,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,T,T,x, x,T,x,T, x,x,x,x, T,x,x,x, T,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,T,T,T, T,T,T,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,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,T,T,x, x,x,T,x, x,x,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,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,T,x, x,x,x,x, x,T,x,x, x,x,x,T, x,T,x,x, T,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,T, x,T,x,x, T,T,T,T, T,T,T,x, x,T,T,T, T,x,x,x, x},
+ {x,T,T,x, x,x,T,x, x,x,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,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,T,x, x,x,x,T, T,T,x,x, x,x,x,T, x,T,x,x, T,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,T, x,T,x,x, T,T,T,T, T,T,T,x, x,T,T,T, T,x,x,x, x},
+ {T,T,T,x, x,x,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, x,x,T,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,T,T,x, x,T,x,T, x,x,x,x, T,x,x,x, T,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,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,x,x,x, x,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,x,x, x,x,x,x, x,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,T, 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,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,T,T,x, x,x,T,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, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,T,x, x,x,x,x, x,T,x,x, x,x,x,T, x,T,x,x, T,x,x,x, x,x,T,T, x,x,T,T, T,T,T,T, T,T,T,T, T,x,x,x, x,x,x,T, x,T,x,x, T,T,T,T, T,T,T,x, x,T,T,T, T,x,x,x, x},
+ {x,T,T,x, x,x,T,x, x,x,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,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,T,x, x,x,x,T, x,T,x,x, x,x,x,T, T,T,x,T, T,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,T, x,T,x,x, T,T,T,T, T,T,T,x, x,T,T,T, T,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,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,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,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,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, 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, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,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,T, 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,T, T,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,T,T, x,x,x,x, x,x,T,x, x,x,x,x, x,x,T,x, T,x,x,T, T,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,T, T,x,x,x, T,x,x,x, x,x,T,x, x,T,T,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, x,x,x,x, x,x,x,T, T,x,x,x, x,T,T,x, x},
+ {x,x,x,x, x,x,T,T, x,x,x,x, x,x,T,x, x,T,x,x, x,x,T,x, T,x,x,T, T,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,T, T,x,x,x, T,x,x,x, x,T,T,x, x,T,T,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, x,x,x,x, x,x,x,T, T,x,x,x, x,T,T,x, x},
+ {x,T,T,x, T,x,T,x, x,x,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,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,T,x, x,x,x,x, x,T,x,x, x,x,x,T, x,T,x,x, T,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,T, x,T,x,x, T,T,T,T, T,T,T,x, x,T,T,T, T,x,x,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
+
+ 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) {
+ Contract.Requires(msg != null);
+ errorStream.WriteLine(errMsgFormat, filename, line, col, msg);
+ count++;
+ }
+
+ string GetSyntaxErrorString(int n) {
+ string s;
+ switch (n) {
+ case 0: s = "EOF expected"; break;
+ case 1: s = "ident expected"; break;
+ case 2: s = "digits expected"; break;
+ case 3: s = "arrayToken expected"; break;
+ case 4: s = "string expected"; break;
+ case 5: s = "colon expected"; break;
+ case 6: s = "lbrace expected"; break;
+ case 7: s = "rbrace expected"; break;
+ case 8: s = "\"ghost\" expected"; break;
+ case 9: s = "\"module\" expected"; break;
+ case 10: s = "\"refines\" expected"; break;
+ case 11: s = "\"import\" expected"; break;
+ case 12: s = "\"opened\" expected"; break;
+ case 13: s = "\"=\" expected"; break;
+ case 14: s = "\";\" expected"; break;
+ case 15: s = "\"as\" expected"; break;
+ case 16: s = "\"default\" expected"; break;
+ case 17: s = "\".\" expected"; break;
+ case 18: s = "\"class\" expected"; break;
+ case 19: s = "\"static\" expected"; break;
+ case 20: s = "\"datatype\" expected"; break;
+ case 21: s = "\"codatatype\" expected"; break;
+ case 22: s = "\"|\" expected"; break;
+ case 23: s = "\"var\" expected"; break;
+ case 24: s = "\",\" expected"; break;
+ case 25: s = "\"type\" expected"; break;
+ case 26: s = "\"(\" expected"; break;
+ case 27: s = "\"==\" expected"; break;
+ case 28: s = "\")\" expected"; break;
+ case 29: s = "\"iterator\" expected"; break;
+ case 30: s = "\"yields\" expected"; break;
+ case 31: s = "\"...\" expected"; break;
+ case 32: s = "\"<\" expected"; break;
+ case 33: s = "\">\" expected"; break;
+ case 34: s = "\"method\" expected"; break;
+ case 35: s = "\"constructor\" expected"; break;
+ case 36: s = "\"returns\" expected"; break;
+ case 37: s = "\"modifies\" expected"; break;
+ case 38: s = "\"free\" expected"; break;
+ case 39: s = "\"requires\" expected"; break;
+ case 40: s = "\"ensures\" expected"; break;
+ case 41: s = "\"decreases\" expected"; break;
+ case 42: s = "\"reads\" expected"; break;
+ case 43: s = "\"yield\" expected"; break;
+ case 44: s = "\"bool\" expected"; break;
+ case 45: s = "\"nat\" expected"; break;
+ case 46: s = "\"int\" expected"; break;
+ case 47: s = "\"set\" expected"; break;
+ case 48: s = "\"multiset\" expected"; break;
+ case 49: s = "\"seq\" expected"; break;
+ case 50: s = "\"map\" expected"; break;
+ case 51: s = "\"object\" expected"; break;
+ case 52: s = "\"function\" expected"; break;
+ case 53: s = "\"predicate\" expected"; break;
+ case 54: s = "\"copredicate\" expected"; break;
+ case 55: s = "\"*\" expected"; break;
+ case 56: s = "\"`\" expected"; break;
+ case 57: s = "\"label\" expected"; break;
+ case 58: s = "\"break\" expected"; break;
+ case 59: s = "\"where\" expected"; break;
+ case 60: s = "\":=\" expected"; break;
+ case 61: s = "\"return\" expected"; break;
+ case 62: s = "\":|\" expected"; break;
+ case 63: s = "\"assume\" expected"; break;
+ case 64: s = "\"new\" expected"; break;
+ case 65: s = "\"[\" expected"; break;
+ case 66: s = "\"]\" expected"; break;
+ case 67: s = "\"choose\" expected"; break;
+ case 68: s = "\"if\" expected"; break;
+ case 69: s = "\"else\" expected"; break;
+ case 70: s = "\"case\" expected"; break;
+ case 71: s = "\"=>\" expected"; break;
+ case 72: s = "\"while\" expected"; break;
+ case 73: s = "\"invariant\" expected"; break;
+ case 74: s = "\"match\" expected"; break;
+ case 75: s = "\"assert\" expected"; break;
+ case 76: s = "\"print\" expected"; break;
+ case 77: s = "\"parallel\" expected"; break;
+ case 78: s = "\"calc\" expected"; break;
+ case 79: s = "\"<=\" expected"; break;
+ case 80: s = "\">=\" expected"; break;
+ case 81: s = "\"!=\" expected"; break;
+ case 82: s = "\"\\u2260\" expected"; break;
+ case 83: s = "\"\\u2264\" expected"; break;
+ case 84: s = "\"\\u2265\" expected"; break;
+ case 85: s = "\"<==>\" expected"; break;
+ case 86: s = "\"\\u21d4\" expected"; break;
+ case 87: s = "\"==>\" expected"; break;
+ case 88: s = "\"\\u21d2\" expected"; break;
+ case 89: s = "\"&&\" expected"; break;
+ case 90: s = "\"\\u2227\" expected"; break;
+ case 91: s = "\"||\" expected"; break;
+ case 92: s = "\"\\u2228\" expected"; break;
+ case 93: s = "\"!!\" expected"; break;
+ case 94: s = "\"in\" expected"; break;
+ case 95: s = "\"!\" expected"; break;
+ case 96: s = "\"+\" expected"; break;
+ case 97: s = "\"-\" expected"; break;
+ case 98: s = "\"/\" expected"; break;
+ case 99: s = "\"%\" expected"; break;
+ case 100: s = "\"\\u00ac\" expected"; break;
+ case 101: s = "\"false\" expected"; break;
+ case 102: s = "\"true\" expected"; break;
+ case 103: s = "\"null\" expected"; break;
+ case 104: s = "\"this\" expected"; break;
+ case 105: s = "\"fresh\" expected"; break;
+ case 106: s = "\"old\" expected"; break;
+ case 107: s = "\"then\" expected"; break;
+ case 108: s = "\"..\" expected"; break;
+ case 109: s = "\"forall\" expected"; break;
+ case 110: s = "\"\\u2200\" expected"; break;
+ case 111: s = "\"exists\" expected"; break;
+ case 112: s = "\"\\u2203\" expected"; break;
+ case 113: s = "\"::\" expected"; break;
+ case 114: s = "\"\\u2022\" expected"; break;
+ case 115: s = "??? expected"; break;
+ case 116: s = "invalid Dafny"; break;
+ case 117: s = "invalid SubModuleDecl"; break;
+ case 118: s = "invalid SubModuleDecl"; break;
+ case 119: s = "invalid SubModuleDecl"; break;
+ case 120: s = "this symbol not expected in ClassDecl"; break;
+ case 121: s = "this symbol not expected in DatatypeDecl"; break;
+ case 122: s = "invalid DatatypeDecl"; break;
+ case 123: s = "this symbol not expected in DatatypeDecl"; break;
+ case 124: s = "this symbol not expected in ArbitraryTypeDecl"; break;
+ case 125: s = "this symbol not expected in IteratorDecl"; break;
+ case 126: s = "invalid IteratorDecl"; break;
+ case 127: s = "invalid ClassMemberDecl"; break;
+ case 128: s = "this symbol not expected in FieldDecl"; break;
+ case 129: s = "this symbol not expected in FieldDecl"; break;
+ case 130: s = "invalid FunctionDecl"; break;
+ case 131: s = "invalid FunctionDecl"; break;
+ case 132: s = "invalid FunctionDecl"; break;
+ case 133: s = "invalid FunctionDecl"; break;
+ case 134: s = "this symbol not expected in MethodDecl"; break;
+ case 135: s = "invalid MethodDecl"; break;
+ case 136: s = "invalid MethodDecl"; break;
+ case 137: s = "invalid TypeAndToken"; break;
+ case 138: s = "this symbol not expected in IteratorSpec"; break;
+ case 139: s = "this symbol not expected in IteratorSpec"; break;
+ case 140: s = "this symbol not expected in IteratorSpec"; break;
+ case 141: s = "this symbol not expected in IteratorSpec"; break;
+ case 142: s = "this symbol not expected in IteratorSpec"; break;
+ case 143: s = "invalid IteratorSpec"; break;
+ case 144: s = "this symbol not expected in IteratorSpec"; break;
+ case 145: s = "invalid IteratorSpec"; break;
+ case 146: s = "this symbol not expected in MethodSpec"; break;
+ case 147: s = "this symbol not expected in MethodSpec"; break;
+ case 148: s = "this symbol not expected in MethodSpec"; break;
+ case 149: s = "this symbol not expected in MethodSpec"; break;
+ case 150: s = "invalid MethodSpec"; break;
+ case 151: s = "this symbol not expected in MethodSpec"; break;
+ case 152: s = "invalid MethodSpec"; break;
+ case 153: s = "invalid FrameExpression"; break;
+ case 154: s = "invalid ReferenceType"; break;
+ case 155: s = "this symbol not expected in FunctionSpec"; break;
+ case 156: s = "this symbol not expected in FunctionSpec"; break;
+ case 157: s = "this symbol not expected in FunctionSpec"; break;
+ case 158: s = "this symbol not expected in FunctionSpec"; break;
+ case 159: s = "this symbol not expected in FunctionSpec"; break;
+ case 160: s = "invalid FunctionSpec"; break;
+ case 161: s = "invalid PossiblyWildFrameExpression"; break;
+ case 162: s = "invalid PossiblyWildExpression"; break;
+ case 163: s = "this symbol not expected in OneStmt"; break;
+ case 164: s = "invalid OneStmt"; break;
+ case 165: s = "this symbol not expected in OneStmt"; break;
+ case 166: s = "invalid OneStmt"; break;
+ case 167: s = "invalid AssertStmt"; break;
+ case 168: s = "invalid AssumeStmt"; break;
+ case 169: s = "invalid UpdateStmt"; break;
+ case 170: s = "invalid UpdateStmt"; break;
+ case 171: s = "invalid IfStmt"; break;
+ case 172: s = "invalid IfStmt"; break;
+ case 173: s = "invalid WhileStmt"; break;
+ case 174: s = "invalid WhileStmt"; break;
+ case 175: s = "invalid CalcStmt"; break;
+ case 176: s = "invalid ReturnStmt"; break;
+ case 177: s = "invalid Rhs"; break;
+ case 178: s = "invalid Lhs"; break;
+ case 179: s = "invalid Guard"; break;
+ case 180: s = "this symbol not expected in LoopSpec"; break;
+ case 181: s = "this symbol not expected in LoopSpec"; break;
+ case 182: s = "this symbol not expected in LoopSpec"; break;
+ case 183: s = "this symbol not expected in LoopSpec"; break;
+ case 184: s = "this symbol not expected in LoopSpec"; break;
+ case 185: s = "this symbol not expected in Invariant"; break;
+ case 186: s = "invalid AttributeArg"; break;
+ case 187: s = "invalid CalcOp"; break;
+ case 188: s = "invalid EquivOp"; break;
+ case 189: s = "invalid ImpliesOp"; break;
+ case 190: s = "invalid AndOp"; break;
+ case 191: s = "invalid OrOp"; break;
+ case 192: s = "invalid RelOp"; break;
+ case 193: s = "invalid AddOp"; break;
+ case 194: s = "invalid UnaryExpression"; break;
+ case 195: s = "invalid UnaryExpression"; break;
+ case 196: s = "invalid MulOp"; break;
+ case 197: s = "invalid NegOp"; break;
+ case 198: s = "invalid EndlessExpression"; break;
+ case 199: s = "invalid Suffix"; break;
+ case 200: s = "invalid Suffix"; break;
+ case 201: s = "invalid Suffix"; break;
+ case 202: s = "invalid DisplayExpr"; break;
+ case 203: s = "invalid MultiSetExpr"; break;
+ case 204: s = "invalid ConstAtomExpression"; break;
+ case 205: s = "invalid QSep"; break;
+ case 206: s = "invalid QuantifierGuts"; break;
+ case 207: s = "invalid Forall"; break;
+ case 208: s = "invalid Exists"; break;
+
+ default: s = "error " + n; break;
+ }
+ return s;
+ }
+
+ 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);
+ }
+
+ public virtual void SemErr(string filename, int line, int col, string/*!*/ msg) {
+ Contract.Requires(msg != null);
+ errorStream.WriteLine(errMsgFormat, filename, line, col, msg);
+ count++;
+ }
+
+ public virtual void Warning(string filename, int line, int col, string msg) {
+ Contract.Requires(msg != null);
+ errorStream.WriteLine(warningMsgFormat, filename, line, col, msg);
+ }
+} // 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
new file mode 100644
index 00000000..36658e2f
--- /dev/null
+++ b/Source/Dafny/Printer.cs
@@ -0,0 +1,1410 @@
+//-----------------------------------------------------------------------------
+//
+// 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;
+
+ [ContractInvariantMethod]
+ void ObjectInvariant()
+ {
+ Contract.Invariant(wr!=null);
+ }
+
+ public Printer(TextWriter wr) {
+ Contract.Requires(wr != null);
+ this.wr = wr;
+ }
+
+ public void PrintProgram(Program prog) {
+ 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);
+ wr.WriteLine();
+ PrintTopLevelDecls(prog.DefaultModuleDef.TopLevelDecls, 0);
+ }
+
+ public void PrintTopLevelDecls(List<TopLevelDecl> decls, int indent) {
+ Contract.Requires(decls!= null);
+ int i = 0;
+ foreach (TopLevelDecl d in decls) {
+ Contract.Assert(d != null);
+ if (d is ArbitraryTypeDecl) {
+ var at = (ArbitraryTypeDecl)d;
+ if (i++ != 0) { wr.WriteLine(); }
+ Indent(indent);
+ PrintClassMethodHelper("type", at.Attributes, at.Name, new List<TypeParameter>());
+ if (at.EqualitySupport == TypeParameter.EqualitySupportValue.Required) {
+ wr.Write("(==)");
+ }
+ 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;
+ 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);
+
+ 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 ----------");
+ PrintClassMethodHelper("class", null, iter.Name, iter.TypeArgs);
+ wr.WriteLine(" {");
+ PrintMembers(iter.Members, indent + IndentAmount);
+ Indent(indent); wr.WriteLine("}");
+ wr.WriteLine("---------- iterator members ----------*/");
+ }
+
+ } else if (d is ClassDecl) {
+ ClassDecl cl = (ClassDecl)d;
+ if (!cl.IsDefaultClass) {
+ if (i++ != 0) { wr.WriteLine(); }
+ PrintClass(cl, indent);
+ } else if (cl.Members.Count == 0) {
+ // print nothing
+ } else {
+ if (i++ != 0) { wr.WriteLine(); }
+ PrintMembers(cl.Members, indent);
+ }
+
+ } else if (d is ModuleDecl) {
+ wr.WriteLine();
+ Indent(indent);
+ if (d is LiteralModuleDecl) {
+ ModuleDefinition module = ((LiteralModuleDecl)d).ModuleDef;
+ 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("{");
+ PrintTopLevelDecls(module.TopLevelDecls, indent + IndentAmount);
+ Indent(indent);
+ wr.WriteLine("}");
+ }
+ } 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 AbstractModuleDecl) {
+ wr.Write("import"); if (((AbstractModuleDecl)d).Opened) wr.Write(" opened");
+ wr.Write(" {0} ", ((AbstractModuleDecl)d).Name);
+ wr.WriteLine("as {0};", Util.Comma(".", ((AbstractModuleDecl)d).Path, id => id.val));
+ }
+ } else {
+ Contract.Assert(false); // unexpected TopLevelDecl
+ }
+ }
+ }
+
+ public void PrintClass(ClassDecl c, int indent) {
+ Contract.Requires(c != null);
+ Indent(indent);
+ PrintClassMethodHelper("class", c.Attributes, c.Name, c.TypeArgs);
+ if (c.Members.Count == 0) {
+ wr.WriteLine(" { }");
+ } else {
+ wr.WriteLine(" {");
+ PrintMembers(c.Members, indent + IndentAmount);
+ Indent(indent);
+ wr.WriteLine("}");
+ }
+ }
+
+ public void PrintMembers(List<MemberDecl> members, int indent)
+ {
+ 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 (m is Method) {
+ if (state != 0) { wr.WriteLine(); }
+ PrintMethod((Method)m, indent);
+ 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);
+ 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);
+ if (typeArgs.Count != 0) {
+ wr.Write("<" +
+ Util.Comma(", ", typeArgs,
+ tp => tp.Name + (tp.EqualitySupport == TypeParameter.EqualitySupportValue.Required? "(==)": ""))
+ + ">");
+ }
+ }
+
+ 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);
+ }
+ wr.Write("}");
+ }
+ }
+
+ public void PrintAttributeArgs(List<Attributes.Argument> args) {
+ Contract.Requires(args != null);
+ string prefix = " ";
+ foreach (Attributes.Argument arg in args) {
+ Contract.Assert(arg != null);
+ wr.Write(prefix);
+ prefix = ", ";
+ if (arg.S != null) {
+ wr.Write("\"{0}\"", arg.S);
+ } else {
+ Contract.Assert( arg.E != null);
+ PrintExpression(arg.E);
+ }
+ }
+ }
+
+ 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);
+ wr.Write(";");
+ if (field.IsUserMutable) {
+ // nothing more to say
+ } else if (field.IsMutable) {
+ wr.Write(" // may change, but not directly by program");
+ } else {
+ wr.Write(" // immutable");
+ }
+ wr.WriteLine();
+ }
+
+ public void PrintFunction(Function f, int indent) {
+ Contract.Requires(f != null);
+ var isPredicate = f is Predicate;
+ Indent(indent);
+ string k = isPredicate ? "predicate" : f is CoPredicate ? "copredicate" : "function";
+ if (f.IsStatic) { k = "static " + k; }
+ if (!f.IsGhost) { k += " method"; }
+ PrintClassMethodHelper(k, f.Attributes, f.Name, f.TypeArgs);
+ if (f.SignatureIsOmitted) {
+ wr.WriteLine(" ...");
+ } else {
+ if (f.OpenParen != null) {
+ PrintFormals(f.Formals);
+ } else {
+ Contract.Assert(isPredicate);
+ }
+ 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) {
+ 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;
+ }
+ }
+
+ public void PrintMethod(Method method, int indent) {
+ Contract.Requires(method != null);
+
+ Indent(indent);
+ string k = method is Constructor ? "constructor" : "method";
+ if (method.IsStatic) { k = "static " + k; }
+ if (method.IsGhost) { k = "ghost " + k; }
+ PrintClassMethodHelper(k, method.Attributes, method.Name, method.TypeArgs);
+ if (method.SignatureIsOmitted) {
+ wr.WriteLine(" ...");
+ } else {
+ PrintFormals(method.Ins);
+ 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) {
+ Indent(indent);
+ PrintStatement(method.Body, indent);
+ wr.WriteLine();
+ }
+ }
+
+ void PrintFormals(List<Formal> ff) {
+ Contract.Requires(ff!=null);
+ 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);
+ }
+
+ 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);
+ wr.WriteLine(";");
+ }
+ }
+
+ void PrintDecreasesSpec(Specification<Expression> decs, int indent) {
+ Contract.Requires(decs != null);
+ if (decs.Expressions != null && decs.Expressions.Count != 0) {
+ Indent(indent);
+ wr.Write("decreases");
+ if (decs.HasAttributes())
+ {
+ PrintAttributes(decs.Attributes);
+ }
+ wr.Write(" ");
+ PrintExpressionList(decs.Expressions);
+ wr.WriteLine(";");
+ }
+ }
+
+ void PrintFrameSpecLine(string kind, List<FrameExpression/*!*/> ee, int indent, Attributes attrs) {
+ 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);
+ wr.WriteLine(";");
+ }
+ }
+
+ void PrintSpec(string kind, List<MaybeFreeExpression> ee, int indent) {
+ Contract.Requires(kind != null);
+ Contract.Requires(ee != null);
+ 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);
+
+ wr.WriteLine(";");
+ }
+ }
+
+ // ----------------------------- 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);
+ }
+ }
+
+ // ----------------------------- 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);
+ 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) {
+ Expression expr = ((PredicateStmt)stmt).Expr;
+ wr.Write(stmt is AssertStmt ? "assert" : "assume");
+ if (stmt.HasAttributes()) {
+ PrintAttributes(stmt.Attributes);
+ }
+ wr.Write(" ");
+ PrintExpression(expr);
+ wr.Write(";");
+
+ } else if (stmt is PrintStmt) {
+ PrintStmt s = (PrintStmt)stmt;
+ wr.Write("print");
+ PrintAttributeArgs(s.Args);
+ 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);
+ wr.Write(" := ");
+ PrintRhs(s.Rhs);
+ wr.Write(";");
+
+ } else if (stmt is VarDecl) {
+ VarDecl s = (VarDecl)stmt;
+ if (s.IsGhost) {
+ wr.Write("ghost ");
+ }
+ wr.Write("var {0}", s.DisplayName);
+ PrintType(": ", s.OptionalType);
+ wr.Write(";");
+
+ } else if (stmt is CallStmt) {
+ CallStmt s = (CallStmt)stmt;
+ if (s.Lhs.Count != 0) {
+ string sep = "";
+ foreach (IdentifierExpr v in s.Lhs) {
+ wr.Write(sep);
+ PrintExpression(v);
+ sep = ", ";
+ }
+ wr.Write(" := ");
+ }
+ if (!(s.Receiver is ImplicitThisExpr)) {
+ PrintExpr(s.Receiver, 0x70, false, false, -1);
+ wr.Write(".");
+ }
+ wr.Write("{0}(", s.MethodName);
+ PrintExpressionList(s.Args);
+ 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 ParallelStmt) {
+ var s = (ParallelStmt)stmt;
+ wr.Write("parallel (");
+ if (s.BoundVars.Count != 0) {
+ PrintQuantifierDomain(s.BoundVars, s.Attributes, s.Range);
+ }
+ if (s.Ens.Count == 0) {
+ wr.Write(") ");
+ } else {
+ wr.WriteLine(")");
+ PrintSpec("ensures", s.Ens, indent + IndentAmount);
+ Indent(indent);
+ }
+ PrintStatement(s.Body, indent);
+
+ } else if (stmt is CalcStmt) {
+ CalcStmt s = (CalcStmt)stmt;
+ wr.Write("calc ");
+ if (s.Op != CalcStmt.DefaultOp) {
+ wr.Write(BinaryExpr.OpcodeString(s.Op));
+ wr.Write(" ");
+ }
+ wr.WriteLine("{");
+ int lineInd = indent + IndentAmount;
+ if (s.Lines.Count > 0) {
+ Indent(lineInd);
+ PrintExpression(s.Lines.First(), lineInd);
+ wr.WriteLine(";");
+ }
+ for (var i = 1; i < s.Lines.Count; i++){
+ var e = s.Lines[i];
+ var h = s.Hints[i - 1];
+ var op = s.CustomOps[i - 1];
+ foreach (var st in h.Body) {
+ Indent(lineInd);
+ PrintStatement(st, lineInd);
+ wr.WriteLine();
+ }
+ Indent(lineInd);
+ if (op != null && (BinaryExpr.Opcode)op != s.Op) {
+ wr.Write(BinaryExpr.OpcodeString((BinaryExpr.Opcode)op));
+ wr.Write(" ");
+ }
+ PrintExpression(e, lineInd);
+ wr.WriteLine(";");
+ }
+ Indent(indent);
+ wr.Write("}");
+
+ } else if (stmt is MatchStmt) {
+ MatchStmt s = (MatchStmt)stmt;
+ wr.Write("match ");
+ PrintExpression(s.Source);
+ wr.WriteLine(" {");
+ int caseInd = indent + IndentAmount;
+ foreach (MatchCaseStmt mc in s.Cases) {
+ 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);
+ sep = ", ";
+ }
+ wr.Write(")");
+ }
+ wr.WriteLine(" =>");
+ foreach (Statement bs in mc.Body) {
+ Indent(caseInd + IndentAmount);
+ PrintStatement(bs, caseInd + IndentAmount);
+ 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);
+ sep = ", ";
+ }
+ PrintUpdateRHS(s);
+ wr.Write(";");
+
+ } else if (stmt is VarDeclStmt) {
+ var s = (VarDeclStmt)stmt;
+ if (s.Lhss[0].IsGhost) {
+ wr.Write("ghost ");
+ }
+ wr.Write("var ");
+ string sep = "";
+ foreach (var lhs in s.Lhss) {
+ wr.Write("{0}{1}", sep, lhs.DisplayName);
+ PrintType(": ", lhs.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 {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected skeleton statement
+ }
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement
+ }
+ }
+
+ /// <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);
+ } 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(" 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) {
+ if (omitGuard) {
+ wr.WriteLine("while ...");
+ } else {
+ wr.Write("while (");
+ PrintGuard(s.Guard);
+ wr.WriteLine(")");
+ }
+
+ PrintSpec("invariant", s.Invariants, indent + IndentAmount);
+ PrintDecreasesSpec(s.Decreases, indent + IndentAmount);
+ if (s.Mod.Expressions != null) {
+ PrintFrameSpecLine("modifies", s.Mod.Expressions, indent + IndentAmount, s.Mod.HasAttributes() ? s.Mod.Attributes : null);
+ }
+ Indent(indent);
+ if (omitBody) {
+ wr.WriteLine("...;");
+ } else {
+ 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);
+ 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);
+ } else if (rhs is HavocRhs) {
+ wr.Write("*");
+ } else if (rhs is TypeRhs) {
+ TypeRhs t = (TypeRhs)rhs;
+ wr.Write("new ");
+ PrintType(t.EType);
+ if (t.ArrayDimensions != null) {
+ string s = "[";
+ foreach (Expression dim in t.ArrayDimensions) {
+ Contract.Assume(dim != null);
+ wr.Write(s);
+ PrintExpression(dim);
+ s = ", ";
+ }
+ wr.Write("]");
+ } else if (t.InitCall != null) {
+ wr.Write(".{0}(", t.InitCall.MethodName);
+ PrintExpressionList(t.InitCall.Args);
+ 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);
+ }
+ }
+
+ // ----------------------------- PrintExpression -----------------------------
+
+ public void PrintExtendedExpr(Expression expr, int indent, bool isRightmost, bool endWithCloseParen) {
+ Contract.Requires(expr != null);
+ Indent(indent);
+ if (expr is ITEExpr) {
+ while (true) {
+ ITEExpr ite = (ITEExpr)expr;
+ wr.Write("if ");
+ PrintExpression(ite.Test);
+ 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);
+ wr.WriteLine(endWithCloseParen ? ")" : "");
+ return;
+ }
+ }
+ } else if (expr is MatchExpr) {
+ MatchExpr me = (MatchExpr)expr;
+ wr.Write("match ");
+ PrintExpression(me.Source);
+ wr.WriteLine();
+ int i = 0;
+ foreach (MatchCaseExpr mc in me.Cases) {
+ bool isLastCase = i == me.Cases.Count - 1;
+ Indent(indent);
+ 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(")");
+ }
+ bool parensNeeded = !isLastCase && mc.Body.WasResolved() && mc.Body.Resolved is MatchExpr;
+ if (parensNeeded) {
+ wr.WriteLine(" => (");
+ } else {
+ wr.WriteLine(" =>");
+ }
+ PrintExtendedExpr(mc.Body, indent + IndentAmount, isLastCase, parensNeeded || (isLastCase && endWithCloseParen));
+ i++;
+ }
+ } else if (expr is ParensExpression) {
+ PrintExtendedExpr(((ParensExpression)expr).E, indent, isRightmost, endWithCloseParen);
+ } else {
+ PrintExpression(expr, indent);
+ wr.WriteLine(endWithCloseParen ? ")" : "");
+ }
+ }
+
+ public void PrintExpression(Expression expr) {
+ Contract.Requires(expr != null);
+ PrintExpr(expr, 0, false, true, -1);
+ }
+
+ /// <summary>
+ /// An indent of -1 means print the entire expression on one line.
+ /// </summary>
+ public void PrintExpression(Expression expr, int indent) {
+ Contract.Requires(expr != null);
+ PrintExpr(expr, 0, false, true, 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, int indent)
+ {
+ Contract.Requires(-1 <= indent);
+ Contract.Requires(expr != null);
+
+ 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 {
+ 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) {
+ DatatypeValue dtv = (DatatypeValue)expr;
+ wr.Write("#{0}.{1}", dtv.DatatypeName, dtv.MemberName);
+ if (dtv.Arguments.Count != 0) {
+ wr.Write("(");
+ PrintExpressionList(dtv.Arguments);
+ 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);
+ wr.Write(e is SetDisplayExpr || e is MultiSetDisplayExpr ? "}" : "]");
+
+ } else if (expr is MapDisplayExpr) {
+ MapDisplayExpr e = (MapDisplayExpr)expr;
+ wr.Write("map");
+ wr.Write("{");
+ PrintExpressionPairList(e.Elements);
+ wr.Write("}");
+ } else if (expr is ExprDotName) {
+ var e = (ExprDotName)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, -1);
+ wr.Write(".");
+ }
+ wr.Write(e.SuffixName);
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)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, -1);
+ wr.Write(".");
+ }
+ wr.Write(e.FieldName);
+ 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);
+
+ if (parensNeeded) { wr.Write("("); }
+ PrintExpr(e.Seq, 0x00, false, false, indent); // BOGUS: fix me
+ wr.Write("[");
+ if (e.SelectOne) {
+ Contract.Assert( e.E0 != null);
+ PrintExpression(e.E0);
+ } else {
+ if (e.E0 != null) {
+ PrintExpression(e.E0);
+ }
+ wr.Write(e.E0 != null && e.E1 != null ? " .. " : "..");
+ if (e.E1 != null) {
+ PrintExpression(e.E1);
+ }
+ }
+ 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);
+
+ if (parensNeeded) { wr.Write("("); }
+ PrintExpr(e.Array, 0x00, false, false, indent); // BOGUS: fix me
+ string prefix = "[";
+ foreach (Expression idx in e.Indices) {
+ Contract.Assert(idx != null);
+ wr.Write(prefix);
+ PrintExpression(idx);
+ prefix = ", ";
+ }
+ wr.Write("]");
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is SeqUpdateExpr) {
+ SeqUpdateExpr e = (SeqUpdateExpr)expr;
+ // determine if parens are needed
+ int opBindingStrength = 0x70;
+ bool parensNeeded = opBindingStrength < contextBindingStrength ||
+ (fragileContext && opBindingStrength == contextBindingStrength);
+
+ if (parensNeeded) { wr.Write("("); }
+ PrintExpr(e.Seq, 00, false, false, indent); // BOGUS: fix me
+ wr.Write("[");
+ PrintExpression(e.Index);
+ wr.Write(" := ");
+ PrintExpression(e.Value);
+ 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 is ImplicitThisExpr) &&
+ opBindingStrength < contextBindingStrength ||
+ (fragileContext && opBindingStrength == contextBindingStrength);
+
+ if (parensNeeded) { wr.Write("("); }
+ if (!(e.Receiver is ImplicitThisExpr)) {
+ PrintExpr(e.Receiver, opBindingStrength, false, false, -1);
+ wr.Write(".");
+ }
+ wr.Write(e.Name);
+ if (e.OpenParen == null) {
+ Contract.Assert(e.Args.Count == 0);
+ } else {
+ wr.Write("(");
+ PrintExpressionList(e.Args);
+ wr.Write(")");
+ }
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is OldExpr) {
+ wr.Write("old(");
+ PrintExpression(((OldExpr)expr).E);
+ wr.Write(")");
+
+ } else if (expr is MultiSetFormingExpr) {
+ wr.Write("multiset(");
+ PrintExpression(((MultiSetFormingExpr)expr).E);
+ wr.Write(")");
+
+ } else if (expr is FreshExpr) {
+ wr.Write("fresh(");
+ PrintExpression(((FreshExpr)expr).E);
+ wr.Write(")");
+
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ if (e.Op == UnaryExpr.Opcode.SeqLength) {
+ wr.Write("|");
+ PrintExpression(e.E);
+ wr.Write("|");
+ } else {
+ // Prefix operator.
+ // determine if parens are needed
+ string op;
+ int opBindingStrength;
+ switch (e.Op) {
+ case UnaryExpr.Opcode.SetChoose:
+ op = "choose "; opBindingStrength = 0; break;
+ case UnaryExpr.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 &&
+ ((UnaryExpr)((ParensExpression)e.E).E).Op == UnaryExpr.Opcode.Not;
+
+ if (parensNeeded) { wr.Write("("); }
+ wr.Write(op);
+ PrintExpr(e.E, opBindingStrength, containsNestedNot, parensNeeded || isRightmost, -1);
+ if (parensNeeded) { 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.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("("); }
+ if (0 <= indent && e.Op == BinaryExpr.Opcode.And) {
+ PrintExpr(e.E0, opBindingStrength, fragileLeftContext, false, indent);
+ wr.WriteLine(" {0}", op);
+ Indent(indent);
+ PrintExpr(e.E1, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, indent);
+ } else if (0 <= indent && e.Op == BinaryExpr.Opcode.Imp) {
+ PrintExpr(e.E0, opBindingStrength, fragileLeftContext, false, indent);
+ wr.WriteLine(" {0}", op);
+ int ind = indent + IndentAmount;
+ Indent(ind);
+ PrintExpr(e.E1, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, ind);
+ } else {
+ PrintExpr(e.E0, opBindingStrength, fragileLeftContext, false, -1);
+ wr.Write(" {0} ", op);
+ PrintExpr(e.E1, opBindingStrength, fragileRightContext, parensNeeded || isRightmost, -1);
+ }
+ if (parensNeeded) { wr.Write(")"); }
+
+ } 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.Operands[0], opBindingStrength, true, false, -1);
+ for (int i = 0; i < e.Operators.Count; i++) {
+ string op = BinaryExpr.OpcodeString(e.Operators[i]);
+ wr.Write(" {0} ", op);
+ PrintExpr(e.Operands[i+1], opBindingStrength, true, i == e.Operators.Count - 1 && (parensNeeded || isRightmost), -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 v in e.Vars) {
+ wr.Write("{0}{1}", sep, v.DisplayName);
+ PrintType(": ", v.Type);
+ sep = ", ";
+ }
+ wr.Write(" := ");
+ PrintExpressionList(e.RHSs);
+ wr.Write("; ");
+ PrintExpression(e.Body);
+ 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 ");
+ PrintQuantifierDomain(e.BoundVars, e.Attributes, e.Range);
+ wr.Write(" :: ");
+ if (0 <= indent) {
+ int ind = indent + IndentAmount;
+ wr.WriteLine();
+ Indent(ind);
+ PrintExpression(e.Term, ind);
+ } else {
+ PrintExpression(e.Term);
+ }
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is NamedExpr) {
+ var e = (NamedExpr)expr;
+ wr.Write("expr {0}: ", e.Name);
+ PrintExpression(e.Body);
+
+ } 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);
+ if (!e.TermIsImplicit) {
+ wr.Write(" :: ");
+ PrintExpression(e.Term);
+ }
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is MapComprehension) {
+ var e = (MapComprehension)expr;
+ bool parensNeeded = !isRightmost;
+ if (parensNeeded) { wr.Write("("); }
+ wr.Write("map ");
+ 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);
+ wr.Write(" :: ");
+ PrintExpression(e.Term);
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is WildcardExpr) {
+ wr.Write("*");
+
+ } else if (expr is PredicateExpr) {
+ var e = (PredicateExpr)expr;
+ bool parensNeeded = !isRightmost;
+ if (parensNeeded) { wr.Write("("); }
+ wr.Write("{0} ", e.Kind);
+ PrintExpression(e.Guard);
+ wr.Write("; ");
+ PrintExpression(e.Body);
+ 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);
+ wr.Write(" then ");
+ PrintExpression(ite.Thn);
+ wr.Write(" else ");
+ PrintExpression(ite.Els);
+ 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, indent);
+
+ } else if (expr is IdentifierSequence) {
+ var e = (IdentifierSequence)expr;
+ string sep = "";
+ foreach (var id in e.Tokens) {
+ wr.Write("{0}{1}", sep, id.val);
+ sep = ".";
+ }
+ if (e.Arguments != null) {
+ wr.Write("(");
+ PrintExpressionList(e.Arguments);
+ wr.Write(")");
+ }
+
+ } else if (expr is MatchExpr) {
+ Contract.Assert(false); throw new cce.UnreachableException(); // MatchExpr is an extended expression and should be printed only using PrintExtendedExpr
+ } 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, indent);
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
+ }
+ }
+
+ 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);
+ }
+ }
+
+ void PrintExpressionList(List<Expression> exprs) {
+ Contract.Requires(exprs != null);
+ string sep = "";
+ foreach (Expression e in exprs) {
+ Contract.Assert(e != null);
+ wr.Write(sep);
+ sep = ", ";
+ PrintExpression(e);
+ }
+ }
+ 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);
+ wr.Write(":=");
+ PrintExpression(p.B);
+ }
+ }
+
+ 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);
+ if (fe.FieldName != null) {
+ wr.Write("`{0}", fe.FieldName);
+ }
+ }
+ }
+ }
+}
diff --git a/Source/Dafny/RefinementTransformer.cs b/Source/Dafny/RefinementTransformer.cs
new file mode 100644
index 00000000..03f8faad
--- /dev/null
+++ b/Source/Dafny/RefinementTransformer.cs
@@ -0,0 +1,1411 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+// This file contains the transformations that are applied to a module that is
+// constructed as a refinement of another. It is invoked during program resolution,
+// so the transformation is done syntactically. Upon return from the RefinementTransformer,
+// the caller is expected to resolve the resulting module.
+//
+// As for now (and perhaps this is always the right thing to do), attributes do
+// not survive the transformation.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Diagnostics.Contracts;
+using IToken = Microsoft.Boogie.IToken;
+
+namespace Microsoft.Dafny
+{
+ public class RefinementToken : TokenWrapper
+ {
+ public readonly ModuleDefinition InheritingModule;
+ public RefinementToken(IToken tok, ModuleDefinition m)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ Contract.Requires(m != null);
+ this.InheritingModule = m;
+ }
+
+ public static bool IsInherited(IToken tok, ModuleDefinition m) {
+ while (tok is NestedToken) {
+ var n = (NestedToken)tok;
+ // check Outer
+ var r = n.Outer as RefinementToken;
+ if (r == null || r.InheritingModule != m) {
+ return false;
+ }
+ // continue to check Inner
+ tok = n.Inner;
+ }
+ var rtok = tok as RefinementToken;
+ return rtok != null && rtok.InheritingModule == m;
+ }
+ public override string filename {
+ get { return WrappedToken.filename + "[" + InheritingModule.Name + "]"; }
+ set { throw new NotSupportedException(); }
+ }
+ }
+
+ public class RefinementTransformer : IRewriter
+ {
+ ResolutionErrorReporter reporter;
+ 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, Program p) {
+ Contract.Requires(reporter != null);
+ this.reporter = reporter;
+ rawCloner = new Cloner();
+ program = p;
+ }
+
+ private ModuleDefinition moduleUnderConstruction; // non-null for the duration of Construct calls
+ private Queue<Action> postTasks = new Queue<Action>(); // empty whenever moduleUnderConstruction==null, these tasks are for the post-resolve phase of module moduleUnderConstruction
+ public Queue<Tuple<Method, Method>> translationMethodChecks = new Queue<Tuple<Method, Method>>(); // contains all the methods that need to be checked for structural refinement.
+ private Method currentMethod;
+
+ public void PreResolve(ModuleDefinition m) {
+
+ if (m.RefinementBase == null) return;
+
+ if (moduleUnderConstruction != null) {
+ postTasks.Clear();
+ }
+ moduleUnderConstruction = m;
+ refinementCloner = new RefinementCloner(moduleUnderConstruction);
+ var prev = m.RefinementBase;
+
+ // Create a simple name-to-decl dictionary. Ignore any duplicates at this time.
+ var declaredNames = new Dictionary<string, int>();
+ for (int i = 0; i < m.TopLevelDecls.Count; i++) {
+ var d = m.TopLevelDecls[i];
+ if (!declaredNames.ContainsKey(d.Name)) {
+ declaredNames.Add(d.Name, i);
+ }
+ }
+
+ // Merge the declarations of prev into the declarations of m
+ foreach (var d in prev.TopLevelDecls) {
+ int index;
+ 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 AbstractModuleDecl)) {
+ reporter.Error(nw, "a module ({0}) can only refine abstract modules", nw.Name);
+ } else {
+ ModuleSignature original = ((AbstractModuleDecl)d).OriginalSignature;
+ ModuleSignature derived = null;
+ if (nw is AliasModuleDecl) {
+ derived = ((AliasModuleDecl)nw).Signature;
+ } else if (nw is AbstractModuleDecl) {
+ derived = ((AbstractModuleDecl)nw).Signature;
+ } else {
+ reporter.Error(nw, "a module ({0}) can only be refined by alias or abstract modules", d.Name);
+ }
+ 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 (d is ArbitraryTypeDecl) {
+ if (nw is ModuleDecl) {
+ reporter.Error(nw, "a module ({0}) must refine another module", nw.Name);
+ } else {
+ bool dDemandsEqualitySupport = ((ArbitraryTypeDecl)d).MustSupportEquality;
+ if (nw is ArbitraryTypeDecl) {
+ if (dDemandsEqualitySupport != ((ArbitraryTypeDecl)nw).MustSupportEquality) {
+ reporter.Error(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 != d.TypeArgs.Count) {
+ reporter.Error(nw, "arbitrary type '{0}' is not allowed to be replaced by a class that takes a different number of 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");
+ } else {
+ Contract.Assert(nw is IndDatatypeDecl);
+ if (nw.TypeArgs.Count != d.TypeArgs.Count) {
+ reporter.Error(nw, "arbitrary type '{0}' is not allowed to be replaced by a datatype that takes a different number of type parameters", nw.Name);
+ } 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 = new UserDefinedType(nw.tok, nw.Name, nw, new List<Type>());
+ postTasks.Enqueue(delegate() {
+ if (!udt.SupportsEquality) {
+ reporter.Error(udt.tok, "datatype '{0}' is used to refine an arbitrary type with equality support, but '{0}' does not support equality", udt.Name);
+ }
+ });
+ }
+ }
+ } else if (d.TypeArgs.Count != nw.TypeArgs.Count) {
+ reporter.Error(nw, "arbitrary type '{0}' is not allowed to be replaced by a type that takes a different number of type parameters", nw.Name);
+ }
+ }
+ } else if (nw is ArbitraryTypeDecl) {
+ reporter.Error(nw, "an arbitrary 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 arbitrary-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);
+ }
+ }
+ }
+ }
+
+ Contract.Assert(moduleUnderConstruction == m); // this should be as it was set earlier in this method
+ }
+
+ public bool CheckIsRefinement(ModuleSignature derived, ModuleSignature original) {
+ // Check refinement by construction.
+ var derivedPointer = derived;
+ while (derivedPointer != null) {
+ if (derivedPointer == original)
+ return true;
+ derivedPointer = derivedPointer.Refines;
+ }
+ // Check structural refinement. Note this involves several checks.
+ // First, we need to know if the two modules are signature compatible;
+ // this is determined immediately as it is necessary for determining
+ // whether resolution will succeed. This involves checking classes, datatypes,
+ // type declarations, and nested modules.
+ // Second, we need to determine whether the specifications will be compatible
+ // (i.e. substitutable), by translating to Boogie.
+
+ var errorCount = reporter.ErrorCount;
+ 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);
+ } else {
+ CheckIsRefinement(((ModuleDecl)nw).Signature, ((ModuleDecl)d).Signature);
+ }
+ } else if (d is ArbitraryTypeDecl) {
+ if (nw is ModuleDecl) {
+ reporter.Error(nw, "a module ({0}) must refine another module", nw.Name);
+ } else {
+ bool dDemandsEqualitySupport = ((ArbitraryTypeDecl)d).MustSupportEquality;
+ if (nw is ArbitraryTypeDecl) {
+ if (dDemandsEqualitySupport != ((ArbitraryTypeDecl)nw).MustSupportEquality) {
+ reporter.Error(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, "arbitrary 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");
+ } else {
+ Contract.Assert(nw is IndDatatypeDecl);
+ if (nw.TypeArgs.Count != 0) {
+ reporter.Error(nw, "arbitrary 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 arbitrary type with equality support, but '{0}' does not support equality", nw.Name);
+ }
+ }
+ }
+ }
+ }
+ } 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);
+ } 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);
+ }
+ // 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);
+ }
+ } 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);
+ } else {
+ CheckClassesAreRefinements((ClassDecl)nw, (ClassDecl)d);
+ }
+ } else {
+ 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);
+ }
+ }
+ return errorCount == reporter.ErrorCount;
+ }
+
+ 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);
+ } else {
+ var map = new Dictionary<string, MemberDecl>();
+ foreach (var mem in nw.Members) {
+ map.Add(mem.Name, mem);
+ }
+ foreach (var m in d.Members) {
+ MemberDecl newMem;
+ if (map.TryGetValue(m.Name, out newMem)) {
+ if (m.IsStatic != newMem.IsStatic) {
+ reporter.Error(newMem, "member {0} must {1}", m.Name, m.IsStatic? "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);
+ if (m.IsGhost || !newField.IsGhost)
+ reporter.Error(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);
+ }
+ } 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");
+ }
+ } else if (m is Function) {
+ if (newMem is Function) {
+ CheckFunctionsAreRefinements((Function)newMem, (Function)m);
+ } else {
+ bool isPredicate = m is Predicate;
+ bool isCoPredicate = m is CoPredicate;
+ string s = isPredicate ? "predicate" : isCoPredicate ? "copredicate" : "function";
+ reporter.Error(newMem, "{0} must be refined by a {0}", s);
+ }
+ }
+ } else {
+ reporter.Error(nw is DefaultClassDecl ? nw.Module.tok : nw.tok, "refining {0} must have member {1}", nw is DefaultClassDecl ? "module" : "class", m.Name);
+ }
+ }
+ }
+ }
+ void CheckAgreementResolvedParameters(IToken tok, List<Formal> old, List<Formal> nw, string name, string thing, string parameterKind) {
+ Contract.Requires(tok != null);
+ Contract.Requires(old != null);
+ Contract.Requires(nw != null);
+ Contract.Requires(name != null);
+ 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);
+ } 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);
+ } 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);
+ } 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);
+ }
+ }
+ }
+ }
+ private void CheckMethodsAreRefinements(Method nw, Method m) {
+ CheckAgreement_TypeParameters(nw.tok, m.TypeArgs, nw.TypeArgs, m.Name, "method", false);
+ CheckAgreementResolvedParameters(nw.tok, m.Ins, nw.Ins, m.Name, "method", "in-parameter");
+ CheckAgreementResolvedParameters(nw.tok, m.Outs, nw.Outs, m.Name, "method", "out-parameter");
+ program.TranslationTasks.Add(new MethodCheck(nw, m));
+ }
+ 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);
+ } 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 CoPredicate) {
+ reporter.Error(nw, "refinement of co-predicates is not supported");
+ } else {
+ // f is a plain Function
+ if (nw is Predicate || nw is CoPredicate) {
+ reporter.Error(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);
+ }
+ }
+ }
+ program.TranslationTasks.Add(new FunctionCheck(nw, f));
+ }
+
+
+ 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");
+ } else {
+ var map = new Dictionary<string, DatatypeCtor>();
+ foreach (var ctor in nn.Ctors) {
+ map.Add(ctor.Name, ctor);
+ }
+ foreach (var ctor in dd.Ctors) {
+ 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);
+ } 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);
+ }
+ 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());
+ }
+ }
+ }
+ } else {
+ reporter.Error(nn, "the constructor {0} must be present in the refining datatype", ctor.Name);
+ }
+ }
+ }
+
+ }
+ // Check that two resolved types are the same in a similar context (the same type parameters, method, class, etc.)
+ // Assumes that prev is in a previous refinement, and next is in some refinement. Note this is not communative.
+ public static bool ResolvedTypesAreTheSame(Type prev, Type next) {
+ Contract.Requires(prev != null);
+ Contract.Requires(next != null);
+ if (prev is TypeProxy || next is TypeProxy)
+ return false;
+
+ if (prev is BoolType) {
+ return next is BoolType;
+ } else if (prev is IntType) {
+ if (next is IntType) {
+ return (prev is NatType) == (next is NatType);
+ } else return false;
+ } 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);
+ } else if (prev is MultiSetType) {
+ return next is MultiSetType && ResolvedTypesAreTheSame(((MultiSetType)prev).Arg, ((MultiSetType)next).Arg);
+ } else if (prev is MapType) {
+ return next is MapType && ResolvedTypesAreTheSame(((MapType)prev).Domain, ((MapType)next).Domain) && ResolvedTypesAreTheSame(((MapType)prev).Range, ((MapType)next).Range);
+ } else if (prev is SeqType) {
+ return next is SeqType && ResolvedTypesAreTheSame(((SeqType)prev).Arg, ((SeqType)next).Arg);
+ } else if (prev is UserDefinedType) {
+ if (!(next is UserDefinedType)) {
+ return false;
+ }
+ UserDefinedType aa = (UserDefinedType)prev;
+ UserDefinedType bb = (UserDefinedType)next;
+ if (aa.ResolvedClass != null && aa.ResolvedClass.Name == bb.ResolvedClass.Name) {
+ // these are both resolved class/datatype types
+ Contract.Assert(aa.TypeArgs.Count == bb.TypeArgs.Count);
+ for (int i = 0; i < aa.TypeArgs.Count; i++)
+ if (!ResolvedTypesAreTheSame(aa.TypeArgs[i], bb.TypeArgs[i]))
+ return false;
+ return true;
+ } else if (aa.ResolvedParam != null && bb.ResolvedParam != null) {
+ // these are both resolved type parameters
+ Contract.Assert(aa.TypeArgs.Count == 0 && bb.TypeArgs.Count == 0);
+ // Note that this is only correct if the two types occur in the same context, ie. both from the same method
+ // or class field.
+ return aa.ResolvedParam.PositionalIndex == bb.ResolvedParam.PositionalIndex &&
+ aa.ResolvedParam.IsToplevelScope == bb.ResolvedParam.IsToplevelScope;
+ } else if (aa.ResolvedParam.IsAbstractTypeDeclaration && bb.ResolvedClass != null) {
+ return (aa.ResolvedParam.Name == bb.ResolvedClass.Name);
+ } else {
+ // something is wrong; either aa or bb wasn't properly resolved, or they aren't the same
+ return false;
+ }
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
+ }
+ }
+ public void PostResolve(ModuleDefinition m) {
+ if (m == moduleUnderConstruction) {
+ while (this.postTasks.Count != 0) {
+ var a = postTasks.Dequeue();
+ a();
+ }
+ } else {
+ postTasks.Clear();
+ }
+ moduleUnderConstruction = null;
+ }
+ Function CloneFunction(IToken tok, Function f, bool isGhost, List<Expression> moreEnsures, Expression moreBody, Expression replacementBody, bool checkPrevPostconditions, Attributes moreAttributes) {
+ Contract.Requires(moreBody == null || f is Predicate);
+ Contract.Requires(moreBody == null || replacementBody == null);
+
+ var tps = f.TypeArgs.ConvertAll(refinementCloner.CloneTypeParam);
+ var formals = f.Formals.ConvertAll(refinementCloner.CloneFormal);
+ var req = f.Req.ConvertAll(refinementCloner.CloneExpr);
+ var reads = f.Reads.ConvertAll(refinementCloner.CloneFrameExpr);
+ var decreases = refinementCloner.CloneSpecExpr(f.Decreases);
+
+ List<Expression> ens;
+ if (checkPrevPostconditions) // note, if a postcondition includes something that changes in the module, the translator will notice this and still re-check the postcondition
+ ens = f.Ens.ConvertAll(rawCloner.CloneExpr);
+ else
+ ens = f.Ens.ConvertAll(refinementCloner.CloneExpr);
+ if (moreEnsures != null) {
+ ens.AddRange(moreEnsures);
+ }
+
+ Expression body;
+ Predicate.BodyOriginKind bodyOrigin;
+ if (replacementBody != null) {
+ body = replacementBody;
+ bodyOrigin = Predicate.BodyOriginKind.DelayedDefinition;
+ } else if (moreBody != null) {
+ if (f.Body == null) {
+ body = moreBody;
+ bodyOrigin = Predicate.BodyOriginKind.DelayedDefinition;
+ } else {
+ body = new BinaryExpr(f.tok, BinaryExpr.Opcode.And, refinementCloner.CloneExpr(f.Body), moreBody);
+ bodyOrigin = Predicate.BodyOriginKind.Extension;
+ }
+ } else {
+ body = refinementCloner.CloneExpr(f.Body);
+ bodyOrigin = Predicate.BodyOriginKind.OriginalOrInherited;
+ }
+
+ if (f is Predicate) {
+ return new Predicate(tok, f.Name, f.IsStatic, isGhost, tps, f.OpenParen, formals,
+ req, reads, ens, decreases, body, bodyOrigin, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), false);
+ } else if (f is CoPredicate) {
+ return new CoPredicate(tok, f.Name, f.IsStatic, tps, f.OpenParen, formals,
+ req, reads, ens, body, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), false);
+ } else {
+ return new Function(tok, f.Name, f.IsStatic, isGhost, tps, f.OpenParen, formals, refinementCloner.CloneType(f.ResultType),
+ req, reads, ens, decreases, body, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), false);
+ }
+ }
+
+ Method CloneMethod(Method m, List<MaybeFreeExpression> moreEnsures, Specification<Expression> decreases, BlockStmt newBody, bool checkPreviousPostconditions, Attributes moreAttributes) {
+ Contract.Requires(m != null);
+ Contract.Requires(decreases != null);
+
+ var tps = m.TypeArgs.ConvertAll(refinementCloner.CloneTypeParam);
+ var ins = m.Ins.ConvertAll(refinementCloner.CloneFormal);
+ var req = m.Req.ConvertAll(refinementCloner.CloneMayBeFreeExpr);
+ var mod = refinementCloner.CloneSpecFrameExpr(m.Mod);
+
+ List<MaybeFreeExpression> ens;
+ if (checkPreviousPostconditions)
+ ens = m.Ens.ConvertAll(rawCloner.CloneMayBeFreeExpr);
+ else
+ ens = m.Ens.ConvertAll(refinementCloner.CloneMayBeFreeExpr);
+ if (moreEnsures != null) {
+ ens.AddRange(moreEnsures);
+ }
+
+ 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), false);
+ } else {
+ return new Method(new RefinementToken(m.tok, moduleUnderConstruction), m.Name, m.IsStatic, m.IsGhost, tps, ins, m.Outs.ConvertAll(refinementCloner.CloneFormal),
+ req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), false);
+ }
+ }
+
+ // -------------------------------------------------- Merging ---------------------------------------------------------------
+
+ IteratorDecl MergeIterator(IteratorDecl nw, IteratorDecl prev) {
+ Contract.Requires(nw != null);
+ 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");
+ }
+ if (nw.YieldRequires.Count != 0) {
+ reporter.Error(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");
+ }
+ 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");
+ }
+ if (nw.Decreases.Expressions.Count != 0) {
+ reporter.Error(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);
+ } 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");
+ CheckAgreement_Parameters(nw.tok, prev.Outs, nw.Outs, nw.Name, "iterator", "yield-parameter");
+ }
+
+ BlockStmt newBody;
+ if (nw.Body == null) {
+ newBody = prev.Body;
+ } else if (prev.Body == null) {
+ newBody = nw.Body;
+ } else {
+ newBody = MergeBlockStmt(nw.Body, prev.Body);
+ }
+
+ var ens = prev.Ensures.ConvertAll(rawCloner.CloneMayBeFreeExpr);
+ ens.AddRange(nw.Ensures);
+ var yens = prev.YieldEnsures.ConvertAll(rawCloner.CloneMayBeFreeExpr);
+ yens.AddRange(nw.YieldEnsures);
+
+ return new IteratorDecl(new RefinementToken(nw.tok, moduleUnderConstruction), nw.Name, moduleUnderConstruction,
+ nw.SignatureIsOmitted ? prev.TypeArgs.ConvertAll(refinementCloner.CloneTypeParam) : nw.TypeArgs,
+ nw.SignatureIsOmitted ? prev.Ins.ConvertAll(refinementCloner.CloneFormal) : nw.Ins,
+ nw.SignatureIsOmitted ? prev.Outs.ConvertAll(refinementCloner.CloneFormal) : nw.Outs,
+ refinementCloner.CloneSpecFrameExpr(prev.Reads),
+ refinementCloner.CloneSpecFrameExpr(prev.Modifies),
+ refinementCloner.CloneSpecExpr(prev.Decreases),
+ prev.Requires.ConvertAll(refinementCloner.CloneMayBeFreeExpr),
+ ens,
+ prev.YieldRequires.ConvertAll(refinementCloner.CloneMayBeFreeExpr),
+ yens,
+ newBody,
+ refinementCloner.MergeAttributes(prev.Attributes, nw.Attributes),
+ false);
+ }
+
+ ClassDecl MergeClass(ClassDecl nw, ClassDecl prev) {
+ CheckAgreement_TypeParameters(nw.tok, prev.TypeArgs, nw.TypeArgs, nw.Name, "class");
+
+ // Create a simple name-to-member dictionary. Ignore any duplicates at this time.
+ var declaredNames = new Dictionary<string, int>();
+ for (int i = 0; i < nw.Members.Count; i++) {
+ var member = nw.Members[i];
+ if (!declaredNames.ContainsKey(member.Name)) {
+ declaredNames.Add(member.Name, i);
+ }
+ }
+
+ // Merge the declarations of prev into the declarations of m
+ foreach (var member in prev.Members) {
+ int index;
+ if (!declaredNames.TryGetValue(member.Name, out index)) {
+ nw.Members.Add(refinementCloner.CloneMember(member));
+ } else {
+ var nwMember = nw.Members[index];
+ 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);
+ } 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);
+ }
+ } else if (nwMember is Function) {
+ var f = (Function)nwMember;
+ bool isPredicate = f is Predicate;
+ bool isCoPredicate = f is CoPredicate;
+ string s = isPredicate ? "predicate" : isCoPredicate ? "copredicate" : "function";
+ if (!(member is Function) || (isPredicate && !(member is Predicate)) || (isCoPredicate && !(member is CoPredicate))) {
+ reporter.Error(nwMember, "a {0} declaration ({1}) can only refine a {0}", s, nwMember.Name);
+ } 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", s);
+ }
+ if (f.Reads.Count != 0) {
+ reporter.Error(f.Reads[0].E.tok, "a refining {0} is not allowed to extend the reads clause", s);
+ }
+ if (f.Decreases.Expressions.Count != 0) {
+ reporter.Error(f.Decreases.Expressions[0].tok, "decreases clause on refining {0} not supported", s);
+ }
+
+ if (prevFunction.IsStatic != f.IsStatic) {
+ reporter.Error(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);
+ } 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);
+ }
+ if (f.SignatureIsOmitted) {
+ Contract.Assert(f.TypeArgs.Count == 0);
+ Contract.Assert(f.Formals.Count == 0);
+ } 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);
+ }
+ }
+
+ Expression moreBody = null;
+ Expression replacementBody = null;
+ if (prevFunction.Body == null) {
+ replacementBody = f.Body;
+ } else if (isPredicate) {
+ moreBody = f.Body;
+ } else if (f.Body != null) {
+ reporter.Error(nwMember, "a refining function is not allowed to extend/change the body");
+ }
+ nw.Members[index] = CloneFunction(f.tok, prevFunction, f.IsGhost, f.Ens, moreBody, replacementBody, prevFunction.Body == null, f.Attributes);
+ }
+
+ } else {
+ var m = (Method)nwMember;
+ if (!(member is Method)) {
+ reporter.Error(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");
+ }
+ 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");
+ }
+ Specification<Expression> decreases;
+ if (Contract.Exists(prevMethod.Decreases.Expressions, e => e is WildcardExpr)) {
+ decreases = m.Decreases;
+ } else {
+ if (m.Decreases.Expressions.Count != 0) {
+ reporter.Error(m.Decreases.Expressions[0].tok, "decreases clause on refining method not supported, unless the refined method was specified with 'decreases *'");
+ }
+ decreases = refinementCloner.CloneSpecExpr(prevMethod.Decreases);
+ }
+ if (prevMethod.IsStatic != m.IsStatic) {
+ reporter.Error(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);
+ } 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);
+ }
+ if (m.SignatureIsOmitted) {
+ Contract.Assert(m.TypeArgs.Count == 0);
+ Contract.Assert(m.Ins.Count == 0);
+ Contract.Assert(m.Outs.Count == 0);
+ } 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");
+ CheckAgreement_Parameters(m.tok, prevMethod.Outs, m.Outs, m.Name, "method", "out-parameter");
+ }
+ currentMethod = m;
+ var replacementBody = m.Body;
+ if (replacementBody != null) {
+ if (prevMethod.Body == null) {
+ // cool
+ } else {
+ replacementBody = MergeBlockStmt(replacementBody, prevMethod.Body);
+ }
+ }
+ nw.Members[index] = CloneMethod(prevMethod, m.Ens, decreases, replacementBody, prevMethod.Body == null, m.Attributes);
+ }
+ }
+ }
+ }
+
+ return nw;
+ }
+ void CheckAgreement_TypeParameters(IToken tok, List<TypeParameter> old, List<TypeParameter> nw, string name, string thing, bool checkNames = true) {
+ Contract.Requires(tok != null);
+ Contract.Requires(old != null);
+ Contract.Requires(nw != null);
+ 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);
+ } 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);
+ } else {
+ // This explains what we want to do and why:
+ // switch (o.EqualitySupport) {
+ // case TypeParameter.EqualitySupportValue.Required:
+ // // here, we will insist that the new type-parameter also explicitly requires equality support (because we don't want
+ // // to wait for the inference to run on the new module)
+ // good = n.EqualitySupport == TypeParameter.EqualitySupportValue.Required;
+ // break;
+ // case TypeParameter.EqualitySupportValue.InferredRequired:
+ // // here, we can allow anything, because even with an Unspecified value, the inference will come up with InferredRequired, like before
+ // good = true;
+ // break;
+ // case TypeParameter.EqualitySupportValue.Unspecified:
+ // // inference didn't come up with anything on the previous module, so the only value we'll allow here is Unspecified as well
+ // good = n.EqualitySupport == TypeParameter.EqualitySupportValue.Unspecified;
+ // break;
+ // }
+ // 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);
+ }
+ }
+ }
+ }
+ }
+
+ void CheckAgreement_Parameters(IToken tok, List<Formal> old, List<Formal> nw, string name, string thing, string parameterKind) {
+ Contract.Requires(tok != null);
+ Contract.Requires(old != null);
+ Contract.Requires(nw != null);
+ Contract.Requires(name != null);
+ 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);
+ } 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);
+ } 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);
+ } 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);
+ } 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);
+ }
+ }
+ }
+ }
+
+ bool TypesAreSyntacticallyEqual(Type t, Type u) {
+ Contract.Requires(t != null);
+ Contract.Requires(u != null);
+ return t.ToString() == u.ToString();
+ }
+
+ BlockStmt MergeBlockStmt(BlockStmt skeleton, BlockStmt oldStmt) {
+ Contract.Requires(skeleton != null);
+ Contract.Requires(oldStmt != null);
+
+ var body = new List<Statement>();
+ int i = 0, j = 0;
+ while (i < skeleton.Body.Count) {
+ var cur = skeleton.Body[i];
+ if (j == oldStmt.Body.Count) {
+ if (!(cur is SkeletonStatement)) {
+ MergeAddStatement(cur, body);
+ } else if (((SkeletonStatement)cur).S == null) {
+ // the "..." matches the empty statement sequence
+ } else {
+ reporter.Error(cur.Tok, "skeleton statement does not match old statement");
+ }
+ i++;
+ } else {
+ var oldS = oldStmt.Body[j];
+ /* See how the two statements match up.
+ * cur oldS result
+ * ------ ------ ------
+ * assert ...; assume E; assert E;
+ * assert ...; assert E; assert E;
+ * assert E; assert E;
+ *
+ * assume ...; assume E; assume E;
+ *
+ * var x := E; var x; var x := E;
+ * var x := E; var x := *; var x := E;
+ * var x := E1; var x :| P; var x := E1; assert P;
+ * var VarProduction; var VarProduction;
+ *
+ * x := E; x := *; x := E;
+ * x := E; x :| P; x := E; assert P;
+ *
+ * if ... Then else Else if (G) Then' else Else' if (G) Merge(Then,Then') else Merge(Else,Else')
+ * if (G) Then else Else if (*) Then' else Else' if (G) Merge(Then,Then') else Merge(Else,Else')
+ *
+ * while ... LoopSpec ... while (G) LoopSpec' Body while (G) Merge(LoopSpec,LoopSpec') Body
+ * while ... LoopSpec Body while (G) LoopSpec' Body' while (G) Merge(LoopSpec,LoopSpec') Merge(Body,Body')
+ * while (G) LoopSpec ... while (*) LoopSpec' Body while (G) Merge(LoopSpec,LoopSpec') Body
+ * while (G) LoopSpec Body while (*) LoopSpec' Body' while (G) Merge(LoopSpec,LoopSpec') Merge(Body,Body')
+ *
+ * ... where x = e; S StmtThatDoesNotMatchS; S' StatementThatDoesNotMatchS[e/x]; Merge( ... where x = e; S , S')
+ * ... where x = e; S StmtThatMatchesS; S' StmtThatMatchesS; S'
+ *
+ * Note, LoopSpec must contain only invariant declarations (as the parser ensures for the first three cases).
+ * Note, there is an implicit "...;" at the end of every block in a skeleton.
+ */
+ if (cur is SkeletonStatement) {
+ var S = ((SkeletonStatement)cur).S;
+ var c = (SkeletonStatement)cur;
+ if (S == null) {
+ var nxt = i + 1 == skeleton.Body.Count ? null : skeleton.Body[i + 1];
+ if (nxt != null && nxt is SkeletonStatement && ((SkeletonStatement)nxt).S == null) {
+ // "...; ...;" is the same as just "...;", so skip this one
+ } else {
+ SubstitutionCloner subber = null;
+ if (c.NameReplacements != null) {
+ var subExprs = new Dictionary<string, Expression>();
+ 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");
+ } else subExprs.Add(c.NameReplacements[k].val, c.ExprReplacements[k]);
+ }
+ subber = new SubstitutionCloner(subExprs, rawCloner);
+ }
+ // skip up until the next thing that matches "nxt"
+ while (nxt == null || !PotentialMatch(nxt, oldS)) {
+ // loop invariant: oldS == oldStmt.Body[j]
+ var s = refinementCloner.CloneStmt(oldS);
+ if (subber != null)
+ s = subber.CloneStmt(s);
+ body.Add(s);
+ j++;
+ if (j == oldStmt.Body.Count) { break; }
+ oldS = oldStmt.Body[j];
+ }
+ 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));
+ }
+ }
+ i++;
+
+ } else if (S is AssertStmt) {
+ var skel = (AssertStmt)S;
+ Contract.Assert(((SkeletonStatement)cur).ConditionOmitted);
+ var oldAssume = oldS as PredicateStmt;
+ if (oldAssume == null) {
+ reporter.Error(cur.Tok, "assert template does not match inherited statement");
+ i++;
+ } else {
+ // 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.
+ var e = refinementCloner.CloneExpr(oldAssume.Expr);
+ var attrs = refinementCloner.MergeAttributes(oldAssume.Attributes, skel.Attributes);
+ body.Add(new AssertStmt(new Translator.ForceCheckToken(skel.Tok), e, new Attributes("prependAssertToken", new List<Attributes.Argument>(), attrs)));
+ i++; j++;
+ }
+
+ } else if (S is AssumeStmt) {
+ var skel = (AssumeStmt)S;
+ Contract.Assert(((SkeletonStatement)cur).ConditionOmitted);
+ var oldAssume = oldS as AssumeStmt;
+ if (oldAssume == null) {
+ reporter.Error(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, e, attrs));
+ i++; j++;
+ }
+
+ } else if (S is IfStmt) {
+ var skel = (IfStmt)S;
+ Contract.Assert(((SkeletonStatement)cur).ConditionOmitted);
+ var oldIf = oldS as IfStmt;
+ if (oldIf == null) {
+ reporter.Error(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 r = new IfStmt(skel.Tok, refinementCloner.CloneExpr(oldIf.Guard), resultingThen, resultingElse);
+ body.Add(r);
+ i++; j++;
+ }
+
+ } else if (S is WhileStmt) {
+ var skel = (WhileStmt)S;
+ var oldWhile = oldS as WhileStmt;
+ if (oldWhile == null) {
+ reporter.Error(cur.Tok, "while-statement template does not match inherited statement");
+ i++;
+ } else {
+ Expression guard;
+ if (((SkeletonStatement)cur).ConditionOmitted) {
+ guard = refinementCloner.CloneExpr(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");
+ }
+ guard = skel.Guard;
+ }
+ // Note, if the loop body is omitted in the skeleton, the parser will have set the loop body to an empty block,
+ // which has the same merging behavior.
+ var r = MergeWhileStmt(skel, oldWhile, guard);
+ body.Add(r);
+ i++; j++;
+ }
+
+ } else {
+ Contract.Assume(false); // unexpected skeleton statement
+ }
+
+ } else if (cur is AssertStmt) {
+ MergeAddStatement(cur, body);
+ i++;
+
+ } else if (cur is VarDeclStmt) {
+ var cNew = (VarDeclStmt)cur;
+ bool doMerge = false;
+ Expression addedAssert = null;
+ if (oldS is VarDeclStmt) {
+ var cOld = (VarDeclStmt)oldS;
+ if (VarDeclAgree(cOld.Lhss, cNew.Lhss)) {
+ var update = cNew.Update as UpdateStmt;
+ if (update != null && update.Rhss.TrueForAll(rhs => !rhs.CanAffectPreviouslyKnownExpressions)) {
+ // Note, we allow switching between ghost and non-ghost, since that seems unproblematic.
+ if (cOld.Update == null) {
+ doMerge = true;
+ } else if (cOld.Update is AssignSuchThatStmt) {
+ doMerge = true;
+ addedAssert = refinementCloner.CloneExpr(((AssignSuchThatStmt)cOld.Update).Expr);
+ } else {
+ var updateOld = (UpdateStmt)cOld.Update; // if cast fails, there are more ConcreteUpdateStatement subclasses than expected
+ doMerge = true;
+ foreach (var rhs in updateOld.Rhss) {
+ if (!(rhs is HavocRhs))
+ doMerge = false;
+ }
+ }
+ }
+ }
+ }
+ if (doMerge) {
+ // Go ahead with the merge:
+ body.Add(cNew);
+ i++; j++;
+ if (addedAssert != null) {
+ body.Add(new AssertStmt(new Translator.ForceCheckToken(addedAssert.tok), addedAssert, null));
+ }
+ } else {
+ MergeAddStatement(cur, body);
+ i++;
+ }
+
+ } else if (cur is AssignStmt) {
+ var cNew = (AssignStmt)cur;
+ var cOld = oldS as AssignStmt;
+ if (cOld == null && oldS is UpdateStmt) {
+ var us = (UpdateStmt)oldS;
+ if (us.ResolvedStatements.Count == 1) {
+ cOld = us.ResolvedStatements[0] as AssignStmt;
+ }
+ }
+ bool doMerge = false;
+ if (cOld != null && cNew.Lhs.Resolved is IdentifierExpr && cOld.Lhs.Resolved is IdentifierExpr) {
+ if (((IdentifierExpr)cNew.Lhs.Resolved).Name == ((IdentifierExpr)cOld.Lhs.Resolved).Name) {
+ if (!(cNew.Rhs is TypeRhs) && cOld.Rhs is HavocRhs) {
+ doMerge = true;
+ }
+ }
+ }
+ if (doMerge) {
+ // Go ahead with the merge:
+ body.Add(cNew);
+ i++; j++;
+ } else {
+ MergeAddStatement(cur, body);
+ i++;
+ }
+
+ } else if (cur is UpdateStmt) {
+ var nw = (UpdateStmt)cur;
+ List<Statement> stmtGenerated = new List<Statement>();
+ bool doMerge = false;
+ if (oldS is UpdateStmt) {
+ var s = (UpdateStmt)oldS;
+ if (LeftHandSidesAgree(s.Lhss, nw.Lhss)) {
+ doMerge = true;
+ stmtGenerated.Add(nw);
+ foreach (var rhs in s.Rhss) {
+ if (!(rhs is HavocRhs))
+ doMerge = false;
+ }
+ }
+ } else if (oldS is AssignSuchThatStmt) {
+ var s = (AssignSuchThatStmt)oldS;
+ if (LeftHandSidesAgree(s.Lhss, nw.Lhss)) {
+ doMerge = true;
+ stmtGenerated.Add(nw);
+ var addedAssert = refinementCloner.CloneExpr(s.Expr);
+ stmtGenerated.Add(new AssertStmt(new Translator.ForceCheckToken(addedAssert.tok), addedAssert, null));
+ }
+ }
+ if (doMerge) {
+ // Go ahead with the merge:
+ Contract.Assert(cce.NonNullElements(stmtGenerated));
+ body.AddRange(stmtGenerated);
+ i++; j++;
+ } else {
+ MergeAddStatement(cur, body);
+ i++;
+ }
+ } else if (cur is IfStmt) {
+ var cNew = (IfStmt)cur;
+ var cOld = oldS as IfStmt;
+ if (cOld != null && cOld.Guard == null) {
+ var r = new IfStmt(cNew.Tok, cNew.Guard, MergeBlockStmt(cNew.Thn, cOld.Thn), MergeElse(cNew.Els, cOld.Els));
+ body.Add(r);
+ i++; j++;
+ } else {
+ MergeAddStatement(cur, body);
+ i++;
+ }
+
+ } else if (cur is WhileStmt) {
+ var cNew = (WhileStmt)cur;
+ var cOld = oldS as WhileStmt;
+ if (cOld != null && cOld.Guard == null) {
+ var r = MergeWhileStmt(cNew, cOld, cNew.Guard);
+ body.Add(r);
+ i++; j++;
+ } else {
+ MergeAddStatement(cur, body);
+ i++;
+ }
+
+ } else if (cur is BlockStmt) {
+ var cNew = (BlockStmt)cur;
+ var cOld = oldS as BlockStmt;
+ if (cOld != null) {
+ var r = MergeBlockStmt(cNew, cOld);
+ body.Add(r);
+ i++; j++;
+ } else {
+ MergeAddStatement(cur, body);
+ i++;
+ }
+ } else {
+ MergeAddStatement(cur, body);
+ i++;
+ }
+ }
+ }
+ // implement the implicit "...;" at the end of each block statement skeleton
+ for (; j < oldStmt.Body.Count; j++) {
+ body.Add(refinementCloner.CloneStmt(oldStmt.Body[j]));
+ }
+ return new BlockStmt(skeleton.Tok, body);
+ }
+
+ private bool LeftHandSidesAgree(List<Expression> old, List<Expression> nw) {
+ if (old.Count != nw.Count)
+ return false;
+ for (int i = 0; i < old.Count; i++) {
+ var a = old[i].Resolved as IdentifierExpr;
+ var b = nw[i] as IdentifierSequence;
+ if (a != null && b != null)
+ if (b.Tokens.Count == 1 && b.Arguments == null)
+ if (a.Name == b.Tokens[0].val)
+ continue;
+ return false;
+ }
+ return true;
+ }
+ private bool VarDeclAgree(List<VarDecl> old, List<VarDecl> nw) {
+ if (old.Count != nw.Count)
+ return false;
+ for (int i = 0; i < old.Count; i++) {
+ if (old[i].Name != nw[i].Name)
+ return false;
+ }
+ return true;
+ }
+
+ bool PotentialMatch(Statement nxt, Statement other) {
+ Contract.Requires(nxt != null);
+ Contract.Requires(!(nxt is SkeletonStatement) || ((SkeletonStatement)nxt).S != null); // nxt is not "...;"
+ Contract.Requires(other != null);
+
+ if (nxt.Labels != null) {
+ for (var olbl = other.Labels; olbl != null; olbl = olbl.Next) {
+ var odata = olbl.Data;
+ for (var l = nxt.Labels; l != null; l = l.Next) {
+ if (odata.Name == l.Data.Name) {
+ return true;
+ }
+ }
+ }
+ return false; // labels of 'nxt' don't match any label of 'other'
+ } else if (nxt is SkeletonStatement) {
+ var S = ((SkeletonStatement)nxt).S;
+ if (S is AssertStmt) {
+ return other is PredicateStmt;
+ } else if (S is AssumeStmt) {
+ return other is AssumeStmt;
+ } else if (S is IfStmt) {
+ return other is IfStmt;
+ } else if (S is WhileStmt) {
+ return other is WhileStmt;
+ } else {
+ Contract.Assume(false); // unexpected skeleton
+ }
+
+ } else if (nxt is IfStmt) {
+ var oth = other as IfStmt;
+ return oth != null && oth.Guard == null;
+ } else if (nxt is WhileStmt) {
+ var oth = other as WhileStmt;
+ return oth != null && oth.Guard == null;
+ } else if (nxt is VarDeclStmt) {
+ var oth = other as VarDeclStmt;
+ return oth != null && VarDeclAgree(((VarDeclStmt)nxt).Lhss, oth.Lhss);
+ } else if (nxt is BlockStmt) {
+ var b = (BlockStmt)nxt;
+ if (b.Labels != null) {
+ var oth = other as BlockStmt;
+ if (oth != null && oth.Labels != null) {
+ return b.Labels.Data.Name == oth.Labels.Data.Name; // both have the same label
+ }
+ } else if (other is BlockStmt && ((BlockStmt)other).Labels == null) {
+ return true; // both are unlabeled
+ }
+ } else if (nxt is UpdateStmt) {
+ var up = (UpdateStmt)nxt;
+ if (other is AssignSuchThatStmt) {
+ var oth = other as AssignSuchThatStmt;
+ return oth != null && LeftHandSidesAgree(oth.Lhss, up.Lhss);
+ }
+ }
+
+ // not a potential match
+ return false;
+ }
+
+ WhileStmt MergeWhileStmt(WhileStmt cNew, WhileStmt cOld, Expression guard) {
+ Contract.Requires(cNew != null);
+ Contract.Requires(cOld != null);
+
+ // Note, the parser produces errors if there are any decreases or modifies clauses (and it creates
+ // the Specification structures with a null list).
+ Contract.Assume(cNew.Mod.Expressions == null);
+
+ // If the previous loop was not specified with "decreases *", then the new loop is not allowed to provide any "decreases" clause.
+ // Any "decreases *" clause is not inherited, so if the previous loop was specified with "decreases *", then the new loop needs
+ // to either redeclare "decreases *", provided a termination-checking "decreases" clause, or give no "decreases" clause and thus
+ // get a default "decreases" loop.
+ Specification<Expression> decr;
+ if (Contract.Exists(cOld.Decreases.Expressions, e => e is WildcardExpr)) {
+ decr = cNew.Decreases; // take the new decreases clauses, whatever they may be (including nothing at all)
+ } else {
+ if (cNew.Decreases.Expressions.Count != 0) {
+ 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 *'");
+ }
+ decr = refinementCloner.CloneSpecExpr(cOld.Decreases);
+ }
+
+ var invs = cOld.Invariants.ConvertAll(refinementCloner.CloneMayBeFreeExpr);
+ invs.AddRange(cNew.Invariants);
+ var r = new RefinedWhileStmt(cNew.Tok, guard, invs, decr, refinementCloner.CloneSpecFrameExpr(cOld.Mod), MergeBlockStmt(cNew.Body, cOld.Body));
+ return r;
+ }
+
+ Statement MergeElse(Statement skeleton, Statement oldStmt) {
+ Contract.Requires(skeleton == null || skeleton is BlockStmt || skeleton is IfStmt || skeleton is SkeletonStatement);
+ Contract.Requires(oldStmt == null || oldStmt is BlockStmt || oldStmt is IfStmt || oldStmt is SkeletonStatement);
+
+ if (skeleton == null) {
+ return refinementCloner.CloneStmt(oldStmt);
+ } else if (skeleton is IfStmt || skeleton is SkeletonStatement) {
+ // wrap a block statement around the if statement
+ skeleton = new BlockStmt(skeleton.Tok, new List<Statement>() { skeleton });
+ }
+
+ if (oldStmt == null) {
+ // make it into an empty block statement
+ oldStmt = new BlockStmt(skeleton.Tok, new List<Statement>());
+ } else if (oldStmt is IfStmt || oldStmt is SkeletonStatement) {
+ // wrap a block statement around the if statement
+ oldStmt = new BlockStmt(oldStmt.Tok, new List<Statement>() { oldStmt });
+ }
+
+ Contract.Assert(skeleton is BlockStmt && oldStmt is BlockStmt);
+ return MergeBlockStmt((BlockStmt)skeleton, (BlockStmt)oldStmt);
+ }
+
+ /// <summary>
+ /// Add "s" to "stmtList", but complain if "s" contains further occurrences of "...", if "s" assigns to a
+ /// variable that was not declared in the refining module, or if "s" has some control flow that jumps to a
+ /// place outside "s".
+ /// </summary>
+ void MergeAddStatement(Statement s, List<Statement> stmtList) {
+ Contract.Requires(s != null);
+ Contract.Requires(stmtList != null);
+ var prevErrorCount = reporter.ErrorCount;
+ CheckIsOkayNewStatement(s, new Stack<string>(), 0);
+ if (reporter.ErrorCount == prevErrorCount) {
+ stmtList.Add(s);
+ }
+ }
+
+ /// <summary>
+ /// See comment on MergeAddStatement.
+ /// </summary>
+ void CheckIsOkayNewStatement(Statement s, Stack<string> labels, int loopLevels) {
+ Contract.Requires(s != null);
+ Contract.Requires(labels != null);
+ Contract.Requires(0 <= loopLevels);
+
+ for (LList<Label> n = s.Labels; n != null; n = n.Next) {
+ 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");
+ } else if (s is ProduceStmt) {
+ reporter.Error(s, (s is YieldStmt ? "yield" : "return") + " 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");
+ }
+ } 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");
+ } else if (s is ConcreteUpdateStatement) {
+ postTasks.Enqueue(() =>
+ {
+ CheckIsOkayUpdateStmt((ConcreteUpdateStatement)s, moduleUnderConstruction, reporter);
+ });
+ } else if (s is CallStmt) {
+ reporter.Error(s.Tok, "cannot have call statement");
+ } else if (s is ParallelStmt) {
+ if (((ParallelStmt)s).Kind == ParallelStmt.ParBodyKind.Assign) // allow Proof and Call (as neither touch any existing state)
+ reporter.Error(s.Tok, "cannot have parallel statement");
+ } else {
+ if (s is WhileStmt || s is AlternativeLoopStmt) {
+ loopLevels++;
+ }
+ foreach (var ss in s.SubStatements) {
+ CheckIsOkayNewStatement(ss, labels, loopLevels);
+ }
+ }
+
+ for (LList<Label> n = s.Labels; n != null; n = n.Next) {
+ labels.Pop();
+ }
+ }
+
+ // Checks that statement stmt, defined in the constructed module m, is a refinement of skip in the parent module
+ private bool CheckIsOkayUpdateStmt(ConcreteUpdateStatement stmt, ModuleDefinition m, ResolutionErrorReporter reporter) {
+ foreach (var lhs in stmt.Lhss) {
+ var l = lhs.Resolved;
+ if (l is IdentifierExpr) {
+ var ident = (IdentifierExpr)l;
+ Contract.Assert(ident.Var is VarDecl || ident.Var is Formal); // LHS identifier expressions must be locals or out parameters (ie. formals)
+ if ((ident.Var is VarDecl && RefinementToken.IsInherited(((VarDecl)ident.Var).Tok, m)) || ident.Var is Formal) {
+ // for some reason, formals are not considered to be inherited.
+ reporter.Error(l.tok, "cannot assign to variable defined previously");
+ return false;
+ }
+ } else if (l is FieldSelectExpr) {
+ if (RefinementToken.IsInherited(((FieldSelectExpr)l).Field.tok, m)) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ if (stmt is UpdateStmt) {
+ var s = (UpdateStmt)stmt;
+ foreach (var rhs in s.Rhss) {
+ if (s.Rhss[0].CanAffectPreviouslyKnownExpressions) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ // ---------------------- additional methods -----------------------------------------------------------------------------
+
+ public static bool ContainsChange(Expression expr, ModuleDefinition m) {
+ Contract.Requires(expr != null);
+ Contract.Requires(m != null);
+
+ if (expr is FunctionCallExpr) {
+ var e = (FunctionCallExpr)expr;
+ if (e.Function.EnclosingClass.Module == m) {
+ var p = e.Function as Predicate;
+ if (p != null && p.BodyOrigin == Predicate.BodyOriginKind.Extension) {
+ return true;
+ }
+ }
+ }
+
+ foreach (var ee in expr.SubExpressions) {
+ if (ContainsChange(ee, m)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ class RefinementCloner : Cloner {
+ ModuleDefinition moduleUnderConstruction;
+ public RefinementCloner(ModuleDefinition m) {
+ moduleUnderConstruction = m;
+ }
+ public override IToken Tok(IToken tok) {
+ return new RefinementToken(tok, moduleUnderConstruction);
+ }
+ public virtual Attributes MergeAttributes(Attributes prevAttrs, Attributes moreAttrs) {
+ if (moreAttrs == null) {
+ return CloneAttributes(prevAttrs);
+ } else {
+ return new Attributes(moreAttrs.Name, moreAttrs.Args.ConvertAll(CloneAttrArg), MergeAttributes(prevAttrs, moreAttrs.Prev));
+ }
+ }
+ }
+ class SubstitutionCloner : Cloner {
+ public Dictionary<string, Expression> Exprs;
+ public SortedSet<string> SubstitutionsMade;
+ Cloner c;
+ public SubstitutionCloner(Dictionary<string, Expression> subs, Cloner c) {
+ Exprs = subs;
+ SubstitutionsMade = new SortedSet<string>();
+ this.c = c;
+ }
+ public override Expression CloneExpr(Expression expr) {
+ if (expr is NamedExpr) {
+ NamedExpr n = (NamedExpr)expr;
+ Expression E;
+ if (Exprs.TryGetValue(n.Name, out E)) {
+ SubstitutionsMade.Add(n.Name);
+ return new NamedExpr(n.tok, n.Name, E, c.CloneExpr(n.Body), E.tok);
+ }
+ }
+ return base.CloneExpr(expr); // in all other cases, just do what the base class would.
+ // note that when we get a named expression that is not in
+ // our substitution list, then we call the base class, which
+ // recurses on the body of the named expression.
+ }
+ }
+} \ No newline at end of file
diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs
new file mode 100644
index 00000000..a876864e
--- /dev/null
+++ b/Source/Dafny/Resolver.cs
@@ -0,0 +1,6706 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Diagnostics.Contracts;
+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}",
+ 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}",
+ tok.filename, tok.line, tok.col - 1,
+ string.Format(msg, args));
+ Console.ForegroundColor = col;
+ }
+ }
+
+ public class Resolver : ResolutionErrorReporter
+ {
+ readonly BuiltIns builtIns;
+
+ //Dictionary<string/*!*/,TopLevelDecl/*!*/>/*!*/ classes; // can map to AmbiguousTopLevelDecl
+ //Dictionary<string, ModuleDecl> importedNames; // the imported modules, as a map.
+ ModuleSignature moduleInfo = null;
+
+ class AmbiguousTopLevelDecl : TopLevelDecl // only used with "classes"
+ {
+ readonly TopLevelDecl A;
+ readonly TopLevelDecl B;
+ public AmbiguousTopLevelDecl(ModuleDefinition m, TopLevelDecl a, TopLevelDecl b)
+ : base(a.tok, a.Name + "/" + b.Name, m, new List<TypeParameter>(), null) {
+ A = a;
+ B = b;
+ }
+ public string ModuleNames() {
+ string nm;
+ if (A is AmbiguousTopLevelDecl) {
+ nm = ((AmbiguousTopLevelDecl)A).ModuleNames();
+ } else {
+ nm = A.Module.Name;
+ }
+ if (B is AmbiguousTopLevelDecl) {
+ nm += ", " + ((AmbiguousTopLevelDecl)B).ModuleNames();
+ } else {
+ nm += ", " + B.Module.Name;
+ }
+ return nm;
+ }
+ }
+
+ class AmbiguousMemberDecl : MemberDecl // only used with "classes"
+ {
+ readonly MemberDecl A;
+ readonly MemberDecl B;
+ public AmbiguousMemberDecl(ModuleDefinition m, MemberDecl a, MemberDecl b)
+ : base(a.tok, a.Name + "/" + b.Name, a.IsStatic, a.IsGhost, null) {
+ A = a;
+ B = b;
+ }
+ public string ModuleNames() {
+ string nm;
+ if (A is AmbiguousMemberDecl) {
+ nm = ((AmbiguousMemberDecl)A).ModuleNames();
+ } else {
+ nm = A.EnclosingClass.Module.Name;
+ }
+ if (B is AmbiguousMemberDecl) {
+ nm += ", " + ((AmbiguousMemberDecl)B).ModuleNames();
+ } else {
+ nm += ", " + B.EnclosingClass.Module.Name;
+ }
+ return nm;
+ }
+ }
+ //Dictionary<string/*!*/, Tuple<DatatypeCtor, bool>> allDatatypeCtors;
+
+ readonly Dictionary<ClassDecl/*!*/, Dictionary<string/*!*/, MemberDecl/*!*/>/*!*/>/*!*/ classMembers = new Dictionary<ClassDecl/*!*/, Dictionary<string/*!*/, MemberDecl/*!*/>/*!*/>();
+ readonly Dictionary<DatatypeDecl/*!*/, Dictionary<string/*!*/, MemberDecl/*!*/>/*!*/>/*!*/ datatypeMembers = new Dictionary<DatatypeDecl/*!*/, Dictionary<string/*!*/, MemberDecl/*!*/>/*!*/>();
+ readonly Dictionary<DatatypeDecl/*!*/, Dictionary<string/*!*/, DatatypeCtor/*!*/>/*!*/>/*!*/ datatypeCtors = new Dictionary<DatatypeDecl/*!*/, Dictionary<string/*!*/, DatatypeCtor/*!*/>/*!*/>();
+ readonly Graph<ModuleDecl/*!*/>/*!*/ dependencies = new Graph<ModuleDecl/*!*/>();
+ private ModuleSignature systemNameInfo = null;
+ private bool useCompileSignatures = false;
+
+ public Resolver(Program prog) {
+ Contract.Requires(prog != null);
+ builtIns = prog.BuiltIns;
+ }
+
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(builtIns != null);
+ Contract.Invariant(cce.NonNullElements(dependencies));
+ Contract.Invariant(cce.NonNullDictionaryAndValues(classMembers) && Contract.ForAll(classMembers.Values, v => cce.NonNullDictionaryAndValues(v)));
+ Contract.Invariant(cce.NonNullDictionaryAndValues(datatypeCtors) && Contract.ForAll(datatypeCtors.Values, v => cce.NonNullDictionaryAndValues(v)));
+ }
+
+ public void ResolveProgram(Program prog) {
+ Contract.Requires(prog != null);
+ 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
+ // 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);
+ }
+ if (ErrorCount > 0) { return; } // give up on trying to resolve anything else
+
+ // fill in module heights
+ List<ModuleDecl> sortedDecls = dependencies.TopologicallySortedComponents();
+ int h = 0;
+ foreach (ModuleDecl m in sortedDecls) {
+ m.Height = h;
+ if (m is LiteralModuleDecl) {
+ var mdef = ((LiteralModuleDecl)m).ModuleDef;
+ mdef.Height = h;
+ prog.Modules.Add(mdef);
+ }
+ h++;
+ }
+
+ var refinementTransformer = new RefinementTransformer(this, prog);
+
+ IRewriter rewriter = new AutoContractsRewriter();
+ systemNameInfo = RegisterTopLevelDecls(prog.BuiltIns.SystemModule, false);
+ foreach (var decl in sortedDecls) {
+ if (decl is LiteralModuleDecl) {
+ // The declaration is a literal module, so it has members and such that we need
+ // to resolve. First we do refinement transformation. Then we construct the signature
+ // of the module. This is the public, externally visible signature. Then we add in
+ // everything that the system defines, as well as any "import" (i.e. "opened" modules)
+ // directives (currently not supported, but this is where we would do it.) This signature,
+ // which is only used while resolving the members of the module is stored in the (basically)
+ // global variable moduleInfo. Then the signatures of the module members are resolved, followed
+ // by the bodies.
+ var literalDecl = (LiteralModuleDecl)decl;
+ var m = (literalDecl).ModuleDef;
+
+ var errorCount = ErrorCount;
+ rewriter.PreResolve(m);
+ ModuleSignature refinedSig = null;
+ if (m.RefinementBaseRoot != null) {
+ if (ResolvePath(m.RefinementBaseRoot, m.RefinementBaseName, out refinedSig)) {
+ if (refinedSig.ModuleDef != null) {
+ m.RefinementBase = refinedSig.ModuleDef;
+ refinementTransformer.PreResolve(m);
+ } else {
+ 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));
+ }
+ } else {
+ Error(m.RefinementBaseName[0], "module ({0}) named as refinement base does not exist", Util.Comma(".", m.RefinementBaseName, x => x.val));
+ }
+ }
+ literalDecl.Signature = RegisterTopLevelDecls(m, true);
+ literalDecl.Signature.Refines = refinedSig;
+ var sig = literalDecl.Signature;
+ // set up environment
+ var preResolveErrorCount = ErrorCount;
+ useCompileSignatures = false;
+ ResolveModuleDefinition(m, sig);
+ if (ErrorCount == preResolveErrorCount) {
+ refinementTransformer.PostResolve(m);
+ // give rewriter a chance to do processing
+ rewriter.PostResolve(m);
+ }
+ if (ErrorCount == errorCount && !m.IsGhost) {
+ // compilation should only proceed if everything is good, including the signature (which preResolveErrorCount does not include);
+ var nw = (new Cloner()).CloneModuleDefinition(m, m.CompileName + "_Compile");
+ var compileSig = RegisterTopLevelDecls(nw, true);
+ compileSig.Refines = refinedSig;
+ sig.CompileSignature = compileSig;
+ useCompileSignatures = true;
+ ResolveModuleDefinition(nw, compileSig);
+ prog.CompileModules.Add(nw);
+ }
+ } else if (decl is AliasModuleDecl) {
+ var alias = (AliasModuleDecl)decl;
+ // resolve the path
+ ModuleSignature p;
+ if (ResolvePath(alias.Root, alias.Path, out p)) {
+ alias.Signature = p;
+ } else {
+ alias.Signature = new ModuleSignature(); // there was an error, give it a valid but empty signature
+ }
+ } else if (decl is AbstractModuleDecl) {
+ var abs = (AbstractModuleDecl)decl;
+ ModuleSignature p;
+ if (ResolvePath(abs.Root, abs.Path, out p)) {
+ abs.Signature = MakeAbstractSignature(p, abs.FullCompileName, abs.Height, prog.Modules);
+ abs.OriginalSignature = p;
+ ModuleSignature compileSig;
+ if (abs.CompilePath != null) {
+ if (ResolvePath(abs.CompileRoot, abs.CompilePath, out compileSig)) {
+ 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));
+ }
+ 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 = new ModuleSignature(); // there was an error, give it a valid but empty signature
+ }
+ } else { Contract.Assert(false); }
+ Contract.Assert(decl.Signature != null);
+ }
+ // compute IsRecursive bit for mutually recursive functions
+ foreach (ModuleDefinition m in prog.Modules) {
+ foreach (var fn in ModuleDefinition.AllFunctions(m.TopLevelDecls)) {
+ if (!fn.IsRecursive) { // note, self-recursion has already been determined
+ int n = m.CallGraph.GetSCCSize(fn);
+ if (2 <= n) {
+ // the function is mutually recursive (note, the SCC does not determine self recursion)
+ fn.IsRecursive = true;
+ }
+ }
+ }
+ }
+ }
+
+ private void ResolveModuleDefinition(ModuleDefinition m, ModuleSignature sig) {
+ moduleInfo = MergeSignature(sig, systemNameInfo);
+ // resolve
+ var datatypeDependencies = new Graph<IndDatatypeDecl>();
+ int prevErrorCount = ErrorCount;
+ ResolveTopLevelDecls_Signatures(m, m.TopLevelDecls, datatypeDependencies);
+ if (ErrorCount == prevErrorCount) {
+ ResolveTopLevelDecls_Meat(m.TopLevelDecls, datatypeDependencies);
+ }
+ }
+
+
+ public class ModuleBindings
+ {
+ private ModuleBindings parent;
+ private Dictionary<string, ModuleDecl> modules;
+ private Dictionary<string, ModuleBindings> bindings;
+
+ public ModuleBindings(ModuleBindings p) {
+ parent = p;
+ modules = new Dictionary<string, ModuleDecl>();
+ bindings = new Dictionary<string, ModuleBindings>();
+ }
+ public bool BindName(string name, ModuleDecl subModule, ModuleBindings b) {
+ if (modules.ContainsKey(name)) {
+ return false;
+ } else {
+ modules.Add(name, subModule);
+ bindings.Add(name, b);
+ return true;
+ }
+ }
+ public bool TryLookup(IToken name, out ModuleDecl m) {
+ Contract.Requires(name != null);
+ if (modules.TryGetValue(name.val, out m)) {
+ return true;
+ } else if (parent != null) {
+ return parent.TryLookup(name, out m);
+ } else return false;
+ }
+ public bool TryLookupIgnore(IToken name, out ModuleDecl m, ModuleDecl ignore) {
+ Contract.Requires(name != null);
+ if (modules.TryGetValue(name.val, out m) && m != ignore) {
+ return true;
+ } else if (parent != null) {
+ return parent.TryLookup(name, out m);
+ } else return false;
+ }
+ public IEnumerable<ModuleDecl> ModuleList {
+ get { return modules.Values; }
+ }
+ public ModuleBindings SubBindings(string name) {
+ ModuleBindings v = null;
+ bindings.TryGetValue(name, out v);
+ return v;
+ }
+ }
+ private ModuleBindings BindModuleNames(ModuleDefinition moduleDecl, ModuleBindings parentBindings) {
+ var bindings = new ModuleBindings(parentBindings);
+
+ foreach (var tld in moduleDecl.TopLevelDecls) {
+ if (tld is LiteralModuleDecl) {
+ 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);
+ }
+ } else if (tld is AbstractModuleDecl) {
+ var subdecl = (AbstractModuleDecl)tld;
+ if (!bindings.BindName(subdecl.Name, subdecl, null)) {
+ Error(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);
+ }
+ }
+ }
+ return bindings;
+ }
+
+ private void ProcessDependenciesDefinition(ModuleDecl decl, ModuleDefinition m, ModuleBindings bindings, Graph<ModuleDecl> dependencies) {
+ if (m.RefinementBaseName != null) {
+ ModuleDecl other;
+ if (!bindings.TryLookup(m.RefinementBaseName[0], out other)) {
+ Error(m, "module {0} named as refinement base does not exist", m.RefinementBaseName[0].val);
+ } else if (other is LiteralModuleDecl && ((LiteralModuleDecl)other).ModuleDef == m) {
+ Error(m, "module cannot refine itself: {0}", m.RefinementBaseName[0].val);
+ } else {
+ Contract.Assert(other != null); // follows from postcondition of TryGetValue
+ dependencies.AddEdge(decl, other);
+ m.RefinementBaseRoot = other;
+ }
+ }
+ foreach (var toplevel in m.TopLevelDecls) {
+ if (toplevel is ModuleDecl) {
+ var d = (ModuleDecl)toplevel;
+ dependencies.AddEdge(decl, d);
+ var subbindings = bindings.SubBindings(d.Name);
+ ProcessDependencies(d, subbindings ?? bindings, dependencies);
+ }
+ }
+ }
+ private void ProcessDependencies(ModuleDecl moduleDecl, ModuleBindings bindings, Graph<ModuleDecl> dependencies) {
+ dependencies.AddVertex(moduleDecl);
+ if (moduleDecl is LiteralModuleDecl) {
+ ProcessDependenciesDefinition(moduleDecl, ((LiteralModuleDecl)moduleDecl).ModuleDef, bindings, dependencies);
+ } else if (moduleDecl is AliasModuleDecl) {
+ var alias = moduleDecl as AliasModuleDecl;
+ ModuleDecl root;
+ if (!bindings.TryLookupIgnore(alias.Path[0], out root, alias))
+ Error(alias.tok, ModuleNotFoundErrorMessage(0, alias.Path));
+ else {
+ dependencies.AddEdge(moduleDecl, root);
+ alias.Root = root;
+ }
+ } else if (moduleDecl is AbstractModuleDecl) {
+ var abs = moduleDecl as AbstractModuleDecl;
+ ModuleDecl root;
+ if (!bindings.TryLookup(abs.Path[0], out root))
+ Error(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));
+ else {
+ dependencies.AddEdge(moduleDecl, root);
+ abs.CompileRoot = root;
+ }
+ }
+ }
+ }
+
+ private string ModuleNotFoundErrorMessage(int i, List<IToken> path) {
+ return "module " + path[i].val + " does not exist" +
+ (1 < path.Count ? " (position " + i.ToString() + " in path " + Util.Comma(".", path, x => x.val) + ")" : "");
+ }
+
+ public static ModuleSignature MergeSignature(ModuleSignature m, ModuleSignature system) {
+ var info = new ModuleSignature();
+ // add the system-declared information, among which we know there are no duplicates
+ foreach (var kv in system.TopLevels) {
+ info.TopLevels.Add(kv.Key, kv.Value);
+ }
+ foreach (var kv in system.Ctors) {
+ info.Ctors.Add(kv.Key, kv.Value);
+ }
+ // add for the module itself
+ foreach (var kv in m.TopLevels) {
+ info.TopLevels[kv.Key] = kv.Value;
+ }
+ foreach (var kv in m.Ctors) {
+ info.Ctors[kv.Key] = kv.Value;
+ }
+ foreach (var kv in m.StaticMembers) {
+ info.StaticMembers[kv.Key] = kv.Value;
+ }
+ info.IsGhost = m.IsGhost;
+ return info;
+ }
+ ModuleSignature RegisterTopLevelDecls(ModuleDefinition moduleDef, bool useImports) {
+ Contract.Requires(moduleDef != null);
+ var sig = new ModuleSignature();
+ sig.ModuleDef = moduleDef;
+ sig.IsGhost = moduleDef.IsGhost;
+ 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 = ((ModuleDecl)im).Signature;
+ // classes:
+ foreach (var kv in s.TopLevels) {
+ TopLevelDecl d;
+ if (sig.TopLevels.TryGetValue(kv.Key, out d)) {
+ sig.TopLevels[kv.Key] = new AmbiguousTopLevelDecl(moduleDef, d, kv.Value);
+ } else {
+ sig.TopLevels.Add(kv.Key, kv.Value);
+ }
+ }
+ // constructors:
+ foreach (var kv in s.Ctors) {
+ Tuple<DatatypeCtor, bool> pair;
+ if (sig.Ctors.TryGetValue(kv.Key, out pair)) {
+ // mark it as a duplicate
+ sig.Ctors[kv.Key] = new Tuple<DatatypeCtor, bool>(pair.Item1, true);
+ } else {
+ // add new
+ sig.Ctors.Add(kv.Key, kv.Value);
+ }
+ }
+ // static members:
+ foreach (var kv in s.StaticMembers) {
+ MemberDecl md;
+ if (sig.StaticMembers.TryGetValue(kv.Key, out md)) {
+ sig.StaticMembers[kv.Key] = new AmbiguousMemberDecl(moduleDef, md, kv.Value);
+ } else {
+ // add new
+ sig.StaticMembers.Add(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
+ foreach (TopLevelDecl d in declarations) {
+ 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);
+ } else {
+ toplevels[d.Name] = d;
+ sig.TopLevels[d.Name] = d;
+ }
+ if (d is ModuleDecl) {
+ // nothing to do
+ } else if (d is ArbitraryTypeDecl) {
+ // nothing more to register
+
+ } else if (d is IteratorDecl) {
+ var iter = (IteratorDecl)d;
+
+ // register the names of the implicit members
+ var members = new Dictionary<string, MemberDecl>();
+ classMembers.Add(iter, members);
+
+ // 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);
+ } else {
+ var field = new SpecialField(p.tok, p.Name, p.CompileName, "", "", p.IsGhost, false, false, p.Type, null);
+ field.EnclosingClass = iter; // resolve here
+ members.Add(p.Name, field);
+ iter.Members.Add(field);
+ }
+ }
+ 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);
+ } else {
+ var field = new SpecialField(p.tok, p.Name, p.CompileName, "", "", p.IsGhost, true, true, p.Type, null);
+ field.EnclosingClass = iter; // resolve here
+ iter.OutsFields.Add(field);
+ members.Add(p.Name, field);
+ iter.Members.Add(field);
+ }
+ }
+ 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);
+ } 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);
+ field.EnclosingClass = iter; // resolve here
+ iter.OutsHistoryFields.Add(field); // for now, just record this field (until all parameters have been added as members)
+ }
+ }
+ // now that already-used 'ys' names have been checked for, add these yield-history variables
+ iter.OutsHistoryFields.ForEach(f => {
+ members.Add(f.Name, f);
+ 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);
+ 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);
+ iter.Members.Add(field);
+ }
+ // finally, add special variables to hold the components of the (explicit or implicit) decreases clause
+ bool inferredDecreases;
+ var decr = Translator.MethodDecreasesWithDefault(iter, out inferredDecreases);
+ if (inferredDecreases) {
+ iter.InferredDecreases = true;
+ Contract.Assert(iter.Decreases.Expressions.Count == 0);
+ iter.Decreases.Expressions.AddRange(decr);
+ }
+ // create the fields; unfortunately, we don't know their types yet, so we'll just insert type proxies for now
+ var i = 0;
+ foreach (var p in iter.Decreases.Expressions) {
+ var nm = "_decreases" + i;
+ var field = new SpecialField(p.tok, nm, nm, "", "", true, false, false, new InferredTypeProxy(), null);
+ field.EnclosingClass = iter; // resolve here
+ iter.DecreasesFields.Add(field);
+ members.Add(field.Name, field);
+ iter.Members.Add(field);
+ i++;
+ }
+
+ // Note, the typeArgs parameter to the following Method/Predicate constructors is passed in as the empty list. What that is
+ // saying is that the Method/Predicate does not take any type parameters over and beyond what the enclosing type (namely, the
+ // iterator type) does.
+ // --- here comes the constructor
+ var init = new Constructor(iter.tok, iter.Name, new List<TypeParameter>(), iter.Ins,
+ new List<MaybeFreeExpression>(),
+ new Specification<FrameExpression>(new List<FrameExpression>(), null),
+ new List<MaybeFreeExpression>(),
+ new Specification<Expression>(new List<Expression>(), null),
+ null, null, false);
+ // --- here comes predicate Valid()
+ var valid = new Predicate(iter.tok, "Valid", false, true, new List<TypeParameter>(), iter.tok,
+ new List<Formal>(),
+ new List<Expression>(),
+ new List<FrameExpression>(),
+ new List<Expression>(),
+ new Specification<Expression>(new List<Expression>(), null),
+ null, Predicate.BodyOriginKind.OriginalOrInherited, null, false);
+ // --- here comes method MoveNext
+ var moveNext = new Method(iter.tok, "MoveNext", false, false, new List<TypeParameter>(),
+ new List<Formal>(), new List<Formal>() { new Formal(iter.tok, "more", Type.Bool, false, false) },
+ new List<MaybeFreeExpression>(),
+ new Specification<FrameExpression>(new List<FrameExpression>(), null),
+ new List<MaybeFreeExpression>(),
+ new Specification<Expression>(new List<Expression>(), null),
+ null, null, false);
+ // add these implicit members to the class
+ init.EnclosingClass = iter;
+ valid.EnclosingClass = iter;
+ moveNext.EnclosingClass = iter;
+ iter.HasConstructor = true;
+ iter.Member_Init = init;
+ iter.Member_Valid = valid;
+ 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);
+ } else {
+ members.Add(init.Name, init);
+ iter.Members.Add(init);
+ }
+ // 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");
+ } 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");
+ } else {
+ members.Add(moveNext.Name, moveNext);
+ iter.Members.Add(moveNext);
+ }
+
+ } else if (d is ClassDecl) {
+ ClassDecl cl = (ClassDecl)d;
+
+ // register the names of the class members
+ var members = new Dictionary<string, MemberDecl>();
+ classMembers.Add(cl, members);
+
+ bool hasConstructor = false;
+ foreach (MemberDecl m in cl.Members) {
+ if (members.ContainsKey(m.Name)) {
+ Error(m, "Duplicate member name: {0}", m.Name);
+ } else {
+ members.Add(m.Name, m);
+ }
+ if (m is Constructor) {
+ hasConstructor = true;
+ }
+ }
+ cl.HasConstructor = hasConstructor;
+ if (cl.IsDefaultClass) {
+ foreach (MemberDecl m in cl.Members) {
+ if (m.IsStatic && (m is Function || m is Method)) {
+ sig.StaticMembers[m.Name] = m;
+ }
+ }
+ }
+
+ } else {
+ DatatypeDecl dt = (DatatypeDecl)d;
+
+ // register the names of the constructors
+ var ctors = new Dictionary<string, DatatypeCtor>();
+ datatypeCtors.Add(dt, ctors);
+ // ... and of the other members
+ var members = new Dictionary<string, MemberDecl>();
+ datatypeMembers.Add(dt, members);
+
+ foreach (DatatypeCtor ctor in dt.Ctors) {
+ if (ctor.Name.EndsWith("?")) {
+ Error(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);
+ } else {
+ ctors.Add(ctor.Name, ctor);
+
+ // create and add the query "method" (field, really)
+ string queryName = ctor.Name + "?";
+ var query = new SpecialField(ctor.tok, queryName, "is_" + ctor.CompileName, "", "", false, false, false, Type.Bool, null);
+ query.EnclosingClass = dt; // resolve here
+ members.Add(queryName, query);
+ ctor.QueryField = query;
+
+ // also register the constructor name globally
+ Tuple<DatatypeCtor, bool> pair;
+ if (sig.Ctors.TryGetValue(ctor.Name, out pair)) {
+ // mark it as a duplicate
+ sig.Ctors[ctor.Name] = new Tuple<DatatypeCtor, bool>(pair.Item1, true);
+ } else {
+ // add new
+ sig.Ctors.Add(ctor.Name, new Tuple<DatatypeCtor, bool>(ctor, false));
+ }
+ }
+ }
+ // add deconstructors now (that is, after the query methods have been added)
+ foreach (DatatypeCtor ctor in dt.Ctors) {
+ foreach (var formal in ctor.Formals) {
+ SpecialField dtor = null;
+ if (formal.HasName) {
+ if (members.ContainsKey(formal.Name)) {
+ Error(ctor, "Name of deconstructor is used by another member of the datatype: {0}", formal.Name);
+ } else {
+ dtor = new DatatypeDestructor(formal.tok, ctor, formal, formal.Name, "dtor_" + formal.Name, "", "", formal.IsGhost, formal.Type, null);
+ dtor.EnclosingClass = dt; // resolve here
+ members.Add(formal.Name, dtor);
+ }
+ }
+ ctor.Destructors.Add(dtor);
+ }
+ }
+ }
+ }
+ return sig;
+ }
+
+ private ModuleSignature MakeAbstractSignature(ModuleSignature p, string Name, int Height, List<ModuleDefinition> mods) {
+ var mod = new ModuleDefinition(Token.NoToken, Name + ".Abs", true, true, null, null, false);
+ mod.Height = Height;
+ foreach (var kv in p.TopLevels) {
+ mod.TopLevelDecls.Add(CloneDeclaration(kv.Value, mod, mods, Name));
+ }
+ var sig = RegisterTopLevelDecls(mod, false);
+ sig.Refines = p.Refines;
+ sig.CompileSignature = p;
+ sig.IsGhost = p.IsGhost;
+ mods.Add(mod);
+ ResolveModuleDefinition(mod, sig);
+ return sig;
+ }
+ TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition m, List<ModuleDefinition> mods, string Name) {
+ Contract.Requires(d != null);
+ Contract.Requires(m != null);
+
+ if (d is ArbitraryTypeDecl) {
+ var dd = (ArbitraryTypeDecl)d;
+ return new ArbitraryTypeDecl(dd.tok, dd.Name, m, dd.EqualitySupport, null);
+ } else if (d is IndDatatypeDecl) {
+ var dd = (IndDatatypeDecl)d;
+ var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
+ var ctors = dd.Ctors.ConvertAll(CloneCtor);
+ var dt = new IndDatatypeDecl(dd.tok, dd.Name, m, tps, ctors, null);
+ 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(dd.tok, dd.Name, m, tps, ctors, null);
+ return dt;
+ } else if (d is ClassDecl) {
+ var dd = (ClassDecl)d;
+ var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
+ var mm = dd.Members.ConvertAll(CloneMember);
+ if (dd is DefaultClassDecl) {
+ return new DefaultClassDecl(m, mm);
+ } else return new ClassDecl(dd.tok, dd.Name, m, tps, mm, null);
+ } else if (d is ModuleDecl) {
+ if (d is LiteralModuleDecl) {
+ return new LiteralModuleDecl(((LiteralModuleDecl)d).ModuleDef, m);
+ } else if (d is AliasModuleDecl) {
+ var a = (AliasModuleDecl)d;
+ var alias = new AliasModuleDecl(a.Path, a.tok, m, a.Opened);
+ alias.ModuleReference = a.ModuleReference;
+ alias.Signature = a.Signature;
+ return alias;
+ } else if (d is AbstractModuleDecl) {
+ var abs = (AbstractModuleDecl)d;
+ var sig = MakeAbstractSignature(abs.OriginalSignature, Name + "." + abs.Name, abs.Height, mods);
+ var a = new AbstractModuleDecl(abs.Path, abs.tok, m, abs.CompilePath, abs.Opened);
+ a.Signature = sig;
+ a.OriginalSignature = abs.OriginalSignature;
+ return a;
+ } else {
+ Contract.Assert(false); // unexpected declaration
+ return null; // to please compiler
+ }
+ } else {
+ Contract.Assert(false); // unexpected declaration
+ return null; // to please compiler
+ }
+ }
+ MemberDecl CloneMember(MemberDecl member) {
+ if (member is Field) {
+ Contract.Assert(!(member is SpecialField)); // we don't expect a SpecialField to be cloned (or do we?)
+ var f = (Field)member;
+ return new Field(f.tok, f.Name, f.IsGhost, f.IsMutable, f.IsUserMutable, CloneType(f.Type), null);
+ } else if (member is Function) {
+ var f = (Function)member;
+ return CloneFunction(f.tok, f, f.IsGhost);
+ } else {
+ var m = (Method)member;
+ return CloneMethod(m);
+ }
+ }
+ TypeParameter CloneTypeParam(TypeParameter tp) {
+ return new TypeParameter(tp.tok, tp.Name);
+ }
+
+ DatatypeCtor CloneCtor(DatatypeCtor ct) {
+ return new DatatypeCtor(ct.tok, ct.Name, ct.Formals.ConvertAll(CloneFormal), null);
+ }
+ Formal CloneFormal(Formal formal) {
+ return new Formal(formal.tok, formal.Name, CloneType(formal.Type), formal.InParam, formal.IsGhost);
+ }
+ Type CloneType(Type t) {
+ if (t is BasicType) {
+ return t;
+ } else if (t is SetType) {
+ var tt = (SetType)t;
+ return new SetType(CloneType(tt.Arg));
+ } else if (t is SeqType) {
+ var tt = (SeqType)t;
+ return new SeqType(CloneType(tt.Arg));
+ } else if (t is MultiSetType) {
+ var tt = (MultiSetType)t;
+ return new MultiSetType(CloneType(tt.Arg));
+ } else if (t is MapType) {
+ var tt = (MapType)t;
+ return new MapType(CloneType(tt.Domain), CloneType(tt.Range));
+ } else if (t is UserDefinedType) {
+ var tt = (UserDefinedType)t;
+ return new UserDefinedType(tt.tok, tt.Name, tt.TypeArgs.ConvertAll(CloneType), tt.Path.ConvertAll(x => x));
+ } else if (t is InferredTypeProxy) {
+ return new InferredTypeProxy();
+ } else {
+ Contract.Assert(false); // unexpected type (e.g., no other type proxies are expected at this time)
+ return null; // to please compiler
+ }
+ }
+ Function CloneFunction(IToken tok, Function f, bool isGhost) {
+
+ var tps = f.TypeArgs.ConvertAll(CloneTypeParam);
+ var formals = f.Formals.ConvertAll(CloneFormal);
+ var req = f.Req.ConvertAll(CloneExpr);
+ var reads = f.Reads.ConvertAll(CloneFrameExpr);
+ var decreases = CloneSpecExpr(f.Decreases);
+
+ var ens = f.Ens.ConvertAll(CloneExpr);
+
+ Expression body = CloneExpr(f.Body);
+
+ if (f is Predicate) {
+ return new Predicate(tok, f.Name, f.IsStatic, isGhost, tps, f.OpenParen, formals,
+ req, reads, ens, decreases, body, Predicate.BodyOriginKind.OriginalOrInherited, null, false);
+ } else if (f is CoPredicate) {
+ return new CoPredicate(tok, f.Name, f.IsStatic, tps, f.OpenParen, formals,
+ req, reads, ens, body, null, false);
+ } else {
+ return new Function(tok, f.Name, f.IsStatic, isGhost, tps, f.OpenParen, formals, CloneType(f.ResultType),
+ req, reads, ens, decreases, body, null, false);
+ }
+ }
+ Method CloneMethod(Method m) {
+ Contract.Requires(m != null);
+
+ var tps = m.TypeArgs.ConvertAll(CloneTypeParam);
+ var ins = m.Ins.ConvertAll(CloneFormal);
+ var req = m.Req.ConvertAll(CloneMayBeFreeExpr);
+ var mod = CloneSpecFrameExpr(m.Mod);
+ var decreases = CloneSpecExpr(m.Decreases);
+
+ var ens = m.Ens.ConvertAll(CloneMayBeFreeExpr);
+
+ if (m is Constructor) {
+ return new Constructor(m.tok, m.Name, tps, ins,
+ req, mod, ens, decreases, null, null, false);
+ } else {
+ return new Method(m.tok, m.Name, m.IsStatic, m.IsGhost, tps, ins, m.Outs.ConvertAll(CloneFormal),
+ req, mod, ens, decreases, null, null, false);
+ }
+ }
+ Specification<Expression> CloneSpecExpr(Specification<Expression> spec) {
+ var ee = spec.Expressions == null ? null : spec.Expressions.ConvertAll(CloneExpr);
+ return new Specification<Expression>(ee, null);
+ }
+ Specification<FrameExpression> CloneSpecFrameExpr(Specification<FrameExpression> frame) {
+ var ee = frame.Expressions == null ? null : frame.Expressions.ConvertAll(CloneFrameExpr);
+ return new Specification<FrameExpression>(ee, null);
+ }
+ FrameExpression CloneFrameExpr(FrameExpression frame) {
+ return new FrameExpression(frame.tok, CloneExpr(frame.E), frame.FieldName);
+ }
+ MaybeFreeExpression CloneMayBeFreeExpr(MaybeFreeExpression expr) {
+ return new MaybeFreeExpression(CloneExpr(expr.E), expr.IsFree);
+ }
+ BoundVar CloneBoundVar(BoundVar bv) {
+ return new BoundVar(bv.tok, bv.Name, CloneType(bv.Type));
+ }
+ Expression CloneExpr(Expression expr) {
+ if (expr == null) {
+ return null;
+ } else if (expr is LiteralExpr) {
+ var e = (LiteralExpr)expr;
+ if (e.Value == null) {
+ return new LiteralExpr(e.tok);
+ } else if (e.Value is bool) {
+ return new LiteralExpr(e.tok, (bool)e.Value);
+ } else {
+ return new LiteralExpr(e.tok, (BigInteger)e.Value);
+ }
+
+ } else if (expr is ThisExpr) {
+ if (expr is ImplicitThisExpr) {
+ return new ImplicitThisExpr(expr.tok);
+ } else {
+ return new ThisExpr(expr.tok);
+ }
+
+ } else if (expr is IdentifierExpr) {
+ var e = (IdentifierExpr)expr;
+ return new IdentifierExpr(e.tok, e.Name);
+
+ } else if (expr is DatatypeValue) {
+ var e = (DatatypeValue)expr;
+ return new DatatypeValue(e.tok, e.DatatypeName, e.MemberName, e.Arguments.ConvertAll(CloneExpr));
+
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ if (expr is SetDisplayExpr) {
+ return new SetDisplayExpr(e.tok, e.Elements.ConvertAll(CloneExpr));
+ } else if (expr is MultiSetDisplayExpr) {
+ return new MultiSetDisplayExpr(e.tok, e.Elements.ConvertAll(CloneExpr));
+ } else {
+ Contract.Assert(expr is SeqDisplayExpr);
+ return new SeqDisplayExpr(e.tok, e.Elements.ConvertAll(CloneExpr));
+ }
+
+ } else if (expr is MapDisplayExpr) {
+ MapDisplayExpr e = (MapDisplayExpr)expr;
+ List<ExpressionPair> pp = new List<ExpressionPair>();
+ foreach (ExpressionPair p in e.Elements) {
+ pp.Add(new ExpressionPair(CloneExpr(p.A), CloneExpr(p.B)));
+ }
+ return new MapDisplayExpr(expr.tok, pp);
+ } else if (expr is ExprDotName) {
+ var e = (ExprDotName)expr;
+ return new ExprDotName(e.tok, CloneExpr(e.Obj), e.SuffixName);
+
+ } else if (expr is FieldSelectExpr) {
+ var e = (FieldSelectExpr)expr;
+ return new FieldSelectExpr(e.tok, CloneExpr(e.Obj), e.FieldName);
+
+ } else if (expr is SeqSelectExpr) {
+ var e = (SeqSelectExpr)expr;
+ return new SeqSelectExpr(e.tok, e.SelectOne, CloneExpr(e.Seq), CloneExpr(e.E0), CloneExpr(e.E1));
+
+ } else if (expr is MultiSelectExpr) {
+ var e = (MultiSelectExpr)expr;
+ return new MultiSelectExpr(e.tok, CloneExpr(e.Array), e.Indices.ConvertAll(CloneExpr));
+
+ } else if (expr is SeqUpdateExpr) {
+ var e = (SeqUpdateExpr)expr;
+ return new SeqUpdateExpr(e.tok, CloneExpr(e.Seq), CloneExpr(e.Index), CloneExpr(e.Value));
+
+ } else if (expr is FunctionCallExpr) {
+ var e = (FunctionCallExpr)expr;
+ return new FunctionCallExpr(e.tok, e.Name, CloneExpr(e.Receiver), e.OpenParen == null ? null : (e.OpenParen), e.Args.ConvertAll(CloneExpr));
+
+ } else if (expr is OldExpr) {
+ var e = (OldExpr)expr;
+ return new OldExpr(e.tok, CloneExpr(e.E));
+
+ } else if (expr is MultiSetFormingExpr) {
+ var e = (MultiSetFormingExpr)expr;
+ return new MultiSetFormingExpr(e.tok, CloneExpr(e.E));
+
+ } else if (expr is FreshExpr) {
+ var e = (FreshExpr)expr;
+ return new FreshExpr(e.tok, CloneExpr(e.E));
+
+ } else if (expr is UnaryExpr) {
+ var e = (UnaryExpr)expr;
+ return new UnaryExpr(e.tok, e.Op, CloneExpr(e.E));
+
+ } else if (expr is BinaryExpr) {
+ var e = (BinaryExpr)expr;
+ return new BinaryExpr(e.tok, e.Op, CloneExpr(e.E0), CloneExpr(e.E1));
+
+ } else if (expr is ChainingExpression) {
+ var e = (ChainingExpression)expr;
+ return CloneExpr(e.E); // just clone the desugaring, since it's already available
+
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ return new LetExpr(e.tok, e.Vars.ConvertAll(CloneBoundVar), e.RHSs.ConvertAll(CloneExpr), CloneExpr(e.Body));
+
+ } else if (expr is ComprehensionExpr) {
+ var e = (ComprehensionExpr)expr;
+ var tk = e.tok;
+ var bvs = e.BoundVars.ConvertAll(CloneBoundVar);
+ var range = CloneExpr(e.Range);
+ var term = CloneExpr(e.Term);
+ if (e is ForallExpr) {
+ return new ForallExpr(tk, bvs, range, term, null);
+ } else if (e is ExistsExpr) {
+ return new ExistsExpr(tk, bvs, range, term, null);
+ } else if (e is MapComprehension) {
+ return new MapComprehension(tk, bvs, range, term);
+ } else {
+ Contract.Assert(e is SetComprehension);
+ return new SetComprehension(tk, bvs, range, term);
+ }
+
+ } else if (expr is WildcardExpr) {
+ return new WildcardExpr(expr.tok);
+
+ } else if (expr is PredicateExpr) {
+ var e = (PredicateExpr)expr;
+ if (e is AssertExpr) {
+ return new AssertExpr(e.tok, CloneExpr(e.Guard), CloneExpr(e.Body));
+ } else {
+ Contract.Assert(e is AssumeExpr);
+ return new AssumeExpr(e.tok, CloneExpr(e.Guard), CloneExpr(e.Body));
+ }
+
+ } else if (expr is ITEExpr) {
+ var e = (ITEExpr)expr;
+ return new ITEExpr(e.tok, CloneExpr(e.Test), CloneExpr(e.Thn), CloneExpr(e.Els));
+
+ } else if (expr is ParensExpression) {
+ var e = (ParensExpression)expr;
+ return CloneExpr(e.E); // skip the parentheses in the clone
+
+ } else if (expr is IdentifierSequence) {
+ var e = (IdentifierSequence)expr;
+ var aa = e.Arguments == null ? null : e.Arguments.ConvertAll(CloneExpr);
+ return new IdentifierSequence(e.Tokens.ConvertAll(tk => (tk)), e.OpenParen == null ? null : (e.OpenParen), aa);
+
+ } else if (expr is MatchExpr) {
+ var e = (MatchExpr)expr;
+ return new MatchExpr(e.tok, CloneExpr(e.Source),
+ e.Cases.ConvertAll(c => new MatchCaseExpr(c.tok, c.Id, c.Arguments.ConvertAll(CloneBoundVar), CloneExpr(c.Body))));
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
+ }
+ }
+
+ private bool ResolvePath(ModuleDecl root, List<IToken> Path, out ModuleSignature p) {
+ p = root.Signature;
+ int i = 1;
+ while (i < Path.Count) {
+ ModuleSignature pp;
+ if (p.FindSubmodule(Path[i].val, out pp)) {
+ p = pp;
+ i++;
+ } else {
+ Error(Path[i], ModuleNotFoundErrorMessage(i, Path));
+ break;
+ }
+ }
+ return i == Path.Count;
+ }
+ public void ResolveTopLevelDecls_Signatures(ModuleDefinition def, List<TopLevelDecl/*!*/>/*!*/ declarations, Graph<IndDatatypeDecl/*!*/>/*!*/ datatypeDependencies) {
+ Contract.Requires(declarations != null);
+ Contract.Requires(datatypeDependencies != null); // more expensive check: Contract.Requires(cce.NonNullElements(datatypeDependencies));
+ foreach (TopLevelDecl d in declarations) {
+ Contract.Assert(d != null);
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(d.TypeArgs, true, d);
+ if (d is ArbitraryTypeDecl) {
+ // nothing to do
+ } else if (d is IteratorDecl) {
+ ResolveIteratorSignature((IteratorDecl)d);
+ } else if (d is ClassDecl) {
+ ResolveClassMemberTypes((ClassDecl)d);
+ } else if (d is ModuleDecl) {
+ var decl = (ModuleDecl)d;
+ if (!def.IsGhost) {
+ if (decl.Signature.IsGhost)
+ {
+ if (!(def.IsDefaultModule)) // _module is allowed to contain ghost modules, but not be ghost itself. Note this presents a challenge to
+ // trusted verification, as toplevels can't be trusted if they invoke ghost module members.
+ Error(d.tok, "ghost modules can only be imported into other ghost modules, not physical ones.");
+ } else {
+ // physical modules are allowed everywhere
+ }
+ } else {
+ // everything is allowed in a ghost module
+ }
+ } else {
+ ResolveCtorTypes((DatatypeDecl)d, datatypeDependencies);
+ }
+ allTypeParameters.PopMarker();
+ }
+ }
+
+ public void ResolveTopLevelDecls_Meat(List<TopLevelDecl/*!*/>/*!*/ declarations, Graph<IndDatatypeDecl/*!*/>/*!*/ datatypeDependencies) {
+ Contract.Requires(declarations != null);
+ Contract.Requires(cce.NonNullElements(datatypeDependencies));
+
+ int prevErrorCount = ErrorCount;
+
+ // Resolve the meat of classes, and the type parameters of all top-level type declarations
+ foreach (TopLevelDecl d in declarations) {
+ Contract.Assert(d != null);
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(d.TypeArgs, false, d);
+ if (d is IteratorDecl) {
+ var iter = (IteratorDecl)d;
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(iter.TypeArgs, false, iter);
+ ResolveIterator(iter);
+ allTypeParameters.PopMarker();
+ ResolveClassMemberBodies(iter); // resolve the automatically generated members
+
+ } else if (d is ClassDecl) {
+ var cl = (ClassDecl)d;
+ ResolveAttributes(cl.Attributes, false);
+ ResolveClassMemberBodies(cl);
+ }
+ allTypeParameters.PopMarker();
+ }
+
+ if (ErrorCount == prevErrorCount) {
+ foreach (TopLevelDecl d in declarations) {
+ if (d is ClassDecl) {
+ foreach (var member in ((ClassDecl)d).Members) {
+ if (member is Method) {
+ var m = (Method)member;
+ 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 (!m.IsTailRecursive && m.Body != null && Contract.Exists(m.Decreases.Expressions, e => e is WildcardExpr)) {
+ Error(m.Decreases.Expressions[0].tok, "'decreases *' is allowed only on tail-recursive methods");
+ }
+ } else if (member is Function) {
+ var f = (Function)member;
+ 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");
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Perform the stratosphere check on inductive datatypes, and compute to what extent the inductive datatypes require equality support
+ foreach (var dtd in datatypeDependencies.TopologicallySortedComponents()) {
+ if (datatypeDependencies.GetSCCRepresentative(dtd) == dtd) {
+ // do the following check once per SCC, so call it on each SCC representative
+ SccStratosphereCheck(dtd, datatypeDependencies);
+ DetermineEqualitySupport(dtd, datatypeDependencies);
+ }
+ }
+
+ if (ErrorCount == prevErrorCount) { // because CheckCoCalls requires the given expression to have been successfully resolved
+ // Perform the guardedness check on co-datatypes
+ foreach (var fn in ModuleDefinition.AllFunctions(declarations)) {
+ var module = fn.EnclosingClass.Module;
+ if (fn.Body != null && module.CallGraph.GetSCCRepresentative(fn) == fn) {
+ bool dealsWithCodatatypes = false;
+ foreach (var m in module.CallGraph.GetSCC(fn)) {
+ var f = (Function)m;
+ if (f.ResultType.InvolvesCoDatatype) {
+ dealsWithCodatatypes = true;
+ break;
+ }
+ }
+ foreach (var m in module.CallGraph.GetSCC(fn)) {
+ var f = (Function)m;
+ var checker = new CoCallResolution(f, dealsWithCodatatypes);
+ checker.CheckCoCalls(f.Body);
+ }
+ }
+ }
+ // Inferred required equality support for datatypes and for Function and Method signatures
+ // First, do datatypes until a fixpoint is reached
+ bool inferredSomething;
+ do {
+ inferredSomething = false;
+ foreach (var d in declarations) {
+ if (d is DatatypeDecl) {
+ var dt = (DatatypeDecl)d;
+ foreach (var tp in dt.TypeArgs) {
+ if (tp.EqualitySupport == TypeParameter.EqualitySupportValue.Unspecified) {
+ // here's our chance to infer the need for equality support
+ foreach (var ctor in dt.Ctors) {
+ foreach (var arg in ctor.Formals) {
+ if (InferRequiredEqualitySupport(tp, arg.Type)) {
+ tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
+ inferredSomething = true;
+ goto DONE_DT; // break out of the doubly-nested loop
+ }
+ }
+ }
+ DONE_DT: ;
+ }
+ }
+ }
+ }
+ } while (inferredSomething);
+ // Now do it for Function and Method signatures
+ foreach (var d in declarations) {
+ if (d is IteratorDecl) {
+ var iter = (IteratorDecl)d;
+ foreach (var tp in iter.TypeArgs) {
+ if (tp.EqualitySupport == TypeParameter.EqualitySupportValue.Unspecified) {
+ // here's our chance to infer the need for equality support
+ foreach (var p in iter.Ins) {
+ if (InferRequiredEqualitySupport(tp, p.Type)) {
+ tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
+ goto DONE;
+ }
+ }
+ foreach (var p in iter.Outs) {
+ if (InferRequiredEqualitySupport(tp, p.Type)) {
+ tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
+ goto DONE;
+ }
+ }
+ DONE: ;
+ }
+ }
+ } else if (d is ClassDecl) {
+ var cl = (ClassDecl)d;
+ foreach (var member in cl.Members) {
+ if (!member.IsGhost) {
+ if (member is Function) {
+ var f = (Function)member;
+ foreach (var tp in f.TypeArgs) {
+ if (tp.EqualitySupport == TypeParameter.EqualitySupportValue.Unspecified) {
+ // here's our chance to infer the need for equality support
+ if (InferRequiredEqualitySupport(tp, f.ResultType)) {
+ tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
+ } else {
+ foreach (var p in f.Formals) {
+ if (InferRequiredEqualitySupport(tp, p.Type)) {
+ tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
+ break;
+ }
+ }
+ }
+ }
+ }
+ } else if (member is Method) {
+ var m = (Method)member;
+ foreach (var tp in m.TypeArgs) {
+ if (tp.EqualitySupport == TypeParameter.EqualitySupportValue.Unspecified) {
+ // here's our chance to infer the need for equality support
+ foreach (var p in m.Ins) {
+ if (InferRequiredEqualitySupport(tp, p.Type)) {
+ tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
+ goto DONE;
+ }
+ }
+ foreach (var p in m.Outs) {
+ if (InferRequiredEqualitySupport(tp, p.Type)) {
+ tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
+ goto DONE;
+ }
+ }
+ DONE: ;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ // Check that all == and != operators in non-ghost contexts are applied to equality-supporting types.
+ // Note that this check can only be done after determining which expressions are ghosts.
+ foreach (var d in declarations) {
+ if (d is IteratorDecl) {
+ var iter = (IteratorDecl)d;
+ foreach (var p in iter.Ins) {
+ if (!p.IsGhost) {
+ CheckEqualityTypes_Type(p.tok, p.Type);
+ }
+ }
+ foreach (var p in iter.Outs) {
+ if (!p.IsGhost) {
+ CheckEqualityTypes_Type(p.tok, p.Type);
+ }
+ }
+ if (iter.Body != null) {
+ CheckEqualityTypes_Stmt(iter.Body);
+ }
+ } else if (d is ClassDecl) {
+ var cl = (ClassDecl)d;
+ foreach (var member in cl.Members) {
+ if (!member.IsGhost) {
+ if (member is Field) {
+ var f = (Field)member;
+ CheckEqualityTypes_Type(f.tok, f.Type);
+ } else if (member is Function) {
+ var f = (Function)member;
+ foreach (var p in f.Formals) {
+ if (!p.IsGhost) {
+ CheckEqualityTypes_Type(p.tok, p.Type);
+ }
+ }
+ CheckEqualityTypes_Type(f.tok, f.ResultType);
+ if (f.Body != null) {
+ CheckEqualityTypes(f.Body);
+ }
+ } else if (member is Method) {
+ var m = (Method)member;
+ foreach (var p in m.Ins) {
+ if (!p.IsGhost) {
+ CheckEqualityTypes_Type(p.tok, p.Type);
+ }
+ }
+ foreach (var p in m.Outs) {
+ if (!p.IsGhost) {
+ CheckEqualityTypes_Type(p.tok, p.Type);
+ }
+ }
+ if (m.Body != null) {
+ CheckEqualityTypes_Stmt(m.Body);
+ }
+ }
+ }
+ }
+ } else if (d is DatatypeDecl) {
+ var dt = (DatatypeDecl)d;
+ foreach (var ctor in dt.Ctors) {
+ foreach (var p in ctor.Formals) {
+ if (!p.IsGhost) {
+ CheckEqualityTypes_Type(p.tok, p.Type);
+ }
+ }
+ }
+ }
+ }
+ // Check that copredicates are not recursive with non-copredicate functions.
+ foreach (var fn in ModuleDefinition.AllFunctions(declarations)) {
+ if (fn.Body != null && (fn is CoPredicate || fn.IsRecursive)) {
+ CoPredicateChecks(fn.Body, fn, CallingPosition.Positive);
+ }
+ }
+ }
+ }
+
+ enum TailRecursionStatus
+ {
+ NotTailRecursive, // contains code that makes the enclosing method body not tail recursive (in way that is supported)
+ CanBeFollowedByAnything, // the code just analyzed does not do any recursive calls
+ TailCallSpent, // the method body is tail recursive, provided that all code that follows it in the method body is ghost
+ }
+
+ /// <summary>
+ /// Checks if "stmts" can be considered tail recursive, and (provided "reportsError" is true) reports an error if not.
+ /// Note, the current implementation is rather conservative in its analysis; upon need, the
+ /// algorithm could be improved.
+ /// In the current implementation, "enclosingMethod" is not allowed to be a mutually recursive method.
+ ///
+ /// The incoming value of "tailCall" is not used, but it's nevertheless a 'ref' parameter to allow the
+ /// body to return the incoming value or to omit assignments to it.
+ /// If the return value is CanBeFollowedByAnything, "tailCall" is unchanged.
+ /// If the return value is TailCallSpent, "tailCall" shows one of the calls where the tail call was spent. (Note,
+ /// there could be several if the statements have branches.)
+ /// If the return value is NoTailRecursive, "tailCall" could be anything. In this case, an error
+ /// message has been reported (provided "reportsErrors" is true).
+ /// </summary>
+ TailRecursionStatus CheckTailRecursive(List<Statement> stmts, Method enclosingMethod, ref CallStmt tailCall, bool reportErrors) {
+ Contract.Requires(stmts != null);
+ var status = TailRecursionStatus.CanBeFollowedByAnything;
+ foreach (var s in stmts) {
+ if (!s.IsGhost) {
+ if (s is ReturnStmt && ((ReturnStmt)s).hiddenUpdate == null) {
+ return status;
+ }
+ 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");
+ }
+ return TailRecursionStatus.NotTailRecursive;
+ }
+ status = CheckTailRecursive(s, enclosingMethod, ref tailCall, reportErrors);
+ if (status == TailRecursionStatus.NotTailRecursive) {
+ return status;
+ }
+ }
+ }
+ return status;
+ }
+
+ /// <summary>
+ /// See CheckTailRecursive(List Statement, ...), including its description of "tailCall".
+ /// In the current implementation, "enclosingMethod" is not allowed to be a mutually recursive method.
+ /// </summary>
+ TailRecursionStatus CheckTailRecursive(Statement stmt, Method enclosingMethod, ref CallStmt tailCall, bool reportErrors) {
+ Contract.Requires(stmt != null && !stmt.IsGhost);
+ if (stmt is PrintStmt) {
+ } else if (stmt is BreakStmt) {
+ } else if (stmt is ReturnStmt) {
+ var s = (ReturnStmt)stmt;
+ if (s.hiddenUpdate != null) {
+ return CheckTailRecursive(s.hiddenUpdate, enclosingMethod, ref tailCall, reportErrors);
+ }
+ } else if (stmt is AssignStmt) {
+ } else if (stmt is VarDecl) {
+ } else if (stmt is CallStmt) {
+ var s = (CallStmt)stmt;
+ if (s.Method == enclosingMethod) {
+ // It's a recursive call. It can be considered a tail call only if the LHS of the call are the
+ // formal out-parameters of the method
+ for (int i = 0; i < s.Lhs.Count; i++) {
+ var formal = enclosingMethod.Outs[i];
+ if (!formal.IsGhost) {
+ var lhs = s.Lhs[i] as IdentifierExpr;
+ if (lhs != null && lhs.Var == formal) {
+ // 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);
+ }
+ return TailRecursionStatus.NotTailRecursive;
+ }
+ }
+ }
+ tailCall = s;
+ return TailRecursionStatus.TailCallSpent;
+ }
+ } else if (stmt is BlockStmt) {
+ var s = (BlockStmt)stmt;
+ return CheckTailRecursive(s.Body, enclosingMethod, ref tailCall, reportErrors);
+ } else if (stmt is IfStmt) {
+ var s = (IfStmt)stmt;
+ var stThen = CheckTailRecursive(s.Thn, enclosingMethod, ref tailCall, reportErrors);
+ if (stThen == TailRecursionStatus.NotTailRecursive) {
+ return stThen;
+ }
+ var stElse = s.Els == null ? TailRecursionStatus.CanBeFollowedByAnything : CheckTailRecursive(s.Els, enclosingMethod, ref tailCall, reportErrors);
+ if (stElse == TailRecursionStatus.NotTailRecursive) {
+ return stElse;
+ } else if (stThen == TailRecursionStatus.TailCallSpent || stElse == TailRecursionStatus.TailCallSpent) {
+ return TailRecursionStatus.TailCallSpent;
+ }
+ } else if (stmt is AlternativeStmt) {
+ var s = (AlternativeStmt)stmt;
+ var status = TailRecursionStatus.CanBeFollowedByAnything;
+ foreach (var alt in s.Alternatives) {
+ var st = CheckTailRecursive(alt.Body, enclosingMethod, ref tailCall, reportErrors);
+ if (st == TailRecursionStatus.NotTailRecursive) {
+ return st;
+ } else if (st == TailRecursionStatus.TailCallSpent) {
+ status = st;
+ }
+ }
+ return status;
+ } else if (stmt is WhileStmt) {
+ var s = (WhileStmt)stmt;
+ var status = CheckTailRecursive(s.Body, enclosingMethod, ref tailCall, reportErrors);
+ if (status != TailRecursionStatus.CanBeFollowedByAnything) {
+ 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");
+ }
+ return TailRecursionStatus.NotTailRecursive;
+ }
+ } else if (stmt is AlternativeLoopStmt) {
+ var s = (AlternativeLoopStmt)stmt;
+ foreach (var alt in s.Alternatives) {
+ var status = CheckTailRecursive(alt.Body, enclosingMethod, ref tailCall, reportErrors);
+ if (status != TailRecursionStatus.CanBeFollowedByAnything) {
+ 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");
+ }
+ return TailRecursionStatus.NotTailRecursive;
+ }
+ }
+ } else if (stmt is ParallelStmt) {
+ var s = (ParallelStmt)stmt;
+ var status = CheckTailRecursive(s.Body, enclosingMethod, ref tailCall, reportErrors);
+ if (status != TailRecursionStatus.CanBeFollowedByAnything) {
+ if (status == TailRecursionStatus.NotTailRecursive) {
+ // an error has already been reported
+ } else if (reportErrors) {
+ Error(tailCall.Tok, "a recursive call inside a parallel statement is not a tail call");
+ }
+ return TailRecursionStatus.NotTailRecursive;
+ }
+ } else if (stmt is MatchStmt) {
+ var s = (MatchStmt)stmt;
+ var status = TailRecursionStatus.CanBeFollowedByAnything;
+ foreach (var kase in s.Cases) {
+ var st = CheckTailRecursive(kase.Body, enclosingMethod, ref tailCall, reportErrors);
+ if (st == TailRecursionStatus.NotTailRecursive) {
+ return st;
+ } else if (st == TailRecursionStatus.TailCallSpent) {
+ status = st;
+ }
+ }
+ return status;
+ } else if (stmt is AssignSuchThatStmt) {
+ } else if (stmt is ConcreteSyntaxStatement) {
+ var s = (ConcreteSyntaxStatement)stmt;
+ return CheckTailRecursive(s.ResolvedStatements, enclosingMethod, ref tailCall, reportErrors);
+ } else {
+ Contract.Assert(false); // unexpected statement type
+ }
+ return TailRecursionStatus.CanBeFollowedByAnything;
+ }
+
+ enum CallingPosition { Positive, Negative, Neither }
+
+ static CallingPosition Invert(CallingPosition cp) {
+ switch (cp) {
+ case CallingPosition.Positive: return CallingPosition.Negative;
+ case CallingPosition.Negative: return CallingPosition.Positive;
+ default: return CallingPosition.Neither;
+ }
+ }
+
+ void CoPredicateChecks(Expression expr, Function context, CallingPosition cp) {
+ Contract.Requires(expr != null);
+ Contract.Requires(context != null);
+ if (expr is ConcreteSyntaxExpression) {
+ var e = (ConcreteSyntaxExpression)expr;
+ CoPredicateChecks(e.Resolved, context, cp);
+ return;
+ } else if (expr is FunctionCallExpr) {
+ var e = (FunctionCallExpr)expr;
+ var moduleCaller = context.EnclosingClass.Module;
+ var moduleCallee = e.Function.EnclosingClass.Module;
+ if (moduleCaller == moduleCallee && moduleCaller.CallGraph.GetSCCRepresentative(context) == moduleCaller.CallGraph.GetSCCRepresentative(e.Function)) {
+ // we're looking at a recursive call
+ if (context is CoPredicate) {
+ if (!(e.Function is CoPredicate)) {
+ Error(e, "a recursive call from a copredicate can go only to other copredicates");
+ } else if (cp != CallingPosition.Positive) {
+ Error(e, "a recursive copredicate call can only be done in positive positions");
+ }
+ } else if (e.Function is CoPredicate) {
+ Error(e, "a recursive call from a non-copredicate can go only to other non-copredicates");
+ }
+ }
+ // fall through to do the subexpressions
+ } else if (expr is UnaryExpr) {
+ var e = (UnaryExpr)expr;
+ if (e.Op == UnaryExpr.Opcode.Not) {
+ CoPredicateChecks(e.E, context, Invert(cp));
+ return;
+ }
+ } else if (expr is BinaryExpr) {
+ var e = (BinaryExpr)expr;
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.And:
+ case BinaryExpr.ResolvedOpcode.Or:
+ CoPredicateChecks(e.E0, context, cp);
+ CoPredicateChecks(e.E1, context, cp);
+ return;
+ case BinaryExpr.ResolvedOpcode.Imp:
+ CoPredicateChecks(e.E0, context, Invert(cp));
+ CoPredicateChecks(e.E1, context, cp);
+ return;
+ default:
+ break;
+ }
+ } else if (expr is MatchExpr) {
+ var e = (MatchExpr)expr;
+ CoPredicateChecks(e.Source, context, CallingPosition.Neither);
+ e.Cases.Iter(kase => CoPredicateChecks(kase.Body, context, cp));
+ return;
+ } else if (expr is ITEExpr) {
+ var e = (ITEExpr)expr;
+ CoPredicateChecks(e.Test, context, CallingPosition.Neither);
+ CoPredicateChecks(e.Thn, context, cp);
+ CoPredicateChecks(e.Els, context, cp);
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ CoPredicateChecks(e.Body, context, cp);
+ return;
+ } else if (expr is QuantifierExpr) {
+ var e = (QuantifierExpr)expr;
+ if (e.Range != null) {
+ CoPredicateChecks(e.Range, context, e is ExistsExpr ? Invert(cp) : cp);
+ }
+ CoPredicateChecks(e.Term, context, cp);
+ return;
+ }
+ expr.SubExpressions.Iter(ee => CoPredicateChecks(ee, context, CallingPosition.Neither));
+ }
+
+ void CheckEqualityTypes_Stmt(Statement stmt) {
+ Contract.Requires(stmt != null);
+ if (stmt.IsGhost) {
+ return;
+ } else if (stmt is PrintStmt) {
+ var s = (PrintStmt)stmt;
+ foreach (var arg in s.Args) {
+ if (arg.E != null) {
+ CheckEqualityTypes(arg.E);
+ }
+ }
+ } else if (stmt is BreakStmt) {
+ } else if (stmt is ProduceStmt) {
+ var s = (ProduceStmt)stmt;
+ if (s.rhss != null) {
+ s.rhss.Iter(CheckEqualityTypes_Rhs);
+ }
+ } else if (stmt is AssignStmt) {
+ AssignStmt s = (AssignStmt)stmt;
+ CheckEqualityTypes(s.Lhs);
+ CheckEqualityTypes_Rhs(s.Rhs);
+ } else if (stmt is VarDecl) {
+ var s = (VarDecl)stmt;
+ s.SubStatements.Iter(CheckEqualityTypes_Stmt);
+ } else if (stmt is CallStmt) {
+ var s = (CallStmt)stmt;
+ CheckEqualityTypes(s.Receiver);
+ s.Args.Iter(CheckEqualityTypes);
+ s.Lhs.Iter(CheckEqualityTypes);
+
+ Contract.Assert(s.Method.TypeArgs.Count <= s.TypeArgumentSubstitutions.Count);
+ var i = 0;
+ foreach (var formalTypeArg in s.Method.TypeArgs) {
+ var actualTypeArg = s.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));
+ }
+ i++;
+ }
+ } else if (stmt is BlockStmt) {
+ var s = (BlockStmt)stmt;
+ s.Body.Iter(CheckEqualityTypes_Stmt);
+ } else if (stmt is IfStmt) {
+ var s = (IfStmt)stmt;
+ if (s.Guard != null) {
+ CheckEqualityTypes(s.Guard);
+ }
+ s.SubStatements.Iter(CheckEqualityTypes_Stmt);
+ } else if (stmt is AlternativeStmt) {
+ var s = (AlternativeStmt)stmt;
+ foreach (var alt in s.Alternatives) {
+ CheckEqualityTypes(alt.Guard);
+ alt.Body.Iter(CheckEqualityTypes_Stmt);
+ }
+ } else if (stmt is WhileStmt) {
+ var s = (WhileStmt)stmt;
+ if (s.Guard != null) {
+ CheckEqualityTypes(s.Guard);
+ }
+ CheckEqualityTypes_Stmt(s.Body);
+ } else if (stmt is AlternativeLoopStmt) {
+ var s = (AlternativeLoopStmt)stmt;
+ foreach (var alt in s.Alternatives) {
+ CheckEqualityTypes(alt.Guard);
+ alt.Body.Iter(CheckEqualityTypes_Stmt);
+ }
+ } else if (stmt is ParallelStmt) {
+ var s = (ParallelStmt)stmt;
+ CheckEqualityTypes(s.Range);
+ CheckEqualityTypes_Stmt(s.Body);
+ } else if (stmt is MatchStmt) {
+ var s = (MatchStmt)stmt;
+ CheckEqualityTypes(s.Source);
+ foreach (MatchCaseStmt mc in s.Cases) {
+ mc.Body.Iter(CheckEqualityTypes_Stmt);
+ }
+ } else if (stmt is ConcreteSyntaxStatement) {
+ var s = (ConcreteSyntaxStatement)stmt;
+ s.ResolvedStatements.Iter(CheckEqualityTypes_Stmt);
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement
+ }
+ }
+
+ void CheckEqualityTypes_Rhs(AssignmentRhs rhs) {
+ Contract.Requires(rhs != null);
+ rhs.SubExpressions.Iter(CheckEqualityTypes);
+ rhs.SubStatements.Iter(CheckEqualityTypes_Stmt);
+ }
+
+ void CheckEqualityTypes(Expression expr) {
+ Contract.Requires(expr != null);
+ if (expr is BinaryExpr) {
+ var e = (BinaryExpr)expr;
+ var t0 = e.E0.Type.Normalize();
+ var t1 = e.E1.Type.Normalize();
+ switch (e.Op) {
+ case BinaryExpr.Opcode.Eq:
+ case BinaryExpr.Opcode.Neq:
+ // First, check a special case: a datatype value (like Nil) that takes no parameters
+ var e0 = e.E0.Resolved as DatatypeValue;
+ var e1 = e.E1.Resolved as DatatypeValue;
+ if (e0 != null && e0.Arguments.Count == 0) {
+ // that's cool
+ } 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));
+ } 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));
+ }
+ break;
+ default:
+ switch (e.ResolvedOp) {
+ // Note, all operations on sets, multisets, and maps are guaranteed to work because of restrictions placed on how
+ // these types are instantiated. (Except: This guarantee does not apply to equality on maps, because the Range type
+ // of maps is not restricted, only the Domain type. However, the equality operator is checked above.)
+ case BinaryExpr.ResolvedOpcode.InSeq:
+ case BinaryExpr.ResolvedOpcode.NotInSeq:
+ 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));
+ } 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));
+ } 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));
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ } else if (expr is ComprehensionExpr) {
+ var e = (ComprehensionExpr)expr;
+ foreach (var bv in e.BoundVars) {
+ CheckEqualityTypes_Type(bv.tok, bv.Type);
+ }
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ foreach (var bv in e.Vars) {
+ CheckEqualityTypes_Type(bv.tok, bv.Type);
+ }
+ } else if (expr is FunctionCallExpr) {
+ var e = (FunctionCallExpr)expr;
+ Contract.Assert(e.Function.TypeArgs.Count <= e.TypeArgumentSubstitutions.Count);
+ var i = 0;
+ 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));
+ }
+ i++;
+ }
+ }
+
+ foreach (var ee in expr.SubExpressions) {
+ CheckEqualityTypes(ee);
+ }
+ }
+
+ void CheckEqualityTypes_Type(IToken tok, Type type) {
+ Contract.Requires(tok != null);
+ Contract.Requires(type != null);
+ type = type.Normalize();
+ if (type is BasicType) {
+ // fine
+ } else if (type is SetType) {
+ var argType = ((SetType)type).Arg;
+ if (!argType.SupportsEquality) {
+ Error(tok, "set argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType));
+ }
+ 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));
+ }
+ CheckEqualityTypes_Type(tok, argType);
+
+ } else if (type is MapType) {
+ var mt = (MapType)type;
+ if (!mt.Domain.SupportsEquality) {
+ Error(tok, "map domain type must support equality (got {0}){1}", mt.Domain, TypeEqualityErrorMessageHint(mt.Domain));
+ }
+ CheckEqualityTypes_Type(tok, mt.Domain);
+ CheckEqualityTypes_Type(tok, mt.Range);
+
+ } else if (type is SeqType) {
+ Type argType = ((SeqType)type).Arg;
+ CheckEqualityTypes_Type(tok, argType);
+
+ } else if (type is UserDefinedType) {
+ var udt = (UserDefinedType)type;
+ if (udt.ResolvedClass != null) {
+ Contract.Assert(udt.ResolvedClass.TypeArgs.Count == udt.TypeArgs.Count);
+ var i = 0;
+ foreach (var argType in udt.TypeArgs) {
+ var formalTypeArg = udt.ResolvedClass.TypeArgs[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));
+ }
+ CheckEqualityTypes_Type(tok, argType);
+ i++;
+ }
+ } else {
+ Contract.Assert(udt.TypeArgs.Count == 0); // TypeParameters have no type arguments
+ }
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
+ }
+ }
+
+ bool CheckTypeInference(Expression e) {
+ if (e == null) return false;
+ foreach (Expression se in e.SubExpressions) {
+ if (CheckTypeInference(se))
+ return true;
+ }
+ if (e.Type is TypeProxy && !(e.Type is InferredTypeProxy || e.Type is ParamTypeProxy || e.Type is ObjectTypeProxy)) {
+ Error(e.tok, "the type of this expression is underspecified, but it cannot be an arbitrary type.");
+ return true;
+ }
+ return false;
+ }
+ void CheckTypeInference(Statement stmt) {
+ Contract.Requires(stmt != null);
+ if (stmt is PrintStmt) {
+ var s = (PrintStmt)stmt;
+ s.Args.Iter(arg => CheckTypeInference(arg.E));
+ } else if (stmt is BreakStmt) {
+ } else if (stmt is ProduceStmt) {
+ var s = (ProduceStmt)stmt;
+ if (s.rhss != null) {
+ s.rhss.Iter(rhs => rhs.SubExpressions.Iter(e => CheckTypeInference(e)));
+ }
+ } else if (stmt is AssignStmt) {
+ AssignStmt s = (AssignStmt)stmt;
+ CheckTypeInference(s.Lhs);
+ s.Rhs.SubExpressions.Iter(e => { CheckTypeInference(e); });
+ } else if (stmt is VarDecl) {
+ var s = (VarDecl)stmt;
+ s.SubStatements.Iter(CheckTypeInference);
+ if (s.Type is TypeProxy && !(s.Type is InferredTypeProxy || s.Type is ParamTypeProxy || s.Type is ObjectTypeProxy)) {
+ Error(s.Tok, "the type of this expression is underspecified, but it cannot be an arbitrary type.");
+ }
+ } else if (stmt is CallStmt) {
+ var s = (CallStmt)stmt;
+ CheckTypeInference(s.Receiver);
+ s.Args.Iter(e => CheckTypeInference(e));
+ s.Lhs.Iter(e => CheckTypeInference(e));
+ } else if (stmt is BlockStmt) {
+ var s = (BlockStmt)stmt;
+ s.Body.Iter(CheckTypeInference);
+ } else if (stmt is IfStmt) {
+ var s = (IfStmt)stmt;
+ if (s.Guard != null) {
+ CheckTypeInference(s.Guard);
+ }
+ s.SubStatements.Iter(CheckTypeInference);
+ } else if (stmt is AlternativeStmt) {
+ var s = (AlternativeStmt)stmt;
+ foreach (var alt in s.Alternatives) {
+ CheckTypeInference(alt.Guard);
+ alt.Body.Iter(CheckTypeInference);
+ }
+ } else if (stmt is WhileStmt) {
+ var s = (WhileStmt)stmt;
+ if (s.Guard != null) {
+ CheckTypeInference(s.Guard);
+ }
+ CheckTypeInference(s.Body);
+ } else if (stmt is AlternativeLoopStmt) {
+ var s = (AlternativeLoopStmt)stmt;
+ foreach (var alt in s.Alternatives) {
+ CheckTypeInference(alt.Guard);
+ alt.Body.Iter(CheckTypeInference);
+ }
+ } else if (stmt is ParallelStmt) {
+ var s = (ParallelStmt)stmt;
+ CheckTypeInference(s.Range);
+ CheckTypeInference(s.Body);
+ } else if (stmt is CalcStmt) {
+ // NadiaToDo: is this correct?
+ var s = (CalcStmt)stmt;
+ s.SubExpressions.Iter(e => CheckTypeInference(e));
+ s.SubStatements.Iter(CheckTypeInference);
+ } else if (stmt is MatchStmt) {
+ var s = (MatchStmt)stmt;
+ CheckTypeInference(s.Source);
+ foreach (MatchCaseStmt mc in s.Cases) {
+ mc.Body.Iter(CheckTypeInference);
+ }
+ } else if (stmt is ConcreteSyntaxStatement) {
+ var s = (ConcreteSyntaxStatement)stmt;
+ s.ResolvedStatements.Iter(CheckTypeInference);
+ } else if (stmt is PredicateStmt) {
+ CheckTypeInference(((PredicateStmt)stmt).Expr);
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement
+ }
+ }
+
+ string TypeEqualityErrorMessageHint(Type argType) {
+ Contract.Requires(argType != null);
+ var tp = argType.AsTypeParameter;
+ if (tp != null) {
+ return string.Format(" (perhaps try declaring type parameter '{0}' on line {1} as '{0}(==)', which says it can only be instantiated with a type that supports equality)", tp.Name, tp.tok.line);
+ }
+ return "";
+ }
+
+ bool InferRequiredEqualitySupport(TypeParameter tp, Type type) {
+ Contract.Requires(tp != null);
+ Contract.Requires(type != null);
+
+ type = type.Normalize();
+ if (type is BasicType) {
+ } else if (type is SetType) {
+ var st = (SetType)type;
+ return st.Arg.AsTypeParameter == tp || InferRequiredEqualitySupport(tp, st.Arg);
+ } else if (type is MultiSetType) {
+ var ms = (MultiSetType)type;
+ return ms.Arg.AsTypeParameter == tp || InferRequiredEqualitySupport(tp, ms.Arg);
+ } else if (type is MapType) {
+ var mt = (MapType)type;
+ return mt.Domain.AsTypeParameter == tp || InferRequiredEqualitySupport(tp, mt.Domain) || InferRequiredEqualitySupport(tp, mt.Range);
+ } else if (type is SeqType) {
+ var sq = (SeqType)type;
+ return InferRequiredEqualitySupport(tp, sq.Arg);
+ } else if (type is UserDefinedType) {
+ var udt = (UserDefinedType)type;
+ if (udt.ResolvedClass != null) {
+ var i = 0;
+ foreach (var argType in udt.TypeArgs) {
+ var formalTypeArg = udt.ResolvedClass.TypeArgs[i];
+ if ((formalTypeArg.MustSupportEquality && argType.AsTypeParameter == tp) || InferRequiredEqualitySupport(tp, argType)) {
+ return true;
+ }
+ i++;
+ }
+ } else {
+ Contract.Assert(udt.TypeArgs.Count == 0); // TypeParameters have no type arguments
+ }
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
+ }
+ return false;
+ }
+
+ ClassDecl currentClass;
+ Function currentFunction;
+ readonly Scope<TypeParameter>/*!*/ allTypeParameters = new Scope<TypeParameter>();
+ 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>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveClassMemberTypes(ClassDecl/*!*/ cl) {
+ Contract.Requires(cl != null);
+ Contract.Requires(currentClass == null);
+ Contract.Ensures(currentClass == null);
+
+ currentClass = cl;
+ foreach (MemberDecl member in cl.Members) {
+ member.EnclosingClass = cl;
+ if (member is Field) {
+ ResolveType(member.tok, ((Field)member).Type, null, false);
+
+ } else if (member is Function) {
+ Function f = (Function)member;
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(f.TypeArgs, true, f);
+ ResolveFunctionSignature(f);
+ allTypeParameters.PopMarker();
+
+ } else if (member is Method) {
+ Method m = (Method)member;
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(m.TypeArgs, true, m);
+ ResolveMethodSignature(m);
+ allTypeParameters.PopMarker();
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member type
+ }
+ }
+ currentClass = null;
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed, and that all types in class members have been resolved
+ /// </summary>
+ void ResolveClassMemberBodies(ClassDecl cl) {
+ Contract.Requires(cl != null);
+ Contract.Requires(currentClass == null);
+ Contract.Ensures(currentClass == null);
+
+ currentClass = cl;
+ foreach (MemberDecl member in cl.Members) {
+ if (member is Field) {
+ ResolveAttributes(member.Attributes, false);
+ // nothing more to do
+
+ } else if (member is Function) {
+ Function f = (Function)member;
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(f.TypeArgs, false, f);
+ ResolveFunction(f);
+ allTypeParameters.PopMarker();
+
+ } else if (member is Method) {
+ Method m = (Method)member;
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(m.TypeArgs, false, m);
+ ResolveMethod(m);
+ allTypeParameters.PopMarker();
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member type
+ }
+ }
+ currentClass = null;
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveCtorTypes(DatatypeDecl/*!*/ dt, Graph<IndDatatypeDecl/*!*/>/*!*/ dependencies) {
+ Contract.Requires(dt != null);
+ Contract.Requires(dependencies != null); // more expensive check: Contract.Requires(cce.NonNullElements(dependencies));
+ foreach (DatatypeCtor ctor in dt.Ctors) {
+
+ ctor.EnclosingDatatype = dt;
+
+ allTypeParameters.PushMarker();
+ ResolveCtorSignature(ctor, dt.TypeArgs);
+ allTypeParameters.PopMarker();
+
+ if (dt is IndDatatypeDecl) {
+ var idt = (IndDatatypeDecl)dt;
+ dependencies.AddVertex(idt);
+ foreach (Formal p in ctor.Formals) {
+ AddDatatypeDependencyEdge(idt, p.Type, dependencies);
+ }
+ }
+ }
+ }
+
+ void AddDatatypeDependencyEdge(IndDatatypeDecl/*!*/ dt, Type/*!*/ tp, Graph<IndDatatypeDecl/*!*/>/*!*/ dependencies) {
+ Contract.Requires(dt != null);
+ Contract.Requires(tp != null);
+ Contract.Requires(dependencies != null); // more expensive check: Contract.Requires(cce.NonNullElements(dependencies));
+
+ var dependee = tp.AsIndDatatype;
+ if (dependee != null && dt.Module == dependee.Module) {
+ dependencies.AddEdge((IndDatatypeDecl)dt, dependee);
+ foreach (var ta in ((UserDefinedType)tp).TypeArgs) {
+ AddDatatypeDependencyEdge(dt, ta, dependencies);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Check that the SCC of 'startingPoint' can be carved up into stratospheres in such a way that each
+ /// datatype has some value that can be constructed from datatypes in lower stratospheres only.
+ /// The algorithm used here is quadratic in the number of datatypes in the SCC. Since that number is
+ /// deemed to be rather small, this seems okay.
+ ///
+ /// As a side effect of this checking, the DefaultCtor field is filled in (for every inductive datatype
+ /// that passes the check). It may be that several constructors could be used as the default, but
+ /// only the first one encountered as recorded. This particular choice is slightly more than an
+ /// implementation detail, because it affects how certain cycles among inductive datatypes (having
+ /// to do with the types used to instantiate type parameters of datatypes) are used.
+ ///
+ /// The role of the SCC here is simply to speed up this method. It would still be correct if the
+ /// equivalence classes in the given SCC were unions of actual SCC's. In particular, this method
+ /// would still work if "dependencies" consisted of one large SCC containing all the inductive
+ /// datatypes in the module.
+ /// </summary>
+ void SccStratosphereCheck(IndDatatypeDecl startingPoint, Graph<IndDatatypeDecl/*!*/>/*!*/ dependencies) {
+ Contract.Requires(startingPoint != null);
+ Contract.Requires(dependencies != null); // more expensive check: Contract.Requires(cce.NonNullElements(dependencies));
+
+ var scc = dependencies.GetSCC(startingPoint);
+ int totalCleared = 0;
+ while (true) {
+ int clearedThisRound = 0;
+ foreach (var dt in scc) {
+ if (dt.DefaultCtor != null) {
+ // previously cleared
+ } else if (ComputeDefaultCtor(dt)) {
+ Contract.Assert(dt.DefaultCtor != null); // should have been set by the successful call to StratosphereCheck)
+ clearedThisRound++;
+ totalCleared++;
+ }
+ }
+ if (totalCleared == scc.Count) {
+ // all is good
+ return;
+ } else if (clearedThisRound != 0) {
+ // some progress was made, so let's keep going
+ } else {
+ // 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);
+ }
+ }
+ return;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Check that the datatype has some constructor all whose argument types can be constructed.
+ /// Returns 'true' and sets dt.DefaultCtor if that is the case.
+ /// </summary>
+ bool ComputeDefaultCtor(IndDatatypeDecl dt) {
+ Contract.Requires(dt != null);
+ Contract.Requires(dt.DefaultCtor == null); // the intention is that this method be called only when DefaultCtor hasn't already been set
+ 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.
+ foreach (DatatypeCtor ctor in dt.Ctors) {
+ var 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;
+ dt.TypeParametersUsedInConstructionByDefaultCtor = new bool[dt.TypeArgs.Count];
+ for (int i = 0; i < dt.TypeArgs.Count; i++) {
+ dt.TypeParametersUsedInConstructionByDefaultCtor[i] = typeParametersUsed.Contains(dt.TypeArgs[i]);
+ }
+ return true;
+ NEXT_OUTER_ITERATION: { }
+ }
+ // no constructor satisfied the requirements, so this is an illegal datatype declaration
+ return false;
+ }
+
+ bool CheckCanBeConstructed(Type tp, List<TypeParameter> typeParametersUsed) {
+ var dependee = tp.AsIndDatatype;
+ if (dependee == null) {
+ // the type is not an inductive datatype, which means it is always possible to construct it
+ if (tp.IsTypeParameter) {
+ typeParametersUsed.Add(((UserDefinedType)tp).ResolvedParam);
+ }
+ return true;
+ } else if (dependee.DefaultCtor == null) {
+ // the type is an inductive datatype that we don't yet know how to construct
+ return false;
+ }
+ // also check the type arguments of the inductive datatype
+ Contract.Assert(((UserDefinedType)tp).TypeArgs.Count == dependee.TypeParametersUsedInConstructionByDefaultCtor.Length);
+ var i = 0;
+ foreach (var ta in ((UserDefinedType)tp).TypeArgs) { // note, "tp" is known to be a UserDefinedType, because that follows from tp being an inductive datatype
+ if (dependee.TypeParametersUsedInConstructionByDefaultCtor[i] && !CheckCanBeConstructed(ta, typeParametersUsed)) {
+ return false;
+ }
+ i++;
+ }
+ return true;
+ }
+
+ void DetermineEqualitySupport(IndDatatypeDecl startingPoint, Graph<IndDatatypeDecl/*!*/>/*!*/ dependencies) {
+ Contract.Requires(startingPoint != null);
+ Contract.Requires(dependencies != null); // more expensive check: Contract.Requires(cce.NonNullElements(dependencies));
+
+ var scc = dependencies.GetSCC(startingPoint);
+ // First, the simple case: If any parameter of any inductive datatype in the SCC is of a codatatype type, then
+ // the whole SCC is incapable of providing the equality operation.
+ foreach (var dt in scc) {
+ Contract.Assume(dt.EqualitySupport == IndDatatypeDecl.ES.NotYetComputed);
+ foreach (var ctor in dt.Ctors) {
+ foreach (var arg in ctor.Formals) {
+ var anotherIndDt = arg.Type.AsIndDatatype;
+ if ((anotherIndDt != null && anotherIndDt.EqualitySupport == IndDatatypeDecl.ES.Never) || arg.Type.IsCoDatatype) {
+ // arg.Type is known never to support equality
+ // So, go around the entire SCC and record what we learnt
+ foreach (var ddtt in scc) {
+ ddtt.EqualitySupport = IndDatatypeDecl.ES.Never;
+ }
+ return; // we are done
+ }
+ }
+ }
+ }
+
+ // Now for the more involved case: we need to determine which type parameters determine equality support for each datatype in the SCC
+ // We start by seeing where each datatype's type parameters are used in a place known to determine equality support.
+ bool thingsChanged = false;
+ foreach (var dt in scc) {
+ if (dt.TypeArgs.Count == 0) {
+ // if the datatype has no type parameters, we certainly won't find any type parameters being used in the arguments types to the constructors
+ continue;
+ }
+ foreach (var ctor in dt.Ctors) {
+ foreach (var arg in ctor.Formals) {
+ var typeArg = arg.Type.AsTypeParameter;
+ if (typeArg != null) {
+ typeArg.NecessaryForEqualitySupportOfSurroundingInductiveDatatype = true;
+ thingsChanged = true;
+ } else {
+ var otherDt = arg.Type.AsIndDatatype;
+ if (otherDt != null && otherDt.EqualitySupport == IndDatatypeDecl.ES.ConsultTypeArguments) { // datatype is in a different SCC
+ var otherUdt = (UserDefinedType)arg.Type.Normalize();
+ var i = 0;
+ foreach (var otherTp in otherDt.TypeArgs) {
+ if (otherTp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype) {
+ var tp = otherUdt.TypeArgs[i].AsTypeParameter;
+ if (tp != null) {
+ tp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype = true;
+ thingsChanged = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ // Then we propagate this information up through the SCC
+ while (thingsChanged) {
+ thingsChanged = false;
+ foreach (var dt in scc) {
+ if (dt.TypeArgs.Count == 0) {
+ // if the datatype has no type parameters, we certainly won't find any type parameters being used in the arguments types to the constructors
+ continue;
+ }
+ foreach (var ctor in dt.Ctors) {
+ foreach (var arg in ctor.Formals) {
+ var otherDt = arg.Type.AsIndDatatype;
+ if (otherDt != null && otherDt.EqualitySupport == IndDatatypeDecl.ES.NotYetComputed) { // otherDt lives in the same SCC
+ var otherUdt = (UserDefinedType)arg.Type.Normalize();
+ var i = 0;
+ foreach (var otherTp in otherDt.TypeArgs) {
+ if (otherTp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype) {
+ var tp = otherUdt.TypeArgs[i].AsTypeParameter;
+ if (tp != null && !tp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype) {
+ tp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype = true;
+ thingsChanged = true;
+ }
+ }
+ i++;
+ }
+ }
+ }
+ }
+ }
+ }
+ // Now that we have computed the .NecessaryForEqualitySupportOfSurroundingInductiveDatatype values, mark the datatypes as ones
+ // where equality support should be checked by looking at the type arguments.
+ foreach (var dt in scc) {
+ dt.EqualitySupport = IndDatatypeDecl.ES.ConsultTypeArguments;
+ }
+ }
+
+ void ResolveAttributes(Attributes attrs, bool twoState) {
+ // 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, twoState, true);
+ }
+ }
+ }
+
+ void ResolveAttributeArgs(List<Attributes.Argument/*!*/>/*!*/ args, bool twoState, bool allowGhosts) {
+ Contract.Requires(args != null);
+ foreach (Attributes.Argument aa in args) {
+ Contract.Assert(aa != null);
+ if (aa.E != null) {
+ ResolveExpression(aa.E, twoState);
+ if (!allowGhosts) {
+ CheckIsNonGhost(aa.E);
+ }
+ }
+ }
+ }
+
+ void ResolveTypeParameters(List<TypeParameter/*!*/>/*!*/ tparams, bool emitErrors, TypeParameter.ParentType/*!*/ parent) {
+ Contract.Requires(tparams != null);
+ Contract.Requires(parent != null);
+ // push non-duplicated type parameter names
+ int index = 0;
+ foreach (TypeParameter tp in tparams) {
+ Contract.Assert(tp != null);
+ if (emitErrors) {
+ // we're seeing this TypeParameter for the first time
+ tp.Parent = parent;
+ tp.PositionalIndex = index;
+ }
+ if (!allTypeParameters.Push(tp.Name, tp) && emitErrors) {
+ Error(tp, "Duplicate type-parameter name: {0}", tp.Name);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveFunctionSignature(Function f) {
+ Contract.Requires(f != null);
+ scope.PushMarker();
+ if (f.SignatureIsOmitted) {
+ Error(f, "function signature can be omitted only in refining functions");
+ }
+ var defaultTypeArguments = f.TypeArgs.Count == 0 ? f.TypeArgs : null;
+ foreach (Formal p in f.Formals) {
+ if (!scope.Push(p.Name, p)) {
+ Error(p, "Duplicate parameter name: {0}", p.Name);
+ }
+ ResolveType(p.tok, p.Type, defaultTypeArguments, true);
+ }
+ ResolveType(f.tok, f.ResultType, defaultTypeArguments, true);
+ scope.PopMarker();
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveFunction(Function f) {
+ Contract.Requires(f != null);
+ Contract.Requires(currentFunction == null);
+ Contract.Ensures(currentFunction == null);
+ scope.PushMarker();
+ currentFunction = f;
+ if (f.IsStatic) {
+ scope.AllowInstance = false;
+ }
+ foreach (Formal p in f.Formals) {
+ scope.Push(p.Name, p);
+ }
+ ResolveAttributes(f.Attributes, false);
+ foreach (Expression r in f.Req) {
+ ResolveExpression(r, 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);
+ }
+ }
+ foreach (FrameExpression fr in f.Reads) {
+ ResolveFrameExpression(fr, "reads");
+ }
+ foreach (Expression r in f.Ens) {
+ ResolveExpression(r, 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);
+ }
+ }
+ foreach (Expression r in f.Decreases.Expressions) {
+ ResolveExpression(r, false);
+ // any type is fine
+ }
+ if (f.Body != null) {
+ var prevErrorCount = ErrorCount;
+ List<IVariable> matchVarContext = new List<IVariable>(f.Formals);
+ ResolveExpression(f.Body, false, matchVarContext);
+ if (!f.IsGhost) {
+ CheckIsNonGhost(f.Body);
+ }
+ 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);
+ }
+ }
+ currentFunction = null;
+ scope.PopMarker();
+ }
+
+ void ResolveFrameExpression(FrameExpression fe, string kind) {
+ Contract.Requires(fe != null);
+ Contract.Requires(kind != null);
+ ResolveExpression(fe.E, false);
+ Type t = fe.E.Type;
+ Contract.Assert(t != null); // follows from postcondition of ResolveExpression
+ if (t is CollectionType) {
+ t = ((CollectionType)t).Arg;
+ }
+ if (t is ObjectType) {
+ // fine, as long as there's no field name
+ if (fe.FieldName != null) {
+ Error(fe.E, "type '{0}' does not contain a field named '{1}'", t, fe.FieldName);
+ }
+ } else if (UserDefinedType.DenotesClass(t) != null) {
+ // fine type
+ if (fe.FieldName != null) {
+ NonProxyType nptype;
+ MemberDecl member = ResolveMember(fe.E.tok, t, fe.FieldName, out nptype);
+ UserDefinedType ctype = (UserDefinedType)nptype; // correctness of cast follows from the DenotesClass test above
+ 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, cce.NonNull(ctype).Name);
+ } else {
+ Contract.Assert(ctype != null && ctype.ResolvedClass != null); // follows from postcondition of ResolveMember
+ fe.Field = (Field)member;
+ }
+ }
+ } else {
+ Error(fe.E, "a {0}-clause expression must denote an object or a collection of objects (instead got {1})", kind, fe.E.Type);
+ }
+ }
+
+ /// <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");
+ }
+ var defaultTypeArguments = m.TypeArgs.Count == 0 ? m.TypeArgs : null;
+ // resolve in-parameters
+ foreach (Formal p in m.Ins) {
+ if (!scope.Push(p.Name, p)) {
+ Error(p, "Duplicate parameter name: {0}", p.Name);
+ }
+ ResolveType(p.tok, p.Type, defaultTypeArguments, true);
+ }
+ // resolve out-parameters
+ foreach (Formal p in m.Outs) {
+ if (!scope.Push(p.Name, p)) {
+ Error(p, "Duplicate parameter name: {0}", p.Name);
+ }
+ ResolveType(p.tok, p.Type, defaultTypeArguments, true);
+ }
+ scope.PopMarker();
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveMethod(Method m) {
+ Contract.Requires(m != null);
+
+ // Add in-parameters to the scope, but don't care about any duplication errors, since they have already been reported
+ scope.PushMarker();
+ if (m.IsStatic) {
+ scope.AllowInstance = false;
+ }
+ foreach (Formal p in m.Ins) {
+ scope.Push(p.Name, p);
+ }
+
+ // Start resolving specification...
+ foreach (MaybeFreeExpression e in m.Req) {
+ ResolveExpression(e.E, 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);
+ }
+ }
+
+ foreach (FrameExpression fe in m.Mod.Expressions) {
+ ResolveFrameExpression(fe, "modifies");
+ }
+
+ foreach (Expression e in m.Decreases.Expressions) {
+ ResolveExpression(e, 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();
+ foreach (Formal p in m.Outs) {
+ scope.Push(p.Name, p);
+ }
+
+ // attributes are allowed to mention both in- and out-parameters
+ ResolveAttributes(m.Attributes, false);
+
+ // ... continue resolving specification
+ foreach (MaybeFreeExpression e in m.Ens) {
+ ResolveExpression(e.E, 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);
+ }
+ }
+
+ // Resolve body
+ if (m.Body != null) {
+ ResolveBlockStatement(m.Body, m.IsGhost, m);
+ }
+
+ scope.PopMarker(); // for the out-parameters and outermost-level locals
+ scope.PopMarker(); // for the in-parameters
+ }
+
+ void ResolveCtorSignature(DatatypeCtor ctor, List<TypeParameter> dtTypeArguments) {
+ Contract.Requires(ctor != null);
+ Contract.Requires(dtTypeArguments != null);
+ foreach (Formal p in ctor.Formals) {
+ ResolveType(p.tok, p.Type, dtTypeArguments, false);
+ }
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveIteratorSignature(IteratorDecl iter) {
+ Contract.Requires(iter != null);
+ scope.PushMarker();
+ if (iter.SignatureIsOmitted) {
+ Error(iter, "iterator signature can be omitted only in refining methods");
+ }
+ var defaultTypeArguments = iter.TypeArgs.Count == 0 ? iter.TypeArgs : null;
+ // resolve the types of the parameters
+ foreach (var p in iter.Ins.Concat(iter.Outs)) {
+ ResolveType(p.tok, p.Type, defaultTypeArguments, true);
+ }
+ // resolve the types of the added fields (in case some of these types would cause the addition of default type arguments)
+ foreach (var p in iter.OutsHistoryFields) {
+ ResolveType(p.tok, p.Type, defaultTypeArguments, true);
+ }
+ scope.PopMarker();
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveIterator(IteratorDecl iter) {
+ Contract.Requires(iter != null);
+ Contract.Requires(currentClass == null);
+ Contract.Ensures(currentClass == null);
+
+ var initialErrorCount = ErrorCount;
+
+ // Add in-parameters to the scope, but don't care about any duplication errors, since they have already been reported
+ scope.PushMarker();
+ scope.AllowInstance = false; // disallow 'this' from use, which means that the special fields and methods added are not accessible in the syntactically given spec
+ iter.Ins.ForEach(p => scope.Push(p.Name, p));
+
+ // Start resolving specification...
+ // we start with the decreases clause, because the _decreases<n> fields were only given type proxies before; we'll know
+ // the types only after resolving the decreases clause (and it may be that some of resolution has already seen uses of
+ // these fields; so, with no further ado, here we go
+ 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, 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);
+ }
+ }
+ foreach (FrameExpression fe in iter.Reads.Expressions) {
+ ResolveFrameExpression(fe, "reads");
+ }
+ foreach (FrameExpression fe in iter.Modifies.Expressions) {
+ ResolveFrameExpression(fe, "modifies");
+ }
+ foreach (MaybeFreeExpression e in iter.Requires) {
+ ResolveExpression(e.E, 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);
+ }
+ }
+
+ scope.PopMarker(); // for the in-parameters
+
+ // We resolve the rest of the specification in an instance context. So mentions of the in- or yield-parameters
+ // get resolved as field dereferences (with an implicit "this")
+ scope.PushMarker();
+ currentClass = iter;
+ Contract.Assert(scope.AllowInstance);
+
+ foreach (MaybeFreeExpression e in iter.YieldRequires) {
+ ResolveExpression(e.E, 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);
+ }
+ }
+ foreach (MaybeFreeExpression e in iter.YieldEnsures) {
+ ResolveExpression(e.E, 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);
+ }
+ }
+ foreach (MaybeFreeExpression e in iter.Ensures) {
+ ResolveExpression(e.E, 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);
+ }
+ }
+
+ ResolveAttributes(iter.Attributes, false);
+
+ var postSpecErrorCount = ErrorCount;
+
+ // Resolve body
+ if (iter.Body != null) {
+ ResolveBlockStatement(iter.Body, false, iter);
+ }
+
+ currentClass = null;
+ scope.PopMarker(); // pop off the AllowInstance setting
+
+ if (postSpecErrorCount == initialErrorCount) {
+ CreateIteratorMethodSpecs(iter);
+ }
+ }
+
+ /// <summary>
+ /// Assumes the specification of the iterator itself has been successfully resolved.
+ /// </summary>
+ void CreateIteratorMethodSpecs(IteratorDecl iter) {
+ Contract.Requires(iter != null);
+
+ // ---------- here comes the constructor ----------
+ // same requires clause as the iterator itself
+ iter.Member_Init.Req.AddRange(iter.Requires);
+ // modifies this;
+ iter.Member_Init.Mod.Expressions.Add(new FrameExpression(iter.tok, new ThisExpr(iter.tok), null));
+ var ens = iter.Member_Init.Ens;
+ foreach (var p in iter.Ins) {
+ // ensures this.x == x;
+ ens.Add(new MaybeFreeExpression(new BinaryExpr(p.tok, BinaryExpr.Opcode.Eq,
+ new FieldSelectExpr(p.tok, new ThisExpr(p.tok), p.Name), new IdentifierExpr(p.tok, p.Name))));
+ }
+ foreach (var p in iter.OutsHistoryFields) {
+ // ensures this.ys == [];
+ ens.Add(new MaybeFreeExpression(new BinaryExpr(p.tok, BinaryExpr.Opcode.Eq,
+ new FieldSelectExpr(p.tok, new ThisExpr(p.tok), p.Name), new SeqDisplayExpr(p.tok, new List<Expression>()))));
+ }
+ // ensures this.Valid();
+ ens.Add(new MaybeFreeExpression(new FunctionCallExpr(iter.tok, "Valid", new ThisExpr(iter.tok), iter.tok, new List<Expression>())));
+ // ensures this._reads == old(ReadsClause);
+ var modSetSingletons = new List<Expression>();
+ Expression frameSet = new SetDisplayExpr(iter.tok, 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");
+ } else if (fr.E.Type.IsRefType) {
+ modSetSingletons.Add(fr.E);
+ } else {
+ frameSet = new BinaryExpr(fr.tok, BinaryExpr.Opcode.Add, frameSet, fr.E);
+ }
+ }
+ ens.Add(new MaybeFreeExpression(new BinaryExpr(iter.tok, BinaryExpr.Opcode.Eq,
+ new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_reads"),
+ new OldExpr(iter.tok, frameSet))));
+ // ensures this._modifies == old(ModifiesClause);
+ modSetSingletons = new List<Expression>();
+ frameSet = new SetDisplayExpr(iter.tok, 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");
+ } else if (fr.E.Type.IsRefType) {
+ modSetSingletons.Add(fr.E);
+ } else {
+ frameSet = new BinaryExpr(fr.tok, BinaryExpr.Opcode.Add, frameSet, fr.E);
+ }
+ }
+ ens.Add(new MaybeFreeExpression(new BinaryExpr(iter.tok, BinaryExpr.Opcode.Eq,
+ new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_modifies"),
+ new OldExpr(iter.tok, frameSet))));
+ // ensures this._new == {};
+ ens.Add(new MaybeFreeExpression(new BinaryExpr(iter.tok, BinaryExpr.Opcode.Eq,
+ new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_new"),
+ new SetDisplayExpr(iter.tok, 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++) {
+ var p = iter.Decreases.Expressions[i];
+ ens.Add(new MaybeFreeExpression(new BinaryExpr(iter.tok, BinaryExpr.Opcode.Eq,
+ new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), iter.DecreasesFields[i].Name),
+ new OldExpr(iter.tok, p))));
+ }
+
+ // ---------- here comes predicate Valid() ----------
+ var reads = iter.Member_Valid.Reads;
+ reads.Add(new FrameExpression(iter.tok, new ThisExpr(iter.tok), null)); // reads this;
+ reads.Add(new FrameExpression(iter.tok, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_reads"), null)); // reads this._reads;
+ reads.Add(new FrameExpression(iter.tok, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_new"), null)); // reads this._new;
+
+ // ---------- here comes method MoveNext() ----------
+ // requires this.Valid();
+ var req = iter.Member_MoveNext.Req;
+ req.Add(new MaybeFreeExpression(new FunctionCallExpr(iter.tok, "Valid", new ThisExpr(iter.tok), iter.tok, new List<Expression>())));
+ // requires YieldRequires;
+ req.AddRange(iter.YieldRequires);
+ // modifies this, this._modifies, this._new;
+ var mod = iter.Member_MoveNext.Mod.Expressions;
+ mod.Add(new FrameExpression(iter.tok, new ThisExpr(iter.tok), null));
+ mod.Add(new FrameExpression(iter.tok, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_modifies"), null));
+ mod.Add(new FrameExpression(iter.tok, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_new"), null));
+ // ensures fresh(_new - old(_new));
+ ens = iter.Member_MoveNext.Ens;
+ ens.Add(new MaybeFreeExpression(new FreshExpr(iter.tok,
+ new BinaryExpr(iter.tok, BinaryExpr.Opcode.Sub,
+ new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_new"),
+ new OldExpr(iter.tok, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_new"))))));
+ // ensures more ==> this.Valid();
+ ens.Add(new MaybeFreeExpression(new BinaryExpr(iter.tok, BinaryExpr.Opcode.Imp,
+ new IdentifierExpr(iter.tok, "more"),
+ new FunctionCallExpr(iter.tok, "Valid", new ThisExpr(iter.tok), iter.tok, new List<Expression>()))));
+ // ensures this.ys == if more then old(this.ys) + [this.y] else old(this.ys);
+ Contract.Assert(iter.OutsFields.Count == iter.OutsHistoryFields.Count);
+ for (int i = 0; i < iter.OutsFields.Count; i++) {
+ var y = iter.OutsFields[i];
+ var ys = iter.OutsHistoryFields[i];
+ var ite = new ITEExpr(iter.tok, new IdentifierExpr(iter.tok, "more"),
+ new BinaryExpr(iter.tok, BinaryExpr.Opcode.Add,
+ new OldExpr(iter.tok, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), ys.Name)),
+ new SeqDisplayExpr(iter.tok, new List<Expression>() { new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), y.Name) })),
+ new OldExpr(iter.tok, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), ys.Name)));
+ var eq = new BinaryExpr(iter.tok, BinaryExpr.Opcode.Eq, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), ys.Name), ite);
+ ens.Add(new MaybeFreeExpression(eq));
+ }
+ // ensures more ==> YieldEnsures;
+ foreach (var ye in iter.YieldEnsures) {
+ ens.Add(new MaybeFreeExpression(
+ new BinaryExpr(iter.tok, BinaryExpr.Opcode.Imp, new IdentifierExpr(iter.tok, "more"), ye.E),
+ ye.IsFree));
+ }
+ // ensures !more ==> Ensures;
+ foreach (var e in iter.Ensures) {
+ ens.Add(new MaybeFreeExpression(new BinaryExpr(iter.tok, BinaryExpr.Opcode.Imp,
+ new UnaryExpr(iter.tok, UnaryExpr.Opcode.Not, new IdentifierExpr(iter.tok, "more")),
+ e.E),
+ e.IsFree));
+ }
+ // decreases this._decreases0, this._decreases1, ...;
+ Contract.Assert(iter.Decreases.Expressions.Count == iter.DecreasesFields.Count);
+ for (int i = 0; i < iter.Decreases.Expressions.Count; i++) {
+ var p = iter.Decreases.Expressions[i];
+ iter.Member_MoveNext.Decreases.Expressions.Add(new FieldSelectExpr(p.tok, new ThisExpr(p.tok), iter.DecreasesFields[i].Name));
+ }
+ iter.Member_MoveNext.Decreases.Attributes = iter.Decreases.Attributes;
+ }
+
+ /// <summary>
+ /// If ResolveType encounters a type "T" that takes type arguments but wasn't given any, then:
+ /// * If "defaultTypeArguments" is non-null and "defaultTypeArgument.Count" equals the number
+ /// of type arguments that "T" expects, then use these default type arguments as "T"'s arguments.
+ /// * If "allowAutoTypeArguments" is true, then infer "T"'s arguments.
+ /// * If "defaultTypeArguments" is non-null AND "allowAutoTypeArguments" is true, then enough
+ /// type parameters will be added to "defaultTypeArguments" to have at least as many type
+ /// parameters as "T" expects, and then a prefix of the "defaultTypeArguments" will be supplied
+ /// as arguments to "T".
+ /// </summary>
+ public void ResolveType(IToken tok, Type type, List<TypeParameter> defaultTypeArguments, bool allowAutoTypeArguments) {
+ Contract.Requires(tok != null);
+ Contract.Requires(type != null);
+ if (type is BasicType) {
+ // nothing to resolve
+ } else if (type is MapType) {
+ MapType mt = (MapType)type;
+ ResolveType(tok, mt.Domain, defaultTypeArguments, allowAutoTypeArguments);
+ ResolveType(tok, mt.Range, defaultTypeArguments, allowAutoTypeArguments);
+ if (mt.Domain.IsSubrangeType || mt.Range.IsSubrangeType) {
+ Error(tok, "sorry, cannot instantiate collection type with a subrange type");
+ }
+ } else if (type is CollectionType) {
+ var t = (CollectionType)type;
+ var argType = t.Arg;
+ ResolveType(tok, argType, defaultTypeArguments, allowAutoTypeArguments);
+ if (argType.IsSubrangeType) {
+ Error(tok, "sorry, cannot instantiate collection type with a subrange type");
+ }
+ } else if (type is UserDefinedType) {
+ UserDefinedType t = (UserDefinedType)type;
+ foreach (Type tt in t.TypeArgs) {
+ ResolveType(t.tok, tt, defaultTypeArguments, allowAutoTypeArguments);
+ if (tt.IsSubrangeType) {
+ Error(t.tok, "sorry, cannot instantiate type parameter with a subrange type");
+ }
+ }
+ TypeParameter tp = t.Path.Count == 0 ? allTypeParameters.Find(t.Name) : null;
+ if (tp != null) {
+ if (t.TypeArgs.Count == 0) {
+ t.ResolvedParam = tp;
+ } else {
+ Error(t.tok, "Type parameter expects no type arguments: {0}", t.Name);
+ }
+ } else if (t.ResolvedClass == null) { // this test is because 'array' is already resolved; TODO: an alternative would be to pre-populate 'classes' with built-in references types like 'array' (and perhaps in the future 'string')
+ TopLevelDecl d = null;
+
+ int j = 0;
+ var sig = moduleInfo;
+ while (j < t.Path.Count) {
+ if (sig.FindSubmodule(t.Path[j].val, out sig)) {
+ j++;
+ sig = GetSignature(sig);
+ } else {
+ Error(t.Path[j], ModuleNotFoundErrorMessage(j, t.Path));
+ break;
+ }
+ }
+ if (j == t.Path.Count) {
+ if (!sig.TopLevels.TryGetValue(t.Name, out d)) {
+ if (j == 0)
+ Error(t.tok, "Undeclared top-level type or type parameter: {0} (did you forget to qualify a name?)", t.Name);
+ else
+ Error(t.tok, "Undeclared type {0} in module {1}", t.Name, t.Path[t.Path.Count - 1].val);
+ }
+ } else {
+ // error has already been reported
+ }
+
+ if (d == null) {
+ // error has been reported above
+ } else if (d is AmbiguousTopLevelDecl) {
+ Error(t.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", t.Name, ((AmbiguousTopLevelDecl)d).ModuleNames());
+ } else if (d is ArbitraryTypeDecl) {
+ t.ResolvedParam = ((ArbitraryTypeDecl)d).TheType; // resolve like a type parameter
+ } else {
+ // d is a class or datatype, and it may have type parameters
+ t.ResolvedClass = d;
+ if (d.TypeArgs.Count != t.TypeArgs.Count && t.TypeArgs.Count == 0) {
+ if (allowAutoTypeArguments && defaultTypeArguments == null) {
+ // add type arguments that will be inferred
+ for (int i = 0; i < d.TypeArgs.Count; i++) {
+ t.TypeArgs.Add(new InferredTypeProxy());
+ }
+ } else if (defaultTypeArguments != null) {
+ // add specific type arguments, drawn from defaultTypeArguments (which may have to be extended)
+ if (allowAutoTypeArguments) {
+ // add to defaultTypeArguments the necessary number of arguments
+ for (int i = defaultTypeArguments.Count; i < d.TypeArgs.Count; i++) {
+ defaultTypeArguments.Add(new TypeParameter(t.tok, "_T" + i));
+ }
+ }
+ if (allowAutoTypeArguments || d.TypeArgs.Count == defaultTypeArguments.Count) {
+ Contract.Assert(d.TypeArgs.Count <= defaultTypeArguments.Count);
+ // automatically supply a prefix of the arguments from defaultTypeArguments
+ for (int i = 0; i < d.TypeArgs.Count; i++) {
+ var typeArg = new UserDefinedType(t.tok, defaultTypeArguments[i].Name, new List<Type>(), null);
+ typeArg.ResolvedParam = defaultTypeArguments[i]; // resolve "typeArg" here
+ t.TypeArgs.Add(typeArg);
+ }
+ }
+ }
+ }
+ // 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 class/datatype: {2}", t.TypeArgs.Count, d.TypeArgs.Count, t.Name);
+ }
+ }
+ }
+
+ } else if (type is TypeProxy) {
+ TypeProxy t = (TypeProxy)type;
+ if (t.T != null) {
+ ResolveType(tok, t.T, defaultTypeArguments, allowAutoTypeArguments);
+ }
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
+ }
+ }
+
+ public bool UnifyTypes(Type a, Type b) {
+ Contract.Requires(a != null);
+ Contract.Requires(b != null);
+ while (a is TypeProxy) {
+ TypeProxy proxy = (TypeProxy)a;
+ if (proxy.T == null) {
+ // merge a and b; to avoid cycles, first get to the bottom of b
+ while (b is TypeProxy && ((TypeProxy)b).T != null) {
+ b = ((TypeProxy)b).T;
+ }
+ return AssignProxy(proxy, b);
+ } else {
+ a = proxy.T;
+ }
+ }
+
+ while (b is TypeProxy) {
+ TypeProxy proxy = (TypeProxy)b;
+ if (proxy.T == null) {
+ // merge a and b (we have already got to the bottom of a)
+ return AssignProxy(proxy, a);
+ } else {
+ b = proxy.T;
+ }
+ }
+
+#if !NO_CHEAP_OBJECT_WORKAROUND
+ if (a is ObjectType || b is ObjectType) { // TODO: remove this temporary hack
+ var other = a is ObjectType ? b : a;
+ if (other is BoolType || other is IntType || other is SetType || other is SeqType || other.IsDatatype) {
+ return false;
+ }
+ // allow anything else with object; this is BOGUS
+ return true;
+ }
+#endif
+ // Now, a and b are non-proxies and stand for the same things as the original a and b, respectively.
+
+ if (a is BoolType) {
+ return b is BoolType;
+ } else if (a is IntType) {
+ return b is IntType;
+ } 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);
+ } else if (a is MultiSetType) {
+ return b is MultiSetType && UnifyTypes(((MultiSetType)a).Arg, ((MultiSetType)b).Arg);
+ } else if (a is MapType) {
+ return b is MapType && 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;
+ }
+ UserDefinedType aa = (UserDefinedType)a;
+ UserDefinedType bb = (UserDefinedType)b;
+ if (aa.ResolvedClass != null && aa.ResolvedClass == bb.ResolvedClass) {
+ // these are both resolved class/datatype types
+ Contract.Assert(aa.TypeArgs.Count == bb.TypeArgs.Count);
+ bool successSoFar = true;
+ for (int i = 0; i < aa.TypeArgs.Count; i++) {
+ if (!UnifyTypes(aa.TypeArgs[i], bb.TypeArgs[i])) {
+ successSoFar = false;
+ }
+ }
+ return successSoFar;
+ } else if (aa.ResolvedParam != null && aa.ResolvedParam == bb.ResolvedParam) {
+ // these are both resolved type parameters
+ Contract.Assert(aa.TypeArgs.Count == 0 && bb.TypeArgs.Count == 0);
+ return true;
+ } else {
+ // something is wrong; either aa or bb wasn't properly resolved, or they don't unify
+ return false;
+ }
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
+ }
+ }
+
+ bool AssignProxy(TypeProxy proxy, Type t) {
+ Contract.Requires(proxy != null);
+ Contract.Requires(t != null);
+ Contract.Requires(proxy.T == null);
+ Contract.Requires(!(t is TypeProxy) || ((TypeProxy)t).T == null);
+ //modifies proxy.T, ((TypeProxy)t).T; // might also change t.T if t is a proxy
+ Contract.Ensures(Contract.Result<bool>() == (proxy == t || proxy.T != null || (t is TypeProxy && ((TypeProxy)t).T != null)));
+ if (proxy == t) {
+ // they are already in the same equivalence class
+ return true;
+
+ } else if (proxy is UnrestrictedTypeProxy) {
+ // it's fine to redirect proxy to t (done below)
+
+ } else if (t is UnrestrictedTypeProxy) {
+ // merge proxy and t by redirecting t to proxy, rather than the other way around
+ ((TypeProxy)t).T = proxy;
+ return true;
+
+ } else if (t is RestrictedTypeProxy) {
+ // Both proxy and t are restricted type proxies. To simplify unification, order proxy and t
+ // according to their types.
+ RestrictedTypeProxy r0 = (RestrictedTypeProxy)proxy;
+ RestrictedTypeProxy r1 = (RestrictedTypeProxy)t;
+ if (r0.OrderID <= r1.OrderID) {
+ return AssignRestrictedProxies(r0, r1);
+ } else {
+ return AssignRestrictedProxies(r1, r0);
+ }
+
+ // In the remaining cases, proxy is a restricted proxy and t is a non-proxy
+ } else if (proxy is DatatypeProxy) {
+ if (t.IsIndDatatype) {
+ // all is fine, proxy can be redirected to t
+ } else {
+ return false;
+ }
+
+ } else if (proxy is ObjectTypeProxy) {
+ if (t is ObjectType || UserDefinedType.DenotesClass(t) != null) {
+ // all is fine, proxy can be redirected to t
+ } else {
+ return false;
+ }
+
+ } else if (proxy is CollectionTypeProxy) {
+ CollectionTypeProxy collProxy = (CollectionTypeProxy)proxy;
+ if (t is CollectionType) {
+ if (!UnifyTypes(collProxy.Arg, ((CollectionType)t).Arg)) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ } else if (proxy is OperationTypeProxy) {
+ OperationTypeProxy opProxy = (OperationTypeProxy)proxy;
+ if (t is IntType || t is SetType || t is MultiSetType || (opProxy.AllowSeq && t is SeqType)) {
+ // this is the expected case
+ } else {
+ return false;
+ }
+
+ } else if (proxy is IndexableTypeProxy) {
+ IndexableTypeProxy iProxy = (IndexableTypeProxy)proxy;
+ if (t is SeqType) {
+ if (!UnifyTypes(iProxy.Arg, ((SeqType)t).Arg)) {
+ return false;
+ }
+ if (!UnifyTypes(iProxy.Domain, Type.Int)) {
+ return false;
+ }
+ } else if (t.IsArrayType && (t.AsArrayType).Dims == 1) {
+ Type elType = UserDefinedType.ArrayElementType(t);
+ if (!UnifyTypes(iProxy.Arg, elType)) {
+ return false;
+ }
+ if (!UnifyTypes(iProxy.Domain, Type.Int)) {
+ return false;
+ }
+ } else if (t is MapType) {
+ if (!UnifyTypes(iProxy.Arg, ((MapType)t).Range)) {
+ return false;
+ }
+ if (!UnifyTypes(iProxy.Domain, ((MapType)t).Domain)) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected proxy type
+ }
+
+ // do the merge, but never infer a subrange type
+ if (t is NatType) {
+ proxy.T = Type.Int;
+ } else {
+ proxy.T = t;
+ }
+ return true;
+ }
+
+ bool AssignRestrictedProxies(RestrictedTypeProxy a, RestrictedTypeProxy b) {
+ Contract.Requires(a != null);
+ Contract.Requires(b != null);
+ Contract.Requires(a != b);
+ Contract.Requires(a.T == null && b.T == null);
+ Contract.Requires(a.OrderID <= b.OrderID);
+ //modifies a.T, b.T;
+ Contract.Ensures(!Contract.Result<bool>() || a.T != null || b.T != null);
+
+ if (a is DatatypeProxy) {
+ if (b is DatatypeProxy) {
+ // all is fine
+ a.T = b;
+ return true;
+ } else {
+ return false;
+ }
+ } else if (a is ObjectTypeProxy) {
+ if (b is ObjectTypeProxy) {
+ // all is fine
+ a.T = b;
+ return true;
+ } else if (b is IndexableTypeProxy) {
+ // the intersection of ObjectTypeProxy and IndexableTypeProxy is an array type
+ a.T = builtIns.ArrayType(1, ((IndexableTypeProxy)b).Arg);
+ b.T = a.T;
+ return true;
+ } else {
+ return false;
+ }
+
+ } else if (a is CollectionTypeProxy) {
+ if (b is CollectionTypeProxy) {
+ a.T = b;
+ return UnifyTypes(((CollectionTypeProxy)a).Arg, ((CollectionTypeProxy)b).Arg);
+ } else if (b is OperationTypeProxy) {
+ if (((OperationTypeProxy)b).AllowSeq) {
+ b.T = a; // a is a stronger constraint than b
+ } else {
+ // a says set<T>,seq<T> and b says int,set; the intersection is set<T>
+ a.T = new SetType(((CollectionTypeProxy)a).Arg);
+ b.T = a.T;
+ }
+ return true;
+ } else if (b is IndexableTypeProxy) {
+ CollectionTypeProxy pa = (CollectionTypeProxy)a;
+ IndexableTypeProxy pb = (IndexableTypeProxy)b;
+ // a and b could be a map or a sequence
+ return true;
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected restricted-proxy type
+ }
+
+ } else if (a is OperationTypeProxy) {
+ OperationTypeProxy pa = (OperationTypeProxy)a;
+ if (b is OperationTypeProxy) {
+ if (!pa.AllowSeq || ((OperationTypeProxy)b).AllowSeq) {
+ b.T = a;
+ } else {
+ a.T = b; // b has the stronger requirement
+ }
+ return true;
+ } else {
+ IndexableTypeProxy pb = (IndexableTypeProxy)b; // cast justification: lse we have unexpected restricted-proxy type
+ if (pa.AllowSeq) {
+ // strengthen a and b to a sequence type
+ b.T = new SeqType(pb.Arg);
+ a.T = b.T;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ } else if (a is IndexableTypeProxy) {
+ Contract.Assert(b is IndexableTypeProxy); // else we have unexpected restricted-proxy type
+ a.T = b;
+ return UnifyTypes(((IndexableTypeProxy)a).Arg, ((IndexableTypeProxy)b).Arg);
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected restricted-proxy type
+ }
+ }
+
+ /// <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) {
+ Contract.Requires(stmt != null);
+ Contract.Requires(codeContext != null);
+ if (stmt is PredicateStmt) {
+ PredicateStmt s = (PredicateStmt)stmt;
+ ResolveAttributes(s.Attributes, false);
+ s.IsGhost = true;
+ ResolveExpression(s.Expr, 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);
+ }
+
+ } else if (stmt is PrintStmt) {
+ PrintStmt s = (PrintStmt)stmt;
+ ResolveAttributeArgs(s.Args, false, 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)");
+ }
+
+ } 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);
+ } 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");
+ } else {
+ Statement target = loopStack[loopStack.Count - s.BreakCount];
+ if (target.Labels == null) {
+ // make sure there is a label, because the compiler and translator will want to see a unique ID
+ 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");
+ } 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);
+ }
+ var s = (ProduceStmt)stmt;
+ if (s.rhss != null) {
+ if (codeContext.Outs.Count != s.rhss.Count)
+ Error(s, "number of {2} parameters does not match declaration (found {0}, expected {1})", s.rhss.Count, codeContext.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.
+ List<Expression> formals = new List<Expression>();
+ int i = 0;
+ foreach (Formal f in codeContext.Outs) {
+ IdentifierExpr ident = new IdentifierExpr(f.tok, f.Name);
+ ident.Var = f;
+ ident.Type = ident.Var.Type;
+ Contract.Assert(f.Type != null);
+ formals.Add(ident);
+ // link the receiver parameter properly:
+ if (s.rhss[i] is TypeRhs) {
+ var r = (TypeRhs)s.rhss[i];
+ if (r.InitCall != null) {
+ r.InitCall.Receiver = ident;
+ }
+ }
+ i++;
+ }
+ s.hiddenUpdate = new UpdateStmt(s.Tok, formals, s.rhss, true);
+ // resolving the update statement will check for return/yield statement specifics.
+ ResolveStatement(s.hiddenUpdate, specContextOnly, codeContext);
+ }
+ } else {// this is a regular return/yield statement.
+ s.hiddenUpdate = null;
+ }
+ } else if (stmt is ConcreteUpdateStatement) {
+ ResolveUpdateStmt((ConcreteUpdateStatement)stmt, specContextOnly, codeContext);
+ } else if (stmt is VarDeclStmt) {
+ var s = (VarDeclStmt)stmt;
+ foreach (var vd in s.Lhss) {
+ ResolveStatement(vd, specContextOnly, codeContext);
+ s.ResolvedStatements.Add(vd);
+ }
+ if (s.Update != null) {
+ ResolveStatement(s.Update, specContextOnly, codeContext);
+ s.ResolvedStatements.Add(s.Update);
+ }
+
+ } else if (stmt is AssignStmt) {
+ AssignStmt s = (AssignStmt)stmt;
+ int prevErrorCount = ErrorCount;
+ if (s.Lhs is SeqSelectExpr) {
+ ResolveSeqSelectExpr((SeqSelectExpr)s.Lhs, true, false); // allow ghosts for now, tighted up below
+ } else {
+ ResolveExpression(s.Lhs, true); // allow ghosts for now, tighted up below
+ }
+ bool lhsResolvedSuccessfully = ErrorCount == 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;
+ if (!var.IsMutable) {
+ Error(stmt, "LHS of assignment must denote a mutable variable or field");
+ }
+ 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)");
+ }
+ }
+ } else if (lhs is FieldSelectExpr) {
+ var fse = (FieldSelectExpr)lhs;
+ if (fse.Field != null) { // otherwise, an error was reported above
+ lvalueIsGhost = fse.Field.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");
+ }
+ }
+ }
+ if (!fse.Field.IsUserMutable) {
+ Error(stmt, "LHS of assignment does not denote a mutable field");
+ }
+ }
+ } else if (lhs is SeqSelectExpr) {
+ var slhs = (SeqSelectExpr)lhs;
+ // LHS is fine, provided the "sequence" is really an array
+ if (lhsResolvedSuccessfully) {
+ Contract.Assert(slhs.Seq.Type != null);
+ if (!UnifyTypes(slhs.Seq.Type, builtIns.ArrayType(1, new InferredTypeProxy()))) {
+ Error(slhs.Seq, "LHS of array assignment must denote an array element (found {0})", slhs.Seq.Type);
+ }
+ 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)");
+ }
+ if (!slhs.SelectOne) {
+ Error(stmt, "cannot assign to a range of array elements (try the 'parallel' statement)");
+ }
+ }
+
+ } 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)");
+ }
+
+ } else {
+ Error(stmt, "LHS of assignment must denote a mutable variable or field");
+ }
+
+ s.IsGhost = lvalueIsGhost;
+ Type lhsType = s.Lhs.Type;
+ if (lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne) {
+ Error(stmt, "cannot assign to a range of array elements (try the 'parallel' statement)");
+ //lhsType = UserDefinedType.ArrayElementType(lhsType);
+ } else {
+ if (s.Rhs is ExprRhs) {
+ ExprRhs rr = (ExprRhs)s.Rhs;
+ ResolveExpression(rr.Expr, true);
+ if (!lvalueIsGhost) {
+ CheckIsNonGhost(rr.Expr);
+ }
+ 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);
+ }
+ } 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);
+ }
+ } else if (s.Rhs is HavocRhs) {
+ // nothing else to do
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected RHS
+ }
+ }
+ } else if (stmt is VarDecl) {
+ VarDecl s = (VarDecl)stmt;
+ if (s.OptionalType != null) {
+ ResolveType(stmt.Tok, s.OptionalType, null, true);
+ s.type = s.OptionalType;
+ }
+ // now that the declaration has been processed, add the name to the scope
+ if (!scope.Push(s.Name, s)) {
+ Error(s, "Duplicate local-variable name: {0}", s.Name);
+ }
+ if (specContextOnly) {
+ // a local variable in a specification-only context might as well be ghost
+ s.IsGhost = true;
+ }
+
+ } else if (stmt is CallStmt) {
+ CallStmt s = (CallStmt)stmt;
+ ResolveCallStmt(s, specContextOnly, codeContext, null);
+
+ } else if (stmt is BlockStmt) {
+ scope.PushMarker();
+ ResolveBlockStatement((BlockStmt)stmt, specContextOnly, 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, 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);
+ }
+ }
+ s.IsGhost = branchesAreSpecOnly;
+ ResolveStatement(s.Thn, branchesAreSpecOnly, codeContext);
+ if (s.Els != null) {
+ ResolveStatement(s.Els, branchesAreSpecOnly, codeContext);
+ }
+
+ } else if (stmt is AlternativeStmt) {
+ var s = (AlternativeStmt)stmt;
+ s.IsGhost = ResolveAlternatives(s.Alternatives, specContextOnly, null, codeContext);
+
+ } else if (stmt is WhileStmt) {
+ WhileStmt s = (WhileStmt)stmt;
+ bool bodyMustBeSpecOnly = specContextOnly;
+ if (s.Guard != null) {
+ int prevErrorCount = ErrorCount;
+ ResolveExpression(s.Guard, 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) {
+ bodyMustBeSpecOnly = UsesSpecFeatures(s.Guard);
+ }
+ }
+
+ foreach (MaybeFreeExpression inv in s.Invariants) {
+ ResolveExpression(inv.E, 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, true);
+ if (bodyMustBeSpecOnly && e is WildcardExpr) {
+ Error(e, "'decreases *' is not allowed on ghost loops");
+ }
+ // any type is fine
+ }
+
+ if (s.Mod.Expressions != null) {
+ foreach (FrameExpression fe in s.Mod.Expressions) {
+ ResolveFrameExpression(fe, "modifies");
+ }
+ }
+ s.IsGhost = bodyMustBeSpecOnly;
+ loopStack.Add(s); // push
+ if (s.Labels == null) { // otherwise, "s" is already in "inSpecOnlyContext" map
+ inSpecOnlyContext.Add(s, specContextOnly);
+ }
+
+ ResolveStatement(s.Body, bodyMustBeSpecOnly, codeContext);
+ loopStack.RemoveAt(loopStack.Count - 1); // pop
+
+ } 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, 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, true);
+ if (s.IsGhost && e is WildcardExpr) {
+ Error(e, "'decreases *' is not allowed on ghost loops");
+ }
+ // any type is fine
+ }
+
+ } else if (stmt is ParallelStmt) {
+ var s = (ParallelStmt)stmt;
+
+ int prevErrorCount = ErrorCount;
+ scope.PushMarker();
+ foreach (BoundVar v in s.BoundVars) {
+ if (!scope.Push(v.Name, v)) {
+ Error(v, "Duplicate bound-variable name: {0}", v.Name);
+ }
+ ResolveType(v.tok, v.Type, null, true);
+ }
+ ResolveExpression(s.Range, true);
+ Contract.Assert(s.Range.Type != null); // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(s.Range.Type, Type.Bool)) {
+ Error(stmt, "range restriction in parallel statement must be of type bool (instead got {0})", s.Range.Type);
+ }
+ foreach (var ens in s.Ens) {
+ ResolveExpression(ens.E, 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);
+ }
+ }
+ // 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, true);
+
+ bool bodyMustBeSpecOnly = specContextOnly || (prevErrorCount == ErrorCount && UsesSpecFeatures(s.Range));
+ if (!bodyMustBeSpecOnly && prevErrorCount == ErrorCount) {
+ var missingBounds = new List<BoundVar>();
+ s.Bounds = DiscoverBounds(s.Tok, s.BoundVars, s.Range, true, false, missingBounds);
+ if (missingBounds.Count != 0) {
+ bodyMustBeSpecOnly = true;
+ }
+ }
+ s.IsGhost = bodyMustBeSpecOnly;
+
+ // clear the labels for the duration of checking the body, because break statements are not allowed to leave a parallel statement
+ var prevLblStmts = labeledStatements;
+ var prevLoopStack = loopStack;
+ labeledStatements = new Scope<Statement>();
+ loopStack = new List<Statement>();
+ ResolveStatement(s.Body, bodyMustBeSpecOnly, codeContext);
+ labeledStatements = prevLblStmts;
+ loopStack = prevLoopStack;
+ scope.PopMarker();
+
+ if (prevErrorCount == ErrorCount) {
+ // 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.
+ s.Kind = ParallelStmt.ParBodyKind.Proof;
+ } else {
+ // There are two special cases:
+ // * Assign, which is the only kind of the parallel statement that allows a heap update.
+ // * Call, which is a single call statement with no side effects or output parameters.
+ // The effect of Assign and the postcondition of Call will be seen outside the parallel
+ // statement.
+ Statement s0 = s.S0;
+ if (s0 is AssignStmt) {
+ s.Kind = ParallelStmt.ParBodyKind.Assign;
+ } else if (s0 is CallStmt) {
+ s.Kind = ParallelStmt.ParBodyKind.Call;
+ } else {
+ s.Kind = ParallelStmt.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 parallel statement will not be known outside the parallel statement; consider using an 'ensures' clause");
+ }
+ }
+ }
+ CheckParallelBodyRestrictions(s.Body, s.Kind);
+ }
+
+ } else if (stmt is CalcStmt) {
+ var prevErrorCount = ErrorCount;
+ CalcStmt s = (CalcStmt)stmt;
+ s.IsGhost = true;
+ if (s.Lines.Count > 0) {
+ var resOp = s.Op;
+ var e0 = s.Lines.First();
+ ResolveExpression(e0, true);
+ Contract.Assert(e0.Type != null); // follows from postcondition of ResolveExpression
+ for (int i = 1; i < s.Lines.Count; i++) {
+ var e1 = s.Lines[i];
+ ResolveExpression(e1, 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);
+ } else {
+ BinaryExpr step;
+ var op = s.CustomOps[i - 1];
+ if (op == null) {
+ step = new BinaryExpr(e0.tok, s.Op, e0, e1); // Use calc-wide operator
+ } else {
+ step = new BinaryExpr(e0.tok, (BinaryExpr.Opcode)op, e0, e1); // Use custom line operator
+ Contract.Assert(CalcStmt.ResultOp(resOp, (BinaryExpr.Opcode)op) != null); // This was checked during parsing
+ resOp = (BinaryExpr.Opcode)CalcStmt.ResultOp(resOp, (BinaryExpr.Opcode)op);
+ }
+ ResolveExpression(step, true);
+ s.Steps.Add(step);
+ }
+ e0 = e1;
+ }
+ foreach (var h in s.Hints) {
+ ResolveStatement(h, true, codeContext);
+ }
+ if (prevErrorCount == ErrorCount && s.Steps.Count > 0) {
+ // do not build Result if there were errors, as it might be ill-typed and produce unnecessary resolution errors
+ s.Result = new BinaryExpr(s.Tok, resOp, s.Lines.First(), s.Lines.Last());
+ ResolveExpression(s.Result, true);
+ }
+ }
+ Contract.Assert(prevErrorCount != ErrorCount || s.Steps.Count == s.Hints.Count);
+ Contract.Assert(prevErrorCount != ErrorCount || s.Steps.Count == 0 || s.Result != null);
+
+ } else if (stmt is MatchStmt) {
+ MatchStmt s = (MatchStmt)stmt;
+ bool bodyIsSpecOnly = specContextOnly;
+ int prevErrorCount = ErrorCount;
+ ResolveExpression(s.Source, true);
+ 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;
+ Dictionary<TypeParameter, Type> subst = new Dictionary<TypeParameter, Type>();
+ if (s.Source.Type.IsDatatype) {
+ sourceType = (UserDefinedType)s.Source.Type;
+ dtd = cce.NonNull((DatatypeDecl)sourceType.ResolvedClass);
+ }
+ 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
+
+ // 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]);
+ }
+ }
+ s.IsGhost = bodyIsSpecOnly;
+
+ Dictionary<string, object> memberNamesUsed = new Dictionary<string, object>(); // this is really a set
+ 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);
+ } 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.ContainsKey(mc.Id)) {
+ Error(mc.tok, "member {0} appears in more than one case", mc.Id);
+ } else {
+ memberNamesUsed.Add(mc.Id, null); // add mc.Id to the set of names used
+ }
+ }
+ }
+ scope.PushMarker();
+ int i = 0;
+ foreach (BoundVar v in mc.Arguments) {
+ if (!scope.Push(v.Name, v)) {
+ Error(v, "Duplicate parameter name: {0}", v.Name);
+ }
+ ResolveType(v.tok, v.Type, null, true);
+ 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);
+ }
+ v.IsGhost = formal.IsGhost;
+ }
+ i++;
+ }
+ foreach (Statement ss in mc.Body) {
+ ResolveStatement(ss, bodyIsSpecOnly, codeContext);
+ }
+ scope.PopMarker();
+ }
+ 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.ContainsKey(ctr.Name)) {
+ s.MissingCases.Add(ctr);
+ }
+ }
+ Contract.Assert(memberNamesUsed.Count + s.MissingCases.Count == dtd.Ctors.Count);
+ }
+
+
+ } 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);
+ }
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException();
+ }
+ }
+ private void ResolveUpdateStmt(ConcreteUpdateStatement s, bool specContextOnly, ICodeContext codeContext) {
+ Contract.Requires(codeContext != null);
+
+ int prevErrorCount = ErrorCount;
+ // First, resolve all LHS's and expression-looking RHS's.
+ SeqSelectExpr arrayRangeLhs = null;
+ var update = s as UpdateStmt;
+
+ foreach (var lhs in s.Lhss) {
+ var ec = ErrorCount;
+ if (lhs is SeqSelectExpr) {
+ var sse = (SeqSelectExpr)lhs;
+ ResolveSeqSelectExpr(sse, true, true);
+ if (arrayRangeLhs == null && !sse.SelectOne) {
+ arrayRangeLhs = sse;
+ }
+ } else {
+ ResolveExpression(lhs, true);
+ }
+ if (update == null && ec == ErrorCount && specContextOnly && !AssignStmt.LhsIsToGhost(lhs)) {
+ Error(lhs, "cannot assign to non-ghost variable in a ghost context");
+ }
+ }
+ IToken firstEffectfulRhs = null;
+ CallRhs callRhs = null;
+ // Resolve RHSs
+ if (update == null) {
+ var suchThat = (AssignSuchThatStmt)s; // this is the other possible subclass
+ ResolveExpression(suchThat.Expr, true);
+ if (suchThat.AssumeToken == null) {
+ // to ease in the verification, only allow local variables as LHSs
+ var lhsNames = new Dictionary<string, object>();
+ foreach (var lhs in s.Lhss) {
+ if (!(lhs.Resolved is IdentifierExpr)) {
+ Error(lhs, "the assign-such-that statement currently only supports local-variable LHSs");
+ } else {
+ var ie = (IdentifierExpr)lhs.Resolved;
+ if (lhsNames.ContainsKey(ie.Name)) {
+ // disallow same LHS.
+ Error(s, "duplicate variable in left-hand side of assign-such-that statement: {0}", ie.Name);
+ } else {
+ lhsNames.Add(ie.Name, null);
+ }
+ }
+ }
+ }
+ } else {
+ foreach (var rhs in update.Rhss) {
+ bool isEffectful;
+ if (rhs is TypeRhs) {
+ var tr = (TypeRhs)rhs;
+ ResolveTypeRhs(tr, s, specContextOnly, codeContext);
+ isEffectful = tr.InitCall != null;
+ } else if (rhs is HavocRhs) {
+ isEffectful = false;
+ } else {
+ var er = (ExprRhs)rhs;
+ if (er.Expr is IdentifierSequence) {
+ var cRhs = ResolveIdentifierSequence((IdentifierSequence)er.Expr, true, true);
+ isEffectful = cRhs != null;
+ callRhs = callRhs ?? cRhs;
+ } else if (er.Expr is FunctionCallExpr) {
+ var cRhs = ResolveFunctionCallExpr((FunctionCallExpr)er.Expr, true, true);
+ isEffectful = cRhs != null;
+ callRhs = callRhs ?? cRhs;
+ } else {
+ ResolveExpression(er.Expr, true);
+ isEffectful = false;
+ }
+ }
+ if (isEffectful && firstEffectfulRhs == null) {
+ firstEffectfulRhs = rhs.Tok;
+ }
+ }
+ }
+ // check for duplicate identifiers on the left (full duplication checking for references and the like is done during verification)
+ var lhsNameSet = new Dictionary<string, object>();
+ foreach (var lhs in s.Lhss) {
+ var ie = lhs.Resolved as IdentifierExpr;
+ if (ie != null) {
+ if (lhsNameSet.ContainsKey(ie.Name)) {
+ if (callRhs != null)
+ // only allow same LHS in a multiassignment, not a call statement
+ Error(s, "duplicate variable in left-hand side of call statement: {0}", ie.Name);
+ } else {
+ lhsNameSet.Add(ie.Name, null);
+ }
+ }
+ }
+ if (update != null) {
+ // figure out what kind of UpdateStmt this is
+ if (firstEffectfulRhs == null) {
+ if (s.Lhss.Count == 0) {
+ Contract.Assert(update.Rhss.Count == 1); // guaranteed by the parser
+ Error(s, "expected method call, found expression");
+ } else if (s.Lhss.Count != update.Rhss.Count) {
+ Error(s, "the number of left-hand sides ({0}) and right-hand sides ({1}) must match for a multi-assignment", s.Lhss.Count, update.Rhss.Count);
+ } else if (arrayRangeLhs != null && s.Lhss.Count != 1) {
+ Error(arrayRangeLhs, "array-range may not be used as LHS of multi-assignment; use separate assignment statements for each array-range assignment");
+ } else if (ErrorCount == prevErrorCount) {
+ // add the statements here in a sequence, but don't use that sequence later for translation (instead, should translated properly as multi-assignment)
+ for (int i = 0; i < s.Lhss.Count; i++) {
+ var a = new AssignStmt(s.Tok, s.Lhss[i].Resolved, update.Rhss[i]);
+ s.ResolvedStatements.Add(a);
+ }
+ }
+
+ } else if (update.CanMutateKnownState) {
+ if (1 < update.Rhss.Count) {
+ Error(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 (callRhs != null) {
+ Error(callRhs.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'.");
+ }
+ var a = new AssignStmt(s.Tok, s.Lhss[0].Resolved, tr);
+ s.ResolvedStatements.Add(a);
+ }
+ }
+
+ } 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");
+ } else if (arrayRangeLhs != null) {
+ Error(arrayRangeLhs, "Assignment to range of array elements must have a simple expression RHS; try using a temporary local variable");
+ } else if (callRhs == null) {
+ // must be a single TypeRhs
+ if (s.Lhss.Count != 1) {
+ Contract.Assert(2 <= s.Lhss.Count); // the parser allows 0 Lhss only if the whole statement looks like an expression (not a TypeRhs)
+ Error(s.Lhss[1].tok, "the number of left-hand sides ({0}) and right-hand sides ({1}) must match for a multi-assignment", s.Lhss.Count, update.Rhss.Count);
+ } else if (ErrorCount == prevErrorCount) {
+ var a = new AssignStmt(s.Tok, s.Lhss[0].Resolved, update.Rhss[0]);
+ s.ResolvedStatements.Add(a);
+ }
+ } else {
+ // a call statement
+ if (ErrorCount == prevErrorCount) {
+ var resolvedLhss = new List<Expression>();
+ foreach (var ll in s.Lhss) {
+ resolvedLhss.Add(ll.Resolved);
+ }
+ var a = new CallStmt(callRhs.Tok, resolvedLhss, callRhs.Receiver, callRhs.MethodName, callRhs.Args);
+ s.ResolvedStatements.Add(a);
+ }
+ }
+ }
+ }
+
+ foreach (var a in s.ResolvedStatements) {
+ ResolveStatement(a, specContextOnly, codeContext);
+ }
+ s.IsGhost = s.ResolvedStatements.TrueForAll(ss => ss.IsGhost);
+ }
+
+ bool ResolveAlternatives(List<GuardedAlternative> alternatives, bool specContextOnly, 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
+ foreach (var alternative in alternatives) {
+ int prevErrorCount = ErrorCount;
+ ResolveExpression(alternative.Guard, 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);
+ }
+ }
+
+ 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();
+ foreach (Statement ss in alternative.Body) {
+ ResolveStatement(ss, isGhost, 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) {
+ Contract.Requires(s != null);
+ Contract.Requires(codeContext != null);
+ bool isInitCall = receiverType != null;
+
+ // resolve receiver
+ ResolveReceiver(s.Receiver, true);
+ Contract.Assert(s.Receiver.Type != null); // follows from postcondition of ResolveExpression
+ if (receiverType == null) {
+ receiverType = s.Receiver.Type;
+ }
+ // resolve the method name
+ NonProxyType nptype;
+ MemberDecl member = ResolveMember(s.Tok, receiverType, s.MethodName, out nptype);
+ Method callee = null;
+ if (member == null) {
+ // error has already been reported by ResolveMember
+ } else if (member is Method) {
+ s.Method = (Method)member;
+ callee = s.Method;
+ if (!isInitCall && callee is Constructor) {
+ Error(s, "a constructor is only allowed to be called 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");
+ }
+ } else {
+ Error(s, "member {0} in type {1} does not refer to a method", s.MethodName, nptype);
+ }
+
+ // resolve left-hand sides
+ foreach (var lhs in s.Lhs) {
+ 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 == null || callee.Ins.Count <= j || callee.Ins[j].IsGhost;
+ ResolveExpression(e, true);
+ if (!allowGhost) {
+ CheckIsNonGhost(e);
+ }
+ j++;
+ }
+
+ if (callee == null) {
+ // error has been reported above
+ } else if (callee.Ins.Count != s.Args.Count) {
+ Error(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");
+ } else {
+ Error(s, "wrong number of method result arguments (got {0}, expected {1})", s.Lhs.Count, callee.Outs.Count);
+ }
+ } else {
+ Contract.Assert(nptype != null); // follows from postcondition of ResolveMember above
+ if (isInitCall) {
+ if (callee.IsStatic) {
+ Error(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");
+ } else if (s.Receiver is StaticReceiverExpr) {
+ Error(s.Receiver, "call to instance method requires an instance");
+ }
+ }
+#if !NO_WORK_TO_BE_DONE
+ UserDefinedType ctype = (UserDefinedType)nptype; // TODO: get rid of this statement, make this code handle any non-proxy type
+#endif
+ // build the type substitution map
+ s.TypeArgumentSubstitutions = new Dictionary<TypeParameter, Type>();
+ for (int i = 0; i < ctype.TypeArgs.Count; i++) {
+ s.TypeArgumentSubstitutions.Add(cce.NonNull(ctype.ResolvedClass).TypeArgs[i], ctype.TypeArgs[i]);
+ }
+ foreach (TypeParameter p in callee.TypeArgs) {
+ s.TypeArgumentSubstitutions.Add(p, new ParamTypeProxy(p));
+ }
+ // type check the arguments
+ for (int i = 0; i < callee.Ins.Count; i++) {
+ Type st = SubstType(callee.Ins[i].Type, s.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);
+ }
+ }
+ for (int i = 0; i < callee.Outs.Count; i++) {
+ Type st = SubstType(callee.Outs[i].Type, s.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);
+ } 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 VarDecl) {
+ // the variable was actually declared in this statement, so auto-declare it as ghost
+ ((VarDecl)ll.Var).MakeGhost();
+ } else {
+ Error(s, "actual out-parameter {0} is required to be a ghost variable", i);
+ }
+ }
+ } else if (resolvedLhs is FieldSelectExpr) {
+ var ll = (FieldSelectExpr)resolvedLhs;
+ if (!ll.Field.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.
+ if (resolvedLhs is IdentifierExpr) {
+ var ll = (IdentifierExpr)resolvedLhs;
+ if (!ll.Var.IsMutable) {
+ Error(resolvedLhs, "LHS of assignment must denote a mutable variable");
+ }
+ } else if (resolvedLhs is FieldSelectExpr) {
+ var ll = (FieldSelectExpr)resolvedLhs;
+ if (!ll.Field.IsUserMutable) {
+ Error(resolvedLhs, "LHS of assignment must denote a mutable field");
+ }
+ }
+ }
+ }
+
+ // Resolution termination check
+ ModuleDefinition callerModule = codeContext.EnclosingModule;
+ ModuleDefinition calleeModule = ((ICodeContext)callee).EnclosingModule;
+ if (callerModule == calleeModule) {
+ // intra-module call; this is allowed; add edge in module's call graph
+ var caller = codeContext is Method ? (Method)codeContext : ((IteratorDecl)codeContext).Member_MoveNext;
+ callerModule.CallGraph.AddEdge(caller, callee);
+ } else {
+ //Contract.Assert(dependencies.Reaches(callerModule, calleeModule));
+ //
+ }
+ }
+ }
+
+ void ResolveBlockStatement(BlockStmt blockStmt, bool specContextOnly, ICodeContext codeContext) {
+ Contract.Requires(blockStmt != null);
+ Contract.Requires(codeContext != null);
+
+ foreach (Statement ss in blockStmt.Body) {
+ labeledStatements.PushMarker();
+ // push labels
+ for (var l = ss.Labels; l != null; l = l.Next) {
+ var lnode = l.Data;
+ 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");
+ } else if (prev != null) {
+ Error(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);
+ }
+ }
+ }
+ ResolveStatement(ss, specContextOnly, codeContext);
+ labeledStatements.PopMarker();
+ }
+ }
+
+ /// <summary>
+ /// This method performs some additional checks on the body "stmt" of a parallel statement of kind "kind".
+ /// </summary>
+ public void CheckParallelBodyRestrictions(Statement stmt, ParallelStmt.ParBodyKind kind) {
+ Contract.Requires(stmt != null);
+ if (stmt is PredicateStmt) {
+ // cool
+ } else if (stmt is PrintStmt) {
+ Error(stmt, "print statement is not allowed inside a parallel statement");
+ } else if (stmt is BreakStmt) {
+ // this case is checked already in the first pass through the parallel 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 parallel statement");
+ } else if (stmt is YieldStmt) {
+ Error(stmt, "yield statement is not allowed inside a parallel statement");
+ } else if (stmt is AssignSuchThatStmt) {
+ var s = (AssignSuchThatStmt)stmt;
+ foreach (var lhs in s.Lhss) {
+ CheckParallelBodyLhs(s.Tok, lhs.Resolved, kind);
+ }
+ } else if (stmt is ConcreteSyntaxStatement) {
+ var s = (ConcreteSyntaxStatement)stmt;
+ foreach (var ss in s.ResolvedStatements) {
+ CheckParallelBodyRestrictions(ss, kind);
+ }
+ } else if (stmt is AssignStmt) {
+ var s = (AssignStmt)stmt;
+ CheckParallelBodyLhs(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 == ParallelStmt.ParBodyKind.Assign) {
+ Error(rhs.Tok, "new allocation not supported in parallel statements");
+ } else {
+ var t = (TypeRhs)rhs;
+ if (t.InitCall != null) {
+ CheckParallelBodyRestrictions(t.InitCall, kind);
+ }
+ }
+ } else if (rhs is ExprRhs) {
+ var r = ((ExprRhs)rhs).Expr.Resolved;
+ if (kind == ParallelStmt.ParBodyKind.Assign && r is UnaryExpr && ((UnaryExpr)r).Op == UnaryExpr.Opcode.SetChoose) {
+ Error(r, "set choose operator not supported inside the enclosing parallel statement");
+ }
+ }
+ } else if (stmt is VarDecl) {
+ // cool
+ } else if (stmt is CallStmt) {
+ var s = (CallStmt)stmt;
+ foreach (var lhs in s.Lhs) {
+ var idExpr = lhs as IdentifierExpr;
+ if (idExpr != null) {
+ if (scope.ContainsDecl(idExpr.Var)) {
+ Error(stmt, "body of parallel statement is attempting to update a variable declared outside the parallel statement");
+ }
+ } else {
+ Error(stmt, "the body of the enclosing parallel statement is not allowed to update heap locations");
+ }
+ }
+ if (!s.Method.IsGhost) {
+ // The reason for this restriction is that the compiler is going to omit the parallel statement altogether--it has
+ // no effect. However, print effects are not documented, so to make sure that the compiler does not omit a call to
+ // 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 parallel statement is not allowed to call non-ghost methods");
+ }
+
+ } else if (stmt is BlockStmt) {
+ var s = (BlockStmt)stmt;
+ scope.PushMarker();
+ foreach (var ss in s.Body) {
+ CheckParallelBodyRestrictions(ss, kind);
+ }
+ scope.PopMarker();
+
+ } else if (stmt is IfStmt) {
+ var s = (IfStmt)stmt;
+ CheckParallelBodyRestrictions(s.Thn, kind);
+ if (s.Els != null) {
+ CheckParallelBodyRestrictions(s.Els, kind);
+ }
+
+ } else if (stmt is AlternativeStmt) {
+ var s = (AlternativeStmt)stmt;
+ foreach (var alt in s.Alternatives) {
+ foreach (var ss in alt.Body) {
+ CheckParallelBodyRestrictions(ss, kind);
+ }
+ }
+
+ } else if (stmt is WhileStmt) {
+ WhileStmt s = (WhileStmt)stmt;
+ CheckParallelBodyRestrictions(s.Body, kind);
+
+ } else if (stmt is AlternativeLoopStmt) {
+ var s = (AlternativeLoopStmt)stmt;
+ foreach (var alt in s.Alternatives) {
+ foreach (var ss in alt.Body) {
+ CheckParallelBodyRestrictions(ss, kind);
+ }
+ }
+
+ } else if (stmt is ParallelStmt) {
+ var s = (ParallelStmt)stmt;
+ switch (s.Kind) {
+ case ParallelStmt.ParBodyKind.Assign:
+ Error(stmt, "a parallel statement with heap updates is not allowed inside the body of another parallel statement");
+ break;
+ case ParallelStmt.ParBodyKind.Call:
+ case ParallelStmt.ParBodyKind.Proof:
+ // these are fine, since they don't update any non-local state
+ break;
+ default:
+ Contract.Assert(false); // unexpected kind
+ break;
+ }
+
+ } else if (stmt is CalcStmt) {
+ // cool
+ // NadiaTodo: ...I assume because it's always ghost
+
+ } else if (stmt is MatchStmt) {
+ var s = (MatchStmt)stmt;
+ foreach (var kase in s.Cases) {
+ foreach (var ss in kase.Body) {
+ CheckParallelBodyRestrictions(ss, kind);
+ }
+ }
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException();
+ }
+ }
+
+ void CheckParallelBodyLhs(IToken tok, Expression lhs, ParallelStmt.ParBodyKind kind) {
+ var idExpr = lhs as IdentifierExpr;
+ if (idExpr != null) {
+ if (scope.ContainsDecl(idExpr.Var)) {
+ Error(tok, "body of parallel statement is attempting to update a variable declared outside the parallel statement");
+ }
+ } else if (kind != ParallelStmt.ParBodyKind.Assign) {
+ Error(tok, "the body of the enclosing parallel statement is not allowed to update heap locations");
+ }
+ }
+
+ Type ResolveTypeRhs(TypeRhs rr, Statement stmt, bool specContextOnly, ICodeContext codeContext) {
+ Contract.Requires(rr != null);
+ Contract.Requires(stmt != null);
+ Contract.Requires(codeContext != null);
+ Contract.Ensures(Contract.Result<Type>() != null);
+
+ if (rr.Type == null) {
+ ResolveType(stmt.Tok, rr.EType, null, true);
+ if (rr.ArrayDimensions == null) {
+ if (!rr.EType.IsRefType) {
+ Error(stmt, "new can be applied only to reference types (got {0})", rr.EType);
+ } else {
+ bool callsConstructor = false;
+ if (rr.InitCall != null) {
+ ResolveCallStmt(rr.InitCall, specContextOnly, codeContext, rr.EType);
+ if (rr.InitCall.Method is Constructor) {
+ callsConstructor = true;
+ }
+ }
+ if (!callsConstructor && rr.EType is UserDefinedType) {
+ var udt = (UserDefinedType)rr.EType;
+ 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.HasConstructor) {
+ Error(stmt, "when allocating an object of type '{0}', one of its constructor methods must be called", cl.Name);
+ }
+ }
+ }
+ rr.Type = rr.EType;
+ } else {
+ int i = 0;
+ if (rr.EType.IsSubrangeType) {
+ Error(stmt, "sorry, cannot instantiate 'array' type with a subrange type");
+ }
+ foreach (Expression dim in rr.ArrayDimensions) {
+ Contract.Assert(dim != null);
+ ResolveExpression(dim, true);
+ if (!UnifyTypes(dim.Type, Type.Int)) {
+ Error(stmt, "new must use an integer expression for the array size (got {0} for index {1})", dim.Type, i);
+ }
+ i++;
+ }
+ rr.Type = builtIns.ArrayType(rr.ArrayDimensions.Count, rr.EType);
+ }
+ }
+ return rr.Type;
+ }
+
+ MemberDecl ResolveMember(IToken tok, Type receiverType, string memberName, out NonProxyType nptype) {
+ Contract.Requires(tok != null);
+ Contract.Requires(receiverType != null);
+ Contract.Requires(memberName != null);
+ Contract.Ensures(Contract.Result<MemberDecl>() == null || Contract.ValueAtReturn(out nptype) != null);
+
+ nptype = null; // prepare for the worst
+ receiverType = receiverType.Normalize();
+ if (receiverType is TypeProxy) {
+ Error(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
+
+ UserDefinedType ctype = UserDefinedType.DenotesClass(receiverType);
+ if (ctype != null) {
+ var cd = (ClassDecl)ctype.ResolvedClass; // correctness of cast follows from postcondition of DenotesClass
+ Contract.Assert(ctype.TypeArgs.Count == cd.TypeArgs.Count); // follows from the fact that ctype was resolved
+ MemberDecl member;
+ if (!classMembers[cd].TryGetValue(memberName, out member)) {
+ Error(tok, "member {0} does not exist in {2} {1}", memberName, ctype.Name, cd is IteratorDecl ? "iterator" : "class");
+ return null;
+ } else {
+ nptype = ctype;
+ return member;
+ }
+ }
+
+ DatatypeDecl dtd = receiverType.AsDatatype;
+ 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);
+ return null;
+ } else {
+ nptype = (UserDefinedType)receiverType;
+ return member;
+ }
+ }
+
+ Error(tok, "type {0} does not have a member {1}", receiverType, memberName);
+ return null;
+ }
+
+ public static Type SubstType(Type type, Dictionary<TypeParameter/*!*/, Type/*!*/>/*!*/ subst) {
+ Contract.Requires(type != null);
+ Contract.Requires(cce.NonNullDictionaryAndValues(subst));
+ Contract.Ensures(Contract.Result<Type>() != null);
+
+ if (type is BasicType) {
+ return type;
+ } else if (type is MapType) {
+ MapType t = (MapType)type;
+ return new MapType(SubstType(t.Domain, subst), SubstType(t.Range, subst));
+ } else if (type is CollectionType) {
+ CollectionType t = (CollectionType)type;
+ Type arg = SubstType(t.Arg, subst);
+ if (arg == t.Arg) {
+ return type;
+ } else if (type is SetType) {
+ return new SetType(arg);
+ } else if (type is MultiSetType) {
+ return new MultiSetType(arg);
+ } else if (type is SeqType) {
+ return new SeqType(arg);
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected collection type
+ }
+ } else if (type is UserDefinedType) {
+ UserDefinedType t = (UserDefinedType)type;
+ if (t.ResolvedParam != null) {
+ Contract.Assert(t.TypeArgs.Count == 0);
+ Type s;
+ if (subst.TryGetValue(t.ResolvedParam, out s)) {
+ return cce.NonNull(s);
+ } else {
+ return type;
+ }
+ } else if (t.ResolvedClass != null) {
+ List<Type> newArgs = null; // allocate it lazily
+ for (int i = 0; i < t.TypeArgs.Count; i++) {
+ Type p = t.TypeArgs[i];
+ Type s = SubstType(p, subst);
+ if (s != p && newArgs == null) {
+ // lazily construct newArgs
+ newArgs = new List<Type>();
+ for (int j = 0; j < i; j++) {
+ newArgs.Add(t.TypeArgs[j]);
+ }
+ }
+ if (newArgs != null) {
+ newArgs.Add(s);
+ }
+ }
+ if (newArgs == null) {
+ // there were no substitutions
+ return type;
+ } else {
+ return new UserDefinedType(t.tok, t.Name, t.ResolvedClass, newArgs);
+ }
+ } else {
+ // there's neither a resolved param nor a resolved class, which means the UserDefinedType wasn't
+ // properly resolved; just return it
+ return type;
+ }
+ } else if (type is TypeProxy) {
+ TypeProxy t = (TypeProxy)type;
+ if (t.T == null) {
+ return type;
+ } else {
+ // bypass the proxy
+ return SubstType(t.T, subst);
+ }
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
+ }
+ }
+
+ public static UserDefinedType GetThisType(IToken tok, ClassDecl cl) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cl != null);
+ Contract.Ensures(Contract.Result<UserDefinedType>() != null);
+
+ List<Type> args = new List<Type>();
+ foreach (TypeParameter tp in cl.TypeArgs) {
+ args.Add(new UserDefinedType(tok, tp.Name, tp));
+ }
+ return new UserDefinedType(tok, cl.Name, cl, args);
+ }
+
+ /// <summary>
+ /// Requires "member" to be declared in a class.
+ /// </summary>
+ public static UserDefinedType GetReceiverType(IToken tok, MemberDecl member) {
+ Contract.Requires(tok != null);
+ Contract.Requires(member != null);
+ Contract.Ensures(Contract.Result<UserDefinedType>() != null);
+
+ return GetThisType(tok, (ClassDecl)member.EnclosingClass);
+ }
+
+ /// <summary>
+ /// "twoState" implies that "old" and "fresh" expressions are allowed.
+ /// </summary>
+ void ResolveExpression(Expression expr, bool twoState) {
+ ResolveExpression(expr, twoState, null);
+ }
+
+ /// <summary>
+ /// "matchVarContext" says which variables are allowed to be used as the source expression in a "match" expression;
+ /// if null, no "match" expression will be allowed.
+ /// </summary>
+ void ResolveExpression(Expression expr, bool twoState, List<IVariable> matchVarContext) {
+ Contract.Requires(expr != null);
+ Contract.Ensures(expr.Type != null);
+ if (expr.Type != null) {
+ // expression has already been resovled
+ return;
+ }
+
+ // The following cases will resolve the subexpressions and will attempt to assign a type of expr. However, if errors occur
+ // and it cannot be determined what the type of expr is, then it is fine to leave expr.Type as null. In that case, the end
+ // of this method will assign proxy type to the expression, which reduces the number of error messages that are produced
+ // while type checking the rest of the program.
+
+ if (expr is ParensExpression) {
+ var e = (ParensExpression)expr;
+ ResolveExpression(e.E, twoState, matchVarContext); // allow "match" expressions inside e.E if the parenthetic expression had been allowed to be a "match" expression
+ e.ResolvedExpression = e.E;
+ e.Type = e.E.Type;
+
+ } else if (expr is ChainingExpression) {
+ var e = (ChainingExpression)expr;
+ ResolveExpression(e.E, twoState);
+ e.ResolvedExpression = e.E;
+ e.Type = e.E.Type;
+
+ } else if (expr is IdentifierSequence) {
+ var e = (IdentifierSequence)expr;
+ ResolveIdentifierSequence(e, twoState, false);
+
+ } else if (expr is LiteralExpr) {
+ LiteralExpr e = (LiteralExpr)expr;
+ if (e.Value == null) {
+ e.Type = new ObjectTypeProxy();
+ } else if (e.Value is BigInteger) {
+ e.Type = Type.Int;
+ } else if (e.Value is bool) {
+ e.Type = Type.Bool;
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected literal type
+ }
+
+ } else if (expr is ThisExpr) {
+ if (!scope.AllowInstance) {
+ Error(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
+ }
+
+ } else if (expr is IdentifierExpr) {
+ IdentifierExpr e = (IdentifierExpr)expr;
+ e.Var = scope.Find(e.Name);
+ if (e.Var == null) {
+ Error(expr, "Identifier does not denote a local variable, parameter, or bound variable: {0}", e.Name);
+ } else {
+ expr.Type = e.Var.Type;
+ }
+
+ } 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);
+ } else if (d is AmbiguousTopLevelDecl) {
+ Error(expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1}", dtv.DatatypeName, ((AmbiguousTopLevelDecl)d).ModuleNames());
+ } else if (!(d is DatatypeDecl)) {
+ Error(expr.tok, "Expected datatype: {0}", dtv.DatatypeName);
+ } else {
+ ResolveDatatypeValue(twoState, dtv, (DatatypeDecl)d);
+ }
+
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ Type elementType = new InferredTypeProxy();
+ foreach (Expression ee in e.Elements) {
+ ResolveExpression(ee, twoState);
+ 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);
+ }
+ }
+ if (expr is SetDisplayExpr) {
+ expr.Type = new SetType(elementType);
+ } else if (expr is MultiSetDisplayExpr) {
+ expr.Type = new MultiSetType(elementType);
+ } else {
+ expr.Type = new SeqType(elementType);
+ }
+ } else if (expr is MapDisplayExpr) {
+ MapDisplayExpr e = (MapDisplayExpr)expr;
+ Type domainType = new InferredTypeProxy();
+ Type rangeType = new InferredTypeProxy();
+ foreach (ExpressionPair p in e.Elements) {
+ ResolveExpression(p.A, twoState);
+ 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);
+ }
+ ResolveExpression(p.B, twoState);
+ 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);
+ }
+ }
+ expr.Type = new MapType(domainType, rangeType);
+ } else if (expr is ExprDotName) {
+ var e = (ExprDotName)expr;
+ // The following call to ResolveExpression is just preliminary. If it succeeds, it is redone below on the resolved expression. Thus,
+ // it's okay to be more lenient here and use coLevel (instead of trying to use CoLevel_Dec(coLevel), which is needed when .Name denotes a
+ // destructor for a co-datatype).
+ ResolveExpression(e.Obj, twoState);
+ Contract.Assert(e.Obj.Type != null); // follows from postcondition of ResolveExpression
+ Expression resolved = ResolvePredicateOrField(expr.tok, e.Obj, e.SuffixName);
+ if (resolved == null) {
+ // error has already been reported by ResolvePredicateOrField
+ } else {
+ // the following will cause e.Obj to be resolved again, but that's still correct
+ e.ResolvedExpression = resolved;
+ ResolveExpression(e.ResolvedExpression, twoState);
+ e.Type = e.ResolvedExpression.Type;
+ }
+
+ } else if (expr is FieldSelectExpr) {
+ var e = (FieldSelectExpr)expr;
+ ResolveExpression(e.Obj, twoState);
+ Contract.Assert(e.Obj.Type != null); // follows from postcondition of ResolveExpression
+ NonProxyType nptype;
+ MemberDecl member = ResolveMember(expr.tok, e.Obj.Type, e.FieldName, out nptype);
+#if !NO_WORK_TO_BE_DONE
+ UserDefinedType ctype = (UserDefinedType)nptype;
+#endif
+ if (member == null) {
+ // error has already been reported by ResolveMember
+ } else if (!(member is Field)) {
+ Error(expr, "member {0} in type {1} does not refer to a field", e.FieldName, cce.NonNull(ctype).Name);
+ } else {
+ Contract.Assert(ctype != null && ctype.ResolvedClass != null); // follows from postcondition of ResolveMember
+ e.Field = (Field)member;
+ if (e.Obj is StaticReceiverExpr) {
+ Error(expr, "a field must be selected via an object, not just a class name");
+ }
+ // build the type substitution map
+ Dictionary<TypeParameter, Type> subst = new Dictionary<TypeParameter, Type>();
+ for (int i = 0; i < ctype.TypeArgs.Count; i++) {
+ subst.Add(ctype.ResolvedClass.TypeArgs[i], ctype.TypeArgs[i]);
+ }
+ e.Type = SubstType(e.Field.Type, subst);
+ }
+
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ ResolveSeqSelectExpr(e, twoState, true);
+
+ } else if (expr is MultiSelectExpr) {
+ MultiSelectExpr e = (MultiSelectExpr)expr;
+
+ ResolveExpression(e.Array, twoState);
+ Contract.Assert(e.Array.Type != null); // follows from postcondition of ResolveExpression
+ Type elementType = new InferredTypeProxy();
+ if (!UnifyTypes(e.Array.Type, builtIns.ArrayType(e.Indices.Count, elementType))) {
+ Error(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, twoState);
+ Contract.Assert(idx.Type != null); // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(idx.Type, Type.Int)) {
+ Error(idx, "array selection requires integer indices (got {0} for index {1})", idx.Type, i);
+ }
+ i++;
+ }
+ e.Type = elementType;
+
+ } else if (expr is SeqUpdateExpr) {
+ SeqUpdateExpr e = (SeqUpdateExpr)expr;
+ ResolveExpression(e.Seq, twoState);
+ Contract.Assert(e.Seq.Type != null); // follows from postcondition of ResolveExpression
+ Type elementType = new InferredTypeProxy();
+ Type domainType = new InferredTypeProxy();
+ Type rangeType = new InferredTypeProxy();
+ if (UnifyTypes(e.Seq.Type, new SeqType(elementType))) {
+ ResolveExpression(e.Index, twoState);
+ 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);
+ }
+ ResolveExpression(e.Value, twoState);
+ 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);
+ }
+ expr.Type = e.Seq.Type;
+ } else if (UnifyTypes(e.Seq.Type, new MapType(domainType, rangeType))) {
+ ResolveExpression(e.Index, twoState);
+ 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);
+ }
+ ResolveExpression(e.Value, twoState);
+ 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);
+ }
+ expr.Type = e.Seq.Type;
+ } else {
+ Error(expr, "update requires a sequence or map (got {0})", e.Seq.Type);
+ }
+
+
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ ResolveFunctionCallExpr(e, twoState, false);
+
+ } else if (expr is OldExpr) {
+ OldExpr e = (OldExpr)expr;
+ if (!twoState) {
+ Error(expr, "old expressions are not allowed in this context");
+ }
+ ResolveExpression(e.E, twoState, null);
+ expr.Type = e.E.Type;
+
+ } else if (expr is MultiSetFormingExpr) {
+ MultiSetFormingExpr e = (MultiSetFormingExpr)expr;
+ ResolveExpression(e.E, twoState);
+ 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.");
+ }
+ expr.Type = new MultiSetType(((CollectionType)e.E.Type).Arg);
+ } else if (expr is FreshExpr) {
+ FreshExpr e = (FreshExpr)expr;
+ if (!twoState) {
+ Error(expr, "fresh expressions are not allowed in this context");
+ }
+ ResolveExpression(e.E, twoState);
+ // the type of e.E must be either an object or a collection of objects
+ Type t = e.E.Type;
+ Contract.Assert(t != null); // follows from postcondition of ResolveExpression
+ if (t is CollectionType) {
+ t = ((CollectionType)t).Arg;
+ }
+ if (t is ObjectType) {
+ // fine
+ } else if (UserDefinedType.DenotesClass(t) != null) {
+ // fine
+ } 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);
+ }
+ expr.Type = Type.Bool;
+
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ ResolveExpression(e.E, twoState);
+ Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression
+ switch (e.Op) {
+ case UnaryExpr.Opcode.Not:
+ if (!UnifyTypes(e.E.Type, Type.Bool)) {
+ Error(expr, "logical negation expects a boolean argument (instead got {0})", e.E.Type);
+ }
+ expr.Type = Type.Bool;
+ break;
+ case UnaryExpr.Opcode.SetChoose:
+ var elType = new InferredTypeProxy();
+ if (!UnifyTypes(e.E.Type, new SetType(elType))) {
+ Error(expr, "choose operator expects a set argument (instead got {0})", e.E.Type);
+ }
+ expr.Type = elType;
+ break;
+ case UnaryExpr.Opcode.SeqLength:
+ if (!UnifyTypes(e.E.Type, new SeqType(new InferredTypeProxy()))) {
+ Error(expr, "length operator expects a sequence argument (instead got {0})", e.E.Type);
+ }
+ expr.Type = Type.Int;
+ break;
+ default:
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected unary operator
+ }
+
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ ResolveExpression(e.E0, twoState);
+ Contract.Assert(e.E0.Type != null); // follows from postcondition of ResolveExpression
+ ResolveExpression(e.E1, twoState);
+ Contract.Assert(e.E1.Type != null); // follows from postcondition of ResolveExpression
+ switch (e.Op) {
+ case BinaryExpr.Opcode.Iff:
+ case BinaryExpr.Opcode.Imp:
+ 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);
+ }
+ expr.Type = Type.Bool;
+ break;
+
+ case BinaryExpr.Opcode.Eq:
+ case BinaryExpr.Opcode.Neq:
+ if (!ComparableTypes(e.E0.Type, e.E1.Type)) {
+ 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);
+ }
+ }
+ 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())) &&
+ !UnifyTypes(e.E0.Type, new MultiSetType(new InferredTypeProxy())) &&
+ !UnifyTypes(e.E0.Type, new MapType(new InferredTypeProxy(), new InferredTypeProxy()))) {
+ Error(expr, "arguments must be of a [multi]set or map type (got {0})", e.E0.Type);
+ }
+ expr.Type = Type.Bool;
+ break;
+
+ case BinaryExpr.Opcode.Lt:
+ case BinaryExpr.Opcode.Le:
+ case BinaryExpr.Opcode.Add: {
+ if (e.Op == BinaryExpr.Opcode.Lt && e.E0.Type.IsIndDatatype) {
+ if (!UnifyTypes(e.E1.Type, new DatatypeProxy())) {
+ 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.IsIndDatatype) {
+ if (!UnifyTypes(e.E0.Type, new DatatypeProxy())) {
+ Error(expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E0.Type);
+ }
+ expr.Type = Type.Bool;
+ } else {
+ bool err = false;
+ if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true))) {
+ Error(expr, "arguments to {0} must be int or a collection type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
+ 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;
+ }
+ if (e.Op != BinaryExpr.Opcode.Add) {
+ expr.Type = Type.Bool;
+ } else if (!err) {
+ expr.Type = e.E0.Type;
+ }
+ }
+ }
+ break;
+
+ case BinaryExpr.Opcode.Sub:
+ case BinaryExpr.Opcode.Mul:
+ case BinaryExpr.Opcode.Gt:
+ case BinaryExpr.Opcode.Ge: {
+ if (e.Op == BinaryExpr.Opcode.Gt && e.E0.Type.IsIndDatatype) {
+ if (!UnifyTypes(e.E1.Type, new DatatypeProxy())) {
+ Error(expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type);
+ }
+ expr.Type = Type.Bool;
+ } else {
+ bool err = false;
+ if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(false))) {
+ Error(expr, "arguments to {0} must be int or a set (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
+ 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;
+ }
+ if (e.Op == BinaryExpr.Opcode.Gt || e.Op == BinaryExpr.Opcode.Ge) {
+ expr.Type = Type.Bool;
+ } else if (!err) {
+ expr.Type = e.E0.Type;
+ }
+ }
+ }
+ break;
+
+ case BinaryExpr.Opcode.In:
+ case BinaryExpr.Opcode.NotIn:
+ if (!UnifyTypes(e.E1.Type, new CollectionTypeProxy(e.E0.Type))) {
+ Error(expr, "second argument to \"{0}\" must be a set 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:
+ case BinaryExpr.Opcode.Mod:
+ if (!UnifyTypes(e.E0.Type, Type.Int)) {
+ 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, Type.Int)) {
+ Error(expr, "second argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type);
+ }
+ expr.Type = Type.Int;
+ break;
+
+ default:
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected operator
+ }
+ e.ResolvedOp = ResolveOp(e.Op, e.E1.Type);
+
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ foreach (var rhs in e.RHSs) {
+ ResolveExpression(rhs, twoState);
+ }
+ scope.PushMarker();
+ if (e.Vars.Count != e.RHSs.Count) {
+ Error(expr, "let expression must have same number of bound variables (found {0}) as RHSs (found {1})", e.Vars.Count, e.RHSs.Count);
+ }
+ int i = 0;
+ foreach (var v in e.Vars) {
+ if (!scope.Push(v.Name, v)) {
+ Error(v, "Duplicate let-variable name: {0}", v.Name);
+ }
+ ResolveType(v.tok, v.Type, null, true);
+ if (i < e.RHSs.Count && !UnifyTypes(v.Type, e.RHSs[i].Type)) {
+ Error(e.RHSs[i].tok, "type of RHS ({0}) does not match type of bound variable ({1})", e.RHSs[i].Type, v.Type);
+ }
+ i++;
+ }
+ ResolveExpression(e.Body, twoState);
+ scope.PopMarker();
+ expr.Type = e.Body.Type;
+
+ } else if (expr is NamedExpr) {
+ var e = (NamedExpr)expr;
+ ResolveExpression(e.Body, twoState);
+ if (e.Contract != null) ResolveExpression(e.Contract, twoState);
+ e.Type = e.Body.Type;
+ } else if (expr is QuantifierExpr) {
+ QuantifierExpr e = (QuantifierExpr)expr;
+ int prevErrorCount = ErrorCount;
+ scope.PushMarker();
+ foreach (BoundVar v in e.BoundVars) {
+ if (!scope.Push(v.Name, v)) {
+ Error(v, "Duplicate bound-variable name: {0}", v.Name);
+ }
+ ResolveType(v.tok, v.Type, null, true);
+ }
+ if (e.Range != null) {
+ ResolveExpression(e.Range, twoState);
+ 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);
+ }
+ }
+ ResolveExpression(e.Term, twoState);
+ 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);
+ }
+ // 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, twoState);
+ scope.PopMarker();
+ expr.Type = Type.Bool;
+
+ if (prevErrorCount == ErrorCount) {
+ var missingBounds = new List<BoundVar>();
+ 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 (currentFunction != null) {
+ 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;
+ scope.PushMarker();
+ foreach (BoundVar v in e.BoundVars) {
+ if (!scope.Push(v.Name, v)) {
+ Error(v, "Duplicate bound-variable name: {0}", v.Name);
+ }
+ ResolveType(v.tok, v.Type, null, true);
+ }
+ ResolveExpression(e.Range, twoState);
+ 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);
+ }
+ ResolveExpression(e.Term, twoState);
+ Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression
+
+ ResolveAttributes(e.Attributes, twoState);
+ scope.PopMarker();
+ expr.Type = new SetType(e.Term.Type);
+
+ if (prevErrorCount == ErrorCount) {
+ var missingBounds = new List<BoundVar>();
+ 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(expr, "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);
+ }
+ }
+ }
+
+ } else if (expr is MapComprehension) {
+ var e = (MapComprehension)expr;
+ int prevErrorCount = ErrorCount;
+ scope.PushMarker();
+ if (e.BoundVars.Count != 1) {
+ Error(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);
+ }
+ ResolveType(v.tok, v.Type, null, true);
+ }
+ ResolveExpression(e.Range, twoState);
+ 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);
+ }
+ ResolveExpression(e.Term, twoState);
+ Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression
+
+ ResolveAttributes(e.Attributes, twoState);
+ scope.PopMarker();
+ expr.Type = new MapType(e.BoundVars[0].Type, e.Term.Type);
+
+ if (prevErrorCount == ErrorCount) {
+ var missingBounds = new List<BoundVar>();
+ 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(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 WildcardExpr) {
+ expr.Type = new SetType(new ObjectType());
+
+ } else if (expr is PredicateExpr) {
+ var e = (PredicateExpr)expr;
+ ResolveExpression(e.Guard, twoState);
+ Contract.Assert(e.Guard.Type != null); // follows from postcondition of ResolveExpression
+ ResolveExpression(e.Body, twoState);
+ Contract.Assert(e.Body.Type != null); // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(e.Guard.Type, Type.Bool)) {
+ Error(expr, "guard condition in {0} expression must be a boolean (instead got {1})", e.Kind, e.Guard.Type);
+ }
+ expr.Type = e.Body.Type;
+
+ } else if (expr is ITEExpr) {
+ ITEExpr e = (ITEExpr)expr;
+ ResolveExpression(e.Test, twoState);
+ Contract.Assert(e.Test.Type != null); // follows from postcondition of ResolveExpression
+ ResolveExpression(e.Thn, twoState);
+ Contract.Assert(e.Thn.Type != null); // follows from postcondition of ResolveExpression
+ ResolveExpression(e.Els, twoState);
+ 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)) {
+ 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) {
+ MatchExpr me = (MatchExpr)expr;
+ Contract.Assert(!twoState); // currently, match expressions are allowed only at the outermost level of function bodies
+ if (matchVarContext == null) {
+ Error(me, "'match' expressions are not supported in this context");
+ matchVarContext = new List<IVariable>();
+ }
+ ResolveExpression(me.Source, twoState);
+ Contract.Assert(me.Source.Type != null); // follows from postcondition of ResolveExpression
+ UserDefinedType sourceType = null;
+ DatatypeDecl dtd = null;
+ Dictionary<TypeParameter, Type> subst = new Dictionary<TypeParameter, Type>();
+ if (me.Source.Type.IsDatatype) {
+ sourceType = (UserDefinedType)me.Source.Type;
+ dtd = cce.NonNull((DatatypeDecl)sourceType.ResolvedClass);
+ }
+ Dictionary<string, DatatypeCtor> ctors;
+ IVariable goodMatchVariable = null;
+ 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;
+ } 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
+
+ IdentifierExpr ie = me.Source.Resolved as IdentifierExpr;
+ if (ie == null || !(ie.Var is Formal || ie.Var is BoundVar)) {
+ Error(me.Source.tok, "match source expression must be a formal parameter of the enclosing function or an enclosing match expression");
+ } else if (!matchVarContext.Contains(ie.Var)) {
+ Error(me.Source.tok, "match source expression '{0}' has already been used as a match source expression in this context", ie.Var.Name);
+ } else {
+ goodMatchVariable = ie.Var;
+ }
+
+ // 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]);
+ }
+ }
+
+ Dictionary<string, object> memberNamesUsed = new Dictionary<string, object>(); // this is really a set
+ 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);
+ } 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.ContainsKey(mc.Id)) {
+ Error(mc.tok, "member {0} appears in more than one case", mc.Id);
+ } else {
+ memberNamesUsed.Add(mc.Id, null); // add mc.Id to the set of names used
+ }
+ }
+ }
+ scope.PushMarker();
+ int i = 0;
+ foreach (BoundVar v in mc.Arguments) {
+ if (!scope.Push(v.Name, v)) {
+ Error(v, "Duplicate parameter name: {0}", v.Name);
+ }
+ ResolveType(v.tok, v.Type, null, true);
+ 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);
+ }
+ v.IsGhost = formal.IsGhost;
+ }
+ i++;
+ }
+ List<IVariable> innerMatchVarContext = new List<IVariable>(matchVarContext);
+ if (goodMatchVariable != null) {
+ innerMatchVarContext.Remove(goodMatchVariable); // this variable is no longer available for matching
+ }
+ innerMatchVarContext.AddRange(mc.Arguments);
+ ResolveExpression(mc.Body, twoState, innerMatchVarContext);
+ 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);
+ }
+ scope.PopMarker();
+ }
+ 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.ContainsKey(ctr.Name)) {
+ me.MissingCases.Add(ctr);
+ }
+ }
+ Contract.Assert(memberNamesUsed.Count + me.MissingCases.Count == dtd.Ctors.Count);
+ }
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
+ }
+
+ if (expr.Type == null) {
+ // some resolution error occurred
+ expr.Type = Type.Flexible;
+ }
+ }
+
+ private void ResolveDatatypeValue(bool twoState, DatatypeValue dtv, DatatypeDecl dt) {
+ // this resolution is a little special, in that the syntax shows only the base name, not its instantiation (which is inferred)
+ List<Type> gt = new List<Type>(dt.TypeArgs.Count);
+ Dictionary<TypeParameter, Type> subst = new Dictionary<TypeParameter, Type>();
+ for (int i = 0; i < dt.TypeArgs.Count; i++) {
+ Type t = new InferredTypeProxy();
+ gt.Add(t);
+ dtv.InferredTypeArgs.Add(t);
+ subst.Add(dt.TypeArgs[i], t);
+ }
+ // Construct a resolved type directly, as we know the declaration is dt.
+ dtv.Type = new UserDefinedType(dtv.tok, dtv.DatatypeName, dt, gt);
+
+ DatatypeCtor ctor;
+ if (!datatypeCtors[dt].TryGetValue(dtv.MemberName, out ctor)) {
+ Error(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})", dtv.DatatypeName, dtv.Arguments.Count, ctor.Formals.Count);
+ }
+ }
+ int j = 0;
+ foreach (Expression arg in dtv.Arguments) {
+ Formal formal = ctor != null && j < ctor.Formals.Count ? ctor.Formals[j] : null;
+ ResolveExpression(arg, twoState, null);
+ 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);
+ }
+ }
+ j++;
+ }
+ }
+
+ private bool ComparableTypes(Type A, Type B) {
+ if (A.IsArrayType && B.IsArrayType) {
+ Type a = UserDefinedType.ArrayElementType(A);
+ Type b = UserDefinedType.ArrayElementType(B);
+ return CouldPossiblyBeSameType(a, b);
+ } else
+ if (A is UserDefinedType && B is UserDefinedType) {
+ UserDefinedType a = (UserDefinedType)A;
+ UserDefinedType b = (UserDefinedType)B;
+ if (a.ResolvedClass != null && b.ResolvedClass != null && a.ResolvedClass.Name == b.ResolvedClass.Name) {
+ if (a.TypeArgs.Count != b.TypeArgs.Count) {
+ return false; // this probably doesn't happen if the classes are the same.
+ }
+ for (int i = 0; i < a.TypeArgs.Count; i++) {
+ if (!CouldPossiblyBeSameType(a.TypeArgs[i], b.TypeArgs[i]))
+ return false;
+ }
+ // all parameters could be the same
+ return true;
+ }
+ // either we don't know what class it is yet, or the classes mismatch
+ return false;
+ }
+ return false;
+ }
+ private bool CouldPossiblyBeSameType(Type A, Type B) {
+ if (A.IsTypeParameter || B.IsTypeParameter) {
+ return true;
+ }
+ if (A.IsArrayType && B.IsArrayType) {
+ Type a = UserDefinedType.ArrayElementType(A);
+ Type b = UserDefinedType.ArrayElementType(B);
+ return CouldPossiblyBeSameType(a, b);
+ }
+ if (A is UserDefinedType && B is UserDefinedType) {
+ UserDefinedType a = (UserDefinedType)A;
+ UserDefinedType b = (UserDefinedType)B;
+ if (a.ResolvedClass != null && b.ResolvedClass != null && a.ResolvedClass == b.ResolvedClass) {
+ if (a.TypeArgs.Count != b.TypeArgs.Count) {
+ return false; // this probably doesn't happen if the classes are the same.
+ }
+ for (int i = 0; i < a.TypeArgs.Count; i++) {
+ if (!CouldPossiblyBeSameType(a.TypeArgs[i], b.TypeArgs[i]))
+ return false;
+ }
+ // all parameters could be the same
+ return true;
+ }
+ // either we don't know what class it is yet, or the classes mismatch
+ return false;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Generate an error for every non-ghost feature used in "expr".
+ /// Requires "expr" to have been successfully resolved.
+ /// </summary>
+ void CheckIsNonGhost(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");
+ return;
+ }
+
+ } else if (expr is FieldSelectExpr) {
+ var e = (FieldSelectExpr)expr;
+ if (e.Field != null && e.Field.IsGhost) {
+ Error(expr, "ghost fields are allowed only in specification contexts");
+ return;
+ }
+
+ } else if (expr is FunctionCallExpr) {
+ 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')");
+ return;
+ }
+ // function is okay, so check all NON-ghost arguments
+ CheckIsNonGhost(e.Receiver);
+ for (int i = 0; i < e.Function.Formals.Count; i++) {
+ if (!e.Function.Formals[i].IsGhost) {
+ CheckIsNonGhost(e.Args[i]);
+ }
+ }
+ }
+ return;
+
+ } else if (expr is DatatypeValue) {
+ 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) {
+ CheckIsNonGhost(e.Arguments[i]);
+ }
+ }
+ return;
+
+ } else if (expr is OldExpr) {
+ Error(expr, "old expressions are allowed only in specification and ghost contexts");
+ return;
+
+ } else if (expr is FreshExpr) {
+ Error(expr, "fresh expressions are allowed only in specification and ghost contexts");
+ return;
+
+ } else if (expr is PredicateExpr) {
+ var e = (PredicateExpr)expr;
+ // ignore the guard
+ CheckIsNonGhost(e.Body);
+ return;
+
+ } else if (expr is BinaryExpr) {
+ var e = (BinaryExpr)expr;
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.RankGt:
+ case BinaryExpr.ResolvedOpcode.RankLt:
+ Error(expr, "rank comparisons are allowed only in specification and ghost contexts");
+ return;
+ default:
+ break;
+ }
+
+ } 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);
+ }
+ return;
+ }
+ } else if (expr is NamedExpr) {
+ if (!moduleInfo.IsGhost)
+ CheckIsNonGhost(((NamedExpr)expr).Body);
+ return;
+ }
+
+ foreach (var ee in expr.SubExpressions) {
+ CheckIsNonGhost(ee);
+ }
+ }
+
+ /// <summary>
+ /// If "!allowMethodCall" or if what is being called does not refer to a method, resolves "e" and returns "null".
+ /// Otherwise (that is, if "allowMethodCall" and what is being called refers to a method), resolves the receiver
+ /// of "e" but NOT the arguments, and returns a CallRhs corresponding to the call.
+ /// </summary>
+ CallRhs ResolveFunctionCallExpr(FunctionCallExpr e, bool twoState, bool allowMethodCall) {
+ ResolveReceiver(e.Receiver, twoState);
+ Contract.Assert(e.Receiver.Type != null); // follows from postcondition of ResolveExpression
+ NonProxyType nptype;
+ MemberDecl member = ResolveMember(e.tok, e.Receiver.Type, e.Name, out nptype);
+#if !NO_WORK_TO_BE_DONE
+ UserDefinedType ctype = (UserDefinedType)nptype;
+#endif
+ if (member == null) {
+ // error has already been reported by ResolveMember
+ } else if (member is Method) {
+ if (allowMethodCall) {
+ // it's a method
+ return new CallRhs(e.tok, e.Receiver, e.Name, e.Args);
+ } else {
+ 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);
+ }
+ } else if (!(member is Function)) {
+ Error(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;
+ if (function is CoPredicate) {
+ ((CoPredicate)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");
+ }
+ if (function.Formals.Count != e.Args.Count) {
+ Error(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) {
+ if (!scope.AllowInstance && e.Receiver is ThisExpr) {
+ // The call really needs an instance, but that instance is given as 'this', which is not
+ // available in this context. In most cases, occurrences of 'this' inside e.Receiver would
+ // have been caught in the recursive call to resolve e.Receiver, but not the specific case
+ // of e.Receiver being 'this' (explicitly or implicitly), for that case needs to be allowed
+ // 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");
+ } else if (e.Receiver is StaticReceiverExpr) {
+ Error(e.Receiver, "call to instance function requires an instance");
+ }
+ }
+ // build the type substitution map
+ e.TypeArgumentSubstitutions = new Dictionary<TypeParameter, Type>();
+ for (int i = 0; i < ctype.TypeArgs.Count; i++) {
+ e.TypeArgumentSubstitutions.Add(cce.NonNull(ctype.ResolvedClass).TypeArgs[i], ctype.TypeArgs[i]);
+ }
+ foreach (TypeParameter p in function.TypeArgs) {
+ e.TypeArgumentSubstitutions.Add(p, new ParamTypeProxy(p));
+ }
+ // type check the arguments
+ for (int i = 0; i < function.Formals.Count; i++) {
+ Expression farg = e.Args[i];
+ ResolveExpression(farg, twoState);
+ 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);
+ }
+ }
+ e.Type = SubstType(function.ResultType, e.TypeArgumentSubstitutions);
+ }
+
+ // Resolution termination check
+ if (currentFunction != null && currentFunction.EnclosingClass != null && function.EnclosingClass != null) {
+ ModuleDefinition callerModule = currentFunction.EnclosingClass.Module;
+ ModuleDefinition calleeModule = function.EnclosingClass.Module;
+ if (callerModule == calleeModule) {
+ // intra-module call; this is allowed; add edge in module's call graph
+ callerModule.CallGraph.AddEdge(currentFunction, function);
+ if (currentFunction == function) {
+ currentFunction.IsRecursive = true; // self recursion (mutual recursion is determined elsewhere)
+ }
+ } else {
+ //Contract.Assert(dependencies.Reaches(callerModule, calleeModule));
+ }
+ }
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// If "!allowMethodCall", or if "e" does not designate a method call, resolves "e" and returns "null".
+ /// Otherwise, resolves all sub-parts of "e" and returns a (resolved) CallRhs expression representing the call.
+ /// </summary>
+ CallRhs ResolveIdentifierSequence(IdentifierSequence e, bool twoState, bool allowMethodCall) {
+ // Look up "id" as follows:
+ // - local variable, parameter, or bound variable (if this clashes with something of interest, one can always rename the local variable locally)
+ // - unamibugous type/module name (class, datatype, sub-module (including submodules of imports) or arbitrary-type)
+ // (if two imported types have the same name, an error message is produced here)
+ // - unambiguous constructor name of a datatype (if two constructors have the same name, an error message is produced here)
+ // - field, function or method name (with implicit receiver) (if the field is occluded by anything above, one can use an explicit "this.")
+ // - iterator
+ // - static function or method in the enclosing module, or its imports.
+
+ Expression r = null; // resolved version of e
+ CallRhs call = null;
+
+ TopLevelDecl decl;
+ Tuple<DatatypeCtor, bool> pair;
+ Dictionary<string, MemberDecl> members;
+ MemberDecl member;
+ var id = e.Tokens[0];
+ if (scope.Find(id.val) != null) {
+ // ----- root is a local variable, parameter, or bound variable
+ r = new IdentifierExpr(id, id.val);
+ ResolveExpression(r, twoState);
+ r = ResolveSuffix(r, e, 1, twoState, allowMethodCall, out call);
+
+ } else if (moduleInfo.TopLevels.TryGetValue(id.val, out decl)) {
+ if (decl is AmbiguousTopLevelDecl) {
+ Error(id, "The name {0} ambiguously refers to a type in one of the modules {1}", id.val, ((AmbiguousTopLevelDecl)decl).ModuleNames());
+ } else if (e.Tokens.Count == 1 && e.Arguments == null) {
+ Error(id, "name of type ('{0}') is used as a variable", id.val);
+ } else if (e.Tokens.Count == 1 && e.Arguments != null) {
+ Error(id, "name of type ('{0}') is used as a function", id.val);
+ // resolve the arguments nonetheless
+ foreach (var arg in e.Arguments) {
+ ResolveExpression(arg, twoState);
+ }
+ } else if (decl is ClassDecl) {
+ // ----- root is a class
+ var cd = (ClassDecl)decl;
+ r = ResolveSuffix(new StaticReceiverExpr(id, cd), e, 1, twoState, allowMethodCall, out call);
+
+ } else if (decl is ModuleDecl) {
+ // ----- root is a submodule
+ if (!(1 < e.Tokens.Count)) {
+ Error(e.tok, "module {0} cannot be used here", ((ModuleDecl)decl).Name);
+ }
+ call = ResolveIdentifierSequenceModuleScope(e, 1, ((ModuleDecl)decl).Signature, twoState, allowMethodCall);
+ } else {
+ // ----- root is a datatype
+ var dt = (DatatypeDecl)decl; // otherwise, unexpected TopLevelDecl
+ var args = (e.Tokens.Count == 2 ? e.Arguments : null) ?? new List<Expression>();
+ r = new DatatypeValue(id, id.val, e.Tokens[1].val, args);
+ ResolveExpression(r, twoState);
+ if (e.Tokens.Count != 2) {
+ r = ResolveSuffix(r, e, 2, twoState, allowMethodCall, out call);
+ }
+ }
+
+ } else if (moduleInfo.Ctors.TryGetValue(id.val, out pair)) {
+ // ----- root is a datatype constructor
+ if (pair.Item2) {
+ // there is more than one constructor with this name
+ Error(id, "the name '{0}' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, '{1}.{0}')", id.val, pair.Item1.EnclosingDatatype.Name);
+ } else {
+ var args = (e.Tokens.Count == 1 ? e.Arguments : null) ?? new List<Expression>();
+ r = new DatatypeValue(id, pair.Item1.EnclosingDatatype.Name, id.val, args);
+ ResolveExpression(r, twoState);
+ if (e.Tokens.Count != 1) {
+ r = ResolveSuffix(r, e, 1, twoState, allowMethodCall, out call);
+ }
+ }
+
+ } else if ((currentClass != null && classMembers.TryGetValue(currentClass, out members) && members.TryGetValue(id.val, out member))
+ || moduleInfo.StaticMembers.TryGetValue(id.val, out member)) // try static members of the current module too.
+ {
+ // ----- field, function, or method
+ if (member is AmbiguousMemberDecl) {
+ Contract.Assert(member.IsStatic); // currently, static members of _default are the only thing which can be ambiguous.
+ Error(id, "The name {0} ambiguously refers to a static member in one of the modules {1}", id.val, ((AmbiguousMemberDecl)member).ModuleNames());
+ } else {
+ Expression receiver;
+ if (member.IsStatic) {
+ receiver = new StaticReceiverExpr(id, (ClassDecl)member.EnclosingClass);
+ } else {
+ if (!scope.AllowInstance) {
+ Error(id, "'this' is not allowed in a 'static' context");
+ // nevertheless, set "receiver" to a value so we can continue resolution
+ }
+ receiver = new ImplicitThisExpr(id);
+ receiver.Type = GetThisType(id, (ClassDecl)member.EnclosingClass); // resolve here
+ }
+ r = ResolveSuffix(receiver, e, 0, twoState, allowMethodCall, out call);
+ }
+
+ } else {
+ Error(id, "unresolved identifier: {0}", id.val);
+ // resolve arguments, if any
+ if (e.Arguments != null) {
+ foreach (var arg in e.Arguments) {
+ ResolveExpression(arg, twoState);
+ }
+ }
+ }
+
+ if (r != null) {
+ e.ResolvedExpression = r;
+ e.Type = r.Type;
+ }
+ return call;
+ }
+
+ CallRhs ResolveIdentifierSequenceModuleScope(IdentifierSequence e, int p, ModuleSignature sig, bool twoState, bool allowMethodCall) {
+ // Look up "id" as follows:
+ // - unamibugous type/module name (class, datatype, sub-module (including submodules of imports) or arbitrary-type)
+ // (if two imported types have the same name, an error message is produced here)
+ // - static function or method of sig.
+ // This is used to look up names that appear after a dot in a sequence identifier. For example, in X.M.*, M should be looked up in X, but
+ // should not consult the local scope. This distingushes this from the above, in that local scope, imported modules, etc. are ignored.
+ Contract.Requires(p < e.Tokens.Count);
+ Expression r = null; // resolved version of e
+ CallRhs call = null;
+
+ TopLevelDecl decl;
+ MemberDecl member;
+ Tuple<DatatypeCtor, bool> pair;
+ var id = e.Tokens[p];
+ sig = GetSignature(sig);
+ if (sig.TopLevels.TryGetValue(id.val, out decl)) {
+ if (decl is AmbiguousTopLevelDecl) {
+ Error(id, "The name {0} ambiguously refers to a something in one of the modules {1}", id.val, ((AmbiguousTopLevelDecl)decl).ModuleNames());
+ } else if (decl is ClassDecl) {
+ // ----- root is a class
+ var cd = (ClassDecl)decl;
+ r = ResolveSuffix(new StaticReceiverExpr(id, cd), e, p + 1, twoState, allowMethodCall, out call);
+
+ } else if (decl is ModuleDecl) {
+ // ----- root is a submodule
+ if (!(p + 1 < e.Tokens.Count)) {
+ Error(e.tok, "module {0} cannot be used here", ((ModuleDecl)decl).Name);
+ }
+ call = ResolveIdentifierSequenceModuleScope(e, p + 1, ((ModuleDecl)decl).Signature, twoState, allowMethodCall);
+ } else {
+ // ----- root is a datatype
+ var dt = (DatatypeDecl)decl; // otherwise, unexpected TopLevelDecl
+ var args = (e.Tokens.Count == p + 2 ? e.Arguments : null) ?? new List<Expression>();
+ r = new DatatypeValue(id, id.val, e.Tokens[p + 1].val, args);
+ ResolveDatatypeValue(twoState, (DatatypeValue)r, dt);
+ if (e.Tokens.Count != p + 2) {
+ r = ResolveSuffix(r, e, p + 2, twoState, allowMethodCall, out call);
+ }
+ }
+ } else if (sig.Ctors.TryGetValue(id.val, out pair)) {
+ // ----- root is a datatype constructor
+ if (pair.Item2) {
+ // there is more than one constructor with this name
+ Error(id, "the name '{0}' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, '{1}.{0}')", id.val, pair.Item1.EnclosingDatatype.Name);
+ } else {
+ var dt = pair.Item1.EnclosingDatatype;
+ var args = (e.Tokens.Count == p + 1 ? e.Arguments : null) ?? new List<Expression>();
+ r = new DatatypeValue(id, dt.Name, id.val, args);
+ ResolveDatatypeValue(twoState, (DatatypeValue)r, dt);
+ if (e.Tokens.Count != p + 1) {
+ r = ResolveSuffix(r, e, p + 1, twoState, allowMethodCall, out call);
+ }
+ }
+ } else if (sig.StaticMembers.TryGetValue(id.val, out member)) // try static members of the current module too.
+ {
+ // ----- function, or method
+ Expression receiver;
+ Contract.Assert(member.IsStatic);
+ receiver = new StaticReceiverExpr(id, (ClassDecl)member.EnclosingClass);
+ r = ResolveSuffix(receiver, e, p, twoState, allowMethodCall, out call);
+
+ } else {
+ Error(id, "unresolved identifier: {0}", id.val);
+ // resolve arguments, if any
+ if (e.Arguments != null) {
+ foreach (var arg in e.Arguments) {
+ ResolveExpression(arg, twoState);
+ }
+ }
+ }
+
+ if (r != null) {
+ e.ResolvedExpression = r;
+ e.Type = r.Type;
+ }
+ return call;
+ }
+
+ private ModuleSignature GetSignature(ModuleSignature sig) {
+ if (useCompileSignatures) {
+ while (sig.CompileSignature != null)
+ sig = sig.CompileSignature;
+ }
+ return sig;
+ }
+ /// <summary>
+ /// Given resolved expression "r" and unresolved expressions e.Tokens[p..] and e.Arguments.
+ /// Returns a resolved version of the expression:
+ /// r . e.Tokens[p] . e.Tokens[p+1] ... . e.Tokens[e.Tokens.Count-1] ( e.Arguments )
+ /// Except, if "allowMethodCall" is "true" and the would-be-returned value designates a method
+ /// call, instead returns null and returns "call" as a non-null value.
+ /// </summary>
+ Expression ResolveSuffix(Expression r, IdentifierSequence e, int p, bool twoState, bool allowMethodCall, out CallRhs call) {
+ Contract.Requires(r != null);
+ Contract.Requires(e != null);
+ Contract.Requires(0 <= p && p <= e.Tokens.Count);
+ Contract.Ensures((Contract.Result<Expression>() != null && Contract.ValueAtReturn(out call) == null) ||
+ (allowMethodCall && Contract.Result<Expression>() == null && Contract.ValueAtReturn(out call) != null));
+
+ call = null;
+ int nonCallArguments = e.Arguments == null ? e.Tokens.Count : e.Tokens.Count - 1;
+ for (; p < nonCallArguments; p++) {
+ var resolved = ResolvePredicateOrField(e.Tokens[p], r, e.Tokens[p].val);
+ if (resolved != null) {
+ r = resolved;
+ ResolveExpression(r, twoState, null);
+ }
+ }
+
+ if (p < e.Tokens.Count) {
+ Contract.Assert(e.Arguments != null);
+
+
+ bool itIsAMethod = false;
+ if (allowMethodCall) {
+ var udt = r.Type.Normalize() as UserDefinedType;
+ if (udt != null && udt.ResolvedClass != null) {
+ Dictionary<string, MemberDecl> members;
+ if (udt.ResolvedClass is ClassDecl) {
+ classMembers.TryGetValue((ClassDecl)udt.ResolvedClass, out members);
+ } else {
+ members = null;
+ }
+ MemberDecl member;
+ if (members != null && members.TryGetValue(e.Tokens[p].val, out member) && member is Method) {
+ itIsAMethod = true;
+ }
+ }
+ }
+ if (itIsAMethod) {
+ // method
+ call = new CallRhs(e.Tokens[p], r, e.Tokens[p].val, e.Arguments);
+ r = null;
+ } else {
+ r = new FunctionCallExpr(e.Tokens[p], e.Tokens[p].val, r, e.OpenParen, e.Arguments);
+ ResolveExpression(r, twoState, null);
+ }
+ } else if (e.Arguments != null) {
+ Contract.Assert(p == e.Tokens.Count);
+ Error(e.OpenParen, "non-function expression is called with parameters");
+ // resolve the arguments nonetheless
+ foreach (var arg in e.Arguments) {
+ ResolveExpression(arg, twoState);
+ }
+ }
+ return r;
+ }
+
+ /// <summary>
+ /// Resolves "obj . suffixName" to either a parameter-less predicate invocation or a field selection.
+ /// Expects "obj" already to have been resolved.
+ /// On success, returns the result of the resolution--as an un-resolved expression.
+ /// On failure, returns null (in which case an error has been reported to the user).
+ /// </summary>
+ Expression/*?*/ ResolvePredicateOrField(IToken tok, Expression obj, string suffixName) {
+ Contract.Requires(tok != null);
+ Contract.Requires(obj != null);
+ Contract.Requires(obj.Type != null); // obj is expected already to have been resolved
+ Contract.Requires(suffixName != null);
+
+ NonProxyType nptype;
+ MemberDecl member = ResolveMember(tok, obj.Type, suffixName, out nptype);
+ if (member == null) {
+ // error has already been reported by ResolveMember
+ return null;
+ } else if (member is Predicate && ((Predicate)member).Formals.Count == 0) {
+ // parameter-less predicates are allowed to be used without parentheses
+ return new FunctionCallExpr(tok, suffixName, obj, null, new List<Expression>());
+ } else {
+ // assume it's a field and let the resolution of the FieldSelectExpr check any further problems
+ return new FieldSelectExpr(tok, obj, suffixName);
+ }
+ }
+
+ /// <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 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.
+ /// </summary>
+ public static List<ComprehensionExpr.BoundedPool> DiscoverBounds(IToken tok, List<BoundVar> bvars, Expression expr, bool polarity, bool returnAllBounds, List<BoundVar> missingBounds) {
+ Contract.Requires(tok != null);
+ Contract.Requires(bvars != null);
+ Contract.Requires(missingBounds != null);
+ 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<ComprehensionExpr.BoundedPool>>() != null &&
+ Contract.Result<List<ComprehensionExpr.BoundedPool>>().Count == bvars.Count &&
+ Contract.OldValue(missingBounds.Count) == missingBounds.Count) ||
+ (!returnAllBounds &&
+ Contract.Result<List<ComprehensionExpr.BoundedPool>>() == null &&
+ Contract.OldValue(missingBounds.Count) < missingBounds.Count));
+
+ var bounds = new List<ComprehensionExpr.BoundedPool>();
+ bool foundError = false;
+ for (int j = 0; j < bvars.Count; j++) {
+ var bv = bvars[j];
+ if (bv.Type is BoolType) {
+ // easy
+ bounds.Add(new ComprehensionExpr.BoolBoundedPool());
+ } else if (bv.Type.IsIndDatatype && (bv.Type.AsIndDatatype).HasFinitePossibleValues) {
+ bounds.Add(new ComprehensionExpr.DatatypeBoundedPool(bv.Type.AsIndDatatype));
+ } else {
+ // Go through the conjuncts of the range expression to look for bounds.
+ Expression lowerBound = bv.Type is NatType ? new LiteralExpr(bv.tok, new BigInteger(0)) : null;
+ Expression upperBound = null;
+ bool foundBoundsForBv = false;
+ 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 && whereIsBv == 1) {
+ 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) {
+ bounds.Add(new ComprehensionExpr.SetBoundedPool(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, Plus(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.SuperSetBoundedPool(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 (whereIsBv == 0 && upperBound == null) {
+ upperBound = e1; // bv < E
+ } else if (whereIsBv == 1 && lowerBound == null) {
+ lowerBound = Plus(e0, 1); // E < bv
+ }
+ break;
+ case BinaryExpr.ResolvedOpcode.Le:
+ if (whereIsBv == 0 && upperBound == null) {
+ upperBound = Plus(e1, 1); // bv <= E
+ } else if (whereIsBv == 1 && lowerBound == null) {
+ lowerBound = e0; // E <= bv
+ }
+ break;
+ default:
+ break;
+ }
+ 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
+ missingBounds.Add(bv); // record failing bound variable
+ foundError = true;
+ }
+ }
+ 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)
+ }
+ return foundError ? null : bounds;
+ }
+
+ /// <summary>
+ /// If the return value is negative, the resulting "e0" and "e1" should not be used.
+ /// Otherwise, the following is true on return:
+ /// The new "e0 op e1" is equivalent to the old "e0 op e1".
+ /// One of "e0" and "e1" is the identifier "boundVars[bvi]"; the return value is either 0 or 1, and indicates which.
+ /// 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(List<BoundVar> boundVars, int bvi, BinaryExpr.ResolvedOpcode op, ref Expression e0, ref Expression e1) {
+ Contract.Requires(e0 != null);
+ Contract.Requires(e1 != null);
+ Contract.Requires(boundVars != null);
+ Contract.Requires(0 <= bvi && bvi < boundVars.Count);
+ Contract.Ensures(Contract.Result<int>() < 2);
+ Contract.Ensures(!(Contract.ValueAtReturn(out e0) is ConcreteSyntaxExpression));
+ Contract.Ensures(!(Contract.ValueAtReturn(out e1) is ConcreteSyntaxExpression));
+
+ var bv = boundVars[bvi];
+ e0 = e0.Resolved;
+ e1 = e1.Resolved;
+
+ // make an initial assessment of where bv is; to continue, we need bv to appear in exactly one operand
+ var fv0 = FreeVariables(e0);
+ var fv1 = FreeVariables(e1);
+ Expression thisSide;
+ Expression thatSide;
+ int whereIsBv;
+ if (fv0.Contains(bv)) {
+ if (fv1.Contains(bv)) {
+ return -1;
+ }
+ whereIsBv = 0;
+ thisSide = e0; thatSide = e1;
+ } else if (fv1.Contains(bv)) {
+ whereIsBv = 1;
+ thisSide = e1; thatSide = e0;
+ } else {
+ return -1;
+ }
+
+ // Next, clean up the side where bv is by adjusting both sides of the expression
+ switch (op) {
+ case BinaryExpr.ResolvedOpcode.EqCommon:
+ case BinaryExpr.ResolvedOpcode.NeqCommon:
+ case BinaryExpr.ResolvedOpcode.Gt:
+ case BinaryExpr.ResolvedOpcode.Ge:
+ case BinaryExpr.ResolvedOpcode.Le:
+ case BinaryExpr.ResolvedOpcode.Lt:
+ // Repeatedly move additive or subtractive terms from thisSide to thatSide
+ while (true) {
+ var bin = thisSide as BinaryExpr;
+ if (bin == null) {
+ break; // done simplifying
+
+ } else if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Add) {
+ // Change "A+B op C" into either "A op C-B" or "B op C-A", depending on where we find bv among A and B.
+ if (!FreeVariables(bin.E1).Contains(bv)) {
+ thisSide = bin.E0.Resolved;
+ thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Sub, thatSide, bin.E1);
+ } else {
+ thisSide = bin.E1.Resolved;
+ thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Sub, thatSide, bin.E0);
+ }
+ ((BinaryExpr)thatSide).ResolvedOp = BinaryExpr.ResolvedOpcode.Sub;
+ thatSide.Type = bin.Type;
+
+ } else if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Sub) {
+ // Change "A-B op C" in a similar way.
+ if (!FreeVariables(bin.E1).Contains(bv)) {
+ // change to "A op C+B"
+ thisSide = bin.E0.Resolved;
+ thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Add, thatSide, bin.E1);
+ ((BinaryExpr)thatSide).ResolvedOp = BinaryExpr.ResolvedOpcode.Add;
+ } else {
+ // In principle, change to "-B op C-A" and then to "B dualOp A-C". But since we don't want
+ // to change "op", we instead end with "A-C op B" and switch the mapping of thisSide/thatSide
+ // to e0/e1 (by inverting "whereIsBv").
+ thisSide = bin.E1.Resolved;
+ thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Sub, bin.E0, thatSide);
+ ((BinaryExpr)thatSide).ResolvedOp = BinaryExpr.ResolvedOpcode.Sub;
+ whereIsBv = 1 - whereIsBv;
+ }
+ thatSide.Type = bin.Type;
+
+ } else {
+ break; // done simplifying
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Now, see if the interesting side is simply bv itself
+ if (thisSide is IdentifierExpr && ((IdentifierExpr)thisSide).Var == bv) {
+ // we're cool
+ } else {
+ // no, the situation is more complicated than we care to understand
+ return -1;
+ }
+
+ // Finally, check that the other side does not contain "bv" or any of the bound variables
+ // listed after "bv" in the quantifier.
+ var fv = FreeVariables(thatSide);
+ for (int i = bvi; i < boundVars.Count; i++) {
+ if (fv.Contains(boundVars[i])) {
+ return -1;
+ }
+ }
+
+ // As we return, also return the adjusted sides
+ if (whereIsBv == 0) {
+ e0 = thisSide; e1 = thatSide;
+ } else {
+ e0 = thatSide; e1 = thisSide;
+ }
+ return whereIsBv;
+ }
+
+ /// <summary>
+ /// Returns all conjuncts of "expr" in "polarity" positions. That is, if "polarity" is "true", then
+ /// returns the conjuncts of "expr" in positive positions; else, returns the conjuncts of "expr" in
+ /// negative positions. The method considers a canonical-like form of the expression that pushes
+ /// negations inwards far enough that one can determine what the result is going to be (so, almost
+ /// a negation normal form).
+ /// As a convenience, arithmetic inequalities are rewritten so that the negation of an arithmetic
+ /// inequality is never returned and the comparisons > and >= are never returned; the negation of
+ /// a common equality or disequality is rewritten analogously.
+ /// Requires "expr" to be successfully resolved.
+ /// Ensures that what is returned is not a ConcreteSyntaxExpression.
+ /// </summary>
+ static IEnumerable<Expression> NormalizedConjuncts(Expression expr, bool polarity) {
+ // We consider 5 cases. To describe them, define P(e)=Conjuncts(e,true) and N(e)=Conjuncts(e,false).
+ // * X ==> Y is treated as a shorthand for !X || Y, and so is described by the remaining cases
+ // * X && Y P(_) = P(X),P(Y) and N(_) = !(X && Y)
+ // * X || Y P(_) = (X || Y) and N(_) = N(X),N(Y)
+ // * !X P(_) = N(X) and N(_) = P(X)
+ // * else P(_) = else and N(_) = !else
+ // So for ==>, we have:
+ // * X ==> Y P(_) = P(!X || Y) = (!X || Y) = (X ==> Y)
+ // N(_) = N(!X || Y) = N(!X),N(Y) = P(X),N(Y)
+ expr = expr.Resolved;
+
+ // Binary expressions
+ var b = expr as BinaryExpr;
+ if (b != null) {
+ bool breakDownFurther = false;
+ bool p0 = polarity;
+ if (b.ResolvedOp == BinaryExpr.ResolvedOpcode.And) {
+ breakDownFurther = polarity;
+ } else if (b.ResolvedOp == BinaryExpr.ResolvedOpcode.Or) {
+ breakDownFurther = !polarity;
+ } else if (b.ResolvedOp == BinaryExpr.ResolvedOpcode.Imp) {
+ breakDownFurther = !polarity;
+ p0 = !p0;
+ }
+ if (breakDownFurther) {
+ foreach (var c in NormalizedConjuncts(b.E0, p0)) {
+ yield return c;
+ }
+ foreach (var c in NormalizedConjuncts(b.E1, polarity)) {
+ yield return c;
+ }
+ yield break;
+ }
+ }
+
+ // Unary expression
+ var u = expr as UnaryExpr;
+ if (u != null && u.Op == UnaryExpr.Opcode.Not) {
+ foreach (var c in NormalizedConjuncts(u.E, !polarity)) {
+ yield return c;
+ }
+ yield break;
+ }
+
+ // no other case applied, so return the expression or its negation, but first clean it up a little
+ b = expr as BinaryExpr;
+ if (b != null) {
+ BinaryExpr.Opcode newOp;
+ BinaryExpr.ResolvedOpcode newROp;
+ bool swapOperands;
+ switch (b.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.Gt: // A > B yield polarity ? (B < A) : (A <= B);
+ newOp = polarity ? BinaryExpr.Opcode.Lt : BinaryExpr.Opcode.Le;
+ newROp = polarity ? BinaryExpr.ResolvedOpcode.Lt : BinaryExpr.ResolvedOpcode.Le;
+ swapOperands = polarity;
+ break;
+ case BinaryExpr.ResolvedOpcode.Ge: // A >= B yield polarity ? (B <= A) : (A < B);
+ newOp = polarity ? BinaryExpr.Opcode.Le : BinaryExpr.Opcode.Lt;
+ newROp = polarity ? BinaryExpr.ResolvedOpcode.Le : BinaryExpr.ResolvedOpcode.Lt;
+ swapOperands = polarity;
+ break;
+ case BinaryExpr.ResolvedOpcode.Le: // A <= B yield polarity ? (A <= B) : (B < A);
+ newOp = polarity ? BinaryExpr.Opcode.Le : BinaryExpr.Opcode.Lt;
+ newROp = polarity ? BinaryExpr.ResolvedOpcode.Le : BinaryExpr.ResolvedOpcode.Lt;
+ swapOperands = !polarity;
+ break;
+ case BinaryExpr.ResolvedOpcode.Lt: // A < B yield polarity ? (A < B) : (B <= A);
+ newOp = polarity ? BinaryExpr.Opcode.Lt : BinaryExpr.Opcode.Le;
+ newROp = polarity ? BinaryExpr.ResolvedOpcode.Lt : BinaryExpr.ResolvedOpcode.Le;
+ swapOperands = !polarity;
+ break;
+ case BinaryExpr.ResolvedOpcode.EqCommon: // A == B yield polarity ? (A == B) : (A != B);
+ newOp = polarity ? BinaryExpr.Opcode.Eq : BinaryExpr.Opcode.Neq;
+ newROp = polarity ? BinaryExpr.ResolvedOpcode.EqCommon : BinaryExpr.ResolvedOpcode.NeqCommon;
+ swapOperands = false;
+ break;
+ case BinaryExpr.ResolvedOpcode.NeqCommon: // A != B yield polarity ? (A != B) : (A == B);
+ newOp = polarity ? BinaryExpr.Opcode.Neq : BinaryExpr.Opcode.Eq;
+ newROp = polarity ? BinaryExpr.ResolvedOpcode.NeqCommon : BinaryExpr.ResolvedOpcode.EqCommon;
+ swapOperands = false;
+ break;
+ default:
+ goto JUST_RETURN_IT;
+ }
+ if (newROp != b.ResolvedOp || swapOperands) {
+ b = new BinaryExpr(b.tok, newOp, swapOperands ? b.E1 : b.E0, swapOperands ? b.E0 : b.E1);
+ b.ResolvedOp = newROp;
+ b.Type = Type.Bool;
+ yield return b;
+ yield break;
+ }
+ }
+ JUST_RETURN_IT: ;
+ if (polarity) {
+ yield return expr;
+ } else {
+ expr = new UnaryExpr(expr.tok, UnaryExpr.Opcode.Not, expr);
+ expr.Type = Type.Bool;
+ yield return expr;
+ }
+ }
+
+ public static Expression Plus(Expression e, int n) {
+ Contract.Requires(0 <= n);
+
+ var nn = new LiteralExpr(e.tok, n);
+ nn.Type = Type.Int;
+ var p = new BinaryExpr(e.tok, BinaryExpr.Opcode.Add, e, nn);
+ p.ResolvedOp = BinaryExpr.ResolvedOpcode.Add;
+ p.Type = Type.Int;
+ return p;
+ }
+
+ public static Expression Minus(Expression e, int n) {
+ Contract.Requires(0 <= n);
+
+ var nn = new LiteralExpr(e.tok, n);
+ nn.Type = Type.Int;
+ var p = new BinaryExpr(e.tok, BinaryExpr.Opcode.Sub, e, nn);
+ p.ResolvedOp = BinaryExpr.ResolvedOpcode.Sub;
+ p.Type = Type.Int;
+ return p;
+ }
+
+ /// <summary>
+ /// Returns the set of free variables in "expr".
+ /// Requires "expr" to be successfully resolved.
+ /// Ensures that the set returned has no aliases.
+ /// </summary>
+ static ISet<IVariable> FreeVariables(Expression expr) {
+ Contract.Requires(expr != null);
+ Contract.Ensures(expr.Type != null);
+
+ if (expr is IdentifierExpr) {
+ var e = (IdentifierExpr)expr;
+ return new HashSet<IVariable>() { e.Var };
+
+ } else if (expr is QuantifierExpr) {
+ var e = (QuantifierExpr)expr;
+ var s = FreeVariables(e.LogicalBody());
+ foreach (var bv in e.BoundVars) {
+ s.Remove(bv);
+ }
+ return s;
+
+ } else if (expr is MatchExpr) {
+ var e = (MatchExpr)expr;
+ 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);
+ }
+ s.UnionWith(t);
+ }
+ return s;
+
+ } else {
+ ISet<IVariable> s = null;
+ foreach (var e in expr.SubExpressions) {
+ var t = FreeVariables(e);
+ if (s == null) {
+ s = t;
+ } else {
+ s.UnionWith(t);
+ }
+ }
+ return s == null ? new HashSet<IVariable>() : s;
+ }
+ }
+
+ void ResolveReceiver(Expression expr, bool twoState) {
+ Contract.Requires(expr != null);
+ Contract.Ensures(expr.Type != null);
+
+ if (expr is ThisExpr && !expr.WasResolved()) {
+ // Allow 'this' here, regardless of scope.AllowInstance. The caller is responsible for
+ // making sure 'this' does not really get used when it's not available.
+ Contract.Assume(currentClass != null); // this is really a precondition, in this case
+ expr.Type = GetThisType(expr.tok, currentClass);
+ } else {
+ ResolveExpression(expr, twoState);
+ }
+ }
+
+ void ResolveSeqSelectExpr(SeqSelectExpr e, bool twoState, bool allowNonUnitArraySelection) {
+ Contract.Requires(e != null);
+ if (e.Type != null) {
+ // already resolved
+ return;
+ }
+
+ bool seqErr = false;
+ ResolveExpression(e.Seq, twoState);
+ Contract.Assert(e.Seq.Type != null); // follows from postcondition of ResolveExpression
+ Type elementType = new InferredTypeProxy();
+ Type domainType = new InferredTypeProxy();
+
+ IndexableTypeProxy expectedType = new IndexableTypeProxy(elementType, domainType);
+ if (!UnifyTypes(e.Seq.Type, expectedType)) {
+ Error(e, "sequence/array/map selection requires a sequence, array 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);
+ }
+ } else {
+ if (UnifyTypes(expectedType, new MapType(new InferredTypeProxy(), new InferredTypeProxy()))) {
+ Error(e, "cannot multiselect a map (got {0} as map type)", e.Seq.Type);
+ }
+ }
+ }
+ if (e.E0 != null) {
+ ResolveExpression(e.E0, twoState);
+ Contract.Assert(e.E0.Type != null); // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(e.E0.Type, domainType)) {
+ Error(e.E0, "sequence/array/map selection requires {1} indices (got {0})", e.E0.Type, domainType);
+ }
+ }
+ if (e.E1 != null) {
+ ResolveExpression(e.E1, twoState);
+ Contract.Assert(e.E1.Type != null); // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(e.E1.Type, domainType)) {
+ Error(e.E1, "sequence/array/map selection requires {1} indices (got {0})", e.E1.Type, domainType);
+ }
+ }
+ if (!seqErr) {
+ if (e.SelectOne) {
+ e.Type = elementType;
+ } else {
+ e.Type = new SeqType(elementType);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Note: this method is allowed to be called even if "type" does not make sense for "op", as might be the case if
+ /// resolution of the binary expression failed. If so, an arbitrary resolved opcode is returned.
+ /// </summary>
+ BinaryExpr.ResolvedOpcode ResolveOp(BinaryExpr.Opcode op, Type operandType) {
+ Contract.Requires(operandType != null);
+ switch (op) {
+ case BinaryExpr.Opcode.Iff: return BinaryExpr.ResolvedOpcode.Iff;
+ case BinaryExpr.Opcode.Imp: return BinaryExpr.ResolvedOpcode.Imp;
+ case BinaryExpr.Opcode.And: return BinaryExpr.ResolvedOpcode.And;
+ case BinaryExpr.Opcode.Or: return BinaryExpr.ResolvedOpcode.Or;
+ case BinaryExpr.Opcode.Eq:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.SetEq;
+ } else if (operandType is MultiSetType) {
+ return BinaryExpr.ResolvedOpcode.MultiSetEq;
+ } else if (operandType is SeqType) {
+ return BinaryExpr.ResolvedOpcode.SeqEq;
+ } else if (operandType is MapType) {
+ return BinaryExpr.ResolvedOpcode.MapEq;
+ } else {
+ return BinaryExpr.ResolvedOpcode.EqCommon;
+ }
+ case BinaryExpr.Opcode.Neq:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.SetNeq;
+ } else if (operandType is MultiSetType) {
+ return BinaryExpr.ResolvedOpcode.MultiSetNeq;
+ } else if (operandType is SeqType) {
+ return BinaryExpr.ResolvedOpcode.SeqNeq;
+ } else if (operandType is MapType) {
+ return BinaryExpr.ResolvedOpcode.MapNeq;
+ } else {
+ return BinaryExpr.ResolvedOpcode.NeqCommon;
+ }
+ case BinaryExpr.Opcode.Disjoint:
+ if (operandType is MultiSetType) {
+ return BinaryExpr.ResolvedOpcode.MultiSetDisjoint;
+ } else if (operandType is MapType) {
+ return BinaryExpr.ResolvedOpcode.MapDisjoint;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Disjoint;
+ }
+ case BinaryExpr.Opcode.Lt:
+ if (operandType.IsIndDatatype || operandType is DatatypeProxy) {
+ return BinaryExpr.ResolvedOpcode.RankLt;
+ } else if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.ProperSubset;
+ } else if (operandType is MultiSetType) {
+ return BinaryExpr.ResolvedOpcode.ProperMultiSubset;
+ } else if (operandType is SeqType) {
+ return BinaryExpr.ResolvedOpcode.ProperPrefix;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Lt;
+ }
+ case BinaryExpr.Opcode.Le:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.Subset;
+ } else if (operandType is MultiSetType) {
+ return BinaryExpr.ResolvedOpcode.MultiSubset;
+ } else if (operandType is SeqType) {
+ return BinaryExpr.ResolvedOpcode.Prefix;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Le;
+ }
+ case BinaryExpr.Opcode.Add:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.Union;
+ } else if (operandType is MultiSetType) {
+ return BinaryExpr.ResolvedOpcode.MultiSetUnion;
+ } else if (operandType is MapType) {
+ return BinaryExpr.ResolvedOpcode.MapUnion;
+ } else if (operandType is SeqType) {
+ return BinaryExpr.ResolvedOpcode.Concat;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Add;
+ }
+ case BinaryExpr.Opcode.Sub:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.SetDifference;
+ } else if (operandType is MultiSetType) {
+ return BinaryExpr.ResolvedOpcode.MultiSetDifference;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Sub;
+ }
+ case BinaryExpr.Opcode.Mul:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.Intersection;
+ } else if (operandType is MultiSetType) {
+ return BinaryExpr.ResolvedOpcode.MultiSetIntersection;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Mul;
+ }
+ case BinaryExpr.Opcode.Gt:
+ if (operandType.IsDatatype) {
+ return BinaryExpr.ResolvedOpcode.RankGt;
+ } else if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.ProperSuperset;
+ } else if (operandType is MultiSetType) {
+ return BinaryExpr.ResolvedOpcode.ProperMultiSuperset;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Gt;
+ }
+ case BinaryExpr.Opcode.Ge:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.Superset;
+ } else if (operandType is MultiSetType) {
+ return BinaryExpr.ResolvedOpcode.MultiSuperset;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Ge;
+ }
+ case BinaryExpr.Opcode.In:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.InSet;
+ } else if (operandType is MultiSetType) {
+ return BinaryExpr.ResolvedOpcode.InMultiSet;
+ } else if (operandType is MapType) {
+ return BinaryExpr.ResolvedOpcode.InMap;
+ } else {
+ return BinaryExpr.ResolvedOpcode.InSeq;
+ }
+ case BinaryExpr.Opcode.NotIn:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.NotInSet;
+ } else if (operandType is MultiSetType) {
+ return BinaryExpr.ResolvedOpcode.NotInMultiSet;
+ } else if (operandType is MapType) {
+ return BinaryExpr.ResolvedOpcode.NotInMap;
+ } else {
+ return BinaryExpr.ResolvedOpcode.NotInSeq;
+ }
+ case BinaryExpr.Opcode.Div: return BinaryExpr.ResolvedOpcode.Div;
+ case BinaryExpr.Opcode.Mod: return BinaryExpr.ResolvedOpcode.Mod;
+ default:
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected operator
+ }
+ }
+
+ /// <summary>
+ /// Returns whether or not 'expr' has any subexpression that uses some feature (like a ghost or quantifier)
+ /// that is allowed only in specification contexts.
+ /// Requires 'expr' to be a successfully resolved expression.
+ /// </summary>
+ bool UsesSpecFeatures(Expression expr) {
+ Contract.Requires(expr != null);
+ Contract.Requires(expr.WasResolved()); // this check approximates the requirement that "expr" be resolved
+
+ if (expr is LiteralExpr) {
+ return false;
+ } else if (expr is ThisExpr) {
+ return false;
+ } else if (expr is IdentifierExpr) {
+ 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));
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ return e.Elements.Exists(ee => UsesSpecFeatures(ee));
+ } else if (expr is MapDisplayExpr) {
+ MapDisplayExpr e = (MapDisplayExpr)expr;
+ return e.Elements.Exists(p => UsesSpecFeatures(p.A) || UsesSpecFeatures(p.B));
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)expr;
+ return cce.NonNull(e.Field).IsGhost || UsesSpecFeatures(e.Obj);
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ return UsesSpecFeatures(e.Seq) ||
+ (e.E0 != null && UsesSpecFeatures(e.E0)) ||
+ (e.E1 != null && UsesSpecFeatures(e.E1));
+ } else if (expr is MultiSelectExpr) {
+ MultiSelectExpr e = (MultiSelectExpr)expr;
+ return UsesSpecFeatures(e.Array) || e.Indices.Exists(ee => UsesSpecFeatures(ee));
+ } else if (expr is SeqUpdateExpr) {
+ SeqUpdateExpr e = (SeqUpdateExpr)expr;
+ return UsesSpecFeatures(e.Seq) ||
+ (e.Index != null && UsesSpecFeatures(e.Index)) ||
+ (e.Value != null && UsesSpecFeatures(e.Value));
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ if (cce.NonNull(e.Function).IsGhost) {
+ return true;
+ }
+ return e.Args.Exists(arg => UsesSpecFeatures(arg));
+ } else if (expr is OldExpr) {
+ OldExpr e = (OldExpr)expr;
+ return UsesSpecFeatures(e.E);
+ } else if (expr is FreshExpr) {
+ return true;
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ return UsesSpecFeatures(e.E);
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.RankLt || e.ResolvedOp == BinaryExpr.ResolvedOpcode.RankGt) {
+ return true;
+ }
+ return UsesSpecFeatures(e.E0) || UsesSpecFeatures(e.E1);
+ } 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));
+ } else if (expr is SetComprehension) {
+ var e = (SetComprehension)expr;
+ return (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));
+ } else if (expr is WildcardExpr) {
+ return false;
+ } else if (expr is PredicateExpr) {
+ var e = (PredicateExpr)expr;
+ return UsesSpecFeatures(e.Body);
+ } else if (expr is ITEExpr) {
+ ITEExpr e = (ITEExpr)expr;
+ return UsesSpecFeatures(e.Test) || UsesSpecFeatures(e.Thn) || UsesSpecFeatures(e.Els);
+ } else if (expr is MatchExpr) {
+ MatchExpr me = (MatchExpr)expr;
+ if (UsesSpecFeatures(me.Source)) {
+ return true;
+ }
+ return me.Cases.Exists(mc => UsesSpecFeatures(mc.Body));
+ } else if (expr is ConcreteSyntaxExpression) {
+ var e = (ConcreteSyntaxExpression)expr;
+ return e.ResolvedExpression != null && UsesSpecFeatures(e.ResolvedExpression);
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
+ }
+ }
+ }
+
+ class CoCallResolution
+ {
+ readonly ModuleDefinition currentModule;
+ readonly Function currentFunction;
+ readonly bool dealsWithCodatatypes;
+
+ public CoCallResolution(Function currentFunction, bool dealsWithCodatatypes) {
+ Contract.Requires(currentFunction != null);
+ this.currentModule = currentFunction.EnclosingClass.Module;
+ this.currentFunction = currentFunction;
+ this.dealsWithCodatatypes = dealsWithCodatatypes;
+ }
+
+ /// <summary>
+ /// Determines which calls in "expr" can be considered to be co-calls, which co-constructor
+ /// invocations host such co-calls, and which destructor operations are not allowed.
+ /// Assumes "expr" to have been successfully resolved.
+ /// </summary>
+ public void CheckCoCalls(Expression expr) {
+ Contract.Requires(expr != null);
+ var candidates = CheckCoCalls(expr, true, null);
+ ProcessCoCallInfo(candidates);
+ }
+
+ struct CoCallInfo
+ {
+ public int GuardCount;
+ public FunctionCallExpr CandidateCall;
+ public DatatypeValue EnclosingCoConstructor;
+ public CoCallInfo(int guardCount, FunctionCallExpr candidateCall, DatatypeValue enclosingCoConstructor) {
+ Contract.Requires(0 <= guardCount);
+ Contract.Requires(candidateCall != null);
+
+ GuardCount = guardCount;
+ CandidateCall = candidateCall;
+ EnclosingCoConstructor = enclosingCoConstructor;
+ }
+
+ public void AdjustGuardCount(int p) {
+ GuardCount += p;
+ }
+ }
+
+ /// <summary>
+ /// Recursively goes through the entire "expr". Every call within the same recursive cluster is a potential
+ /// co-call. All such calls will get their .CoCall field filled in, indicating whether or not the call
+ /// is a co-call. If the situation deals with co-datatypes, then one of the NoBecause... values is chosen (rather
+ /// than just No), so that any error message that may later be produced when trying to prove termination of the
+ /// recursive call can include a note pointing out that the call was not selected to be a co-call.
+ /// "allowCallsWithinRecursiveCluster" is passed in as "false" if the enclosing context has no easy way of
+ /// controlling the uses of "expr" (for example, if the enclosing context passes "expr" to a function or binds
+ /// "expr" to a variable).
+ /// </summary>
+ List<CoCallInfo> CheckCoCalls(Expression expr, bool allowCallsWithinRecursiveCluster, DatatypeValue coContext) {
+ Contract.Requires(expr != null);
+ Contract.Ensures(allowCallsWithinRecursiveCluster || Contract.Result<List<CoCallInfo>>().Count == 0);
+
+ var candidates = new List<CoCallInfo>();
+ expr = expr.Resolved;
+ if (expr is DatatypeValue) {
+ var e = (DatatypeValue)expr;
+ if (e.Ctor.EnclosingDatatype is CoDatatypeDecl) {
+ foreach (var arg in e.Arguments) {
+ foreach (var c in CheckCoCalls(arg, allowCallsWithinRecursiveCluster, e)) {
+ c.AdjustGuardCount(1);
+ candidates.Add(c);
+ }
+ }
+ return candidates;
+ }
+ } else if (expr is FieldSelectExpr) {
+ var e = (FieldSelectExpr)expr;
+ if (e.Field.EnclosingClass is CoDatatypeDecl) {
+ foreach (var c in CheckCoCalls(e.Obj, allowCallsWithinRecursiveCluster, null)) {
+ if (c.GuardCount <= 1) {
+ // This call was not guarded (c.GuardedCount == 0) or the guard for this candidate co-call is
+ // being removed (c.GuardedCount == 1), so this call is not allowed as a co-call.
+ // (So, don't include "res" among "results".)
+ c.CandidateCall.CoCall = FunctionCallExpr.CoCallResolution.NoBecauseIsNotGuarded;
+ } else {
+ c.AdjustGuardCount(-1);
+ candidates.Add(c);
+ }
+ }
+ return candidates;
+ }
+ } else if (expr is MatchExpr) {
+ var e = (MatchExpr)expr;
+ var r = CheckCoCalls(e.Source, false, null);
+ Contract.Assert(r.Count == 0); // follows from postcondition of CheckCoCalls
+ foreach (var kase in e.Cases) {
+ r = CheckCoCalls(kase.Body, allowCallsWithinRecursiveCluster, null);
+ candidates.AddRange(r);
+ }
+ return candidates;
+ } else if (expr is FunctionCallExpr) {
+ var e = (FunctionCallExpr)expr;
+ // First, consider the arguments of the call, making sure that they do not include calls within the recursive cluster.
+ // (Note, if functions could have a "destruction level" declaration that promised to only destruct its arguments by
+ // so much, then some recursive calls within the cluster could be allowed.)
+ foreach (var arg in e.Args) {
+ var r = CheckCoCalls(arg, false, null);
+ Contract.Assert(r.Count == 0); // follows from postcondition of CheckCoCalls
+ }
+ // Second, investigate the possibility that this call itself may be a candidate co-call
+ if (currentModule.CallGraph.GetSCCRepresentative(currentFunction) == currentModule.CallGraph.GetSCCRepresentative(e.Function)) {
+ // This call goes to another function in the same recursive cluster
+ if (e.Function.Reads.Count != 0) {
+ // this call is disqualified from being a co-call, because of side effects
+ if (!dealsWithCodatatypes) {
+ e.CoCall = FunctionCallExpr.CoCallResolution.No;
+ } else if (coContext != null) {
+ e.CoCall = FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasSideEffects;
+ } else {
+ e.CoCall = FunctionCallExpr.CoCallResolution.NoBecauseIsNotGuarded;
+ }
+ } else if (!allowCallsWithinRecursiveCluster) {
+ if (!dealsWithCodatatypes) {
+ e.CoCall = FunctionCallExpr.CoCallResolution.No;
+ } else {
+ e.CoCall = FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsAreNotAllowedInThisContext;
+ }
+ } else {
+ // e.CoCall is not filled in here, but will be filled in when the list of candidates are processed
+ candidates.Add(new CoCallInfo(0, e, coContext));
+ }
+ }
+ return candidates;
+ } else if (expr is OldExpr) {
+ var e = (OldExpr)expr;
+ // here, "coContext" is passed along (the use of "old" says this must be ghost code, so the compiler does not need to handle this case)
+ return CheckCoCalls(e.E, allowCallsWithinRecursiveCluster, coContext);
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ foreach (var rhs in e.RHSs) {
+ var r = CheckCoCalls(rhs, false, null);
+ Contract.Assert(r.Count == 0); // follows from postcondition of CheckCoCalls
+ }
+ return CheckCoCalls(e.Body, allowCallsWithinRecursiveCluster, null);
+ } else if (expr is ComprehensionExpr) {
+ var e = (ComprehensionExpr)expr;
+ foreach (var ee in e.SubExpressions) {
+ var r = CheckCoCalls(ee, false, null);
+ Contract.Assert(r.Count == 0); // follows from postcondition of CheckCoCalls
+ }
+ return candidates;
+ }
+
+ // Default handling:
+ foreach (var ee in expr.SubExpressions) {
+ var r = CheckCoCalls(ee, allowCallsWithinRecursiveCluster, null);
+ candidates.AddRange(r);
+ }
+ if (expr.Type is BasicType) {
+ // nothing can escape this expression, so process the results now
+ ProcessCoCallInfo(candidates);
+ candidates.Clear();
+ }
+ return candidates;
+ }
+
+ /// <summary>
+ /// This method is to be called when it has been determined that all candidate
+ /// co-calls in "info" are indeed allowed as co-calls.
+ /// </summary>
+ void ProcessCoCallInfo(List<CoCallInfo> coCandidates) {
+ foreach (var c in coCandidates) {
+ if (c.GuardCount != 0) {
+ c.CandidateCall.CoCall = FunctionCallExpr.CoCallResolution.Yes;
+ c.EnclosingCoConstructor.IsCoCall = true;
+ } else if (dealsWithCodatatypes) {
+ c.CandidateCall.CoCall = FunctionCallExpr.CoCallResolution.NoBecauseIsNotGuarded;
+ } else {
+ c.CandidateCall.CoCall = FunctionCallExpr.CoCallResolution.No;
+ }
+ }
+ }
+ }
+
+ class Scope<Thing> where Thing : class
+ {
+ [Rep]
+ readonly List<string> names = new List<string>(); // a null means a marker
+ [Rep]
+ readonly List<Thing> things = new List<Thing>();
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(names != null);
+ Contract.Invariant(things != null);
+ Contract.Invariant(names.Count == things.Count);
+ Contract.Invariant(-1 <= scopeSizeWhereInstancesWereDisallowed && scopeSizeWhereInstancesWereDisallowed <= names.Count);
+ }
+
+ int scopeSizeWhereInstancesWereDisallowed = -1;
+
+ public bool AllowInstance {
+ get { return scopeSizeWhereInstancesWereDisallowed == -1; }
+ set {
+ Contract.Requires(AllowInstance && !value); // only allowed to change from true to false (that's all that's currently needed in Dafny); Pop is what can make the change in the other direction
+ scopeSizeWhereInstancesWereDisallowed = names.Count;
+ }
+ }
+
+ public void PushMarker() {
+ names.Add(null);
+ things.Add(null);
+ }
+
+ public void PopMarker() {
+ int n = names.Count;
+ while (true) {
+ n--;
+ if (names[n] == null) {
+ break;
+ }
+ }
+ names.RemoveRange(n, names.Count - n);
+ things.RemoveRange(n, things.Count - n);
+ if (names.Count < scopeSizeWhereInstancesWereDisallowed) {
+ scopeSizeWhereInstancesWereDisallowed = -1;
+ }
+ }
+
+ // 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) {
+ Contract.Requires(name != null);
+ Contract.Requires(thing != null);
+ if (Find(name, true) != null) {
+ return false;
+ } else {
+ names.Add(name);
+ things.Add(thing);
+ return true;
+ }
+ }
+
+ Thing Find(string name, bool topScopeOnly) {
+ Contract.Requires(name != null);
+ for (int n = names.Count; 0 <= --n; ) {
+ if (names[n] == null) {
+ if (topScopeOnly) {
+ return null; // no present
+ }
+ } else if (names[n] == name) {
+ Thing t = things[n];
+ Contract.Assert(t != null);
+ return t;
+ }
+ }
+ return null; // not present
+ }
+
+ public Thing Find(string name) {
+ Contract.Requires(name != null);
+ return Find(name, false);
+ }
+
+ public bool ContainsDecl(Thing t) {
+ return things.Exists(thing => thing == t);
+ }
+ }
+}
diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs
new file mode 100644
index 00000000..c95be2f4
--- /dev/null
+++ b/Source/Dafny/Rewriter.cs
@@ -0,0 +1,342 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+
+namespace Microsoft.Dafny
+{
+ [ContractClass(typeof(IRewriterContracts))]
+ public interface IRewriter
+ {
+ void PreResolve(ModuleDefinition m);
+ void PostResolve(ModuleDefinition m);
+ }
+ [ContractClassFor(typeof(IRewriter))]
+ abstract class IRewriterContracts : IRewriter
+ {
+ public void PreResolve(ModuleDefinition m) {
+ Contract.Requires(m != null);
+ }
+ public void PostResolve(ModuleDefinition m) {
+ Contract.Requires(m != null);
+ }
+ }
+
+ public class AutoGeneratedToken : TokenWrapper
+ {
+ public AutoGeneratedToken(Boogie.IToken wrappedToken)
+ : base(wrappedToken)
+ {
+ Contract.Requires(wrappedToken != null);
+ }
+ }
+
+ /// <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:
+ /// - 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; }
+ /// </summary>
+ public class AutoContractsRewriter : IRewriter
+ {
+ public 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) {
+ ProcessClassPreResolve((ClassDecl)d);
+ }
+ }
+ }
+
+ void ProcessClassPreResolve(ClassDecl cl) {
+ // 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());
+ cl.Members.Add(new Field(new AutoGeneratedToken(cl.tok), "Repr", true, ty, null));
+ }
+
+ foreach (var member in cl.Members) {
+ bool sayYes = true;
+ if (Attributes.ContainsBool(member.Attributes, "autocontracts", ref sayYes) && !sayYes) {
+ continue;
+ }
+ Boogie.IToken tok = new AutoGeneratedToken(member.tok);
+ if (member is Function && member.Name == "Valid" && !member.IsStatic) {
+ var valid = (Function)member;
+ // reads this;
+ valid.Reads.Add(new FrameExpression(tok, new ThisExpr(tok), null));
+ // reads Repr;
+ valid.Reads.Add(new FrameExpression(tok, new FieldSelectExpr(tok, new ImplicitThisExpr(tok), "Repr"), null));
+ } else if (member is Constructor) {
+ var ctor = (Constructor)member;
+ // modifies this;
+ ctor.Mod.Expressions.Add(new FrameExpression(tok, new ImplicitThisExpr(tok), null));
+ // ensures Valid();
+ ctor.Ens.Insert(0, new MaybeFreeExpression(new FunctionCallExpr(tok, "Valid", new ImplicitThisExpr(tok), tok, new List<Expression>())));
+ // ensures fresh(Repr - {this});
+ var freshness = new FreshExpr(tok, new BinaryExpr(tok, BinaryExpr.Opcode.Sub,
+ new FieldSelectExpr(tok, new ImplicitThisExpr(tok), "Repr"),
+ new SetDisplayExpr(tok, new List<Expression>() { new ThisExpr(tok) })));
+ ctor.Ens.Insert(1, new MaybeFreeExpression(freshness));
+ } else if (member is Method && !member.IsStatic) {
+ var m = (Method)member;
+ // requires Valid();
+ m.Req.Insert(0, new MaybeFreeExpression(new FunctionCallExpr(tok, "Valid", new ImplicitThisExpr(tok), tok, new List<Expression>())));
+ // If this is a mutating method, we should also add a modifies clause and a postcondition, but we don't do that if it's
+ // a simple query method. However, we won't know if it's a simple query method until after resolution, so we'll add the
+ // rest of the spec then.
+ }
+ }
+ }
+
+ public 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) {
+ ProcessClassPostResolve((ClassDecl)d);
+ }
+ }
+ }
+
+ 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"
+ Field ReprField = null;
+ Function Valid = null;
+ var subobjects = new List<Tuple<Field, Field>>();
+ foreach (var member in cl.Members) {
+ var field = member as Field;
+ if (field != null) {
+ bool sayYes = true;
+ if (field.Name == "Repr") {
+ ReprField = field;
+ } else if (Attributes.ContainsBool(field.Attributes, "autocontracts", ref sayYes) && !sayYes) {
+ // ignore this field
+ } else if (field.Type is ObjectType) {
+ subobjects.Add(new Tuple<Field, Field>(field, null));
+ } else if (field.Type.IsRefType) {
+ var rcl = (ClassDecl)((UserDefinedType)field.Type).ResolvedClass;
+ Field rRepr = null;
+ foreach (var memb in rcl.Members) {
+ var f = memb as Field;
+ if (f != null && f.Name == "Repr") {
+ rRepr = f;
+ break;
+ }
+ }
+ subobjects.Add(new Tuple<Field, Field>(field, rRepr));
+ }
+ } else if (member is Function && member.Name == "Valid" && !member.IsStatic) {
+ var fn = (Function)member;
+ if (fn.Formals.Count == 0 && fn.ResultType is BoolType) {
+ Valid = fn;
+ }
+ }
+ }
+ Contract.Assert(ReprField != null); // we expect there to be a "Repr" field, since we added one in PreResolve
+
+ Boogie.IToken clTok = new AutoGeneratedToken(cl.tok);
+ Type ty = new UserDefinedType(clTok, cl.Name, cl, new List<Type>());
+ var self = new ThisExpr(clTok);
+ self.Type = ty;
+ var implicitSelf = new ImplicitThisExpr(clTok);
+ implicitSelf.Type = ty;
+ var Repr = new FieldSelectExpr(clTok, implicitSelf, "Repr");
+ Repr.Field = ReprField;
+ Repr.Type = ReprField.Type;
+ var cNull = new LiteralExpr(clTok);
+ cNull.Type = new ObjectType();
+
+ foreach (var member in cl.Members) {
+ bool sayYes = true;
+ if (Attributes.ContainsBool(member.Attributes, "autocontracts", ref sayYes) && !sayYes) {
+ continue;
+ }
+ Boogie.IToken tok = new AutoGeneratedToken(member.tok);
+ if (member is Function && member.Name == "Valid" && !member.IsStatic) {
+ var valid = (Function)member;
+ if (valid.IsGhost && valid.ResultType is BoolType) {
+ var c0 = BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.InSet, self, Repr); // this in Repr
+ var c1 = BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.NotInSet, cNull, Repr); // null !in Repr
+ var c = BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.And, c0, c1);
+
+ foreach (var ff in subobjects) {
+ var F = new FieldSelectExpr(tok, implicitSelf, ff.Item1.Name);
+ F.Field = ff.Item1;
+ F.Type = ff.Item1.Type;
+
+ c0 = BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.NeqCommon, F, cNull);
+ c1 = BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.InSet, F, Repr);
+ if (ff.Item2 == null) {
+ // F != null ==> F in Repr (so, nothing else to do)
+ } else {
+ // F != null ==> F in Repr && F.Repr <= Repr && this !in F.Repr
+ var FRepr = new FieldSelectExpr(tok, F, ff.Item2.Name);
+ FRepr.Field = ff.Item2;
+ FRepr.Type = ff.Item2.Type;
+ var c2 = BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.Subset, FRepr, Repr);
+ var c3 = BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.NotInSet, self, FRepr);
+ c1 = BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.And, c1, BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.And, c2, c3));
+ }
+ c = BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.And, c, BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.Imp, c0, c1));
+ }
+
+ if (valid.Body == null) {
+ valid.Body = c;
+ } else {
+ valid.Body = BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.And, c, valid.Body);
+ }
+ }
+
+ } else if (member is Constructor) {
+ var ctor = (Constructor)member;
+ if (ctor.Body == null) {
+ ctor.Body = new BlockStmt(tok, new List<Statement>());
+ }
+ // TODO: these assignments should be included on every return path
+ var bodyStatements = ((BlockStmt)ctor.Body).Body;
+ // Repr := {this};
+ var e = new SetDisplayExpr(tok, new List<Expression>() { self });
+ e.Type = new SetType(new ObjectType());
+ Statement s = new AssignStmt(tok, Repr, new ExprRhs(e));
+ s.IsGhost = true;
+ bodyStatements.Add(s);
+
+ AddSubobjectReprs(tok, subobjects, bodyStatements, self, implicitSelf, cNull, Repr);
+
+ } else if (member is Method && !member.IsStatic) {
+ var m = (Method)member;
+ if (Valid != null && !IsSimpleQueryMethod(m)) {
+ // modifies Repr;
+ m.Mod.Expressions.Add(new FrameExpression(Repr.tok, Repr, null));
+ // ensures Valid();
+ var valid = new FunctionCallExpr(tok, "Valid", implicitSelf, tok, new List<Expression>());
+ valid.Function = Valid;
+ valid.Type = Type.Bool;
+ m.Ens.Insert(0, new MaybeFreeExpression(valid));
+ // ensures fresh(Repr - old(Repr));
+ var e0 = new OldExpr(tok, Repr);
+ e0.Type = Repr.Type;
+ var e1 = new BinaryExpr(tok, BinaryExpr.Opcode.Sub, Repr, e0);
+ e1.ResolvedOp = BinaryExpr.ResolvedOpcode.SetDifference;
+ e1.Type = Repr.Type;
+ var freshness = new FreshExpr(tok, e1);
+ freshness.Type = Type.Bool;
+ m.Ens.Insert(1, new MaybeFreeExpression(freshness));
+
+ if (m.Body == null) {
+ m.Body = new BlockStmt(tok, new List<Statement>());
+ }
+ // TODO: these assignments should be included on every return path
+ var bodyStatements = ((BlockStmt)m.Body).Body;
+ AddSubobjectReprs(tok, subobjects, bodyStatements, self, implicitSelf, cNull, Repr);
+ }
+ }
+ }
+ }
+
+ void AddSubobjectReprs(Boogie.IToken tok, List<Tuple<Field, Field>> subobjects, List<Statement> bodyStatements,
+ Expression self, Expression implicitSelf, Expression cNull, Expression Repr) {
+
+ foreach (var ff in subobjects) {
+ var F = new FieldSelectExpr(tok, implicitSelf, ff.Item1.Name);
+ F.Field = ff.Item1;
+ F.Type = ff.Item1.Type;
+
+ Expression e = new SetDisplayExpr(tok, new List<Expression>() { F });
+ e.Type = new SetType(new ObjectType());
+ var rhs = new BinaryExpr(tok, BinaryExpr.Opcode.Add, Repr, e);
+ rhs.ResolvedOp = BinaryExpr.ResolvedOpcode.Union;
+ rhs.Type = Repr.Type;
+ if (ff.Item2 == null) {
+ // Repr := Repr + {F} (so, nothing else to do)
+ } else {
+ // Repr := Repr + {F} + F.Repr
+ var FRepr = new FieldSelectExpr(tok, F, ff.Item2.Name);
+ FRepr.Field = ff.Item2;
+ FRepr.Type = ff.Item2.Type;
+ rhs = new BinaryExpr(tok, BinaryExpr.Opcode.Add, rhs, FRepr);
+ rhs.ResolvedOp = BinaryExpr.ResolvedOpcode.Union;
+ rhs.Type = Repr.Type;
+ }
+ // Repr := Repr + ...;
+ Statement s = new AssignStmt(tok, Repr, new ExprRhs(rhs));
+ s.IsGhost = true;
+ // wrap if statement around s
+ e = BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.NeqCommon, F, cNull);
+ var thn = new BlockStmt(tok, new List<Statement>() { s });
+ thn.IsGhost = true;
+ s = new IfStmt(tok, e, thn, null);
+ s.IsGhost = true;
+ // finally, add s to the body
+ bodyStatements.Add(s);
+ }
+ }
+
+ bool IsSimpleQueryMethod(Method m) {
+ // A simple query method has out parameters, its body has no effect other than to assign to them,
+ // and the postcondition does not explicitly mention the pre-state.
+ return m.Outs.Count != 0 && m.Body != null && LocalAssignsOnly(m.Body) &&
+ m.Ens.TrueForAll(mfe => !Translator.MentionsOldState(mfe.E));
+ }
+
+ bool LocalAssignsOnly(Statement s) {
+ Contract.Requires(s != null);
+ if (s is AssignStmt) {
+ var ss = (AssignStmt)s;
+ return ss.Lhs.Resolved is IdentifierExpr;
+ } else if (s is ConcreteUpdateStatement) {
+ var ss = (ConcreteUpdateStatement)s;
+ return ss.Lhss.TrueForAll(e => e.Resolved is IdentifierExpr);
+ } else if (s is CallStmt) {
+ return false;
+ } else {
+ foreach (var ss in s.SubStatements) {
+ if (!LocalAssignsOnly(ss)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public static BinaryExpr BinBoolExpr(Boogie.IToken tok, BinaryExpr.ResolvedOpcode rop, Expression e0, Expression e1) {
+ var p = new BinaryExpr(tok, BinaryExpr.ResolvedOp2SyntacticOp(rop), e0, e1);
+ p.ResolvedOp = rop;
+ p.Type = Type.Bool;
+ return p;
+ }
+ }
+}
diff --git a/Source/Dafny/Scanner.cs b/Source/Dafny/Scanner.cs
new file mode 100644
index 00000000..b23931b6
--- /dev/null
+++ b/Source/Dafny/Scanner.cs
@@ -0,0 +1,823 @@
+
+using System;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using System.Diagnostics.Contracts;
+using Microsoft.Boogie;
+
+
+namespace Microsoft.Dafny {
+
+//-----------------------------------------------------------------------------------
+// Buffer
+//-----------------------------------------------------------------------------------
+public class Buffer {
+ // This Buffer supports the following cases:
+ // 1) seekable stream (file)
+ // a) whole stream in buffer
+ // b) part of stream in buffer
+ // 2) non seekable stream (network, console)
+
+ public const int EOF = 65535 + 1; // char.MaxValue + 1;
+ const int MIN_BUFFER_LENGTH = 1024; // 1KB
+ const int MAX_BUFFER_LENGTH = MIN_BUFFER_LENGTH * 64; // 64KB
+ byte[]/*!*/ buf; // input buffer
+ int bufStart; // position of first byte in buffer relative to input stream
+ int bufLen; // length of buffer
+ int fileLen; // length of input stream (may change if the stream is no file)
+ int bufPos; // current position in buffer
+ Stream/*!*/ stream; // input stream (seekable)
+ bool isUserStream; // was the stream opened by the user?
+
+ [ContractInvariantMethod]
+ void ObjectInvariant(){
+ Contract.Invariant(buf != null);
+ Contract.Invariant(stream != null);
+ }
+
+// [NotDelayed]
+ public Buffer (Stream/*!*/ s, bool isUserStream) : base() {
+ Contract.Requires(s != null);
+ stream = s; this.isUserStream = isUserStream;
+
+ int fl, bl;
+ if (s.CanSeek) {
+ fl = (int) s.Length;
+ bl = fl < MAX_BUFFER_LENGTH ? fl : MAX_BUFFER_LENGTH; // Math.Min(fileLen, MAX_BUFFER_LENGTH);
+ bufStart = Int32.MaxValue; // nothing in the buffer so far
+ } else {
+ fl = bl = bufStart = 0;
+ }
+
+ buf = new byte[(bl>0) ? bl : MIN_BUFFER_LENGTH];
+ fileLen = fl; bufLen = bl;
+
+ if (fileLen > 0) Pos = 0; // setup buffer to position 0 (start)
+ else bufPos = 0; // index 0 is already after the file, thus Pos = 0 is invalid
+ if (bufLen == fileLen && s.CanSeek) Close();
+ }
+
+ protected Buffer(Buffer/*!*/ b) { // called in UTF8Buffer constructor
+ Contract.Requires(b != null);
+ buf = b.buf;
+ bufStart = b.bufStart;
+ bufLen = b.bufLen;
+ fileLen = b.fileLen;
+ bufPos = b.bufPos;
+ stream = b.stream;
+ // keep destructor from closing the stream
+ //b.stream = null;
+ isUserStream = b.isUserStream;
+ // keep destructor from closing the stream
+ b.isUserStream = true;
+ }
+
+ ~Buffer() { Close(); }
+
+ protected void Close() {
+ if (!isUserStream && stream != null) {
+ stream.Close();
+ //stream = null;
+ }
+ }
+
+ public virtual int Read () {
+ if (bufPos < bufLen) {
+ return buf[bufPos++];
+ } else if (Pos < fileLen) {
+ Pos = Pos; // shift buffer start to Pos
+ return buf[bufPos++];
+ } else if (stream != null && !stream.CanSeek && ReadNextStreamChunk() > 0) {
+ return buf[bufPos++];
+ } else {
+ return EOF;
+ }
+ }
+
+ public int Peek () {
+ int curPos = Pos;
+ int ch = Read();
+ Pos = curPos;
+ return ch;
+ }
+
+ public string/*!*/ GetString (int beg, int end) {
+ Contract.Ensures(Contract.Result<string>() != null);
+ int len = 0;
+ char[] buf = new char[end - beg];
+ int oldPos = Pos;
+ Pos = beg;
+ while (Pos < end) buf[len++] = (char) Read();
+ Pos = oldPos;
+ return new String(buf, 0, len);
+ }
+
+ public int Pos {
+ get { return bufPos + bufStart; }
+ set {
+ if (value >= fileLen && stream != null && !stream.CanSeek) {
+ // Wanted position is after buffer and the stream
+ // is not seek-able e.g. network or console,
+ // thus we have to read the stream manually till
+ // the wanted position is in sight.
+ while (value >= fileLen && ReadNextStreamChunk() > 0);
+ }
+
+ if (value < 0 || value > fileLen) {
+ throw new FatalError("buffer out of bounds access, position: " + value);
+ }
+
+ if (value >= bufStart && value < bufStart + bufLen) { // already in buffer
+ bufPos = value - bufStart;
+ } else if (stream != null) { // must be swapped in
+ stream.Seek(value, SeekOrigin.Begin);
+ bufLen = stream.Read(buf, 0, buf.Length);
+ bufStart = value; bufPos = 0;
+ } else {
+ // set the position to the end of the file, Pos will return fileLen.
+ bufPos = fileLen - bufStart;
+ }
+ }
+ }
+
+ // Read the next chunk of bytes from the stream, increases the buffer
+ // if needed and updates the fields fileLen and bufLen.
+ // Returns the number of bytes read.
+ private int ReadNextStreamChunk() {
+ int free = buf.Length - bufLen;
+ if (free == 0) {
+ // in the case of a growing input stream
+ // we can neither seek in the stream, nor can we
+ // foresee the maximum length, thus we must adapt
+ // the buffer size on demand.
+ byte[] newBuf = new byte[bufLen * 2];
+ Array.Copy(buf, newBuf, bufLen);
+ buf = newBuf;
+ free = bufLen;
+ }
+ int read = stream.Read(buf, bufLen, free);
+ if (read > 0) {
+ fileLen = bufLen = (bufLen + read);
+ return read;
+ }
+ // end of stream reached
+ return 0;
+ }
+}
+
+//-----------------------------------------------------------------------------------
+// UTF8Buffer
+//-----------------------------------------------------------------------------------
+public class UTF8Buffer: Buffer {
+ public UTF8Buffer(Buffer/*!*/ b): base(b) {Contract.Requires(b != null);}
+
+ public override int Read() {
+ int ch;
+ do {
+ ch = base.Read();
+ // until we find a utf8 start (0xxxxxxx or 11xxxxxx)
+ } while ((ch >= 128) && ((ch & 0xC0) != 0xC0) && (ch != EOF));
+ if (ch < 128 || ch == EOF) {
+ // nothing to do, first 127 chars are the same in ascii and utf8
+ // 0xxxxxxx or end of file character
+ } else if ((ch & 0xF0) == 0xF0) {
+ // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ int c1 = ch & 0x07; ch = base.Read();
+ int c2 = ch & 0x3F; ch = base.Read();
+ int c3 = ch & 0x3F; ch = base.Read();
+ int c4 = ch & 0x3F;
+ ch = (((((c1 << 6) | c2) << 6) | c3) << 6) | c4;
+ } else if ((ch & 0xE0) == 0xE0) {
+ // 1110xxxx 10xxxxxx 10xxxxxx
+ int c1 = ch & 0x0F; ch = base.Read();
+ int c2 = ch & 0x3F; ch = base.Read();
+ int c3 = ch & 0x3F;
+ ch = (((c1 << 6) | c2) << 6) | c3;
+ } else if ((ch & 0xC0) == 0xC0) {
+ // 110xxxxx 10xxxxxx
+ int c1 = ch & 0x1F; ch = base.Read();
+ int c2 = ch & 0x3F;
+ ch = (c1 << 6) | c2;
+ }
+ return ch;
+ }
+}
+
+//-----------------------------------------------------------------------------------
+// Scanner
+//-----------------------------------------------------------------------------------
+public class Scanner {
+ const char EOL = '\n';
+ const int eofSym = 0; /* pdt */
+ const int maxT = 115;
+ const int noSym = 115;
+
+
+ [ContractInvariantMethod]
+ void objectInvariant(){
+ Contract.Invariant(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(errorHandler != null);
+ }
+
+ public Buffer/*!*/ buffer; // scanner buffer
+
+ Token/*!*/ t; // current token
+ int ch; // current input character
+ int pos; // byte position of current character
+ int charPos;
+ int col; // column number of current character
+ int line; // line number of current character
+ int oldEols; // EOLs that appeared in a comment;
+ static readonly Hashtable/*!*/ start; // maps first token character to start state
+
+ Token/*!*/ tokens; // list of tokens already peeked (first token is a dummy)
+ Token/*!*/ pt; // current peek token
+
+ char[]/*!*/ tval = new char[128]; // text of current token
+ int tlen; // length of current token
+
+ private string/*!*/ Filename;
+ private Errors/*!*/ errorHandler;
+
+ static Scanner() {
+ start = new Hashtable(128);
+ for (int i = 39; i <= 39; ++i) start[i] = 1;
+ for (int i = 63; i <= 63; ++i) start[i] = 1;
+ for (int i = 65; i <= 90; ++i) start[i] = 1;
+ for (int i = 92; i <= 92; ++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 = 48; i <= 57; ++i) start[i] = 7;
+ for (int i = 34; i <= 34; ++i) start[i] = 8;
+ start[97] = 12;
+ start[58] = 56;
+ start[123] = 10;
+ start[125] = 11;
+ start[61] = 57;
+ start[59] = 19;
+ start[46] = 58;
+ start[124] = 59;
+ start[44] = 20;
+ start[40] = 21;
+ start[41] = 22;
+ start[60] = 60;
+ start[62] = 61;
+ start[42] = 24;
+ start[96] = 25;
+ start[91] = 28;
+ start[93] = 29;
+ start[33] = 62;
+ start[8800] = 33;
+ start[8804] = 34;
+ start[8805] = 35;
+ start[8660] = 38;
+ start[8658] = 40;
+ start[38] = 41;
+ start[8743] = 43;
+ start[8744] = 45;
+ start[43] = 47;
+ start[45] = 48;
+ start[47] = 49;
+ start[37] = 50;
+ start[172] = 51;
+ start[8704] = 52;
+ start[8707] = 53;
+ start[8226] = 55;
+ start[Buffer.EOF] = -1;
+
+ }
+
+// [NotDelayed]
+ public Scanner (string/*!*/ fileName, Errors/*!*/ errorHandler) : 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);
+ Filename = fileName;
+ Init();
+ } catch (IOException) {
+ throw new FatalError("Cannot open file " + fileName);
+ }
+ }
+
+// [NotDelayed]
+ public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fileName) : 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.errorHandler = errorHandler;
+ this.Filename = fileName;
+ Init();
+ }
+
+ void Init() {
+ pos = -1; line = 1; col = 0;
+ oldEols = 0;
+ NextCh();
+ if (ch == 0xEF) { // check optional byte order mark for UTF-8
+ NextCh(); int ch1 = ch;
+ NextCh(); int ch2 = ch;
+ if (ch1 != 0xBB || ch2 != 0xBF) {
+ throw new FatalError(String.Format("illegal byte order mark: EF {0,2:X} {1,2:X}", ch1, ch2));
+ }
+ buffer = new UTF8Buffer(buffer); col = 0;
+ NextCh();
+ }
+ pt = tokens = new Token(); // first token is a dummy
+ }
+
+ string/*!*/ ReadToEOL(){
+ Contract.Ensures(Contract.Result<string>() != null);
+ int p = buffer.Pos;
+ int ch = buffer.Read();
+ // replace isolated '\r' by '\n' in order to make
+ // eol handling uniform across Windows, Unix and Mac
+ if (ch == '\r' && buffer.Peek() != '\n') ch = EOL;
+ while (ch != EOL && ch != Buffer.EOF){
+ ch = buffer.Read();
+ // replace isolated '\r' by '\n' in order to make
+ // eol handling uniform across Windows, Unix and Mac
+ if (ch == '\r' && buffer.Peek() != '\n') ch = EOL;
+ }
+ string/*!*/ s = buffer.GetString(p, buffer.Pos);
+ Contract.Assert(s!=null);
+ return s;
+ }
+
+ void NextCh() {
+ if (oldEols > 0) { ch = EOL; oldEols--; }
+ else {
+// pos = buffer.Pos;
+// ch = buffer.Read(); col++;
+// // replace isolated '\r' by '\n' in order to make
+// // eol handling uniform across Windows, Unix and Mac
+// if (ch == '\r' && buffer.Peek() != '\n') ch = EOL;
+// if (ch == EOL) { line++; col = 0; }
+
+ while (true) {
+ pos = buffer.Pos;
+ ch = buffer.Read(); col++;
+ // replace isolated '\r' by '\n' in order to make
+ // eol handling uniform across Windows, Unix and Mac
+ if (ch == '\r' && buffer.Peek() != '\n') ch = EOL;
+ if (ch == EOL) {
+ line++; col = 0;
+ } else if (ch == '#' && col == 1) {
+ int prLine = line;
+ int prColumn = 0;
+
+ string/*!*/ hashLine = ReadToEOL();
+ Contract.Assert(hashLine!=null);
+ col = 0;
+ line++;
+
+ hashLine = hashLine.TrimEnd(null);
+ if (hashLine.StartsWith("line ") || hashLine == "line") {
+ // parse #line pragma: #line num [filename]
+ string h = hashLine.Substring(4).TrimStart(null);
+ int x = h.IndexOf(' ');
+ if (x == -1) {
+ x = h.Length; // this will be convenient below when we look for a filename
+ }
+ try {
+ int li = int.Parse(h.Substring(0, x));
+
+ h = h.Substring(x).Trim();
+
+ // act on #line
+ line = li;
+ if (h.Length != 0) {
+ // a filename was specified
+ Filename = h;
+ }
+ continue; // successfully parsed and acted on the #line pragma
+
+ } catch (FormatException) {
+ // just fall down through to produce an error message
+ }
+ this.errorHandler.SemErr(Filename, prLine, prColumn, "Malformed (#line num [filename]) pragma: #" + hashLine);
+ continue;
+ }
+
+ this.errorHandler.SemErr(Filename, prLine, prColumn, "Unrecognized pragma: #" + hashLine);
+ continue;
+ }
+ return;
+ }
+
+
+ }
+
+ }
+
+ void AddCh() {
+ if (tlen >= tval.Length) {
+ char[] newBuf = new char[2 * tval.Length];
+ Array.Copy(tval, 0, newBuf, 0, tval.Length);
+ tval = newBuf;
+ }
+ if (ch != Buffer.EOF) {
+ tval[tlen++] = (char) ch;
+ NextCh();
+ }
+ }
+
+
+
+ bool Comment0() {
+ int level = 1, pos0 = pos, line0 = line, col0 = col, charPos0 = charPos;
+ NextCh();
+ if (ch == '/') {
+ NextCh();
+ for(;;) {
+ if (ch == 10) {
+ level--;
+ if (level == 0) { oldEols = line - line0; NextCh(); return true; }
+ NextCh();
+ } else if (ch == Buffer.EOF) return false;
+ else NextCh();
+ }
+ } else {
+ buffer.Pos = pos0; NextCh(); line = line0; col = col0; charPos = charPos0;
+ }
+ return false;
+ }
+
+ bool Comment1() {
+ int level = 1, pos0 = pos, line0 = line, col0 = col, charPos0 = charPos;
+ NextCh();
+ if (ch == '*') {
+ NextCh();
+ for(;;) {
+ if (ch == '*') {
+ NextCh();
+ if (ch == '/') {
+ level--;
+ if (level == 0) { oldEols = line - line0; NextCh(); return true; }
+ NextCh();
+ }
+ } else if (ch == '/') {
+ NextCh();
+ if (ch == '*') {
+ level++; NextCh();
+ }
+ } else if (ch == Buffer.EOF) return false;
+ else NextCh();
+ }
+ } else {
+ buffer.Pos = pos0; NextCh(); line = line0; col = col0; charPos = charPos0;
+ }
+ return false;
+ }
+
+
+ void CheckLiteral() {
+ switch (t.val) {
+ case "ghost": t.kind = 8; break;
+ case "module": t.kind = 9; break;
+ case "refines": t.kind = 10; break;
+ case "import": t.kind = 11; break;
+ case "opened": t.kind = 12; break;
+ case "as": t.kind = 15; break;
+ case "default": t.kind = 16; break;
+ case "class": t.kind = 18; break;
+ case "static": t.kind = 19; break;
+ case "datatype": t.kind = 20; break;
+ case "codatatype": t.kind = 21; break;
+ case "var": t.kind = 23; break;
+ case "type": t.kind = 25; break;
+ case "iterator": t.kind = 29; break;
+ case "yields": t.kind = 30; break;
+ case "method": t.kind = 34; break;
+ case "constructor": t.kind = 35; break;
+ case "returns": t.kind = 36; break;
+ case "modifies": t.kind = 37; break;
+ case "free": t.kind = 38; break;
+ case "requires": t.kind = 39; break;
+ case "ensures": t.kind = 40; break;
+ case "decreases": t.kind = 41; break;
+ case "reads": t.kind = 42; break;
+ case "yield": t.kind = 43; break;
+ case "bool": t.kind = 44; break;
+ case "nat": t.kind = 45; break;
+ case "int": t.kind = 46; break;
+ case "set": t.kind = 47; break;
+ case "multiset": t.kind = 48; break;
+ case "seq": t.kind = 49; break;
+ case "map": t.kind = 50; break;
+ case "object": t.kind = 51; break;
+ case "function": t.kind = 52; break;
+ case "predicate": t.kind = 53; break;
+ case "copredicate": t.kind = 54; break;
+ case "label": t.kind = 57; break;
+ case "break": t.kind = 58; break;
+ case "where": t.kind = 59; break;
+ case "return": t.kind = 61; break;
+ case "assume": t.kind = 63; break;
+ case "new": t.kind = 64; break;
+ case "choose": t.kind = 67; break;
+ case "if": t.kind = 68; break;
+ case "else": t.kind = 69; break;
+ case "case": t.kind = 70; break;
+ case "while": t.kind = 72; break;
+ case "invariant": t.kind = 73; break;
+ case "match": t.kind = 74; break;
+ case "assert": t.kind = 75; break;
+ case "print": t.kind = 76; break;
+ case "parallel": t.kind = 77; break;
+ case "calc": t.kind = 78; break;
+ case "in": t.kind = 94; break;
+ case "false": t.kind = 101; break;
+ case "true": t.kind = 102; break;
+ case "null": t.kind = 103; break;
+ case "this": t.kind = 104; break;
+ case "fresh": t.kind = 105; break;
+ case "old": t.kind = 106; break;
+ case "then": t.kind = 107; break;
+ case "forall": t.kind = 109; break;
+ case "exists": t.kind = 111; break;
+ default: break;
+ }
+ }
+
+ Token/*!*/ NextToken() {
+ Contract.Ensures(Contract.Result<Token>() != null);
+ while (ch == ' ' ||
+ ch >= 9 && ch <= 10 || ch == 13
+ ) NextCh();
+ if (ch == '/' && Comment0() ||ch == '/' && Comment1()) return NextToken();
+ int recKind = noSym;
+ int recEnd = pos;
+ t = new Token();
+ t.pos = pos; t.col = col; t.line = line;
+ t.filename = this.Filename;
+ int state;
+ if (start.ContainsKey(ch)) {
+ Contract.Assert(start[ch] != null);
+ state = (int) start[ch];
+ }
+ else { state = 0; }
+ tlen = 0; AddCh();
+
+ switch (state) {
+ case -1: { t.kind = eofSym; break; } // NextCh already done
+ case 0: {
+ if (recKind != noSym) {
+ tlen = recEnd - t.pos;
+ SetScannerBehindT();
+ }
+ t.kind = recKind; break;
+ } // NextCh already done
+ case 1:
+ recEnd = pos; recKind = 1;
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 1;}
+ else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
+ case 2:
+ recEnd = pos; recKind = 1;
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 2;}
+ else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
+ case 3:
+ recEnd = pos; recKind = 1;
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 3;}
+ else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
+ case 4:
+ recEnd = pos; recKind = 1;
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 4;}
+ else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
+ case 5:
+ recEnd = pos; recKind = 1;
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 5;}
+ else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
+ case 6:
+ recEnd = pos; recKind = 1;
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 6;}
+ else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
+ case 7:
+ recEnd = pos; recKind = 2;
+ if (ch >= '0' && ch <= '9') {AddCh(); goto case 7;}
+ else {t.kind = 2; break;}
+ case 8:
+ if (ch == '"') {AddCh(); goto case 9;}
+ else if (ch >= ' ' && ch <= '!' || ch >= '#' && ch <= '~') {AddCh(); goto case 8;}
+ else {goto case 0;}
+ case 9:
+ {t.kind = 4; break;}
+ case 10:
+ {t.kind = 6; break;}
+ case 11:
+ {t.kind = 7; break;}
+ case 12:
+ recEnd = pos; recKind = 1;
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch == '_' || ch >= 'a' && ch <= 'q' || ch >= 's' && ch <= 'z') {AddCh(); goto case 2;}
+ else if (ch == 'r') {AddCh(); goto case 14;}
+ else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
+ case 13:
+ recEnd = pos; recKind = 1;
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 13;}
+ else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
+ case 14:
+ recEnd = pos; recKind = 1;
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch == '_' || ch >= 'a' && ch <= 'q' || ch >= 's' && ch <= 'z') {AddCh(); goto case 3;}
+ else if (ch == 'r') {AddCh(); goto case 15;}
+ else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
+ case 15:
+ recEnd = pos; recKind = 1;
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch == '_' || ch >= 'b' && ch <= 'z') {AddCh(); goto case 4;}
+ else if (ch == 'a') {AddCh(); goto case 16;}
+ else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
+ case 16:
+ recEnd = pos; recKind = 1;
+ if (ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch == '_' || ch >= 'a' && ch <= 'x' || ch == 'z') {AddCh(); goto case 5;}
+ else if (ch == 'y') {AddCh(); goto case 17;}
+ else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
+ case 17:
+ recEnd = pos; recKind = 3;
+ if (ch == 39 || ch == '0' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 6;}
+ else if (ch >= '1' && ch <= '9') {AddCh(); goto case 18;}
+ else {t.kind = 3; break;}
+ case 18:
+ recEnd = pos; recKind = 3;
+ if (ch == 39 || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 13;}
+ else if (ch >= '0' && ch <= '9') {AddCh(); goto case 18;}
+ else {t.kind = 3; break;}
+ case 19:
+ {t.kind = 14; break;}
+ case 20:
+ {t.kind = 24; break;}
+ case 21:
+ {t.kind = 26; break;}
+ case 22:
+ {t.kind = 28; break;}
+ case 23:
+ {t.kind = 31; break;}
+ case 24:
+ {t.kind = 55; break;}
+ case 25:
+ {t.kind = 56; break;}
+ case 26:
+ {t.kind = 60; break;}
+ case 27:
+ {t.kind = 62; break;}
+ case 28:
+ {t.kind = 65; break;}
+ case 29:
+ {t.kind = 66; break;}
+ case 30:
+ {t.kind = 71; break;}
+ case 31:
+ {t.kind = 80; break;}
+ case 32:
+ {t.kind = 81; break;}
+ case 33:
+ {t.kind = 82; break;}
+ case 34:
+ {t.kind = 83; break;}
+ case 35:
+ {t.kind = 84; break;}
+ case 36:
+ if (ch == '>') {AddCh(); goto case 37;}
+ else {goto case 0;}
+ case 37:
+ {t.kind = 85; break;}
+ case 38:
+ {t.kind = 86; break;}
+ case 39:
+ {t.kind = 87; break;}
+ case 40:
+ {t.kind = 88; break;}
+ case 41:
+ if (ch == '&') {AddCh(); goto case 42;}
+ else {goto case 0;}
+ case 42:
+ {t.kind = 89; break;}
+ case 43:
+ {t.kind = 90; break;}
+ case 44:
+ {t.kind = 91; break;}
+ case 45:
+ {t.kind = 92; break;}
+ case 46:
+ {t.kind = 93; break;}
+ case 47:
+ {t.kind = 96; break;}
+ case 48:
+ {t.kind = 97; break;}
+ case 49:
+ {t.kind = 98; break;}
+ case 50:
+ {t.kind = 99; break;}
+ case 51:
+ {t.kind = 100; break;}
+ case 52:
+ {t.kind = 110; break;}
+ case 53:
+ {t.kind = 112; break;}
+ case 54:
+ {t.kind = 113; break;}
+ case 55:
+ {t.kind = 114; break;}
+ case 56:
+ recEnd = pos; recKind = 5;
+ if (ch == '=') {AddCh(); goto case 26;}
+ else if (ch == '|') {AddCh(); goto case 27;}
+ else if (ch == ':') {AddCh(); goto case 54;}
+ else {t.kind = 5; break;}
+ case 57:
+ recEnd = pos; recKind = 13;
+ if (ch == '=') {AddCh(); goto case 63;}
+ else if (ch == '>') {AddCh(); goto case 30;}
+ else {t.kind = 13; break;}
+ case 58:
+ recEnd = pos; recKind = 17;
+ if (ch == '.') {AddCh(); goto case 64;}
+ else {t.kind = 17; break;}
+ case 59:
+ recEnd = pos; recKind = 22;
+ if (ch == '|') {AddCh(); goto case 44;}
+ else {t.kind = 22; break;}
+ case 60:
+ recEnd = pos; recKind = 32;
+ if (ch == '=') {AddCh(); goto case 65;}
+ else {t.kind = 32; break;}
+ case 61:
+ recEnd = pos; recKind = 33;
+ if (ch == '=') {AddCh(); goto case 31;}
+ else {t.kind = 33; break;}
+ case 62:
+ recEnd = pos; recKind = 95;
+ if (ch == '=') {AddCh(); goto case 32;}
+ else if (ch == '!') {AddCh(); goto case 46;}
+ else {t.kind = 95; break;}
+ case 63:
+ recEnd = pos; recKind = 27;
+ if (ch == '>') {AddCh(); goto case 39;}
+ else {t.kind = 27; break;}
+ case 64:
+ recEnd = pos; recKind = 108;
+ if (ch == '.') {AddCh(); goto case 23;}
+ else {t.kind = 108; break;}
+ case 65:
+ recEnd = pos; recKind = 79;
+ if (ch == '=') {AddCh(); goto case 36;}
+ else {t.kind = 79; break;}
+
+ }
+ t.val = new String(tval, 0, tlen);
+ return t;
+ }
+
+ private void SetScannerBehindT() {
+ buffer.Pos = t.pos;
+ NextCh();
+ line = t.line; col = t.col;
+ for (int i = 0; i < tlen; i++) NextCh();
+ }
+
+ // get the next token (possibly a token already seen during peeking)
+ public Token/*!*/ Scan () {
+ Contract.Ensures(Contract.Result<Token>() != null);
+ if (tokens.next == null) {
+ return NextToken();
+ } else {
+ pt = tokens = tokens.next;
+ return tokens;
+ }
+ }
+
+ // peek for the next token, ignore pragmas
+ public Token/*!*/ Peek () {
+ Contract.Ensures(Contract.Result<Token>() != null);
+ do {
+ if (pt.next == null) {
+ pt.next = NextToken();
+ }
+ pt = pt.next;
+ } while (pt.kind > maxT); // skip pragmas
+
+ return pt;
+ }
+
+ // make sure that peeking starts at the current scan position
+ public void ResetPeek () { pt = tokens; }
+
+} // 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
new file mode 100644
index 00000000..4ae1c185
--- /dev/null
+++ b/Source/Dafny/SccGraph.cs
@@ -0,0 +1,370 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+
+namespace Microsoft.Dafny {
+
+ public class Graph<Node> where Node : class
+ {
+ enum VisitedStatus { Unvisited, OnStack, Visited }
+ 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
+ [ContractInvariantMethod]
+ void ObjectInvariant()
+ {
+ Contract.Invariant(cce.NonNullElements(Successors));
+ Contract.Invariant(SccMembers==null || cce.NonNullElements(SccMembers));
+ }
+
+ public Vertex SccRepresentative; // null if not computed
+
+ public int SccId; // valid only for SCC representatives; indicates position of this representative vertex in the graph's topological sort
+ // the following field is used during the computation of SCCs and of reachability
+ public VisitedStatus Visited;
+ // the following fields are used during the computation of SCCs:
+ public int DfNumber;
+ public int LowLink;
+ // the following field is used during a Reaches computation
+ public int Gen; // generation <= Gen means this vertex has been visited in the current generation
+
+ public Vertex(Node n) {
+ N = n;
+ }
+ public void AddSuccessor(Vertex v) {
+ Contract.Requires(v != null);
+ Successors.Add(v);
+ }
+ }
+
+
+ [ContractInvariantMethod]
+ void ObjectInvariant()
+ {
+ Contract.Invariant(vertices!=null);
+ Contract.Invariant(cce.NonNullElements(vertices.Values));
+ Contract.Invariant(topologicallySortedRepresentatives==null || cce.NonNullElements(topologicallySortedRepresentatives));
+ Contract.Invariant(!sccComputed || topologicallySortedRepresentatives != null);
+ }
+
+ Dictionary<Node, Vertex/*!*/>/*!*/ vertices = new Dictionary<Node, Vertex/*!*/>();
+ bool sccComputed = false;
+ List<Vertex/*!*/> topologicallySortedRepresentatives; // computed by the SCC computation
+
+ public int SccCount {
+ get {
+ ComputeSCCs();
+ Contract.Assert(topologicallySortedRepresentatives != null); // follows from postcondition of ComputeSCCs and the object invariant
+ return topologicallySortedRepresentatives.Count;
+ }
+ }
+ int generation = 0;
+
+ public Graph()
+ {
+ }
+
+ /// <summary>
+ /// Idempotently adds a vertex 'n' to the graph.
+ /// </summary>
+ public void AddVertex(Node n) {
+ GetVertex(n);
+ }
+
+ /// <summary>
+ /// Idempotently adds a vertex 'n' to the graph and then returns the Vertex for it.
+ /// </summary>
+ Vertex GetVertex(Node n) {
+ Contract.Ensures(Contract.Result<Vertex>() != null);
+
+ Vertex v = FindVertex(n);
+ if (v == null) {
+ v = new Vertex(n);
+ vertices.Add(n, v);
+ if (sccComputed) {
+ Contract.Assert(topologicallySortedRepresentatives != null); // follows from object invariant
+ v.SccRepresentative = v;
+ v.SccMembers = new List<Vertex>();
+ v.SccMembers.Add(v);
+ v.SccId = topologicallySortedRepresentatives.Count;
+ topologicallySortedRepresentatives.Add(v);
+ }
+ }
+ return v;
+ }
+
+ /// <summary>
+ /// Returns the vertex for 'n' if 'n' is in the graph. Otherwise, returns null.
+ /// </summary>
+ 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!)
+ return v;
+ } else {
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Idempotently adds verices 'from' and 'to' the graph, and then
+ /// adds an edge from 'from' to 'to'.
+ /// </summary>
+ public void AddEdge(Node from, Node to) {
+ Vertex v0 = GetVertex(from);
+ Vertex v1 = GetVertex(to);
+ v0.AddSuccessor(v1);
+ sccComputed = false; // the addition of an edge may invalidate any previous computation of the graph's SCCs
+ }
+
+ /// <summary>
+ /// Idempotently adds 'n' as a vertex and then returns a Node that is the representative element of the
+ /// strongly connected component containing 'n'.
+ /// </summary>
+ public Node GetSCCRepresentative(Node n) {
+ return GetSCCRepr(n).N;
+ }
+
+ /// <summary>
+ /// Idempotently adds 'n' as a vertex. Then, returns the number of SCCs before the SCC of 'n' in the
+ /// topologically sorting of SCCs.
+ /// </summary>
+ public int GetSCCRepresentativeId(Node n) {
+ return GetSCCRepr(n).SccId;
+ }
+
+ Vertex GetSCCRepr(Node n) {
+ Contract.Ensures(Contract.Result<Vertex>() != null);
+
+ Vertex v = GetVertex(n);
+ ComputeSCCs();
+ Contract.Assert(v.SccRepresentative != null); // follows from what ComputeSCCs does
+ return v.SccRepresentative;
+ }
+
+ /// <summary>
+ /// Returns a list of the topologically sorted SCCs, each represented in the list by its representative node.
+ /// </summary>
+ public List<Node> TopologicallySortedComponents() {
+ Contract.Ensures(cce.NonNullElements(Contract.Result<List<Node>>()));
+ ComputeSCCs();
+ Contract.Assert(topologicallySortedRepresentatives != null); // follows from object invariant
+ List<Node> nn = new List<Node>();
+ foreach (Vertex v in topologicallySortedRepresentatives) {
+ nn.Add(v.N);
+ }
+ return nn;
+ }
+
+ /// <summary>
+ /// Idempotently adds 'n' as a vertex and then returns the set of Node's in the strongly connected component
+ /// that contains 'n'.
+ /// </summary>
+ public List<Node> GetSCC(Node n) {Contract.Ensures(cce.NonNullElements(Contract.Result<List<Node>>()));
+ Vertex v = GetVertex(n);
+ ComputeSCCs();
+ Vertex repr = v.SccRepresentative;
+ Contract.Assert(repr != null && repr.SccMembers != null); // follows from postcondition of ComputeSCCs
+ List<Node> nn = new List<Node>();
+ foreach (Vertex w in repr.SccMembers) {
+ nn.Add(w.N);
+ }
+ return nn;
+ }
+
+ /// <summary>
+ /// Idempotently adds 'n' as a vertex and then returns the size of the set of Node's in the strongly connected component
+ /// that contains 'n'.
+ /// </summary>
+ public int GetSCCSize(Node n){
+ Contract.Ensures(1 <= Contract.Result<int>());
+
+ Vertex v = GetVertex(n);
+ ComputeSCCs();
+ Vertex repr = v.SccRepresentative;
+ Contract.Assert(repr != null && repr.SccMembers != null); // follows from postcondition of ComputeSCCs
+ return repr.SccMembers.Count;
+ }
+
+ /// <summary>
+ /// This method sets the SccRepresentative fields of the graph's vertices so that two
+ /// vertices have the same representative iff they are in the same strongly connected
+ /// component.
+ /// As a side effect, this method may change the Visited, DfNumber, and LowLink fields
+ /// of the vertices.
+ /// </summary>
+ void ComputeSCCs()
+ {
+ Contract.Ensures(sccComputed);
+
+ if (sccComputed) { return; } // check if already computed
+
+ // reset all SCC information
+ topologicallySortedRepresentatives = new List<Vertex>();
+ foreach (Vertex v in vertices.Values) {
+ v.Visited = VisitedStatus.Unvisited;
+ v.SccMembers = null;
+ }
+ Stack<Vertex> stack = new Stack<Vertex>();
+ int cnt = 0;
+ foreach (Vertex v in vertices.Values) {
+ if (v.Visited == VisitedStatus.Unvisited) {
+ SearchC(v, stack, ref cnt);
+ }
+ }
+ Contract.Assert(cnt == vertices.Count); // sanity check that everything has been visited
+
+ sccComputed = true;
+ }
+
+ /// <summary>
+ /// This is the 'SearchC' procedure from the Aho, Hopcroft, and Ullman book 'The Design and Analysis of Computer Algorithms'.
+ /// </summary>
+ void SearchC(Vertex/*!*/ v, Stack<Vertex/*!*/>/*!*/ stack, ref int cnt){
+ Contract.Requires(v != null);
+ Contract.Requires(cce.NonNullElements(stack));
+ Contract.Requires(v.Visited == VisitedStatus.Unvisited);
+ Contract.Requires(topologicallySortedRepresentatives != null);
+ Contract.Ensures(v.Visited != VisitedStatus.Unvisited);
+
+ v.DfNumber = cnt;
+ cnt++;
+ v.LowLink = v.DfNumber;
+ stack.Push(v);
+ v.Visited = VisitedStatus.OnStack;
+
+ foreach (Vertex w in v.Successors) {
+ if (w.Visited == VisitedStatus.Unvisited) {
+ SearchC(w, stack, ref cnt);
+ v.LowLink = Math.Min(v.LowLink, w.LowLink);
+ } else if (w.Visited == VisitedStatus.OnStack) {
+ Contract.Assert(w.DfNumber < v.DfNumber || v.LowLink <= w.DfNumber); // the book also has the guard 'w.DfNumber < v.DfNumber', but that seems unnecessary to me, so this assert is checking my understanding
+ v.LowLink = Math.Min(v.LowLink, w.DfNumber);
+ }
+ }
+
+ if (v.LowLink == v.DfNumber) {
+ // The SCC containing 'v' has now been computed.
+ v.SccId = topologicallySortedRepresentatives.Count;
+ topologicallySortedRepresentatives.Add(v);
+ v.SccMembers = new List<Vertex>();
+ while (true) {
+ Vertex x = stack.Pop();
+ x.Visited = VisitedStatus.Visited;
+ x.SccRepresentative = v;
+ v.SccMembers.Add(x);
+ if (x == v) { break; }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns null if the graph has no cycles. If the graph does contain some cycle, returns the list of
+ /// vertices on one such cycle.
+ /// </summary>
+ public List<Node> TryFindCycle() {
+ // reset all visited information
+ foreach (Vertex v in vertices.Values) {
+ v.Visited = VisitedStatus.Unvisited;
+ }
+
+ foreach (Vertex v in vertices.Values) {
+ Contract.Assert(v.Visited != VisitedStatus.OnStack);
+ if (v.Visited == VisitedStatus.Unvisited) {
+ List<Vertex> cycle = CycleSearch(v);
+ if (cycle != null) {
+ List<Node> nodes = new List<Node>();
+ foreach (Vertex v_ in cycle) {
+ nodes.Add(v_.N);
+ }
+ return nodes; // a cycle is found
+ }
+ }
+ }
+ return null; // there are no cycles
+ }
+
+ /// <summary>
+ /// A return of null means there are no cycles involving any vertex in the subtree rooted at v.
+ /// A non-null return means a cycle has been found. Then:
+ /// If v.Visited == Visited, then the entire cycle is described in the returned list.
+ /// If v.Visited == OnStack, then the cycle consists of the vertices strictly deeper than
+ /// w on the stack followed by the vertices (in reverse order) in the returned list, where
+ /// w is the first vertex in the list returned.
+ /// </summary>
+ List<Vertex/*!*/> CycleSearch(Vertex v)
+ {
+ Contract.Requires(v != null);
+ Contract.Requires(v.Visited == VisitedStatus.Unvisited);
+ Contract.Ensures(v.Visited != VisitedStatus.Unvisited);
+ Contract.Ensures(Contract.Result<List<Vertex>>() != null || v.Visited == VisitedStatus.Visited);
+ Contract.Ensures(Contract.Result<List<Vertex>>() == null || Contract.Result<List<Vertex>>().Count != 0);
+
+ v.Visited = VisitedStatus.OnStack;
+ foreach (Vertex succ in v.Successors) {
+ // todo: I would use a 'switch' statement, but there seems to be a bug in the Spec# compiler's type checking.
+ if (succ.Visited == VisitedStatus.Visited) {
+ // there is no cycle in the subtree rooted at succ, hence this path does not give rise to any cycles
+ } else if (succ.Visited == VisitedStatus.OnStack) {
+ // we found a cycle!
+ List<Vertex> cycle = new List<Vertex>();
+ cycle.Add(succ);
+ if (v == succ) {
+ // entire cycle has been found
+ v.Visited = VisitedStatus.Visited;
+ }
+ return cycle;
+ } else {
+ Contract.Assert(succ.Visited == VisitedStatus.Unvisited);
+ List<Vertex> cycle = CycleSearch(succ);
+ if (cycle != null) {
+ if (succ.Visited == VisitedStatus.Visited) {
+ // the entire cycle has been collected
+ v.Visited = VisitedStatus.Visited;
+ return cycle;
+ } else {
+ cycle.Add(succ);
+ if (v == cycle[0]) {
+ // the entire cycle has been collected and we are the first to find out
+ v.Visited = VisitedStatus.Visited;
+ }
+ return cycle;
+ }
+ }
+ }
+ }
+ v.Visited = VisitedStatus.Visited; // there are no cycles from here on
+ return null;
+ }
+
+ /// <summary>
+ /// Returns whether or not 'source' reaches 'sink' in the graph.
+ /// 'source' and 'sink' need not be in the graph; if neither is, the return value
+ /// is source==sink.
+ /// </summary>
+ public bool Reaches(Node source, Node sink) {
+ Vertex a = FindVertex(source);
+ Vertex b = FindVertex(sink);
+ if (a == null || b == null) {
+ return source.Equals(sink);
+ }
+ generation++;
+ return ReachSearch(a, b);
+ }
+
+ bool ReachSearch(Vertex source, Vertex sink) {
+ Contract.Requires(source != null);
+ Contract.Requires(sink != null);
+ if (source == sink) {
+ return true;
+ } else if (source.Gen == generation) {
+ // already visited
+ return false;
+ } else {
+ source.Gen = generation;
+ return Contract.Exists(source.Successors,succ=> ReachSearch(succ, sink));
+ }
+ }
+ }
+}
diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs
new file mode 100644
index 00000000..40795129
--- /dev/null
+++ b/Source/Dafny/Translator.cs
@@ -0,0 +1,9249 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Diagnostics.Contracts;
+using Bpl = Microsoft.Boogie;
+using System.Text;
+using Microsoft.Boogie;
+
+namespace Microsoft.Dafny {
+ public class Translator {
+ [NotDelayed]
+ public Translator() {
+ Bpl.Program boogieProgram = ReadPrelude();
+ if (boogieProgram != null) {
+ sink = boogieProgram;
+ predef = FindPredefinedDecls(boogieProgram);
+ }
+ }
+
+ // translation state
+ readonly Dictionary<TopLevelDecl/*!*/,Bpl.Constant/*!*/>/*!*/ classes = new Dictionary<TopLevelDecl/*!*/,Bpl.Constant/*!*/>();
+ 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>();
+ Program program;
+
+ [ContractInvariantMethod]
+ void ObjectInvariant()
+ {
+ Contract.Invariant(cce.NonNullDictionaryAndValues(classes));
+ Contract.Invariant(cce.NonNullDictionaryAndValues(fields));
+ Contract.Invariant(cce.NonNullDictionaryAndValues(fieldFunctions));
+ Contract.Invariant(codeContext == null || codeContext.EnclosingModule == currentModule);
+ }
+
+ readonly Bpl.Program sink;
+ readonly PredefinedDecls predef;
+
+ internal class PredefinedDecls {
+ public readonly Bpl.Type RefType;
+ public readonly Bpl.Type BoxType;
+ public readonly Bpl.Type TickType;
+ private readonly Bpl.TypeSynonymDecl setTypeCtor;
+ private readonly Bpl.TypeSynonymDecl multiSetTypeCtor;
+ private readonly Bpl.TypeCtorDecl mapTypeCtor;
+ public readonly Bpl.Function ArrayLength;
+ private readonly Bpl.TypeCtorDecl seqTypeCtor;
+ readonly Bpl.TypeCtorDecl fieldName;
+ public readonly Bpl.Type HeapType;
+ public readonly string HeapVarName;
+ public readonly Bpl.Type ClassNameType;
+ public readonly Bpl.Type NameFamilyType;
+ public readonly Bpl.Type DatatypeType;
+ public readonly Bpl.Type DtCtorId;
+ public readonly Bpl.Expr Null;
+ private readonly Bpl.Constant allocField;
+ public readonly Bpl.Constant ClassDotArray;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(RefType != null);
+ Contract.Invariant(BoxType != null);
+ Contract.Invariant(TickType != null);
+ Contract.Invariant(setTypeCtor != null);
+ Contract.Invariant(multiSetTypeCtor != null);
+ Contract.Invariant(ArrayLength != null);
+ Contract.Invariant(seqTypeCtor != null);
+ Contract.Invariant(fieldName != null);
+ Contract.Invariant(HeapType != null);
+ Contract.Invariant(HeapVarName != null);
+ Contract.Invariant(ClassNameType != null);
+ Contract.Invariant(NameFamilyType != null);
+ Contract.Invariant(DatatypeType != null);
+ Contract.Invariant(DtCtorId != null);
+ Contract.Invariant(Null != null);
+ Contract.Invariant(allocField != null);
+ Contract.Invariant(ClassDotArray != null);
+ }
+
+
+ public Bpl.Type SetType(IToken tok, 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 Bpl.TypeSeq(ty));
+ }
+
+ public Bpl.Type MultiSetType(IToken tok, Bpl.Type ty) {
+ Contract.Requires(tok != null);
+ Contract.Requires(ty != null);
+ Contract.Ensures(Contract.Result<Bpl.Type>() != null);
+
+ return new Bpl.TypeSynonymAnnotation(Token.NoToken, multiSetTypeCtor, new Bpl.TypeSeq(ty));
+ }
+ public Bpl.Type MapType(IToken tok, Bpl.Type tya, Bpl.Type tyb) {
+ Contract.Requires(tok != null);
+ Contract.Requires(tya != null && tyb != null);
+ Contract.Ensures(Contract.Result<Bpl.Type>() != null);
+
+ return new Bpl.CtorType(Token.NoToken, mapTypeCtor, new Bpl.TypeSeq(tya, tyb));
+ }
+
+ public Bpl.Type SeqType(IToken tok, Bpl.Type ty) {
+ Contract.Requires(tok != null);
+ Contract.Requires(ty != null);
+ Contract.Ensures(Contract.Result<Bpl.Type>() != null);
+ return new Bpl.CtorType(Token.NoToken, seqTypeCtor, new Bpl.TypeSeq(ty));
+ }
+
+ public Bpl.Type FieldName(IToken tok, Bpl.Type ty) {
+ Contract.Requires(tok != null);
+ Contract.Requires(ty != null);
+ Contract.Ensures(Contract.Result<Bpl.Type>() != null);
+
+ return new Bpl.CtorType(tok, fieldName, new Bpl.TypeSeq(ty));
+ }
+
+ public Bpl.IdentifierExpr Alloc(IToken tok) {
+ Contract.Requires(tok != null);
+ Contract.Ensures(Contract.Result<Bpl.IdentifierExpr>() != null);
+
+ return new Bpl.IdentifierExpr(tok, allocField);
+ }
+
+ public PredefinedDecls(Bpl.TypeCtorDecl refType, Bpl.TypeCtorDecl boxType, Bpl.TypeCtorDecl tickType,
+ Bpl.TypeSynonymDecl setTypeCtor, Bpl.TypeSynonymDecl multiSetTypeCtor, Bpl.TypeCtorDecl mapTypeCtor, Bpl.Function arrayLength, Bpl.TypeCtorDecl seqTypeCtor, Bpl.TypeCtorDecl fieldNameType,
+ Bpl.GlobalVariable heap, Bpl.TypeCtorDecl classNameType, Bpl.TypeCtorDecl nameFamilyType,
+ Bpl.TypeCtorDecl datatypeType, Bpl.TypeCtorDecl dtCtorId,
+ Bpl.Constant allocField, Bpl.Constant classDotArray) {
+ #region Non-null preconditions on parameters
+ Contract.Requires(refType != null);
+ Contract.Requires(boxType != null);
+ Contract.Requires(tickType != null);
+ Contract.Requires(setTypeCtor != null);
+ Contract.Requires(multiSetTypeCtor != null);
+ Contract.Requires(seqTypeCtor != null);
+ Contract.Requires(fieldNameType != null);
+ Contract.Requires(heap != null);
+ Contract.Requires(classNameType != null);
+ Contract.Requires(datatypeType != null);
+ Contract.Requires(dtCtorId != null);
+ Contract.Requires(allocField != null);
+ Contract.Requires(classDotArray != null);
+ #endregion
+
+ Bpl.CtorType refT = new Bpl.CtorType(Token.NoToken, refType, new Bpl.TypeSeq());
+ this.RefType = refT;
+ this.BoxType = new Bpl.CtorType(Token.NoToken, boxType, new Bpl.TypeSeq());
+ this.TickType = new Bpl.CtorType(Token.NoToken, tickType, new Bpl.TypeSeq());
+ this.setTypeCtor = setTypeCtor;
+ this.multiSetTypeCtor = multiSetTypeCtor;
+ this.mapTypeCtor = mapTypeCtor;
+ this.ArrayLength = arrayLength;
+ this.seqTypeCtor = seqTypeCtor;
+ this.fieldName = fieldNameType;
+ this.HeapType = heap.TypedIdent.Type;
+ this.HeapVarName = heap.Name;
+ this.ClassNameType = new Bpl.CtorType(Token.NoToken, classNameType, new Bpl.TypeSeq());
+ this.NameFamilyType = new Bpl.CtorType(Token.NoToken, nameFamilyType, new Bpl.TypeSeq());
+ this.DatatypeType = new Bpl.CtorType(Token.NoToken, datatypeType, new Bpl.TypeSeq());
+ this.DtCtorId = new Bpl.CtorType(Token.NoToken, dtCtorId, new Bpl.TypeSeq());
+ this.allocField = allocField;
+ this.Null = new Bpl.IdentifierExpr(Token.NoToken, "null", refT);
+ this.ClassDotArray = classDotArray;
+ }
+ }
+
+ static PredefinedDecls FindPredefinedDecls(Bpl.Program prog) {
+ Contract.Requires(prog != null);
+ if (prog.Resolve() != 0) {
+ Console.WriteLine("Error: resolution errors encountered in Dafny prelude");
+ return null;
+ }
+
+ Bpl.TypeCtorDecl refType = null;
+ Bpl.TypeSynonymDecl setTypeCtor = null;
+ Bpl.TypeSynonymDecl multiSetTypeCtor = null;
+ Bpl.Function arrayLength = null;
+ Bpl.TypeCtorDecl seqTypeCtor = null;
+ Bpl.TypeCtorDecl fieldNameType = null;
+ Bpl.TypeCtorDecl classNameType = null;
+ Bpl.TypeCtorDecl nameFamilyType = null;
+ Bpl.TypeCtorDecl datatypeType = null;
+ Bpl.TypeCtorDecl dtCtorId = null;
+ Bpl.TypeCtorDecl boxType = null;
+ Bpl.TypeCtorDecl tickType = null;
+ Bpl.TypeCtorDecl mapTypeCtor = null;
+ Bpl.GlobalVariable heap = null;
+ Bpl.Constant allocField = null;
+ Bpl.Constant classDotArray = null;
+ foreach (var d in prog.TopLevelDeclarations) {
+ if (d is Bpl.TypeCtorDecl) {
+ Bpl.TypeCtorDecl dt = (Bpl.TypeCtorDecl)d;
+ if (dt.Name == "Seq") {
+ seqTypeCtor = dt;
+ } else if (dt.Name == "Field") {
+ fieldNameType = dt;
+ } else if (dt.Name == "ClassName") {
+ classNameType = dt;
+ } else if (dt.Name == "DatatypeType") {
+ datatypeType = dt;
+ } else if (dt.Name == "DtCtorId") {
+ dtCtorId = dt;
+ } else if (dt.Name == "ref") {
+ refType = dt;
+ } else if (dt.Name == "NameFamily") {
+ nameFamilyType = dt;
+ } else if (dt.Name == "BoxType") {
+ boxType = dt;
+ } else if (dt.Name == "TickType") {
+ tickType = dt;
+ } else if (dt.Name == "Map") {
+ mapTypeCtor = dt;
+ }
+ } else if (d is Bpl.TypeSynonymDecl) {
+ Bpl.TypeSynonymDecl dt = (Bpl.TypeSynonymDecl)d;
+ if (dt.Name == "Set") {
+ setTypeCtor = dt;
+ }
+ if (dt.Name == "MultiSet") {
+ multiSetTypeCtor = dt;
+ }
+ } else if (d is Bpl.Constant) {
+ Bpl.Constant c = (Bpl.Constant)d;
+ if (c.Name == "alloc") {
+ allocField = c;
+ } else if (c.Name == "class._System.array") {
+ classDotArray = c;
+ }
+ } else if (d is Bpl.GlobalVariable) {
+ Bpl.GlobalVariable v = (Bpl.GlobalVariable)d;
+ if (v.Name == "$Heap") {
+ heap = v;
+ }
+ } else if (d is Bpl.Function) {
+ var f = (Bpl.Function)d;
+ if (f.Name == "_System.array.Length")
+ arrayLength = f;
+ }
+ }
+ if (seqTypeCtor == null) {
+ 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 (multiSetTypeCtor == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type MultiSet");
+ } else if (mapTypeCtor == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type Map");
+ } else if (arrayLength == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of function _System.array.Length");
+ } else if (fieldNameType == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type Field");
+ } else if (classNameType == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type ClassName");
+ } else if (nameFamilyType == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type NameFamily");
+ } else if (datatypeType == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type DatatypeType");
+ } else if (dtCtorId == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type DtCtorId");
+ } else if (refType == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type ref");
+ } else if (boxType == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type BoxType");
+ } else if (tickType == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type TickType");
+ } else if (heap == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of $Heap");
+ } else if (allocField == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of constant alloc");
+ } else if (classDotArray == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of class._System.array");
+ } else {
+ return new PredefinedDecls(refType, boxType, tickType,
+ setTypeCtor, multiSetTypeCtor, mapTypeCtor, arrayLength, seqTypeCtor, fieldNameType, heap, classNameType, nameFamilyType, datatypeType, dtCtorId,
+ allocField, classDotArray);
+ }
+ return null;
+ }
+
+ static Bpl.Program ReadPrelude() {
+ string preludePath = DafnyOptions.O.DafnyPrelude;
+ if (preludePath == null)
+ {
+ //using (System.IO.Stream stream = cce.NonNull( System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("DafnyPrelude.bpl")) // Use this once Spec#/VSIP supports designating a non-.resx project item as an embedded resource
+ string codebase = cce.NonNull(System.IO.Path.GetDirectoryName(cce.NonNull(System.Reflection.Assembly.GetExecutingAssembly().Location)));
+ preludePath = System.IO.Path.Combine(codebase, "DafnyPrelude.bpl");
+ }
+
+ Bpl.Program prelude;
+ int errorCount = Bpl.Parser.Parse(preludePath, (List<string>)null, out prelude);
+ if (prelude == null || errorCount > 0) {
+ return null;
+ } else {
+ return prelude;
+ }
+/*
+ List<string!> defines = new List<string!>();
+ using (System.IO.Stream stream = new System.IO.FileStream(preludePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
+ {
+ BoogiePL.Buffer.Fill(new System.IO.StreamReader(stream), defines);
+ //BoogiePL.Scanner.Init("<DafnyPrelude.bpl>");
+ Bpl.Program prelude;
+ int errorCount = BoogiePL.Parser.Parse(out prelude);
+ if (prelude == null || errorCount > 0) {
+ return null;
+ } else {
+ return prelude;
+ }
+ }
+*/
+ }
+
+ public Bpl.Program Translate(Program p) {
+ Contract.Requires(p != null);
+ Contract.Ensures(Contract.Result<Bpl.Program>() != null);
+
+ program = p;
+
+ if (sink == null || predef == null) {
+ // something went wrong during construction, which reads the prelude; an error has
+ // already been printed, so just return an empty program here (which is non-null)
+ return new Bpl.Program();
+ }
+
+ foreach (TopLevelDecl d in program.BuiltIns.SystemModule.TopLevelDecls) {
+ if (d is ArbitraryTypeDecl) {
+ // nothing to do--this is treated just like a type parameter
+ } else if (d is DatatypeDecl) {
+ AddDatatype((DatatypeDecl)d);
+ } else {
+ AddClassMembers((ClassDecl)d);
+ }
+ }
+ foreach (ModuleDefinition m in program.Modules) {
+ foreach (TopLevelDecl d in m.TopLevelDecls) {
+ if (d is ArbitraryTypeDecl) {
+ // nothing to do--this is treated just like a type parameter
+ } else if (d is DatatypeDecl) {
+ AddDatatype((DatatypeDecl)d);
+ } else if (d is ModuleDecl) {
+ // submodules have already been added as a top level module, ignore this.
+ } else if (d is ClassDecl) {
+ AddClassMembers((ClassDecl)d);
+ if (d is IteratorDecl) {
+ AddIteratorSpecAndBody((IteratorDecl)d);
+ }
+ } else {
+ Contract.Assert(false);
+ }
+ }
+ }
+ foreach(var c in fieldConstants.Values) {
+ sink.TopLevelDeclarations.Add(c);
+ }
+ HashSet<Tuple<string, string>> checkedMethods = new HashSet<Tuple<string, string>>();
+ HashSet<Tuple<string, string>> checkedFunctions = new HashSet<Tuple<string, string>>();
+ foreach (var t in program.TranslationTasks) {
+ if (t is MethodCheck) {
+ var m = (MethodCheck)t;
+ var id = new Tuple<string, string>(m.Refined.FullCompileName, m.Refining.FullCompileName);
+ if (!checkedMethods.Contains(id)) {
+ AddMethodRefinementCheck(m);
+ checkedMethods.Add(id);
+ }
+ } else if (t is FunctionCheck) {
+ var f = (FunctionCheck)t;
+ var id = new Tuple<string, string>(f.Refined.FullCompileName, f.Refining.FullCompileName);
+ if (!checkedFunctions.Contains(id)) {
+ AddFunctionRefinementCheck(f);
+ checkedFunctions.Add(id);
+ }
+ }
+ }
+ return sink;
+ }
+
+ void AddDatatype(DatatypeDecl dt)
+ {
+ Contract.Requires(dt != null);
+ Contract.Requires(sink != null && predef != null);
+ sink.TopLevelDeclarations.Add(GetClass(dt));
+
+ foreach (DatatypeCtor ctor in dt.Ctors) {
+ // Add: function #dt.ctor(paramTypes) returns (DatatypeType);
+ Bpl.VariableSeq argTypes = new Bpl.VariableSeq();
+ foreach (Formal arg in ctor.Formals) {
+ Bpl.Variable a = new Bpl.Formal(arg.tok, new Bpl.TypedIdent(arg.tok, Bpl.TypedIdent.NoName, TrType(arg.Type)), true);
+ argTypes.Add(a);
+ }
+ Bpl.Variable resType = new Bpl.Formal(ctor.tok, new Bpl.TypedIdent(ctor.tok, Bpl.TypedIdent.NoName, predef.DatatypeType), false);
+ Bpl.Function fn = new Bpl.Function(ctor.tok, ctor.FullName, argTypes, resType);
+ sink.TopLevelDeclarations.Add(fn);
+
+ // Add: axiom (forall params :: #dt.ctor(params)-has-the-expected-type);
+ Bpl.VariableSeq bvs;
+ List<Bpl.Expr> args;
+ CreateBoundVariables(ctor.Formals, out bvs, out args);
+ Bpl.Expr ct = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
+ List<Type> tpArgs = new List<Type>(); // we use an empty list of type arguments, because we don't want Good_Datatype to produce any DtTypeParams predicates anyway
+ Bpl.Expr wh = new ExpressionTranslator(this, predef, ctor.tok).Good_Datatype(ctor.tok, ct, dt, tpArgs);
+ if (bvs.Length != 0) {
+ wh = new Bpl.ForallExpr(ctor.tok, bvs, wh);
+ }
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(ctor.tok, wh));
+
+ // Add: const unique ##dt.ctor: DtCtorId;
+ Bpl.Constant cid = new Bpl.Constant(ctor.tok, new Bpl.TypedIdent(ctor.tok, "#" + ctor.FullName, predef.DtCtorId), true);
+ sink.TopLevelDeclarations.Add(cid);
+
+ // 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);
+ Bpl.Expr q = Bpl.Expr.Eq(lhs, new Bpl.IdentifierExpr(ctor.tok, cid));
+ if (bvs.Length != 0) {
+ q = new Bpl.ForallExpr(ctor.tok, bvs, q);
+ }
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(ctor.tok, q));
+ // Add: axiom (forall d: DatatypeType :: dt.ctor?(d) ==> (exists params :: d == #dt.ctor(params));
+ CreateBoundVariables(ctor.Formals, out bvs, out args);
+ lhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
+ var dBv = new Bpl.BoundVariable(ctor.tok, new Bpl.TypedIdent(ctor.tok, "d", predef.DatatypeType));
+ var dId = new Bpl.IdentifierExpr(ctor.tok, dBv.Name, predef.DatatypeType);
+ q = Bpl.Expr.Eq(dId, lhs);
+ if (bvs.Length != 0) {
+ q = new Bpl.ExistsExpr(ctor.tok, bvs, q);
+ }
+ q = Bpl.Expr.Imp(FunctionCall(ctor.tok, ctor.QueryField.FullCompileName, Bpl.Type.Bool, dId), q);
+ q = new Bpl.ForallExpr(ctor.tok, new VariableSeq(dBv), q);
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(ctor.tok, q));
+
+ // Add: function dt.ctor?(this: DatatypeType): bool { DatatypeCtorId(this) == ##dt.ctor }
+ fn = GetReadonlyField(ctor.QueryField);
+ lhs = FunctionCall(ctor.tok, BuiltinFunction.DatatypeCtorId, null, new Bpl.IdentifierExpr(ctor.tok, fn.InParams[0].Name, predef.DatatypeType));
+ fn.Body = Bpl.Expr.Eq(lhs, new Bpl.IdentifierExpr(ctor.tok, cid)); // this uses the "cid" defined for the previous axiom
+ sink.TopLevelDeclarations.Add(fn);
+
+ // Add: axiom (forall params, h: HeapType ::
+ // { DtAlloc(#dt.ctor(params), h) }
+ // $IsGoodHeap(h) ==>
+ // (DtAlloc(#dt.ctor(params), h) <==> ...each param has its expected type...));
+ CreateBoundVariables(ctor.Formals, out bvs, out args);
+ lhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
+ Bpl.BoundVariable hVar = new Bpl.BoundVariable(ctor.tok, new Bpl.TypedIdent(ctor.tok, "$h", predef.HeapType));
+ Bpl.Expr h = new Bpl.IdentifierExpr(ctor.tok, hVar);
+ bvs.Add(hVar); args.Add(h);
+ ExpressionTranslator etranH = new ExpressionTranslator(this, predef, h);
+ Bpl.Expr isGoodHeap = FunctionCall(ctor.tok, BuiltinFunction.IsGoodHeap, null, h);
+ lhs = FunctionCall(ctor.tok, BuiltinFunction.DtAlloc, null, lhs, h);
+ Bpl.Expr pt = Bpl.Expr.True;
+ int i = 0;
+ foreach (Formal arg in ctor.Formals) {
+ Bpl.Expr whp = GetWhereClause(arg.tok, args[i], arg.Type, etranH);
+ if (whp != null) {
+ pt = BplAnd(pt, whp);
+ }
+ i++;
+ }
+ Bpl.Trigger tr = new Bpl.Trigger(ctor.tok, true, new ExprSeq(lhs));
+ q = new Bpl.ForallExpr(ctor.tok, bvs, tr, Bpl.Expr.Imp(isGoodHeap, Bpl.Expr.Iff(lhs, pt)));
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(ctor.tok, q));
+
+ // Add injectivity axioms:
+ i = 0;
+ foreach (Formal arg in ctor.Formals) {
+ // function ##dt.ctor#i(DatatypeType) returns (Ti);
+ var sf = ctor.Destructors[i];
+ if (sf != null) {
+ fn = GetReadonlyField(sf);
+ } else {
+ Contract.Assert(!arg.HasName);
+ argTypes = new Bpl.VariableSeq();
+ argTypes.Add(new Bpl.Formal(ctor.tok, new Bpl.TypedIdent(ctor.tok, Bpl.TypedIdent.NoName, predef.DatatypeType), true));
+ resType = new Bpl.Formal(arg.tok, new Bpl.TypedIdent(arg.tok, Bpl.TypedIdent.NoName, TrType(arg.Type)), false);
+ string nm = "#" + ctor.FullName + "#" + i;
+ fn = new Bpl.Function(ctor.tok, nm, argTypes, resType);
+ }
+ sink.TopLevelDeclarations.Add(fn);
+ // axiom (forall params :: ##dt.ctor#i(#dt.ctor(params)) == params_i);
+ CreateBoundVariables(ctor.Formals, out bvs, out args);
+ lhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
+ lhs = FunctionCall(ctor.tok, fn.Name, TrType(arg.Type), lhs);
+ q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Eq(lhs, args[i]));
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(ctor.tok, q));
+
+ if (dt is IndDatatypeDecl) {
+ if (arg.Type.IsDatatype || arg.Type.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)));
+ CreateBoundVariables(ctor.Formals, out bvs, out args);
+ lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null,
+ arg.Type.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));
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(ctor.tok, q));
+ } else if (arg.Type is SeqType) {
+ // axiom (forall params, i: int :: 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])));
+ 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, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs)));
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(ctor.tok, q));
+ } else if (arg.Type is SetType) {
+ // axiom (forall params, d: Datatype :: arg[d] ==> DtRank(d) < DtRank(#dt.ctor(params)));
+ // that is:
+ // axiom (forall params, d: Datatype :: 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));
+ 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)));
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(ctor.tok, q));
+ } else if (arg.Type is MultiSetType) {
+ // axiom (forall params, d: Datatype :: 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)));
+ 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));
+ 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)));
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(ctor.tok, q));
+ }
+ }
+ i++;
+ }
+ }
+
+ // Add:
+ // function $IsA#Dt(d: DatatypeType): bool {
+ // Dt.Ctor0?(d) || Dt.Ctor1?(d) || ...
+ // }
+ var cases_dBv = new Bpl.Formal(dt.tok, new Bpl.TypedIdent(dt.tok, "d", predef.DatatypeType), true);
+ var cases_dId = new Bpl.IdentifierExpr(dt.tok, cases_dBv.Name, predef.DatatypeType);
+ Bpl.Expr cases_body = null;
+ foreach (DatatypeCtor ctor in dt.Ctors) {
+ var disj = FunctionCall(ctor.tok, ctor.QueryField.FullCompileName, Bpl.Type.Bool, cases_dId);
+ cases_body = cases_body == null ? disj : Bpl.Expr.Or(cases_body, disj);
+ }
+ var cases_resType = new Bpl.Formal(dt.tok, new Bpl.TypedIdent(dt.tok, Bpl.TypedIdent.NoName, Bpl.Type.Bool), false);
+ var cases_fn = new Bpl.Function(dt.tok, "$IsA#" + dt.FullCompileName, new Bpl.VariableSeq(cases_dBv), cases_resType);
+ cases_fn.Body = cases_body;
+ sink.TopLevelDeclarations.Add(cases_fn);
+ }
+
+ void CreateBoundVariables(List<Formal/*!*/>/*!*/ formals, out Bpl.VariableSeq/*!*/ bvs, out List<Bpl.Expr/*!*/>/*!*/ args)
+ {
+ Contract.Requires(formals != null);
+ Contract.Ensures(Contract.ValueAtReturn(out bvs).Length == Contract.ValueAtReturn(out args).Count);
+ Contract.Ensures(Contract.ValueAtReturn(out bvs) != null);
+ Contract.Ensures(cce.NonNullElements(Contract.ValueAtReturn(out args)));
+
+ bvs = new Bpl.VariableSeq();
+ args = new List<Bpl.Expr>();
+ foreach (Formal arg in formals) {
+ Contract.Assert(arg != null);
+ var nm = string.Format("a{0}#{1}", bvs.Length, otherTmpVarCount);
+ otherTmpVarCount++;
+ Bpl.Variable bv = new Bpl.BoundVariable(arg.tok, new Bpl.TypedIdent(arg.tok, nm, TrType(arg.Type)));
+ bvs.Add(bv);
+ args.Add(new Bpl.IdentifierExpr(arg.tok, bv));
+ }
+ }
+
+ void AddClassMembers(ClassDecl c)
+ {
+ Contract.Requires(sink != null && predef != null);
+ Contract.Requires(c != null);
+ if (c.Name == "array") {
+ classes.Add(c, predef.ClassDotArray);
+ } else {
+ sink.TopLevelDeclarations.Add(GetClass(c));
+ }
+
+ foreach (MemberDecl member in c.Members) {
+ if (member is Field) {
+ Field f = (Field)member;
+ if (f.IsMutable) {
+ Bpl.Constant fc = GetField(f);
+ sink.TopLevelDeclarations.Add(fc);
+ } else {
+ Bpl.Function ff = GetReadonlyField(f);
+ if (ff != predef.ArrayLength)
+ sink.TopLevelDeclarations.Add(ff);
+ }
+
+ AddAllocationAxiom(f);
+
+ } else if (member is Function) {
+ Function f = (Function)member;
+ AddFunction(f);
+ if (f.IsRecursive) {
+ AddLimitedAxioms(f, 2);
+ AddLimitedAxioms(f, 1);
+ }
+ for (int layerOffset = 0; layerOffset < 2; layerOffset++) {
+ var body = f.Body == null ? null : f.Body.Resolved;
+ if (body is MatchExpr) {
+ AddFunctionAxiomCase(f, (MatchExpr)body, null, layerOffset);
+ AddFunctionAxiom(f, null, f.Ens, null, layerOffset);
+ } else {
+ AddFunctionAxiom(f, body, f.Ens, null, layerOffset);
+ }
+ if (!f.IsRecursive) { break; }
+ }
+ AddFrameAxiom(f);
+ AddWellformednessCheck(f);
+ if (f is CoPredicate) {
+ AddCoinductionPrinciple((CoPredicate)f);
+ }
+
+ } else if (member is Method) {
+ Method m = (Method)member;
+ bool isRefinementMethod = RefinementToken.IsInherited(m.tok, m.EnclosingClass.Module);
+
+ // wellformedness check for method specification
+ Bpl.Procedure proc = AddMethod(m, 0, isRefinementMethod);
+ sink.TopLevelDeclarations.Add(proc);
+ if (m.EnclosingClass is IteratorDecl && m == ((IteratorDecl)m.EnclosingClass).Member_MoveNext) {
+ // skip the well-formedness check, because it has already been done for the iterator
+ } else {
+ AddMethodImpl(m, proc, true);
+ }
+ // the method itself
+ proc = AddMethod(m, 1, isRefinementMethod);
+ sink.TopLevelDeclarations.Add(proc);
+ if (isRefinementMethod) {
+ proc = AddMethod(m, 2, isRefinementMethod);
+ sink.TopLevelDeclarations.Add(proc);
+ proc = AddMethod(m, 3, isRefinementMethod);
+ sink.TopLevelDeclarations.Add(proc);
+ }
+ if (m.Body != null) {
+ // ...and its implementation
+ AddMethodImpl(m, proc, false);
+ }
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member
+ }
+ }
+ }
+
+ void AddIteratorSpecAndBody(IteratorDecl iter) {
+ Contract.Requires(iter != null);
+
+ bool isRefinementMethod = RefinementToken.IsInherited(iter.tok, iter.Module);
+
+ // wellformedness check for method specification
+ Bpl.Procedure proc = AddIteratorProc(iter, 0, isRefinementMethod);
+ sink.TopLevelDeclarations.Add(proc);
+ AddIteratorWellformed(iter, proc);
+ // the method itself
+ if (iter.Body != null) {
+ proc = AddIteratorProc(iter, 1, isRefinementMethod);
+ sink.TopLevelDeclarations.Add(proc);
+ if (isRefinementMethod) {
+ proc = AddIteratorProc(iter, 3, isRefinementMethod);
+ sink.TopLevelDeclarations.Add(proc);
+ }
+ // ...and its implementation
+ AddIteratorImpl(iter, proc);
+ }
+ }
+
+ /// <summary>
+ /// This method is expected to be called at most 3 times for each procedure in the program:
+ /// * once with kind==0, which says to create a procedure for the wellformedness check of the
+ /// method's specification
+ /// * once with kind==1, which says to create the ordinary procedure for the method, always
+ /// suitable for inter-module callers, and for non-refinement methods also suitable for
+ /// the implementation and intra-module callers of the method
+ /// * possibly once with kind==3 (allowed only if isRefinementMethod), which says to create
+ /// a procedure suitable for the implementation of a refinement method
+ /// </summary>
+ Bpl.Procedure AddIteratorProc(IteratorDecl iter, int kind, bool isRefinementMethod) {
+ Contract.Requires(iter != null);
+ Contract.Requires(0 <= kind && kind < 4 && kind != 2);
+ Contract.Requires(isRefinementMethod || kind < 2);
+ Contract.Requires(predef != null);
+ Contract.Requires(currentModule == null && codeContext == null);
+ Contract.Ensures(currentModule == null && codeContext == null);
+ Contract.Ensures(Contract.Result<Bpl.Procedure>() != null);
+
+ currentModule = iter.Module;
+ codeContext = iter;
+
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, iter.tok);
+
+ Bpl.VariableSeq inParams, outParams;
+ GenerateMethodParametersChoose(iter.tok, iter, true, true, false, etran, out inParams, out outParams);
+
+ var req = new Bpl.RequiresSeq();
+ var mod = new Bpl.IdentifierExprSeq();
+ var ens = new Bpl.EnsuresSeq();
+ if (kind == 0 || (kind == 1 && !isRefinementMethod) || kind == 3) { // the other cases have no need for a free precondition
+ // free requires mh == ModuleContextHeight && InMethodContext;
+ Bpl.Expr context = Bpl.Expr.And(
+ Bpl.Expr.Eq(Bpl.Expr.Literal(iter.Module.Height), etran.ModuleContextHeight()),
+ etran.InMethodContext());
+ req.Add(Requires(iter.tok, true, context, null, null));
+ }
+ mod.Add((Bpl.IdentifierExpr/*TODO: this cast is rather dubious*/)etran.HeapExpr);
+ mod.Add(etran.Tick());
+
+ if (kind != 0) {
+ string comment = "user-defined preconditions";
+ foreach (var p in iter.Requires) {
+ bool splitHappened; // we actually don't care
+ foreach (var s in TrSplitExpr(p.E, etran, out splitHappened)) {
+ if (kind == 2 && RefinementToken.IsInherited(s.E.tok, currentModule)) {
+ // this precondition was inherited into this module, so just ignore it
+ } else {
+ req.Add(Requires(s.E.tok, s.IsFree, s.E, null, null));
+ // the free here is not linked to the free on the original expression (this is free things generated in the splitting.)
+ }
+ }
+ comment = null;
+ }
+ comment = "user-defined postconditions";
+ foreach (MaybeFreeExpression p in iter.Ensures) {
+ if (p.IsFree && !DafnyOptions.O.DisallowSoundnessCheating) {
+ ens.Add(Ensures(p.E.tok, true, etran.TrExpr(p.E), null, comment));
+ } else {
+ bool splitHappened; // we actually don't care
+ foreach (var s in TrSplitExpr(p.E, etran, out splitHappened)) {
+ if (kind == 3 && RefinementToken.IsInherited(s.E.tok, currentModule)) {
+ // this postcondition was inherited into this module, so just ignore it
+ } else {
+ ens.Add(Ensures(s.E.tok, s.IsFree, s.E, null, null));
+ }
+ }
+ }
+ comment = null;
+ }
+ foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(iter.tok, iter.Modifies.Expressions, etran.Old, etran, etran.Old)) {
+ ens.Add(Ensures(tri.tok, tri.IsFree, tri.Expr, tri.ErrorMessage, tri.Comment));
+ }
+ }
+
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(iter.TypeArgs);
+ string name;
+ switch (kind) {
+ case 0: name = "CheckWellformed$$" + iter.FullCompileName; break;
+ case 1: name = iter.FullCompileName; break;
+ case 3: name = string.Format("RefinementImpl_{0}$${1}", iter.Module.Name, iter.FullCompileName); break;
+ default: Contract.Assert(false); throw new cce.UnreachableException(); // unexpected kind
+ }
+ Bpl.Procedure proc = new Bpl.Procedure(iter.tok, name, typeParams, inParams, outParams, req, mod, ens);
+
+ currentModule = null;
+ codeContext = null;
+
+ return proc;
+ }
+
+ void AddIteratorWellformed(IteratorDecl iter, Procedure proc) {
+ currentModule = iter.Module;
+ codeContext = iter;
+
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(iter.TypeArgs);
+ Bpl.VariableSeq inParams = Bpl.Formal.StripWhereClauses(proc.InParams);
+ Contract.Assert(1 <= inParams.Length); // there should at least be a receiver parameter
+ Contract.Assert(proc.OutParams.Length == 0);
+
+ var builder = new Bpl.StmtListBuilder();
+ var etran = new ExpressionTranslator(this, predef, iter.tok);
+ var localVariables = new Bpl.VariableSeq();
+
+ 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)));
+ }
+ // check well-formedness of the decreases clauses
+ foreach (var p in iter.Decreases.Expressions) {
+ CheckWellformed(p, new WFOptions(), localVariables, builder, etran);
+ }
+ // Note: the reads and modifies clauses are not checked for well-formedness (is that sound?), because it used to
+ // be that the syntax was not rich enough for programmers to specify modifies clauses and always being
+ // absolutely well-defined.
+
+ // 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)));
+ }
+
+ // play havoc with the heap, except at the locations prescribed by (this._reads - this._modifies - {this})
+ var th = new ThisExpr(iter.tok);
+ th.Type = Resolver.GetThisType(iter.tok, iter); // resolve here
+ var rds = new FieldSelectExpr(iter.tok, th, iter.Member_Reads.Name);
+ rds.Field = iter.Member_Reads; // resolve here
+ rds.Type = iter.Member_Reads.Type; // resolve here
+ var mod = new FieldSelectExpr(iter.tok, th, iter.Member_Modifies.Name);
+ mod.Field = iter.Member_Modifies; // resolve here
+ mod.Type = iter.Member_Modifies.Type; // resolve here
+ builder.Add(new Bpl.CallCmd(iter.tok, "$IterHavoc0",
+ new List<Bpl.Expr>() { etran.TrExpr(th), etran.TrExpr(rds), etran.TrExpr(mod) },
+ new List<Bpl.IdentifierExpr>()));
+
+ // assume the automatic yield-requires precondition (which is always well-formed): this.Valid()
+ var validCall = new FunctionCallExpr(iter.tok, "Valid", th, iter.tok, new List<Expression>());
+ validCall.Function = iter.Member_Valid; // resolve here
+ validCall.Type = Type.Bool; // resolve here
+ builder.Add(new Bpl.AssumeCmd(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)));
+ }
+
+ // simulate a modifies this, this._modifies, this._new;
+ var nw = new FieldSelectExpr(iter.tok, th, iter.Member_New.Name);
+ nw.Field = iter.Member_New; // resolve here
+ nw.Type = iter.Member_New.Type; // resolve here
+ builder.Add(new Bpl.CallCmd(iter.tok, "$IterHavoc1",
+ new List<Bpl.Expr>() { etran.TrExpr(th), etran.TrExpr(mod), etran.TrExpr(nw) },
+ new List<Bpl.IdentifierExpr>()));
+
+ // check wellformedness of postconditions
+ var yeBuilder = new Bpl.StmtListBuilder();
+ var endBuilder = new Bpl.StmtListBuilder();
+ foreach (var p in iter.YieldEnsures) {
+ CheckWellformed(p.E, new WFOptions(), localVariables, yeBuilder, etran);
+ yeBuilder.Add(new Bpl.AssumeCmd(p.E.tok, etran.TrExpr(p.E)));
+ }
+ foreach (var p in iter.Ensures) {
+ CheckWellformed(p.E, new WFOptions(), localVariables, endBuilder, etran);
+ endBuilder.Add(new Bpl.AssumeCmd(p.E.tok, etran.TrExpr(p.E)));
+ }
+ builder.Add(new Bpl.IfCmd(iter.tok, null, yeBuilder.Collect(iter.tok), null, endBuilder.Collect(iter.tok)));
+
+ stmts = builder.Collect(iter.tok);
+
+ QKeyValue kv = etran.TrAttributes(iter.Attributes, null);
+
+ Bpl.Implementation impl = new Bpl.Implementation(iter.tok, proc.Name,
+ typeParams, inParams, new VariableSeq(),
+ localVariables, stmts, kv);
+ sink.TopLevelDeclarations.Add(impl);
+
+ currentModule = null;
+ codeContext = null;
+ loopHeapVarCount = 0;
+ otherTmpVarCount = 0;
+ _tmpIEs.Clear();
+ }
+
+ void AddIteratorImpl(IteratorDecl iter, Bpl.Procedure proc) {
+ Contract.Requires(iter != null);
+ Contract.Requires(proc != null);
+ Contract.Requires(sink != null && predef != null);
+ Contract.Requires(iter.Body != null);
+ Contract.Requires(currentModule == null && codeContext == null && yieldCountVariable == null && loopHeapVarCount == 0 && _tmpIEs.Count == 0);
+ Contract.Ensures(currentModule == null && codeContext == null && yieldCountVariable == null && loopHeapVarCount == 0 && _tmpIEs.Count == 0);
+
+ currentModule = iter.Module;
+ codeContext = iter;
+
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(iter.TypeArgs);
+ Bpl.VariableSeq inParams = Bpl.Formal.StripWhereClauses(proc.InParams);
+ Contract.Assert(1 <= inParams.Length); // there should at least be a receiver parameter
+ Contract.Assert(proc.OutParams.Length == 0);
+
+ Bpl.StmtListBuilder builder = new Bpl.StmtListBuilder();
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, iter.tok);
+ Bpl.VariableSeq localVariables = new Bpl.VariableSeq();
+ GenerateIteratorImplPrelude(iter, inParams, new VariableSeq(), builder, localVariables);
+
+ // 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)));
+ }
+ 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)));
+ }
+ // add the _yieldCount variable, and assume its initial value to be 0
+ yieldCountVariable = new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, "_yieldCount", Bpl.Type.Int));
+ 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))));
+ // 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));
+ localVariables.Add(new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, "$_OldIterHeap", predef.HeapType, wh)));
+
+ // do an initial YieldHavoc
+ YieldHavoc(iter.tok, iter, builder, etran);
+
+ // translate the body of the method
+ var stmts = TrStmt2StmtList(builder, iter.Body, localVariables, etran);
+
+ QKeyValue kv = etran.TrAttributes(iter.Attributes, null);
+
+ Bpl.Implementation impl = new Bpl.Implementation(iter.tok, proc.Name,
+ typeParams, inParams, new VariableSeq(),
+ localVariables, stmts, kv);
+ sink.TopLevelDeclarations.Add(impl);
+
+ currentModule = null;
+ codeContext = null;
+ yieldCountVariable = null;
+ loopHeapVarCount = 0;
+ otherTmpVarCount = 0;
+ _tmpIEs.Clear();
+ }
+
+ Bpl.Expr YieldCountAssumption(IteratorDecl iter, ExpressionTranslator etran) {
+ Contract.Requires(iter != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(yieldCountVariable != null);
+ Bpl.Expr wh = Bpl.Expr.True;
+ foreach (var ys in iter.OutsHistoryFields) {
+ // add the conjunct: _yieldCount == |this.ys|
+ wh = Bpl.Expr.And(wh, Bpl.Expr.Eq(new Bpl.IdentifierExpr(iter.tok, yieldCountVariable),
+ FunctionCall(iter.tok, BuiltinFunction.SeqLength, null,
+ ExpressionTranslator.ReadHeap(iter.tok, etran.HeapExpr,
+ new Bpl.IdentifierExpr(iter.tok, etran.This, predef.RefType),
+ new Bpl.IdentifierExpr(iter.tok, GetField(ys))))));
+ }
+ return wh;
+ }
+
+ void AddFunctionAxiomCase(Function f, MatchExpr me, Specialization prev, int layerOffset) {
+ Contract.Requires(f != null);
+ Contract.Requires(me != null);
+ Contract.Requires(layerOffset == 0 || layerOffset == 1);
+
+ IVariable formal = ((IdentifierExpr)me.Source.Resolved).Var; // correctness of casts follows from what resolution checks
+ foreach (MatchCaseExpr mc in me.Cases) {
+ Contract.Assert(mc.Ctor != null); // the field is filled in by resolution
+ Specialization s = new Specialization(formal, mc, prev);
+ var body = mc.Body.Resolved;
+ if (body is MatchExpr) {
+ AddFunctionAxiomCase(f, (MatchExpr)body, s, layerOffset);
+ } else {
+ AddFunctionAxiom(f, body, new List<Expression>(), s, layerOffset);
+ }
+ }
+ }
+
+ class Specialization
+ {
+ public readonly List<Formal/*!*/> Formals;
+ public readonly List<Expression/*!*/> ReplacementExprs;
+ public readonly List<BoundVar/*!*/> ReplacementFormals;
+ public readonly Dictionary<IVariable, Expression> SubstMap;
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(cce.NonNullElements(Formals));
+ Contract.Invariant(cce.NonNullElements(ReplacementExprs));
+ Contract.Invariant(Formals.Count == ReplacementExprs.Count);
+ Contract.Invariant(cce.NonNullElements(ReplacementFormals));
+ Contract.Invariant(SubstMap != null);
+ }
+
+ public Specialization(IVariable formal, MatchCase mc, Specialization prev) {
+ Contract.Requires(formal is Formal || formal is BoundVar);
+ Contract.Requires(mc != null);
+ Contract.Requires(prev == null || formal is BoundVar || !prev.Formals.Contains((Formal)formal));
+
+ List<Expression> rArgs = new List<Expression>();
+ foreach (BoundVar p in mc.Arguments) {
+ IdentifierExpr ie = new IdentifierExpr(p.tok, p.UniqueName);
+ ie.Var = p; ie.Type = ie.Var.Type; // resolve it here
+ rArgs.Add(ie);
+ }
+ // create and resolve datatype value
+ var r = new DatatypeValue(mc.tok, mc.Ctor.EnclosingDatatype.Name, mc.Ctor.Name, rArgs);
+ r.Ctor = mc.Ctor;
+ r.Type = new UserDefinedType(mc.tok, mc.Ctor.EnclosingDatatype.Name, new List<Type>()/*this is not right, but it seems like it won't matter here*/, null);
+
+ Dictionary<IVariable, Expression> substMap = new Dictionary<IVariable, Expression>();
+ substMap.Add(formal, r);
+
+ // Fill in the fields
+ Formals = new List<Formal>();
+ ReplacementExprs = new List<Expression>();
+ ReplacementFormals = new List<BoundVar>();
+ SubstMap = new Dictionary<IVariable, Expression>();
+ if (prev != null) {
+ Formals.AddRange(prev.Formals);
+ foreach (var e in prev.ReplacementExprs) {
+ ReplacementExprs.Add(Substitute(e, null, substMap));
+ }
+ foreach (var rf in prev.ReplacementFormals) {
+ if (rf != formal) {
+ ReplacementFormals.Add(rf);
+ }
+ }
+ foreach (var entry in prev.SubstMap) {
+ SubstMap.Add(entry.Key, Substitute(entry.Value, null, substMap));
+ }
+ }
+ if (formal is Formal) {
+ Formals.Add((Formal)formal);
+ ReplacementExprs.Add(r);
+ }
+ ReplacementFormals.AddRange(mc.Arguments);
+ SubstMap.Add(formal, r);
+ }
+ }
+
+ void AddFunctionAxiom(Function/*!*/ f, Expression body, List<Expression/*!*/>/*!*/ ens, Specialization specialization, int layerOffset) {
+ if (f is Predicate || f is CoPredicate) {
+ var ax = FunctionAxiom(f, FunctionAxiomVisibility.IntraModuleOnly, body, ens, specialization, layerOffset);
+ sink.TopLevelDeclarations.Add(ax);
+ ax = FunctionAxiom(f, FunctionAxiomVisibility.ForeignModuleOnly, body, ens, specialization, layerOffset);
+ sink.TopLevelDeclarations.Add(ax);
+ } else {
+ var ax = FunctionAxiom(f, FunctionAxiomVisibility.All, body, ens, specialization, layerOffset);
+ sink.TopLevelDeclarations.Add(ax);
+ }
+ }
+
+ enum FunctionAxiomVisibility { All, IntraModuleOnly, ForeignModuleOnly }
+
+ Bpl.Axiom/*!*/ FunctionAxiom(Function/*!*/ f, FunctionAxiomVisibility visibility, Expression body, List<Expression/*!*/>/*!*/ ens, Specialization specialization, int layerOffset) {
+ Contract.Requires(f != null);
+ Contract.Requires(ens != null);
+ Contract.Requires(layerOffset == 0 || (layerOffset == 1 && f.IsRecursive));
+ Contract.Requires(predef != null);
+ Contract.Requires(f.EnclosingClass != null);
+
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, f.tok);
+
+ // axiom
+ // mh < ModuleContextHeight || // (a)
+ // (mh == ModuleContextHeight && (fh <= FunctionContextHeight || InMethodContext)) // (b)
+ // ==>
+ // (forall $Heap, formals ::
+ // { f(args) }
+ // f#canCall(args) ||
+ // ( (mh != ModuleContextHeight || fh != FunctionContextHeight || InMethodContext) && // (c)
+ // $IsHeap($Heap) && this != null && formals-have-the-expected-types &&
+ // Pre($Heap,args))
+ // ==>
+ // body-can-make-its-calls && // generated only for layerOffset==0
+ // f(args) == body && // (d)
+ // ens && // generated only for layerOffset==0
+ // f(args)-has-the-expected-type); // generated only for layerOffset==0
+ //
+ // The variables "formals" are the formals of function "f"; except, if a specialization is provided, then
+ // "specialization.Formals" (which are expected to be among the formals of "f") are excluded and replaced by
+ // "specialization.ReplacementFormals".
+ // The list "args" is the list of formals of function "f"; except, if a specialization is provided, then
+ // each of the "specialization.Formals" is replaced by the corresponding expression in "specialization.ReplacementExprs".
+ // If a specialization is provided, occurrences of "specialization.Formals" in "body", "f.Req", and "f.Ens"
+ // are also replaced by those corresponding expressions.
+ //
+ // The translation of "body" uses the #limited form whenever the callee is in the same SCC of the call graph.
+ //
+ // if layerOffset==1, then the names f#2 and f are used instead of f and f#limited.
+ //
+ // Visibility: The above description is for visibility==All. If visibility==IntraModuleOnly, then
+ // disjunct (a) is dropped (which also has a simplifying effect on (c)). Finally, if visibility==ForeignModuleOnly,
+ // then disjunct (b) is dropped (which also has a simplify effect on(c)); furthermore, if f is a Predicate,
+ // then the equality in (d) is replaced by an implication.
+ //
+ // Note, an antecedent $Heap[this,alloc] is intentionally left out: including it would only weaken
+ // the axiom. Moreover, leaving it out does not introduce any soundness problem, because the Dafny
+ // allocation statement changes only an allocation bit and then re-assumes $IsGoodHeap; so if it is
+ // sound after that, then it would also have been sound just before the allocation.
+ //
+ Bpl.VariableSeq formals = new Bpl.VariableSeq();
+ Bpl.ExprSeq args = new Bpl.ExprSeq();
+ Bpl.BoundVariable bv = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, predef.HeapVarName, predef.HeapType));
+ formals.Add(bv);
+ args.Add(new Bpl.IdentifierExpr(f.tok, bv));
+ // ante: $IsHeap($Heap) && this != null && formals-have-the-expected-types &&
+ Bpl.Expr ante = FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr);
+
+ Bpl.BoundVariable bvThis;
+ Bpl.Expr bvThisIdExpr;
+ if (f.IsStatic) {
+ bvThis = null;
+ bvThisIdExpr = null;
+ } else {
+ bvThis = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, etran.This, predef.RefType));
+ formals.Add(bvThis);
+ bvThisIdExpr = new Bpl.IdentifierExpr(f.tok, bvThis);
+ args.Add(bvThisIdExpr);
+ // add well-typedness conjunct to antecedent
+ Type thisType = Resolver.GetReceiverType(f.tok, f);
+ Bpl.Expr wh = Bpl.Expr.And(
+ Bpl.Expr.Neq(bvThisIdExpr, predef.Null),
+ etran.GoodRef(f.tok, bvThisIdExpr, thisType));
+ ante = Bpl.Expr.And(ante, wh);
+ }
+ if (specialization != null) {
+ foreach (BoundVar p in specialization.ReplacementFormals) {
+ bv = new Bpl.BoundVariable(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)));
+ formals.Add(bv);
+ // add well-typedness conjunct to antecedent
+ Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, bv), p.Type, etran);
+ if (wh != null) { ante = Bpl.Expr.And(ante, wh); }
+ }
+ }
+ foreach (Formal p in f.Formals) {
+ int i = specialization == null ? -1 : specialization.Formals.FindIndex(val => val == p);
+ if (i == -1) {
+ bv = new Bpl.BoundVariable(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)));
+ formals.Add(bv);
+ Bpl.Expr formal = new Bpl.IdentifierExpr(p.tok, bv);
+ args.Add(formal);
+ // add well-typedness conjunct to antecedent
+ Bpl.Expr wh = GetWhereClause(p.tok, formal, p.Type, etran);
+ if (wh != null) { ante = Bpl.Expr.And(ante, wh); }
+ } else {
+ args.Add(etran.TrExpr(specialization.ReplacementExprs[i]));
+ // note, well-typedness conjuncts for the replacement formals has already been done above
+ }
+ }
+
+ // mh < ModuleContextHeight || (mh == ModuleContextHeight && (fh <= FunctionContextHeight || InMethodContext))
+ ModuleDefinition mod = f.EnclosingClass.Module;
+ var activateForeign = Bpl.Expr.Lt(Bpl.Expr.Literal(mod.Height), etran.ModuleContextHeight());
+ var activateIntra =
+ Bpl.Expr.And(
+ Bpl.Expr.Eq(Bpl.Expr.Literal(mod.Height), etran.ModuleContextHeight()),
+ Bpl.Expr.Or(
+ Bpl.Expr.Le(Bpl.Expr.Literal(mod.CallGraph.GetSCCRepresentativeId(f)), etran.FunctionContextHeight()),
+ etran.InMethodContext()));
+ Bpl.Expr activate =
+ visibility == FunctionAxiomVisibility.All ? Bpl.Expr.Or(activateForeign, activateIntra) :
+ visibility == FunctionAxiomVisibility.IntraModuleOnly ? activateIntra : activateForeign;
+
+ var substMap = new Dictionary<IVariable, Expression>();
+ if (specialization != null) {
+ substMap = specialization.SubstMap;
+ }
+ Bpl.IdentifierExpr funcID = new Bpl.IdentifierExpr(f.tok, FunctionName(f, 1+layerOffset), TrType(f.ResultType));
+ Bpl.Expr funcAppl = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(funcID), args);
+
+ Bpl.Expr pre = Bpl.Expr.True;
+ foreach (Expression req in f.Req) {
+ pre = BplAnd(pre, etran.TrExpr(Substitute(req, null, substMap)));
+ }
+ // useViaContext: (mh != ModuleContextHeight || fh != FunctionContextHeight || InMethodContext)
+ Bpl.Expr useViaContext = visibility == FunctionAxiomVisibility.ForeignModuleOnly ? Bpl.Expr.True :
+ Bpl.Expr.Or(Bpl.Expr.Or(
+ visibility == FunctionAxiomVisibility.IntraModuleOnly ?
+ (Bpl.Expr)Bpl.Expr.False :
+ Bpl.Expr.Neq(Bpl.Expr.Literal(mod.Height), etran.ModuleContextHeight()),
+ Bpl.Expr.Neq(Bpl.Expr.Literal(mod.CallGraph.GetSCCRepresentativeId(f)), etran.FunctionContextHeight())),
+ etran.InMethodContext());
+ // useViaCanCall: f#canCall(args)
+ Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(f.tok, f.FullCompileName + "#canCall", Bpl.Type.Bool);
+ Bpl.Expr useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), args);
+
+ // ante := useViaCanCall || (useViaContext && typeAnte && pre)
+ ante = Bpl.Expr.Or(useViaCanCall, BplAnd(useViaContext, BplAnd(ante, pre)));
+
+ Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(funcAppl));
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
+ Bpl.Expr meat;
+ if (body == null) {
+ meat = Bpl.Expr.True;
+ } else {
+ Expression bodyWithSubst = Substitute(body, null, substMap);
+ if (layerOffset == 0) {
+ meat = Bpl.Expr.And(
+ CanCallAssumption(bodyWithSubst, etran),
+ visibility == FunctionAxiomVisibility.ForeignModuleOnly && (f is Predicate || f is CoPredicate) ?
+ Bpl.Expr.Imp(funcAppl, etran.LimitedFunctions(f).TrExpr(bodyWithSubst)) :
+ Bpl.Expr.Eq(funcAppl, etran.LimitedFunctions(f).TrExpr(bodyWithSubst)));
+ } else {
+ meat = visibility == FunctionAxiomVisibility.ForeignModuleOnly && (f is Predicate || f is CoPredicate) ?
+ Bpl.Expr.Imp(funcAppl, etran.TrExpr(bodyWithSubst)) :
+ Bpl.Expr.Eq(funcAppl, etran.TrExpr(bodyWithSubst));
+ }
+ }
+ if (layerOffset == 0) {
+ foreach (Expression p in ens) {
+ Bpl.Expr q = etran.LimitedFunctions(f).TrExpr(Substitute(p, null, substMap));
+ meat = BplAnd(meat, q);
+ }
+ Bpl.Expr whr = GetWhereClause(f.tok, funcAppl, f.ResultType, etran);
+ if (whr != null) { meat = Bpl.Expr.And(meat, whr); }
+ }
+ Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, formals, null, tr, Bpl.Expr.Imp(ante, meat));
+ string comment = "definition axiom for " + FunctionName(f, 1+layerOffset);
+ if (visibility == FunctionAxiomVisibility.IntraModuleOnly) {
+ comment += " (intra-module)";
+ } else if (visibility == FunctionAxiomVisibility.ForeignModuleOnly) {
+ comment += " (foreign modules)";
+ }
+ if (specialization != null) {
+ string sep = "{0}, specialized for '{1}'";
+ foreach (var formal in specialization.Formals) {
+ comment = string.Format(sep, comment, formal.Name);
+ sep = "{0}, '{1}'";
+ }
+ }
+ return new Bpl.Axiom(f.tok, Bpl.Expr.Imp(activate, ax), comment);
+ }
+
+ void AddLimitedAxioms(Function f, int fromLayer) {
+ Contract.Requires(f != null);
+ Contract.Requires(f.IsRecursive);
+ Contract.Requires(fromLayer == 1 || fromLayer == 2);
+ Contract.Requires(sink != null && predef != null);
+ // With fromLayer==1, generate:
+ // axiom (forall formals :: { f(args) } f(args) == f#limited(args))
+ // With fromLayer==2, generate:
+ // axiom (forall formals :: { f#2(args) } f#2(args) == f(args))
+
+ Bpl.VariableSeq formals = new Bpl.VariableSeq();
+ Bpl.ExprSeq args = new Bpl.ExprSeq();
+ Bpl.BoundVariable bv = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, predef.HeapVarName, predef.HeapType));
+ formals.Add(bv);
+ args.Add(new Bpl.IdentifierExpr(f.tok, bv));
+ Bpl.BoundVariable bvThis;
+ Bpl.Expr bvThisIdExpr;
+ if (f.IsStatic) {
+ bvThis = null;
+ bvThisIdExpr = null;
+ } else {
+ bvThis = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType));
+ formals.Add(bvThis);
+ bvThisIdExpr = new Bpl.IdentifierExpr(f.tok, bvThis);
+ args.Add(bvThisIdExpr);
+ }
+ foreach (Formal p in f.Formals) {
+ bv = new Bpl.BoundVariable(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)));
+ formals.Add(bv);
+ args.Add(new Bpl.IdentifierExpr(p.tok, bv));
+ }
+
+ Bpl.FunctionCall origFuncID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, FunctionName(f, fromLayer), TrType(f.ResultType)));
+ Bpl.Expr origFuncAppl = new Bpl.NAryExpr(f.tok, origFuncID, args);
+ Bpl.FunctionCall limitedFuncID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, FunctionName(f, fromLayer-1), TrType(f.ResultType)));
+ Bpl.Expr limitedFuncAppl = new Bpl.NAryExpr(f.tok, limitedFuncID, args);
+
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
+
+ Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(origFuncAppl));
+ Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, formals, null, tr, Bpl.Expr.Eq(origFuncAppl, limitedFuncAppl));
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax));
+ }
+
+ /// <summary>
+ /// Returns the appropriate Boogie function for the given function. In particular:
+ /// Layer 2: f#2 --currently used only for induction axioms
+ /// Layer 1: f --this is the default name
+ /// Layer 0: f#limited --does not trigger the function definition axiom
+ /// </summary>
+ public static string FunctionName(Function f, int layer) {
+ Contract.Requires(f != null);
+ Contract.Requires(0 <= layer && layer < 3);
+ Contract.Ensures(Contract.Result<string>() != null);
+
+ string name = f.FullCompileName;
+ switch (layer) {
+ case 2: name += "#2"; break;
+ case 0: name += "#limited"; break;
+ }
+ return name;
+ }
+
+ /// <summary>
+ /// Generate:
+ /// axiom (forall h: [ref, Field x]x, o: ref ::
+ /// { h[o,f] }
+ /// $IsGoodHeap(h) && o != null && h[o,alloc] ==> h[o,f]-has-the-expected-type);
+ /// </summary>
+ void AddAllocationAxiom(Field f)
+ {
+ Contract.Requires(f != null);
+ Contract.Requires(sink != null && predef != null);
+
+ Bpl.BoundVariable hVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$h", predef.HeapType));
+ Bpl.Expr h = new Bpl.IdentifierExpr(f.tok, hVar);
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, h);
+ Bpl.BoundVariable oVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$o", predef.RefType));
+ Bpl.Expr o = new Bpl.IdentifierExpr(f.tok, oVar);
+
+ // h[o,f]
+ Bpl.Expr oDotF;
+ if (f.IsMutable) {
+ oDotF = ExpressionTranslator.ReadHeap(f.tok, h, o, new Bpl.IdentifierExpr(f.tok, GetField(f)));
+ } else {
+ oDotF = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(GetReadonlyField(f)), new Bpl.ExprSeq(o));
+ }
+
+ Bpl.Expr wh = GetWhereClause(f.tok, oDotF, f.Type, etran);
+ if (wh != null) {
+ // ante: $IsGoodHeap(h) && o != null && h[o,alloc]
+ Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.And(
+ FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, h),
+ Bpl.Expr.Neq(o, predef.Null)),
+ etran.IsAlloced(f.tok, o));
+ Bpl.Expr body = Bpl.Expr.Imp(ante, wh);
+ Bpl.Trigger tr = f.IsMutable ? new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(oDotF)) : null; // the trigger must include both "o" and "h"
+ Bpl.Expr ax = new Bpl.ForallExpr(f.tok, new Bpl.VariableSeq(hVar, oVar), tr, body);
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax));
+ }
+ }
+
+ Bpl.Expr InSeqRange(IToken tok, Bpl.Expr index, Bpl.Expr seq, bool isSequence, Bpl.Expr lowerBound, bool includeUpperBound) {
+ Contract.Requires(tok != null);
+ Contract.Requires(index != null);
+ Contract.Requires(seq != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ if (lowerBound == null) {
+ lowerBound = Bpl.Expr.Literal(0);
+ }
+ Bpl.Expr lower = Bpl.Expr.Le(lowerBound, index);
+ Bpl.Expr length = isSequence ?
+ FunctionCall(tok, BuiltinFunction.SeqLength, null, seq) :
+ ArrayLength(tok, seq, 1, 0);
+ Bpl.Expr upper;
+ if (includeUpperBound) {
+ upper = Bpl.Expr.Le(index, length);
+ } else {
+ upper = Bpl.Expr.Lt(index, length);
+ }
+ return Bpl.Expr.And(lower, upper);
+ }
+
+ ModuleDefinition currentModule = null; // the name of the module whose members are currently being translated
+ ICodeContext codeContext = null; // the method/iterator whose implementation is currently being translated
+ LocalVariable yieldCountVariable = null; // non-null when an iterator body is being translated
+ int loopHeapVarCount = 0;
+ int otherTmpVarCount = 0;
+ Dictionary<string, Bpl.IdentifierExpr> _tmpIEs = new Dictionary<string, Bpl.IdentifierExpr>();
+ Bpl.IdentifierExpr GetTmpVar_IdExpr(IToken tok, string name, Bpl.Type ty, Bpl.VariableSeq locals) // local variable that's shared between statements that need it
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(name != null);
+ Contract.Requires(ty != null);
+ Contract.Requires(locals != null);
+ Contract.Ensures(Contract.Result<Bpl.IdentifierExpr>() != null);
+
+ Bpl.IdentifierExpr ie;
+ if (_tmpIEs.TryGetValue(name, out ie)) {
+ Contract.Assume(ie.Type.Equals(ty));
+ } else {
+ // the "tok" and "ty" of the first request for this variable is the one we use
+ var v = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name, ty)); // important for the "$nw" client: no where clause (see GetNewVar_IdExpr)
+ locals.Add(v);
+ ie = new Bpl.IdentifierExpr(tok, v);
+ _tmpIEs.Add(name, ie);
+ }
+ return ie;
+ }
+
+ Bpl.IdentifierExpr GetPrevHeapVar_IdExpr(IToken tok, Bpl.VariableSeq locals) { // local variable that's shared between statements that need it
+ Contract.Requires(tok != null);
+ Contract.Requires(locals != null); Contract.Requires(predef != null);
+ Contract.Ensures(Contract.Result<Bpl.IdentifierExpr>() != null);
+
+ return GetTmpVar_IdExpr(tok, "$prevHeap", predef.HeapType, locals);
+ }
+
+ Bpl.IdentifierExpr GetNewVar_IdExpr(IToken tok, Bpl.VariableSeq locals) // local variable that's shared between statements that need it
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(locals != null);
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.Result<Bpl.IdentifierExpr>() != null);
+
+ // important: the following declaration produces no where clause (that's why we're going through the trouble of setting of this variable in the first place)
+ return GetTmpVar_IdExpr(tok, "$nw", predef.RefType, locals);
+ }
+
+ /// <summary>
+ /// Returns an expression whose value is the same as "expr", but that is guaranteed to preserve the its value passed
+ /// the evaluation of other expressions. If necessary, a new local variable called "name" with type "ty" is added to "locals" and
+ /// assigned in "builder" to be used to hold the value of "expr". It is assumed that all requests for a given "name"
+ /// have the same type "ty" and that these variables can be shared.
+ /// As an optimization, if "otherExprsCanAffectPreviouslyKnownExpressions" is "false", then "expr" itself is returned.
+ /// </summary>
+ Bpl.Expr SaveInTemp(Bpl.Expr expr, bool otherExprsCanAffectPreviouslyKnownExpressions, string name, Bpl.Type ty, Bpl.StmtListBuilder builder, Bpl.VariableSeq locals) {
+ Contract.Requires(expr != null);
+ Contract.Requires(name != null);
+ Contract.Requires(ty != null);
+ Contract.Requires(locals != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ if (otherExprsCanAffectPreviouslyKnownExpressions) {
+ var save = GetTmpVar_IdExpr(expr.tok, name, ty, locals);
+ builder.Add(Bpl.Cmd.SimpleAssign(expr.tok, save, expr));
+ return save;
+ } else {
+ return expr;
+ }
+ }
+
+ void AddMethodImpl(Method m, Bpl.Procedure proc, bool wellformednessProc)
+ {
+ Contract.Requires(m != null);
+ Contract.Requires(proc != null);
+ Contract.Requires(sink != null && predef != null);
+ Contract.Requires(wellformednessProc || m.Body != null);
+ Contract.Requires(currentModule == null && codeContext == null && loopHeapVarCount == 0 && _tmpIEs.Count == 0);
+ Contract.Ensures(currentModule == null && codeContext == null && loopHeapVarCount == 0 && _tmpIEs.Count == 0);
+
+ currentModule = m.EnclosingClass.Module;
+ codeContext = m;
+
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(m.TypeArgs);
+ Bpl.VariableSeq inParams = Bpl.Formal.StripWhereClauses(proc.InParams);
+ Bpl.VariableSeq outParams = Bpl.Formal.StripWhereClauses(proc.OutParams);
+
+ Bpl.StmtListBuilder builder = new Bpl.StmtListBuilder();
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, m.tok);
+ Bpl.VariableSeq localVariables = new Bpl.VariableSeq();
+ GenerateImplPrelude(m, inParams, outParams, builder, localVariables);
+
+ Bpl.StmtList stmts;
+ if (!wellformednessProc) {
+ if (3 <= DafnyOptions.O.Induction && m.IsGhost && m.Mod.Expressions.Count == 0 && m.Outs.Count == 0) {
+ 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' ]];
+ // parallel (this', y' | Pre(this', x, y') && VF(this', x, y') << VF(this, x, y)) {
+ // this'.M(x, y');
+ // }
+ // Generate bound variables for the parallel 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.FullCompileName, Bpl.Type.Bool));
+ var f = new Bpl.IdentifierExpr(inFormal.tok, inFormal.UniqueName, TrType(inFormal.Type));
+ builder.Add(new Bpl.AssumeCmd(inFormal.tok, new Bpl.NAryExpr(inFormal.tok, funcID, new Bpl.ExprSeq(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);
+ }
+ }
+
+ // 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
+ } 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 recursiveCall = new CallStmt(m.tok, new List<Expression>(), recursiveCallReceiver, m.Name, recursiveCallArgs);
+ recursiveCall.Method = m; // 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 = DafnyAnd(parRange, neqNull);
+ }
+ foreach (var pre in m.Req) {
+ if (!pre.IsFree) {
+ parRange = DafnyAnd(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<Bpl.Expr>();
+ var decrCaller = new List<Bpl.Expr>();
+ bool decrInferred; // we don't actually care
+ foreach (var ee in MethodDecreasesWithDefault(m, out decrInferred)) {
+ decrToks.Add(ee.tok);
+ decrTypes.Add(ee.Type);
+ 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, decrCallee, decrCaller, exprTran, null, null, false, true);
+ };
+
+#if VERIFY_CORRECTNESS_OF_TRANSLATION_PARALLEL_RANGE
+ var definedness = new Bpl.StmtListBuilder();
+ var exporter = new Bpl.StmtListBuilder();
+ TrParallelCall(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)));
+#else
+ TrParallelCall(m.tok, parBoundVars, parRange, decrCheck, 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
+ stmts = TrStmt2StmtList(builder, m.Body, localVariables, etran);
+ } 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)));
+ }
+ // Note: the modifies clauses are not checked for well-formedness (is that sound?), because it used to
+ // be that the syntax was not rich enough for programmers to specify modifies clauses and always being
+ // absolutely well-defined.
+ // check well-formedness of the decreases clauses
+ foreach (Expression p in m.Decreases.Expressions)
+ {
+ CheckWellformed(p, new WFOptions(), localVariables, builder, etran);
+ }
+
+ // play havoc with the heap according to the modifies clause
+ builder.Add(new Bpl.HavocCmd(m.tok, new Bpl.IdentifierExprSeq((Bpl.IdentifierExpr/*TODO: this cast is rather dubious*/)etran.HeapExpr)));
+ // assume the usual two-state boilerplate information
+ foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(m.tok, m.Mod.Expressions, etran.Old, etran, etran.Old))
+ {
+ if (tri.IsFree) {
+ builder.Add(new Bpl.AssumeCmd(m.tok, tri.Expr));
+ }
+ }
+
+ // also play havoc with the out parameters
+ if (outParams.Length != 0) { // don't create an empty havoc statement
+ Bpl.IdentifierExprSeq outH = new Bpl.IdentifierExprSeq();
+ foreach (Bpl.Variable b in outParams) {
+ Contract.Assert(b != null);
+ outH.Add(new Bpl.IdentifierExpr(b.tok, b));
+ }
+ builder.Add(new Bpl.HavocCmd(m.tok, outH));
+ }
+
+ // 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)));
+ }
+
+ stmts = builder.Collect(m.tok);
+ }
+
+ QKeyValue kv = etran.TrAttributes(m.Attributes, null);
+
+ Bpl.Implementation impl = new Bpl.Implementation(m.tok, proc.Name,
+ typeParams, inParams, outParams,
+ localVariables, stmts, kv);
+ sink.TopLevelDeclarations.Add(impl);
+
+ currentModule = null;
+ codeContext = null;
+ loopHeapVarCount = 0;
+ otherTmpVarCount = 0;
+ _tmpIEs.Clear();
+ }
+
+ void GenerateImplPrelude(Method m, Bpl.VariableSeq inParams, Bpl.VariableSeq outParams,
+ Bpl.StmtListBuilder builder, Bpl.VariableSeq localVariables){
+ Contract.Requires(m != null);
+ Contract.Requires(inParams != null);
+ Contract.Requires(outParams != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(localVariables != null);
+ Contract.Requires(predef != null);
+
+ // set up the information used to verify the method's modifies clause
+ DefineFrame(m.tok, m.Mod.Expressions, builder, localVariables, null);
+ }
+
+ void GenerateIteratorImplPrelude(IteratorDecl iter, Bpl.VariableSeq inParams, Bpl.VariableSeq outParams,
+ Bpl.StmtListBuilder builder, Bpl.VariableSeq localVariables) {
+ Contract.Requires(iter != null);
+ Contract.Requires(inParams != null);
+ Contract.Requires(outParams != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(localVariables != null);
+ Contract.Requires(predef != null);
+
+ // set up the information used to verify the method's modifies clause
+ var iteratorFrame = new List<FrameExpression>();
+ var th = new ThisExpr(iter.tok);
+ th.Type = Resolver.GetThisType(iter.tok, iter); // resolve here
+ iteratorFrame.Add(new FrameExpression(iter.tok, th, null));
+ iteratorFrame.AddRange(iter.Modifies.Expressions);
+ DefineFrame(iter.tok, iteratorFrame, builder, localVariables, null);
+ }
+
+ Bpl.Cmd CaptureState(IToken tok, string/*?*/ additionalInfo) {
+ Contract.Requires(tok != null);
+ Contract.Ensures(Contract.Result<Bpl.Cmd>() != null);
+ string description = string.Format("{0}({1},{2}){3}{4}", tok.filename, tok.line, tok.col, additionalInfo == null ? "" : ": ", additionalInfo ?? "");
+ QKeyValue kv = new QKeyValue(tok, "captureState", new List<object>() { description }, null);
+ return new Bpl.AssumeCmd(tok, Bpl.Expr.True, kv);
+ }
+ Bpl.Cmd CaptureState(IToken tok) {
+ Contract.Requires(tok != null);
+ Contract.Ensures(Contract.Result<Bpl.Cmd>() != null);
+ return CaptureState(tok, null);
+ }
+
+ void DefineFrame(IToken/*!*/ tok, List<FrameExpression/*!*/>/*!*/ frameClause, Bpl.StmtListBuilder/*!*/ builder, Bpl.VariableSeq/*!*/ localVariables, string name){
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(frameClause));
+ Contract.Requires(builder != null);
+ Contract.Requires(cce.NonNullElements(localVariables));
+ Contract.Requires(predef != null);
+
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, tok);
+ // Declare a local variable $_Frame: <alpha>[ref, Field alpha]bool
+ Bpl.IdentifierExpr theFrame = etran.TheFrame(tok); // this is a throw-away expression, used only to extract the type of the $_Frame variable
+ Contract.Assert(theFrame.Type != null); // follows from the postcondition of TheFrame
+ Bpl.LocalVariable frame = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name ?? theFrame.Name, theFrame.Type));
+ localVariables.Add(frame);
+ // $_Frame := (lambda<alpha> $o: ref, $f: Field alpha :: $o != null && $Heap[$o,alloc] ==> ($o,$f) in Modifies/Reads-Clause);
+ Bpl.TypeVariable alpha = new Bpl.TypeVariable(tok, "alpha");
+ Bpl.BoundVariable oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType));
+ Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(tok, oVar);
+ Bpl.BoundVariable fVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$f", predef.FieldName(tok, alpha)));
+ Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(tok, fVar);
+ Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.Neq(o, predef.Null), etran.IsAlloced(tok, o));
+ Bpl.Expr consequent = InRWClause(tok, o, f, frameClause, etran, null, null);
+ Bpl.Expr lambda = new Bpl.LambdaExpr(tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fVar), null,
+ Bpl.Expr.Imp(ante, consequent));
+
+ builder.Add(Bpl.Cmd.SimpleAssign(tok, new Bpl.IdentifierExpr(tok, frame), lambda));
+ }
+
+ void CheckFrameSubset(IToken tok, List<FrameExpression/*!*/>/*!*/ calleeFrame,
+ Expression receiverReplacement, Dictionary<IVariable,Expression/*!*/> substMap,
+ ExpressionTranslator/*!*/ etran, Bpl.StmtListBuilder/*!*/ builder, string errorMessage,
+ Bpl.QKeyValue kv)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(calleeFrame));
+ Contract.Requires((receiverReplacement == null) == (substMap == null));
+ Contract.Requires(substMap == null || cce.NonNullDictionaryAndValues(substMap));
+ Contract.Requires(etran != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(errorMessage != null);
+ Contract.Requires(predef != null);
+
+ // emit: assert (forall<alpha> o: ref, f: Field alpha :: o != null && $Heap[o,alloc] && (o,f) in subFrame ==> $_Frame[o,f]);
+ Bpl.TypeVariable alpha = new Bpl.TypeVariable(tok, "alpha");
+ Bpl.BoundVariable oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType));
+ Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(tok, oVar);
+ Bpl.BoundVariable fVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$f", predef.FieldName(tok, alpha)));
+ Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(tok, fVar);
+ Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.Neq(o, predef.Null), etran.IsAlloced(tok, o));
+ Bpl.Expr oInCallee = InRWClause(tok, o, f, calleeFrame, etran, receiverReplacement, substMap);
+ Bpl.Expr inEnclosingFrame = Bpl.Expr.Select(etran.TheFrame(tok), o, f);
+ Bpl.Expr q = new Bpl.ForallExpr(tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fVar),
+ Bpl.Expr.Imp(Bpl.Expr.And(ante, oInCallee), inEnclosingFrame));
+ builder.Add(Assert(tok, q, errorMessage, kv));
+ }
+
+ /// <summary>
+ /// Generates:
+ /// axiom (forall h0: HeapType, h1: HeapType, formals... ::
+ /// { HeapSucc(h0,h1), F(h1,formals) }
+ /// heaps are well-formed and formals are allocated AND
+ /// HeapSucc(h0,h1)
+ /// AND
+ /// (forall(alpha) o: ref, f: Field alpha ::
+ /// o != null AND h0[o,alloc] AND h1[o,alloc] AND
+ /// o in reads clause of formals in h0
+ /// IMPLIES h0[o,f] == h1[o,f])
+ /// IMPLIES
+ /// F(h0,formals) == F(h1,formals)
+ /// );
+ ///
+ /// If the function is a recursive function, then the same axiom is also produced for "F#limited" instead of "F".
+ /// </summary>
+ void AddFrameAxiom(Function f)
+ {
+ Contract.Requires(f != null);
+ Contract.Requires(sink != null && predef != null);
+
+ Bpl.BoundVariable h0Var = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$h0", predef.HeapType));
+ Bpl.BoundVariable h1Var = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$h1", predef.HeapType));
+ Bpl.Expr h0 = new Bpl.IdentifierExpr(f.tok, h0Var);
+ Bpl.Expr h1 = new Bpl.IdentifierExpr(f.tok, h1Var);
+ ExpressionTranslator etran0 = new ExpressionTranslator(this, predef, h0);
+ ExpressionTranslator etran1 = new ExpressionTranslator(this, predef, h1);
+
+ Bpl.Expr wellFormed = Bpl.Expr.And(
+ FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, etran0.HeapExpr),
+ FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, etran1.HeapExpr));
+
+ Bpl.TypeVariable alpha = new Bpl.TypeVariable(f.tok, "alpha");
+ Bpl.BoundVariable oVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$o", predef.RefType));
+ Bpl.Expr o = new Bpl.IdentifierExpr(f.tok, oVar);
+ Bpl.BoundVariable fieldVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$f", predef.FieldName(f.tok, alpha)));
+ Bpl.Expr field = new Bpl.IdentifierExpr(f.tok, fieldVar);
+ Bpl.Expr oNotNull = Bpl.Expr.Neq(o, predef.Null);
+ 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(ExpressionTranslator.ReadHeap(f.tok, h0, o, field), ExpressionTranslator.ReadHeap(f.tok, h1, o, field));
+
+ Bpl.Expr heapSucc = FunctionCall(f.tok, BuiltinFunction.HeapSucc, null, h0, h1);
+ Bpl.Expr r0 = InRWClause(f.tok, o, field, f.Reads, etran0, null, null);
+ Bpl.Expr q0 = new Bpl.ForallExpr(f.tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fieldVar),
+ Bpl.Expr.Imp(Bpl.Expr.And(oNotNullAlloced, r0), unchanged));
+
+ // bvars: h0, h1, formals
+ // f0args: h0, formals
+ // f1args: h1, formals
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ Bpl.ExprSeq f0args = new Bpl.ExprSeq();
+ Bpl.ExprSeq f1args = new Bpl.ExprSeq();
+ bvars.Add(h0Var); bvars.Add(h1Var);
+ f0args.Add(h0);
+ f1args.Add(h1);
+ if (!f.IsStatic) {
+ Bpl.BoundVariable thVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType));
+ Bpl.Expr th = new Bpl.IdentifierExpr(f.tok, thVar);
+ bvars.Add(thVar);
+ f0args.Add(th);
+ f1args.Add(th);
+
+ Type thisType = Resolver.GetReceiverType(f.tok, f);
+ Bpl.Expr wh = Bpl.Expr.And(Bpl.Expr.Neq(th, predef.Null),
+ Bpl.Expr.And(etran0.GoodRef(f.tok, th, thisType), etran1.GoodRef(f.tok, th, thisType)));
+ wellFormed = Bpl.Expr.And(wellFormed, wh);
+ }
+
+ foreach (Formal p in f.Formals) {
+ Bpl.BoundVariable bv = new Bpl.BoundVariable(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)));
+ bvars.Add(bv);
+ Bpl.Expr formal = new Bpl.IdentifierExpr(p.tok, bv);
+ f0args.Add(formal);
+ f1args.Add(formal);
+ Bpl.Expr wh = GetWhereClause(p.tok, formal, p.Type, etran0);
+ if (wh != null) { wellFormed = Bpl.Expr.And(wellFormed, wh); }
+ wh = GetWhereClause(p.tok, formal, p.Type, etran1);
+ if (wh != null) { wellFormed = Bpl.Expr.And(wellFormed, wh); }
+ }
+
+ string axiomComment = "frame axiom for " + f.FullCompileName;
+ Bpl.FunctionCall fn = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullCompileName, TrType(f.ResultType)));
+ while (fn != null) {
+ Bpl.Expr F0 = new Bpl.NAryExpr(f.tok, fn, f0args);
+ Bpl.Expr F1 = new Bpl.NAryExpr(f.tok, fn, f1args);
+ Bpl.Expr eq = Bpl.Expr.Eq(F0, F1);
+ Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(heapSucc, F1));
+
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
+ Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, bvars, null, tr,
+ Bpl.Expr.Imp(Bpl.Expr.And(wellFormed, heapSucc),
+ Bpl.Expr.Imp(q0, eq)));
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax, axiomComment));
+ if (axiomComment != null && f.IsRecursive) {
+ fn = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, FunctionName(f, 0), TrType(f.ResultType)));
+ axiomComment = null; // the comment goes only with the first frame axiom
+ } else {
+ break; // no more frame axioms to produce
+ }
+ }
+ }
+
+ Bpl.Expr/*!*/ InRWClause(IToken/*!*/ tok, Bpl.Expr/*!*/ o, Bpl.Expr/*!*/ f, List<FrameExpression/*!*/>/*!*/ rw, ExpressionTranslator/*!*/ etran,
+ Expression receiverReplacement, Dictionary<IVariable,Expression/*!*/> substMap) {
+ Contract.Requires(tok != null);
+ Contract.Requires(o != null);
+ Contract.Requires(f != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(cce.NonNullElements(rw));
+ Contract.Requires(substMap == null || cce.NonNullDictionaryAndValues(substMap));
+ Contract.Requires(predef != null);
+ Contract.Requires((receiverReplacement == null) == (substMap == null));
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ // requires o to denote an expression of type RefType
+ // "rw" is is allowed to contain a WildcardExpr
+
+ Bpl.Expr disjunction = null;
+ foreach (FrameExpression rwComponent in rw) {
+ Expression e = rwComponent.E;
+ if (receiverReplacement != null) {
+ Contract.Assert(substMap != null);
+ e = Substitute(e, receiverReplacement, substMap);
+ }
+ Bpl.Expr disjunct;
+ if (e is WildcardExpr) {
+ disjunct = Bpl.Expr.True;
+ } else if (e.Type is SetType) {
+ // old(e)[Box(o)]
+ disjunct = etran.TrInSet(tok, o, e, ((SetType)e.Type).Arg);
+ } else if (e.Type is SeqType) {
+ // (exists i: int :: 0 <= i && i < Seq#Length(old(e)) && Seq#Index(old(e),i) == Box(o))
+ Bpl.Expr boxO = FunctionCall(tok, BuiltinFunction.Box, null, o);
+ Bpl.Variable iVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$i", Bpl.Type.Int));
+ Bpl.Expr i = new Bpl.IdentifierExpr(tok, iVar);
+ 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 Bpl.VariableSeq(iVar), Bpl.Expr.And(iBounds, Bpl.Expr.Eq(XsubI, boxO)));
+ } else {
+ // o == old(e)
+ disjunct = Bpl.Expr.Eq(o, etran.TrExpr(e));
+ }
+ disjunct = Bpl.Expr.And(IsTotal(e, etran), disjunct);
+ if (rwComponent.Field != null) {
+ disjunct = Bpl.Expr.And(disjunct, Bpl.Expr.Eq(f, new Bpl.IdentifierExpr(rwComponent.E.tok, GetField(rwComponent.Field))));
+ }
+ if (disjunction == null) {
+ disjunction = disjunct;
+ } else {
+ disjunction = Bpl.Expr.Or(disjunction, disjunct);
+ }
+ }
+ if (disjunction == null) {
+ return Bpl.Expr.False;
+ } else {
+ return disjunction;
+ }
+ }
+
+ void AddWellformednessCheck(Function f) {
+ Contract.Requires(f != null);
+ Contract.Requires(sink != null && predef != null);
+ Contract.Requires(f.EnclosingClass != null);
+ Contract.Requires(currentModule == null);
+ Contract.Ensures(currentModule == null);
+
+ currentModule = f.EnclosingClass.Module;
+
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, f.tok);
+ // parameters of the procedure
+ Bpl.VariableSeq inParams = new Bpl.VariableSeq();
+ if (!f.IsStatic) {
+ Bpl.Expr wh = Bpl.Expr.And(
+ Bpl.Expr.Neq(new Bpl.IdentifierExpr(f.tok, "this", predef.RefType), predef.Null),
+ etran.GoodRef(f.tok, new Bpl.IdentifierExpr(f.tok, "this", predef.RefType), Resolver.GetReceiverType(f.tok, f)));
+ Bpl.Formal thVar = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType, wh), true);
+ inParams.Add(thVar);
+ }
+ foreach (Formal p in f.Formals) {
+ Bpl.Type varType = TrType(p.Type);
+ Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.UniqueName, varType), p.Type, etran);
+ inParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, varType, wh), true));
+ }
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
+ // the procedure itself
+ Bpl.RequiresSeq req = new Bpl.RequiresSeq();
+ // free requires mh == ModuleContextHeight && fh == FunctionContextHeight;
+ ModuleDefinition mod = f.EnclosingClass.Module;
+ Bpl.Expr context = Bpl.Expr.And(
+ Bpl.Expr.Eq(Bpl.Expr.Literal(mod.Height), etran.ModuleContextHeight()),
+ Bpl.Expr.Eq(Bpl.Expr.Literal(mod.CallGraph.GetSCCRepresentativeId(f)), etran.FunctionContextHeight()));
+ req.Add(Requires(f.tok, true, context, null, null));
+ // check that postconditions hold
+ var ens = new Bpl.EnsuresSeq();
+ 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, etran);
+ foreach (var s in splits) {
+ if (!s.IsFree && !RefinementToken.IsInherited(s.E.tok, currentModule)) {
+ ens.Add(Ensures(s.E.tok, s.IsFree, s.E, null, null));
+ }
+ }
+ }
+ Bpl.Procedure proc = new Bpl.Procedure(f.tok, "CheckWellformed$$" + f.FullCompileName, typeParams, inParams, new Bpl.VariableSeq(),
+ req, new Bpl.IdentifierExprSeq(), ens, etran.TrAttributes(f.Attributes, null));
+ sink.TopLevelDeclarations.Add(proc);
+
+ VariableSeq implInParams = Bpl.Formal.StripWhereClauses(proc.InParams);
+ Bpl.VariableSeq locals = new Bpl.VariableSeq();
+ Bpl.StmtListBuilder builder = new Bpl.StmtListBuilder();
+
+ // check well-formedness of the preconditions (including termination, but no reads checks), and then
+ // assume each one of them
+ foreach (Expression p in f.Req) {
+ CheckWellformed(p, new WFOptions(f, null, false), locals, builder, etran);
+ builder.Add(new Bpl.AssumeCmd(p.tok, etran.TrExpr(p)));
+ }
+ // Note: the reads clauses are not checked for well-formedness (is that sound?), because it used to
+ // be that the syntax was not rich enough for programmers to specify reads clauses and always being
+ // absolutely well-defined.
+ // check well-formedness of the decreases clauses (including termination, but no reads checks)
+ foreach (Expression p in f.Decreases.Expressions)
+ {
+ CheckWellformed(p, new WFOptions(f, null, false), locals, builder, etran);
+ }
+ // Generate:
+ // if (*) {
+ // check well-formedness of postcondition
+ // assume false; // don't go on to check the postconditions
+ // } else {
+ // check well-formedness of body
+ // // fall through to check the postconditions themselves
+ // }
+ // Here go the postconditions (termination checks included, but no reads checks)
+ StmtListBuilder postCheckBuilder = new StmtListBuilder();
+ // Assume the type returned by the call itself respects its type (this matter if the type is "nat", for example)
+ {
+ var args = new Bpl.ExprSeq();
+ args.Add(etran.HeapExpr);
+ if (!f.IsStatic) {
+ args.Add(new Bpl.IdentifierExpr(f.tok, etran.This, predef.RefType));
+ }
+ foreach (var p in f.Formals) {
+ args.Add(new Bpl.IdentifierExpr(p.tok, p.UniqueName, TrType(p.Type)));
+ }
+ Bpl.IdentifierExpr funcID = new Bpl.IdentifierExpr(f.tok, FunctionName(f, 1), TrType(f.ResultType));
+ Bpl.Expr funcAppl = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(funcID), args);
+
+ var wh = GetWhereClause(f.tok, funcAppl, f.ResultType, etran);
+ if (wh != null) {
+ postCheckBuilder.Add(new Bpl.AssumeCmd(f.tok, wh));
+ }
+ }
+ // Now for the ensures clauses
+ foreach (Expression p in f.Ens) {
+ CheckWellformed(p, new WFOptions(f, 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)));
+ }
+ // 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));
+ } else {
+ Bpl.FunctionCall funcID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullCompileName, TrType(f.ResultType)));
+ Bpl.ExprSeq args = new Bpl.ExprSeq();
+ args.Add(etran.HeapExpr);
+ foreach (Variable p in implInParams) {
+ args.Add(new Bpl.IdentifierExpr(f.tok, p));
+ }
+ Bpl.Expr funcAppl = new Bpl.NAryExpr(f.tok, funcID, args);
+
+ DefineFrame(f.tok, f.Reads, bodyCheckBuilder, locals, null);
+ CheckWellformedWithResult(f.Body, new WFOptions(f, null, true), funcAppl, f.ResultType, locals, bodyCheckBuilder, etran);
+ }
+ // Combine the two, letting the postcondition be checked on after the "bodyCheckBuilder" branch
+ postCheckBuilder.Add(new Bpl.AssumeCmd(f.tok, Bpl.Expr.False));
+ builder.Add(new Bpl.IfCmd(f.tok, null, postCheckBuilder.Collect(f.tok), null, bodyCheckBuilder.Collect(f.tok)));
+
+ Bpl.Implementation impl = new Bpl.Implementation(f.tok, proc.Name,
+ typeParams, implInParams, new Bpl.VariableSeq(),
+ locals, builder.Collect(f.tok));
+ sink.TopLevelDeclarations.Add(impl);
+
+ Contract.Assert(currentModule == f.EnclosingClass.Module);
+ currentModule = null;
+ }
+
+ Bpl.Expr CtorInvocation(MatchCase mc, ExpressionTranslator etran, Bpl.VariableSeq locals, StmtListBuilder localTypeAssumptions) {
+ Contract.Requires(mc != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(locals != null);
+ Contract.Requires(localTypeAssumptions != null);
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ Bpl.ExprSeq args = new Bpl.ExprSeq();
+ for (int i = 0; i < mc.Arguments.Count; i++) {
+ BoundVar p = mc.Arguments[i];
+ Bpl.Variable local = new Bpl.LocalVariable(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)));
+ locals.Add(local);
+ 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));
+ }
+ args.Add(etran.CondApplyBox(mc.tok, new Bpl.IdentifierExpr(p.tok, local), cce.NonNull(p.Type), t));
+ }
+ Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(mc.tok, mc.Ctor.FullName, predef.DatatypeType);
+ return new Bpl.NAryExpr(mc.tok, new Bpl.FunctionCall(id), args);
+ }
+
+ Bpl.Expr CtorInvocation(IToken tok, DatatypeCtor ctor, ExpressionTranslator etran, Bpl.VariableSeq locals, StmtListBuilder localTypeAssumptions) {
+ Contract.Requires(tok != null);
+ Contract.Requires(ctor != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(locals != null);
+ Contract.Requires(localTypeAssumptions != null);
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ // create local variables for the formals
+ var args = new ExprSeq();
+ foreach (Formal arg in ctor.Formals) {
+ Contract.Assert(arg != null);
+ var nm = string.Format("a{0}#{1}", args.Length, otherTmpVarCount);
+ otherTmpVarCount++;
+ Bpl.Variable bv = new Bpl.LocalVariable(arg.tok, new Bpl.TypedIdent(arg.tok, nm, TrType(arg.Type)));
+ locals.Add(bv);
+ args.Add(new Bpl.IdentifierExpr(arg.tok, bv));
+ }
+
+ Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(tok, ctor.FullName, predef.DatatypeType);
+ return new Bpl.NAryExpr(tok, new Bpl.FunctionCall(id), args);
+ }
+
+ Bpl.Expr IsTotal(Expression expr, ExpressionTranslator etran) {
+ Contract.Requires(expr != null);Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ if (expr is LiteralExpr || expr is ThisExpr || expr is IdentifierExpr || expr is WildcardExpr || expr is BoogieWrapper) {
+ return Bpl.Expr.True;
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ return IsTotal(e.Elements, etran);
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)expr;
+ if (e.Obj is ThisExpr) {
+ return Bpl.Expr.True;
+ } else {
+ var t = IsTotal(e.Obj, etran);
+ if (e.Obj.Type.IsRefType) {
+ t = BplAnd(t, Bpl.Expr.Neq(etran.TrExpr(e.Obj), predef.Null));
+ } else if (e.Field is DatatypeDestructor) {
+ var dtor = (DatatypeDestructor)e.Field;
+ t = BplAnd(t, FunctionCall(e.tok, dtor.EnclosingCtor.QueryField.FullCompileName, Bpl.Type.Bool, etran.TrExpr(e.Obj)));
+ }
+ return t;
+ }
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ bool isSequence = e.Seq.Type is SeqType;
+ if (e.Seq.Type is MapType) {
+ Bpl.Expr total = IsTotal(e.Seq, etran);
+ Bpl.Expr map = etran.TrExpr(e.Seq);
+ var e0 = etran.TrExpr(e.E0);
+ Bpl.Expr inDomain = FunctionCall(expr.tok, BuiltinFunction.MapDomain, predef.MapType(e.tok, predef.BoxType, predef.BoxType), map);
+ inDomain = Bpl.Expr.Select(inDomain, etran.BoxIfNecessary(e.tok, e0, e.E0.Type));
+ total = BplAnd(total, inDomain);
+ return total;
+ } else {
+ Bpl.Expr total = IsTotal(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, IsTotal(e.E0, etran));
+ total = BplAnd(total, InSeqRange(expr.tok, e0, seq, isSequence, null, !e.SelectOne));
+ }
+ if (e.E1 != null) {
+ total = BplAnd(total, IsTotal(e.E1, etran));
+ total = BplAnd(total, InSeqRange(expr.tok, etran.TrExpr(e.E1), seq, isSequence, e0, true));
+ }
+ return total;
+ }
+ } else if (expr is MultiSelectExpr) {
+ MultiSelectExpr e = (MultiSelectExpr)expr;
+ Bpl.Expr total = IsTotal(e.Array, etran);
+ Bpl.Expr array = etran.TrExpr(e.Array);
+ int i = 0;
+ foreach (Expression idx in e.Indices) {
+ total = BplAnd(total, IsTotal(idx, 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);
+ total = BplAnd(total, Bpl.Expr.And(lower, upper));
+ i++;
+ }
+ return total;
+ } else if (expr is SeqUpdateExpr) {
+ SeqUpdateExpr e = (SeqUpdateExpr)expr;
+ Bpl.Expr total = IsTotal(e.Seq, etran);
+ Bpl.Expr seq = etran.TrExpr(e.Seq);
+ Bpl.Expr index = etran.TrExpr(e.Index);
+ total = BplAnd(total, IsTotal(e.Index, etran));
+ total = BplAnd(total, InSeqRange(expr.tok, index, seq, true, null, false));
+ total = BplAnd(total, IsTotal(e.Value, etran));
+ return total;
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ Contract.Assert(e.Function != null); // follows from the fact that expr has been successfully resolved
+ // check well-formedness of receiver
+ Bpl.Expr r = IsTotal(e.Receiver, etran);
+ if (!e.Function.IsStatic && !(e.Receiver is ThisExpr)) {
+ r = BplAnd(r, Bpl.Expr.Neq(etran.TrExpr(e.Receiver), predef.Null));
+ }
+ // check well-formedness of the other parameters
+ r = BplAnd(r, IsTotal(e.Args, etran));
+ // create a substitution map from each formal parameter to the corresponding actual parameter
+ Dictionary<IVariable,Expression> substMap = new Dictionary<IVariable,Expression>();
+ for (int i = 0; i < e.Function.Formals.Count; i++) {
+ var formal = e.Function.Formals[i];
+ var s = CheckSubrange_Expr(e.Args[i].tok, etran.TrExpr(e.Args[i]), formal.Type);
+ if (s != null) {
+ r = BplAnd(r, s);
+ }
+ substMap.Add(formal, e.Args[i]);
+ }
+ // check that the preconditions for the call hold
+ foreach (Expression p in e.Function.Req) {
+ Expression precond = Substitute(p, e.Receiver, substMap);
+ r = BplAnd(r, etran.TrExpr(precond));
+ }
+ // TODO: if this is a recursive call, also conjoin the well-ordering predicate
+ return r;
+ } else if (expr is DatatypeValue) {
+ DatatypeValue dtv = (DatatypeValue)expr;
+ var r = IsTotal(dtv.Arguments, etran);
+ for (int i = 0; i < dtv.Ctor.Formals.Count; i++) {
+ var formal = dtv.Ctor.Formals[i];
+ var arg = dtv.Arguments[i];
+ var s = CheckSubrange_Expr(arg.tok, etran.TrExpr(arg), formal.Type);
+ if (s != null) {
+ r = BplAnd(r, s);
+ }
+ }
+ return r;
+ } else if (expr is OldExpr) {
+ OldExpr e = (OldExpr)expr;
+ return IsTotal(e.E, etran.Old);
+ } else if (expr is MultiSetFormingExpr) {
+ MultiSetFormingExpr e = (MultiSetFormingExpr)expr;
+ return IsTotal(e.E, etran);
+ } else if (expr is FreshExpr) {
+ FreshExpr e = (FreshExpr)expr;
+ return IsTotal(e.E, etran);
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ Bpl.Expr t = IsTotal(e.E, etran);
+ if (e.Op == UnaryExpr.Opcode.SetChoose) {
+ Bpl.Expr emptySet = FunctionCall(expr.tok, BuiltinFunction.SetEmpty, predef.BoxType);
+ return Bpl.Expr.And(t, Bpl.Expr.Neq(etran.TrExpr(e.E), emptySet));
+ } else if (e.Op == UnaryExpr.Opcode.SeqLength && !(e.E.Type is SeqType)) {
+ return Bpl.Expr.And(t, Bpl.Expr.Neq(etran.TrExpr(e.E), predef.Null));
+ }
+ return t;
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ Bpl.Expr t0 = IsTotal(e.E0, etran);
+ Bpl.Expr t1 = IsTotal(e.E1, etran);
+ Bpl.Expr z = null;
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.And:
+ case BinaryExpr.ResolvedOpcode.Imp:
+ t1 = Bpl.Expr.Imp(etran.TrExpr(e.E0), t1);
+ break;
+ case BinaryExpr.ResolvedOpcode.Or:
+ t1 = Bpl.Expr.Imp(Bpl.Expr.Not(etran.TrExpr(e.E0)), t1);
+ break;
+ case BinaryExpr.ResolvedOpcode.Div:
+ case BinaryExpr.ResolvedOpcode.Mod:
+ z = Bpl.Expr.Neq(etran.TrExpr(e.E1), Bpl.Expr.Literal(0));
+ break;
+ default:
+ break;
+ }
+ Bpl.Expr r = BplAnd(t0, t1);
+ return z == null ? r : BplAnd(r, z);
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ Bpl.Expr total = Bpl.Expr.True;
+ foreach (var rhs in e.RHSs) {
+ total = BplAnd(total, IsTotal(rhs, etran));
+ }
+ return BplAnd(total, IsTotal(etran.GetSubstitutedBody(e), etran));
+
+ } else if (expr is ComprehensionExpr) {
+ var e = (ComprehensionExpr)expr;
+ var total = IsTotal(e.Term, etran);
+ if (e.Range != null) {
+ total = BplAnd(IsTotal(e.Range, etran), BplImp(etran.TrExpr(e.Range), total));
+ }
+ if (total != Bpl.Expr.True) {
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ Bpl.Expr typeAntecedent = etran.TrBoundVariables(e.BoundVars, bvars);
+ total = new Bpl.ForallExpr(expr.tok, bvars, Bpl.Expr.Imp(typeAntecedent, total));
+ }
+ return total;
+ } else if (expr is PredicateExpr) {
+ var e = (PredicateExpr)expr;
+ Bpl.Expr gTotal = IsTotal(e.Guard, etran);
+ Bpl.Expr g = etran.TrExpr(e.Guard);
+ Bpl.Expr bTotal = IsTotal(e.Body, etran);
+ if (e is AssertExpr || DafnyOptions.O.DisallowSoundnessCheating) {
+ return BplAnd(gTotal, BplAnd(g, bTotal));
+ } else {
+ return BplAnd(gTotal, Bpl.Expr.Imp(g, bTotal));
+ }
+ } else if (expr is ITEExpr) {
+ ITEExpr e = (ITEExpr)expr;
+ Bpl.Expr total = IsTotal(e.Test, etran);
+ Bpl.Expr test = etran.TrExpr(e.Test);
+ total = BplAnd(total, Bpl.Expr.Imp(test, IsTotal(e.Thn, etran)));
+ total = BplAnd(total, Bpl.Expr.Imp(Bpl.Expr.Not(test), IsTotal(e.Els, etran)));
+ return total;
+ } else if (expr is ConcreteSyntaxExpression) {
+ var e = (ConcreteSyntaxExpression)expr;
+ return IsTotal(e.ResolvedExpression, etran);
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
+ }
+ }
+
+ Bpl.Expr/*!*/ IsTotal(List<Expression/*!*/>/*!*/ exprs, ExpressionTranslator/*!*/ etran) {
+ Contract.Requires(etran != null);
+ Contract.Requires(exprs != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ Bpl.Expr total = Bpl.Expr.True;
+ foreach (Expression e in exprs) {
+ Contract.Assert(e != null);
+ total = BplAnd(total, IsTotal(e, etran));
+ }
+ return total;
+ }
+
+ Bpl.Expr CanCallAssumption(Expression expr, ExpressionTranslator etran) {
+ Contract.Requires(expr != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ if (expr is LiteralExpr || expr is ThisExpr || expr is IdentifierExpr || expr is WildcardExpr || expr is BoogieWrapper) {
+ return Bpl.Expr.True;
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ return CanCallAssumption(e.Elements, etran);
+ } else if (expr is MapDisplayExpr) {
+ MapDisplayExpr e = (MapDisplayExpr)expr;
+ List<Expression> l = new List<Expression>();
+ foreach (ExpressionPair p in e.Elements) {
+ l.Add(p.A); l.Add(p.B);
+ }
+ return CanCallAssumption(l, etran);
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)expr;
+ if (e.Obj is ThisExpr) {
+ return Bpl.Expr.True;
+ } else {
+ Bpl.Expr r = CanCallAssumption(e.Obj, etran);
+ return r;
+ }
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ //bool isSequence = e.Seq.Type is SeqType;
+ 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) {
+ total = BplAnd(total, CanCallAssumption(e.E1, etran));
+ }
+ return total;
+ } else if (expr is MultiSelectExpr) {
+ MultiSelectExpr e = (MultiSelectExpr)expr;
+ Bpl.Expr total = CanCallAssumption(e.Array, etran);
+ foreach (Expression idx in e.Indices) {
+ total = BplAnd(total, CanCallAssumption(idx, etran));
+ }
+ return total;
+ } else if (expr is SeqUpdateExpr) {
+ SeqUpdateExpr e = (SeqUpdateExpr)expr;
+ 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;
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ Contract.Assert(e.Function != null); // follows from the fact that expr has been successfully resolved
+ // 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));
+ // get to assume canCall
+ Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(expr.tok, e.Function.FullCompileName + "#canCall", Bpl.Type.Bool);
+ ExprSeq args = etran.FunctionInvocationArguments(e);
+ 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;
+ return CanCallAssumption(dtv.Arguments, etran);
+ } else if (expr is OldExpr) {
+ OldExpr e = (OldExpr)expr;
+ return CanCallAssumption(e.E, etran.Old);
+ } else if (expr is MultiSetFormingExpr) {
+ MultiSetFormingExpr e = (MultiSetFormingExpr)expr;
+ return CanCallAssumption(e.E, etran);
+ } else if (expr is FreshExpr) {
+ FreshExpr e = (FreshExpr)expr;
+ return CanCallAssumption(e.E, etran);
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ Bpl.Expr t = CanCallAssumption(e.E, etran);
+ return t;
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ Bpl.Expr t0 = CanCallAssumption(e.E0, etran);
+ Bpl.Expr t1 = CanCallAssumption(e.E1, etran);
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.And:
+ case BinaryExpr.ResolvedOpcode.Imp:
+ t1 = Bpl.Expr.Imp(etran.TrExpr(e.E0), t1);
+ break;
+ case BinaryExpr.ResolvedOpcode.Or:
+ t1 = Bpl.Expr.Imp(Bpl.Expr.Not(etran.TrExpr(e.E0)), t1);
+ break;
+ default:
+ break;
+ }
+ return BplAnd(t0, t1);
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ Bpl.Expr canCall = Bpl.Expr.True;
+ foreach (var rhs in e.RHSs) {
+ canCall = BplAnd(canCall, CanCallAssumption(rhs, etran));
+ }
+ return BplAnd(canCall, CanCallAssumption(etran.GetSubstitutedBody(e), etran));
+
+ } else if (expr is NamedExpr) {
+ var e = (NamedExpr)expr;
+ var canCall = CanCallAssumption(e.Body, etran);
+ if (e.Contract != null)
+ return BplAnd(canCall, CanCallAssumption(e.Contract, etran));
+ else return canCall;
+ } else if (expr is ComprehensionExpr) {
+ var e = (ComprehensionExpr)expr;
+ var total = CanCallAssumption(e.Term, etran);
+ if (e.Range != null) {
+ total = BplAnd(CanCallAssumption(e.Range, etran), BplImp(etran.TrExpr(e.Range), total));
+ }
+ if (total != Bpl.Expr.True) {
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ Bpl.Expr typeAntecedent = etran.TrBoundVariables(e.BoundVars, bvars);
+ total = new Bpl.ForallExpr(expr.tok, bvars, Bpl.Expr.Imp(typeAntecedent, total));
+ }
+ return total;
+ } else if (expr is PredicateExpr) {
+ var e = (PredicateExpr)expr;
+ Bpl.Expr gCanCall = CanCallAssumption(e.Guard, etran);
+ Bpl.Expr bCanCall = CanCallAssumption(e.Body, etran);
+ if (e is AssertExpr || DafnyOptions.O.DisallowSoundnessCheating) {
+ return BplAnd(gCanCall, bCanCall);
+ } else {
+ Bpl.Expr g = etran.TrExpr(e.Guard);
+ return BplAnd(gCanCall, Bpl.Expr.Imp(g, bCanCall));
+ }
+ } else if (expr is ITEExpr) {
+ 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)));
+ return total;
+ } else if (expr is ConcreteSyntaxExpression) {
+ var e = (ConcreteSyntaxExpression)expr;
+ return CanCallAssumption(e.ResolvedExpression, etran);
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
+ }
+ }
+
+ Bpl.Expr/*!*/ CanCallAssumption(List<Expression/*!*/>/*!*/ exprs, ExpressionTranslator/*!*/ etran) {
+ Contract.Requires(etran != null);
+ Contract.Requires(exprs != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ Bpl.Expr total = Bpl.Expr.True;
+ foreach (Expression e in exprs) {
+ Contract.Assert(e != null);
+ total = BplAnd(total, CanCallAssumption(e, etran));
+ }
+ return total;
+ }
+
+ Expression DafnyAnd(Expression a, Expression b) {
+ Contract.Requires(a != null);
+ Contract.Requires(b != null);
+ Contract.Ensures(Contract.Result<Expression>() != null);
+
+ if (LiteralExpr.IsTrue(a)) {
+ return b;
+ } else if (LiteralExpr.IsTrue(b)) {
+ return a;
+ } else {
+ BinaryExpr and = new BinaryExpr(a.tok, BinaryExpr.Opcode.And, a, b);
+ and.ResolvedOp = BinaryExpr.ResolvedOpcode.And; // resolve here
+ and.Type = Type.Bool; // resolve here
+ return and;
+ }
+ }
+
+ Bpl.Expr BplAnd(Bpl.Expr a, Bpl.Expr b) {
+ Contract.Requires(a != null);
+ Contract.Requires(b != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ if (a == Bpl.Expr.True) {
+ return b;
+ } else if (b == Bpl.Expr.True) {
+ return a;
+ } else {
+ return Bpl.Expr.And(a, b);
+ }
+ }
+
+ Bpl.Expr BplOr(Bpl.Expr a, Bpl.Expr b) {
+ Contract.Requires(a != null);
+ Contract.Requires(b != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ if (a == Bpl.Expr.False) {
+ return b;
+ } else if (b == Bpl.Expr.False) {
+ return a;
+ } else {
+ return Bpl.Expr.Or(a, b);
+ }
+ }
+
+ Bpl.Expr BplImp(Bpl.Expr a, Bpl.Expr b) {
+ Contract.Requires(a != null);
+ Contract.Requires(b != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ if (a == Bpl.Expr.True || b == Bpl.Expr.True) {
+ return b;
+ } else if (a == Bpl.Expr.False) {
+ return Bpl.Expr.True;
+ } else {
+ return Bpl.Expr.Imp(a, b);
+ }
+ }
+
+ void CheckNonNull(IToken tok, Expression e, Bpl.StmtListBuilder builder, ExpressionTranslator etran, Bpl.QKeyValue kv) {
+ Contract.Requires(tok != null);
+ Contract.Requires(e != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
+
+ if (e is ThisExpr) {
+ // already known to be non-null
+ } else {
+ builder.Add(Assert(tok, Bpl.Expr.Neq(etran.TrExpr(e), predef.Null), "target object may be null", kv));
+ }
+ }
+
+ /// <summary>
+ /// Instances of WFContext are used as an argument to CheckWellformed, supplying options for the
+ /// checks to be performed.
+ /// If non-null, "Decr" gives the caller to be used for termination checks. If it is null, no
+ /// termination checks are performed.
+ /// If "SelfCallsAllowance" is non-null, termination checks will be omitted for calls that look
+ /// 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.
+ /// </summary>
+ class WFOptions
+ {
+ public readonly Function Decr;
+ public readonly Function SelfCallsAllowance;
+ public readonly bool DoReadsChecks;
+ public readonly Bpl.QKeyValue AssertKv;
+ public WFOptions() { }
+ public WFOptions(Function decr, Function selfCallsAllowance, bool doReadsChecks) {
+ Decr = decr;
+ SelfCallsAllowance = selfCallsAllowance;
+ DoReadsChecks = doReadsChecks;
+ }
+ public WFOptions(Bpl.QKeyValue kv) {
+ AssertKv = kv;
+ }
+ }
+
+ void TrStmt_CheckWellformed(Expression expr, Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran, bool subsumption) {
+ Contract.Requires(expr != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(locals != null);
+ Contract.Requires(etran != null);
+
+ Bpl.QKeyValue kv;
+ if (subsumption) {
+ kv = null; // this is the default behavior of Boogie's assert
+ } else {
+ List<object> args = new List<object>();
+ // {:subsumption 0}
+ args.Add(Bpl.Expr.Literal(0));
+ 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)));
+ }
+
+ void CheckWellformed(Expression expr, WFOptions options, Bpl.VariableSeq locals, Bpl.StmtListBuilder builder, ExpressionTranslator etran) {
+ CheckWellformedWithResult(expr, options, null, null, locals, builder, etran);
+ }
+
+ /// <summary>
+ /// Adds to "builder" code that checks the well-formedness of "expr". Any local variables introduced
+ /// in this code are added to "locals".
+ /// If "result" is non-null, then after checking the well-formedness of "expr", the generated code will
+ /// assume the equivalent of "result == expr".
+ /// See class WFOptions for descriptions of the specified options.
+ /// </summary>
+ void CheckWellformedWithResult(Expression expr, WFOptions options, Bpl.Expr result, Type resultType,
+ Bpl.VariableSeq locals, Bpl.StmtListBuilder builder, ExpressionTranslator etran) {
+ Contract.Requires(expr != null);
+ Contract.Requires(options != null);
+ Contract.Requires((result == null) == (resultType == null));
+ Contract.Requires(locals != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
+
+ if (expr is LiteralExpr || expr is ThisExpr || expr is IdentifierExpr || expr is WildcardExpr || expr is BoogieWrapper) {
+ // always allowed
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ foreach (Expression el in e.Elements) {
+ CheckWellformed(el, options, locals, builder, etran);
+ }
+ } else if (expr is MapDisplayExpr) {
+ MapDisplayExpr e = (MapDisplayExpr)expr;
+ foreach (ExpressionPair p in e.Elements) {
+ CheckWellformed(p.A, options, locals, builder, etran);
+ CheckWellformed(p.B, options, locals, builder, etran);
+ }
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)expr;
+ CheckWellformed(e.Obj, options, locals, builder, etran);
+ if (e.Obj.Type.IsRefType) {
+ CheckNonNull(expr.tok, e.Obj, builder, etran, options.AssertKv);
+ } else if (e.Field is DatatypeDestructor) {
+ var dtor = (DatatypeDestructor)e.Field;
+ var correctConstructor = FunctionCall(e.tok, dtor.EnclosingCtor.QueryField.FullCompileName, 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));
+ } 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)));
+ }
+ }
+ if (options.DoReadsChecks && e.Field.IsMutable) {
+ builder.Add(Assert(expr.tok, Bpl.Expr.SelectTok(expr.tok, etran.TheFrame(expr.tok), etran.TrExpr(e.Obj), GetField(e)), "insufficient reads clause to read field", options.AssertKv));
+ }
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ bool isSequence = e.Seq.Type is SeqType;
+ CheckWellformed(e.Seq, options, locals, builder, etran);
+ Bpl.Expr seq = etran.TrExpr(e.Seq);
+ if (e.Seq.Type.IsArrayType) {
+ builder.Add(Assert(e.Seq.tok, Bpl.Expr.Neq(seq, predef.Null), "array may be null"));
+ }
+ Bpl.Expr e0 = null;
+ if (e.Seq.Type is MapType) {
+ e0 = etran.TrExpr(e.E0);
+ CheckWellformed(e.E0, options, locals, builder, etran);
+ Bpl.Expr inDomain = FunctionCall(expr.tok, BuiltinFunction.MapDomain, predef.MapType(e.tok, predef.BoxType, predef.BoxType), seq);
+ inDomain = Bpl.Expr.Select(inDomain, etran.BoxIfNecessary(e.tok, e0, e.E0.Type));
+ builder.Add(Assert(expr.tok, inDomain, "element may not be in domain", options.AssertKv));
+
+ } else {
+ if (e.E0 != null) {
+ e0 = etran.TrExpr(e.E0);
+ CheckWellformed(e.E0, options, locals, builder, etran);
+ builder.Add(Assert(expr.tok, InSeqRange(expr.tok, e0, seq, isSequence, null, !e.SelectOne), e.SelectOne ? "index out of range" : "lower bound out of range", options.AssertKv));
+ }
+ if (e.E1 != null) {
+ CheckWellformed(e.E1, options, locals, builder, etran);
+ builder.Add(Assert(expr.tok, InSeqRange(expr.tok, etran.TrExpr(e.E1), seq, isSequence, e0, true), "upper bound " + (e.E0 == null ? "" : "below lower bound or ") + "above length of " + (isSequence ? "sequence" : "array"), options.AssertKv));
+ }
+ }
+ if (options.DoReadsChecks && cce.NonNull(e.Seq.Type).IsArrayType) {
+ if (e.SelectOne) {
+ Contract.Assert(e.E0 != null);
+ Bpl.Expr fieldName = FunctionCall(expr.tok, BuiltinFunction.IndexField, null, etran.TrExpr(e.E0));
+ builder.Add(Assert(expr.tok, Bpl.Expr.SelectTok(expr.tok, etran.TheFrame(expr.tok), seq, fieldName), "insufficient reads clause to read array element", options.AssertKv));
+ } else {
+ Bpl.Expr lowerBound = e.E0 == null ? Bpl.Expr.Literal(0) : etran.TrExpr(e.E0);
+ Contract.Assert((e.Seq.Type).AsArrayType.Dims == 1);
+ Bpl.Expr upperBound = e.E1 == null ? ArrayLength(e.tok, seq, 1, 0) : etran.TrExpr(e.E1);
+ // check that, for all i in lowerBound..upperBound, a[i] is in the frame
+ Bpl.BoundVariable iVar = new Bpl.BoundVariable(e.tok, new Bpl.TypedIdent(e.tok, "$i", Bpl.Type.Int));
+ Bpl.IdentifierExpr i = new Bpl.IdentifierExpr(e.tok, iVar);
+ 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 Bpl.VariableSeq(iVar), Bpl.Expr.Imp(range, allowedToRead));
+ builder.Add(Assert(expr.tok, qq, "insufficient reads clause to read the indicated range of array elements", options.AssertKv));
+ }
+ }
+ } else if (expr is MultiSelectExpr) {
+ 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) {
+ 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++;
+ }
+ } else if (expr is SeqUpdateExpr) {
+ SeqUpdateExpr e = (SeqUpdateExpr)expr;
+ CheckWellformed(e.Seq, options, locals, builder, etran);
+ Bpl.Expr seq = etran.TrExpr(e.Seq);
+ Bpl.Expr index = etran.TrExpr(e.Index);
+ CheckWellformed(e.Index, options, locals, builder, etran);
+ if (e.Seq.Type is SeqType) {
+ builder.Add(Assert(expr.tok, InSeqRange(expr.tok, index, seq, true, null, false), "index out of range", options.AssertKv));
+ } else {
+ Contract.Assert(e.Seq.Type is MapType);
+ // updates add to maps, so are always valid if the values are well formed.
+ }
+ CheckWellformed(e.Value, options, locals, builder, etran);
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ Contract.Assert(e.Function != null); // follows from the fact that expr has been successfully resolved
+ // check well-formedness of receiver
+ CheckWellformed(e.Receiver, options, locals, builder, etran);
+ if (!e.Function.IsStatic && !(e.Receiver is ThisExpr)) {
+ CheckNonNull(expr.tok, e.Receiver, builder, etran, options.AssertKv);
+ }
+ // check well-formedness of the other parameters
+ foreach (Expression arg in e.Args) {
+ CheckWellformed(arg, options, locals, builder, etran);
+ }
+ // create a local variable for each formal parameter, and assign each actual parameter to the corresponding local
+ Dictionary<IVariable, Expression> substMap = new Dictionary<IVariable, Expression>();
+ for (int i = 0; i < e.Function.Formals.Count; i++) {
+ Formal p = e.Function.Formals[i];
+ VarDecl local = new VarDecl(p.tok, p.Name, p.Type, p.IsGhost);
+ local.type = local.OptionalType; // resolve local here
+ IdentifierExpr ie = new IdentifierExpr(local.Tok, local.UniqueName);
+ ie.Var = local; ie.Type = ie.Var.Type; // resolve ie here
+ substMap.Add(p, ie);
+ locals.Add(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.UniqueName, TrType(local.Type))));
+ Bpl.IdentifierExpr lhs = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified?
+ Expression ee = e.Args[i];
+ CheckSubrange(ee.tok, etran.TrExpr(ee), p.Type, builder);
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(p.tok, lhs, etran.CondApplyBox(p.tok, etran.TrExpr(ee), cce.NonNull(ee.Type), p.Type));
+ builder.Add(cmd);
+ }
+ // 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
+ // access to expressions of the appropriate type and that are allocated in the current state. However, if the function is
+ // invoked in the 'old' state, then we need to check that its arguments were all available at that time as well.
+ if (etran.UsesOldHeap) {
+ if (!e.Function.IsStatic) {
+ Bpl.Expr wh = GetWhereClause(e.Receiver.tok, etran.TrExpr(e.Receiver), e.Receiver.Type, etran);
+ if (wh != null) {
+ builder.Add(Assert(e.Receiver.tok, wh, "receiver argument must be allocated in the state in which the function is invoked"));
+ }
+ }
+ for (int i = 0; i < e.Args.Count; i++) {
+ Expression ee = e.Args[i];
+ Bpl.Expr wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran);
+ if (wh != null) {
+ builder.Add(Assert(ee.tok, wh, "argument must be allocated in the state in which the function is invoked"));
+ }
+ }
+ }
+ // check that the preconditions for the call hold
+ foreach (Expression p in e.Function.Req) {
+ Expression precond = Substitute(p, e.Receiver, substMap);
+ builder.Add(Assert(expr.tok, etran.TrExpr(precond), "possible violation of function precondition", options.AssertKv));
+ }
+ Bpl.Expr allowance = null;
+ if (options.Decr != null || options.DoReadsChecks) {
+ if (options.DoReadsChecks) {
+ // check that the callee reads only what the caller is already allowed to read
+ CheckFrameSubset(expr.tok, e.Function.Reads, e.Receiver, substMap, etran, builder, "insufficient reads clause to invoke function", options.AssertKv);
+ }
+
+ if (options.Decr != null && e.CoCall != FunctionCallExpr.CoCallResolution.Yes && !(e.Function is CoPredicate)) {
+ // check that the decreases measure goes down
+ ModuleDefinition module = cce.NonNull(e.Function.EnclosingClass).Module;
+ if (module == cce.NonNull(options.Decr.EnclosingClass).Module) {
+ if (module.CallGraph.GetSCCRepresentative(e.Function) == module.CallGraph.GetSCCRepresentative(options.Decr)) {
+ bool contextDecrInferred, calleeDecrInferred;
+ List<Expression> contextDecreases = FunctionDecreasesWithDefault(options.Decr, out contextDecrInferred);
+ List<Expression> calleeDecreases = FunctionDecreasesWithDefault(e.Function, out calleeDecrInferred);
+ if (e.Function == options.SelfCallsAllowance) {
+ allowance = Bpl.Expr.True;
+ if (!e.Function.IsStatic) {
+ allowance = BplAnd(allowance, Bpl.Expr.Eq(etran.TrExpr(e.Receiver), new Bpl.IdentifierExpr(e.tok, etran.This, predef.RefType)));
+ }
+ for (int i = 0; i < e.Args.Count; i++) {
+ Expression ee = e.Args[i];
+ Formal ff = e.Function.Formals[i];
+ allowance = BplAnd(allowance, Bpl.Expr.Eq(etran.TrExpr(ee), new Bpl.IdentifierExpr(e.tok, ff.UniqueName, TrType(ff.Type))));
+ }
+ }
+ string hint;
+ switch (e.CoCall) {
+ case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasSideEffects:
+ hint = "note that only functions without side effects can called co-recursively";
+ break;
+ case FunctionCallExpr.CoCallResolution.NoBecauseIsNotGuarded:
+ hint = "note that the call is not sufficiently guarded to be used co-recursively";
+ break;
+ case FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsAreNotAllowedInThisContext:
+ hint = "note that calls cannot be co-recursive in this context";
+ break;
+ case FunctionCallExpr.CoCallResolution.No:
+ hint = null;
+ break;
+ default:
+ Contract.Assert(false); // unexpected CoCallResolution
+ goto case FunctionCallExpr.CoCallResolution.No; // please the compiler
+ }
+ CheckCallTermination(expr.tok, contextDecreases, calleeDecreases, allowance, e.Receiver, substMap, etran, builder,
+ contextDecrInferred, hint);
+ }
+ }
+ }
+ }
+ // all is okay, so allow this function application access to the function's axiom, except if it was okay because of the self-call allowance.
+ Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(expr.tok, e.Function.FullCompileName + "#canCall", Bpl.Type.Bool);
+ ExprSeq args = etran.FunctionInvocationArguments(e);
+ 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)));
+
+ } else if (expr is DatatypeValue) {
+ DatatypeValue dtv = (DatatypeValue)expr;
+ for (int i = 0; i < dtv.Ctor.Formals.Count; i++) {
+ var formal = dtv.Ctor.Formals[i];
+ var arg = dtv.Arguments[i];
+ CheckWellformed(arg, options, locals, builder, etran);
+ CheckSubrange(arg.tok, etran.TrExpr(arg), formal.Type, builder);
+ }
+ } else if (expr is OldExpr) {
+ OldExpr e = (OldExpr)expr;
+ CheckWellformed(e.E, options, locals, builder, etran.Old);
+ } else if (expr is MultiSetFormingExpr) {
+ MultiSetFormingExpr e = (MultiSetFormingExpr)expr;
+ CheckWellformed(e.E, options, locals, builder, etran);
+ } else if (expr is FreshExpr) {
+ FreshExpr e = (FreshExpr)expr;
+ CheckWellformed(e.E, options, locals, builder, etran);
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ CheckWellformed(e.E, options, locals, builder, etran);
+ if (e.Op == UnaryExpr.Opcode.SetChoose) {
+ Bpl.Expr emptySet = FunctionCall(expr.tok, BuiltinFunction.SetEmpty, predef.BoxType);
+ builder.Add(Assert(expr.tok, Bpl.Expr.Neq(etran.TrExpr(e.E), emptySet), "choose is defined only on nonempty sets"));
+ } else if (e.Op == UnaryExpr.Opcode.SeqLength && !(e.E.Type is SeqType)) {
+ CheckNonNull(expr.tok, e.E, builder, etran, options.AssertKv);
+ }
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ CheckWellformed(e.E0, options, locals, builder, etran);
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.And:
+ case BinaryExpr.ResolvedOpcode.Imp: {
+ Bpl.StmtListBuilder b = new Bpl.StmtListBuilder();
+ CheckWellformed(e.E1, options, locals, b, etran);
+ builder.Add(new Bpl.IfCmd(expr.tok, etran.TrExpr(e.E0), b.Collect(expr.tok), null, null));
+ }
+ break;
+ case BinaryExpr.ResolvedOpcode.Or: {
+ Bpl.StmtListBuilder b = new Bpl.StmtListBuilder();
+ CheckWellformed(e.E1, options, locals, b, etran);
+ builder.Add(new Bpl.IfCmd(expr.tok, Bpl.Expr.Not(etran.TrExpr(e.E0)), b.Collect(expr.tok), null, null));
+ }
+ break;
+ case BinaryExpr.ResolvedOpcode.Div:
+ case BinaryExpr.ResolvedOpcode.Mod:
+ CheckWellformed(e.E1, options, locals, builder, etran);
+ builder.Add(Assert(expr.tok, Bpl.Expr.Neq(etran.TrExpr(e.E1), Bpl.Expr.Literal(0)), "possible division by zero", options.AssertKv));
+ break;
+ default:
+ CheckWellformed(e.E1, options, locals, builder, etran);
+ break;
+ }
+
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+
+ var substMap = new Dictionary<IVariable, Expression>();
+ Contract.Assert(e.Vars.Count == e.RHSs.Count); // checked by resolution
+ for (int i = 0; i < e.Vars.Count; i++) {
+ var vr = e.Vars[i];
+ var tp = TrType(vr.Type);
+ var v = new Bpl.LocalVariable(vr.tok, new Bpl.TypedIdent(vr.tok, vr.UniqueName, tp));
+ locals.Add(v);
+ var lhs = new Bpl.IdentifierExpr(vr.tok, vr.UniqueName, tp);
+
+ CheckWellformedWithResult(e.RHSs[i], options, lhs, vr.Type, locals, builder, etran);
+ substMap.Add(vr, new BoogieWrapper(lhs, vr.Type));
+ }
+ CheckWellformedWithResult(Substitute(e.Body, null, substMap), options, result, resultType, locals, builder, etran);
+ result = null;
+
+ } else if (expr is NamedExpr) {
+ var e = (NamedExpr)expr;
+ CheckWellformedWithResult(e.Body, options, result, resultType, locals, builder, etran);
+ if (e.Contract != null) {
+ CheckWellformedWithResult(e.Contract, options, result, resultType, locals, builder, etran);
+ var theSame = Bpl.Expr.Eq(etran.TrExpr(e.Body), etran.TrExpr(e.Contract));
+ builder.Add(Assert(new ForceCheckToken(e.ReplacerToken), theSame, "replacement must be the same value"));
+ }
+ } else if (expr is ComprehensionExpr) {
+ var e = (ComprehensionExpr)expr;
+ var substMap = SetupBoundVarsAsLocals(e.BoundVars, builder, locals, etran);
+ Expression body = Substitute(e.Term, null, substMap);
+ if (e.Range == null) {
+ CheckWellformed(body, options, locals, builder, etran);
+ } else {
+ Expression range = Substitute(e.Range, null, substMap);
+ CheckWellformed(range, options, locals, builder, etran);
+
+ Bpl.StmtListBuilder b = new Bpl.StmtListBuilder();
+ CheckWellformed(body, options, locals, b, etran);
+ builder.Add(new Bpl.IfCmd(expr.tok, etran.TrExpr(range), b.Collect(expr.tok), null, null));
+ }
+
+ } else if (expr is PredicateExpr) {
+ var e = (PredicateExpr)expr;
+ CheckWellformed(e.Guard, options, locals, builder, etran);
+ if (e is AssertExpr || DafnyOptions.O.DisallowSoundnessCheating) {
+ bool splitHappened;
+ var ss = TrSplitExpr(e.Guard, etran, out splitHappened);
+ if (!splitHappened) {
+ builder.Add(Assert(e.Guard.tok, etran.TrExpr(e.Guard), "condition in assert expression might not hold"));
+ } else {
+ foreach (var split in ss) {
+ if (!split.IsFree) {
+ builder.Add(AssertNS(split.E.tok, split.E, "condition in assert expression might not hold"));
+ }
+ }
+ builder.Add(new Bpl.AssumeCmd(e.tok, etran.TrExpr(e.Guard)));
+ }
+ } else {
+ builder.Add(new Bpl.AssumeCmd(e.tok, etran.TrExpr(e.Guard)));
+ }
+ CheckWellformed(e.Body, options, locals, builder, etran);
+
+ } else if (expr is ITEExpr) {
+ ITEExpr e = (ITEExpr)expr;
+ CheckWellformed(e.Test, options, locals, builder, etran);
+ Bpl.StmtListBuilder bThen = new Bpl.StmtListBuilder();
+ Bpl.StmtListBuilder bElse = new Bpl.StmtListBuilder();
+ CheckWellformed(e.Thn, options, locals, bThen, etran);
+ CheckWellformed(e.Els, options, locals, bElse, etran);
+ builder.Add(new Bpl.IfCmd(expr.tok, etran.TrExpr(e.Test), bThen.Collect(expr.tok), null, bElse.Collect(expr.tok)));
+
+ } else if (expr is MatchExpr) {
+ MatchExpr me = (MatchExpr)expr;
+ CheckWellformed(me.Source, options, locals, builder, etran);
+ 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));
+ StmtList els = elsBldr.Collect(expr.tok);
+ foreach (var missingCtor in me.MissingCases) {
+ // havoc all bound variables
+ var b = new Bpl.StmtListBuilder();
+ VariableSeq newLocals = new VariableSeq();
+ Bpl.Expr r = CtorInvocation(me.tok, missingCtor, etran, newLocals, b);
+ locals.AddRange(newLocals);
+
+ if (newLocals.Length != 0) {
+ Bpl.IdentifierExprSeq havocIds = new Bpl.IdentifierExprSeq();
+ foreach (Variable local in newLocals) {
+ havocIds.Add(new Bpl.IdentifierExpr(local.tok, local));
+ }
+ builder.Add(new Bpl.HavocCmd(me.tok, havocIds));
+ }
+ b.Add(Assert(me.tok, Bpl.Expr.False, "missing case in case statement: " + missingCtor.Name));
+
+ Bpl.Expr guard = Bpl.Expr.Eq(src, r);
+ ifCmd = new Bpl.IfCmd(me.tok, guard, b.Collect(me.tok), ifCmd, els);
+ els = null;
+ }
+ for (int i = me.Cases.Count; 0 <= --i; ) {
+ MatchCaseExpr mc = me.Cases[i];
+ Bpl.StmtListBuilder b = new Bpl.StmtListBuilder();
+ Bpl.Expr ct = CtorInvocation(mc, etran, locals, b);
+ // generate: if (src == ctor(args)) { assume args-is-well-typed; mc.Body is well-formed; assume Result == TrExpr(case); } else ...
+ CheckWellformedWithResult(mc.Body, options, result, resultType, locals, b, etran);
+ ifCmd = new Bpl.IfCmd(mc.tok, Bpl.Expr.Eq(src, ct), b.Collect(mc.tok), ifCmd, els);
+ els = null;
+ }
+ builder.Add(ifCmd);
+ result = null;
+
+ } else if (expr is ConcreteSyntaxExpression) {
+ var e = (ConcreteSyntaxExpression)expr;
+ CheckWellformedWithResult(e.ResolvedExpression, options, result, resultType, locals, builder, etran);
+ result = null;
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
+ }
+
+ if (result != null) {
+ 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)));
+ }
+ }
+
+ /// <summary>
+ /// Returns true if it is known how to meaningfully compare the type's inhabitants.
+ /// </summary>
+ static bool IsOrdered(Type t) {
+ return !t.IsTypeParameter && !t.IsCoDatatype;
+ }
+
+ public static List<Expression> MethodDecreasesWithDefault(ICodeContext m, out bool inferredDecreases) {
+ Contract.Requires(m != null);
+
+ inferredDecreases = false;
+ List<Expression> decr = m.Decreases.Expressions;
+ if (decr.Count == 0) {
+ decr = new List<Expression>();
+ foreach (Formal p in m.Ins) {
+ if (IsOrdered(p.Type)) {
+ IdentifierExpr ie = new IdentifierExpr(p.tok, p.Name);
+ ie.Var = p; ie.Type = ie.Var.Type; // resolve it here
+ decr.Add(ie); // use the method's first parameter instead
+ }
+ }
+ inferredDecreases = true;
+ } else if (m is IteratorDecl) {
+ inferredDecreases = ((IteratorDecl)m).InferredDecreases;
+ }
+ return decr;
+ }
+
+ List<Expression> FunctionDecreasesWithDefault(Function f, out bool inferredDecreases) {
+ Contract.Requires(f != null);
+
+ inferredDecreases = false;
+ List<Expression> decr = f.Decreases.Expressions;
+ if (decr.Count == 0) {
+ decr = new List<Expression>();
+ if (f.Reads.Count == 0) {
+ foreach (Formal p in f.Formals) {
+ if (IsOrdered(p.Type)) {
+ IdentifierExpr ie = new IdentifierExpr(p.tok, p.UniqueName);
+ ie.Var = p; ie.Type = ie.Var.Type; // resolve it here
+ decr.Add(ie); // use the function's first parameter instead
+ }
+ }
+ inferredDecreases = true;
+ } else {
+ decr.Add(FrameToObjectSet(f.Reads)); // use its reads clause instead
+ }
+ }
+ return decr;
+ }
+
+ List<Expression> LoopDecreasesWithDefault(IToken tok, Expression Guard, List<Expression> Decreases, out bool inferredDecreases) {
+ Contract.Requires(tok != null);
+ Contract.Requires(Decreases != null);
+
+ List<Expression> theDecreases = Decreases;
+ inferredDecreases = false;
+ if (theDecreases.Count == 0 && Guard != null) {
+ theDecreases = new List<Expression>();
+ Expression prefix = null;
+ foreach (Expression guardConjunct in Conjuncts(Guard)) {
+ Expression guess = null;
+ BinaryExpr bin = guardConjunct as BinaryExpr;
+ if (bin != null) {
+ switch (bin.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.Lt:
+ case BinaryExpr.ResolvedOpcode.Le:
+ // for A < B and A <= B, use the decreases B - A
+ guess = CreateIntSub(tok, bin.E1, bin.E0);
+ break;
+ case BinaryExpr.ResolvedOpcode.Ge:
+ case BinaryExpr.ResolvedOpcode.Gt:
+ // for A >= B and A > B, use the decreases A - B
+ guess = CreateIntSub(tok, bin.E0, bin.E1);
+ break;
+ case BinaryExpr.ResolvedOpcode.NeqCommon:
+ if (bin.E0.Type is IntType) {
+ // for A != B where A and B are integers, use the absolute difference between A and B (that is: if 0 <= A-B then A-B else B-A)
+ Expression AminusB = CreateIntSub(tok, bin.E0, bin.E1);
+ Expression BminusA = CreateIntSub(tok, bin.E1, bin.E0);
+ Expression zero = CreateIntLiteral(tok, 0);
+ BinaryExpr test = new BinaryExpr(tok, BinaryExpr.Opcode.Le, zero, AminusB);
+ test.ResolvedOp = BinaryExpr.ResolvedOpcode.Le; // resolve here
+ test.Type = Type.Bool; // resolve here
+ guess = CreateIntITE(tok, test, AminusB, BminusA);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (guess != null) {
+ if (prefix != null) {
+ // Make the following guess: if prefix then guess else -1
+ Expression negativeOne = CreateIntLiteral(tok, -1);
+ guess = CreateIntITE(tok, prefix, guess, negativeOne);
+ }
+ theDecreases.Add(guess);
+ inferredDecreases = true;
+ break; // ignore any further conjuncts
+ }
+ if (prefix == null) {
+ prefix = guardConjunct;
+ } else {
+ prefix = DafnyAnd(prefix, guardConjunct);
+ }
+ }
+ }
+ if (yieldCountVariable != null) {
+ var decr = new List<Expression>();
+ decr.Add(new BoogieWrapper(new Bpl.IdentifierExpr(tok, yieldCountVariable), new EverIncreasingType()));
+ decr.AddRange(theDecreases);
+ theDecreases = decr;
+ }
+ return theDecreases;
+ }
+
+ /// <summary>
+ /// This Dafny type, which exists only during translation of Dafny into Boogie, represents
+ /// an integer component in a "decreases" clause whose order is (\lambda x,y :: x GREATER y),
+ /// not the usual (\lambda x,y :: x LESS y AND 0 ATMOST y).
+ /// </summary>
+ public class EverIncreasingType : BasicType
+ {
+ [Pure]
+ public override string TypeName(ModuleDefinition context) {
+ return "_increasingInt";
+ }
+ }
+
+ Expression FrameToObjectSet(List<FrameExpression> fexprs) {
+ Contract.Requires(fexprs != null);
+ Contract.Ensures(Contract.Result<Expression>() != null);
+
+ List<Expression> sets = new List<Expression>();
+ List<Expression> singletons = null;
+ foreach (FrameExpression fe in fexprs) {
+ Contract.Assert(fe != null);
+ if (fe.E is WildcardExpr) {
+ // drop wildcards altogether
+ } else {
+ Expression e = fe.E; // keep only fe.E, drop any fe.Field designation
+ Contract.Assert(e.Type != null); // should have been resolved already
+ if (e.Type.IsRefType) {
+ // e represents a singleton set
+ if (singletons == null) {
+ singletons = new List<Expression>();
+ }
+ singletons.Add(e);
+ } else if (e.Type is SeqType) {
+ // e represents a sequence
+ // Add: set x :: x in e
+ var bv = new BoundVar(e.tok, "_s2s_" + otherTmpVarCount, ((SeqType)e.Type).Arg);
+ otherTmpVarCount++; // use this counter, but for a Dafny name (the idea being that the number and the initial "_" in the name might avoid name conflicts)
+ var bvIE = new IdentifierExpr(e.tok, bv.Name);
+ bvIE.Var = bv; // resolve here
+ bvIE.Type = bv.Type; // resolve here
+ 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);
+ s.Type = new SetType(new ObjectType()); // resolve here
+ sets.Add(s);
+ } else {
+ // e is already a set
+ Contract.Assert(e.Type is SetType);
+ sets.Add(e);
+ }
+ }
+ }
+ if (singletons != null) {
+ Expression display = new SetDisplayExpr(singletons[0].tok, singletons);
+ display.Type = new SetType(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
+ 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
+ s = union;
+ }
+ return s;
+ }
+ }
+
+ void CloneVariableAsBoundVar(IToken tok, IVariable iv, string prefix, out BoundVar bv, out IdentifierExpr ie) {
+ Contract.Requires(tok != null);
+ Contract.Requires(iv != null);
+ Contract.Requires(prefix != null);
+ Contract.Ensures(Contract.ValueAtReturn(out bv) != null);
+ Contract.Ensures(Contract.ValueAtReturn(out ie) != null);
+
+ bv = new BoundVar(tok, prefix + otherTmpVarCount, iv.Type);
+ otherTmpVarCount++; // use this counter, but for a Dafny name (the idea being that the number and the initial "_" in the name might avoid name conflicts)
+ ie = new IdentifierExpr(tok, bv.Name);
+ ie.Var = bv; // resolve here
+ ie.Type = bv.Type; // resolve here
+ }
+
+ Bpl.Constant GetClass(TopLevelDecl cl)
+ {
+ Contract.Requires(cl != null);
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.Result<Bpl.Constant>() != null);
+
+ Bpl.Constant cc;
+ if (classes.TryGetValue(cl, out cc)) {
+ Contract.Assert(cc != null);
+ } else {
+ cc = new Bpl.Constant(cl.tok, new Bpl.TypedIdent(cl.tok, "class." + cl.FullCompileName, predef.ClassNameType), !cl.Module.IsAbstract);
+ classes.Add(cl, cc);
+ }
+ return cc;
+ }
+
+ Bpl.Constant GetFieldNameFamily(string n) {
+ Contract.Requires(n != null);
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.Result<Bpl.Constant>() != null);
+ Bpl.Constant cc;
+ if (fieldConstants.TryGetValue(n, out cc)) {
+ Contract.Assert(cc != null);
+ } else {
+ cc = new Bpl.Constant(Token.NoToken, new Bpl.TypedIdent(Token.NoToken, "field$" + n, predef.NameFamilyType), true);
+ fieldConstants.Add(n, cc);
+ }
+ return cc;
+ }
+
+ Bpl.Expr GetTypeExpr(IToken tok, Type type)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(type != null);
+ Contract.Requires(predef != null);
+ while (true) {
+ TypeProxy tp = type as TypeProxy;
+ if (tp == null) {
+ break;
+ } else if (tp.T == null) {
+ // unresolved proxy
+ // TODO: what to do here?
+ return null;
+ } else {
+ type = tp.T;
+ }
+ }
+
+ if (type is BoolType) {
+ return new Bpl.IdentifierExpr(tok, "class._System.bool", predef.ClassNameType);
+ } else if (type is IntType) {
+ return new Bpl.IdentifierExpr(tok, "class._System.int", predef.ClassNameType);
+ } else if (type is ObjectType) {
+ return new Bpl.IdentifierExpr(tok, GetClass(program.BuiltIns.ObjectDecl));
+ } else if (type is CollectionType) {
+ CollectionType ct = (CollectionType)type;
+ Bpl.Expr a = GetTypeExpr(tok, ct.Arg);
+ if (a == null) {
+ return null;
+ }
+ Bpl.Expr t = new Bpl.IdentifierExpr(tok,
+ ct is SetType ? "class._System.set" :
+ ct is SeqType ? "class._System.seq" :
+ "class._System.multiset",
+ predef.ClassNameType);
+ return FunctionCall(tok, BuiltinFunction.TypeTuple, null, t, a);
+ } else {
+ UserDefinedType ct = (UserDefinedType)type;
+ if (ct.ResolvedClass == null) {
+ return null; // TODO: what to do here?
+ }
+ Bpl.Expr t = new Bpl.IdentifierExpr(tok, GetClass(ct.ResolvedClass));
+ foreach (Type arg in ct.TypeArgs) {
+ Bpl.Expr a = GetTypeExpr(tok, arg);
+ if (a == null) {
+ return null;
+ }
+ t = FunctionCall(tok, BuiltinFunction.TypeTuple, null, t, a);
+ }
+ return t;
+ }
+ }
+
+ Bpl.Constant GetField(Field f)
+ {
+ Contract.Requires(f != null && f.IsMutable);
+ Contract.Requires(sink != null && predef != null);
+ Contract.Ensures(Contract.Result<Bpl.Constant>() != null);
+
+ Bpl.Constant fc;
+ if (fields.TryGetValue(f, out fc)) {
+ Contract.Assert(fc != null);
+ } else {
+ // const f: Field ty;
+ Bpl.Type ty = predef.FieldName(f.tok, TrType(f.Type));
+ fc = new Bpl.Constant(f.tok, new Bpl.TypedIdent(f.tok, f.FullCompileName, ty), false);
+ fields.Add(f, fc);
+ // axiom FDim(f) == 0 && FieldOfDecl(C, name) == f;
+ Bpl.Expr fdim = Bpl.Expr.Eq(FunctionCall(f.tok, BuiltinFunction.FDim, ty, Bpl.Expr.Ident(fc)), Bpl.Expr.Literal(0));
+ Bpl.Expr declType = Bpl.Expr.Eq(FunctionCall(f.tok, BuiltinFunction.FieldOfDecl, ty, new Bpl.IdentifierExpr(f.tok, GetClass(cce.NonNull(f.EnclosingClass))), new Bpl.IdentifierExpr(f.tok, GetFieldNameFamily(f.Name))), Bpl.Expr.Ident(fc));
+ Bpl.Axiom ax = new Bpl.Axiom(f.tok, Bpl.Expr.And(fdim, declType));
+ sink.TopLevelDeclarations.Add(ax);
+ }
+ return fc;
+ }
+
+ Bpl.Function GetReadonlyField(Field f)
+ {
+ Contract.Requires(f != null && !f.IsMutable);
+ Contract.Requires(sink != null && predef != null);
+ Contract.Ensures(Contract.Result<Bpl.Function>() != null);
+
+ Bpl.Function ff;
+ if (fieldFunctions.TryGetValue(f, out ff)) {
+ Contract.Assert(ff != null);
+ } else {
+ if (f.EnclosingClass is ArrayClassDecl && f.Name == "Length") { // link directly to the function in the prelude.
+ fieldFunctions.Add(f, predef.ArrayLength);
+ return predef.ArrayLength;
+ }
+ // function f(Ref): ty;
+ Bpl.Type ty = TrType(f.Type);
+ Bpl.VariableSeq args = new Bpl.VariableSeq();
+ Bpl.Type receiverType = f.EnclosingClass is ClassDecl ? predef.RefType : predef.DatatypeType;
+ args.Add(new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "this", receiverType), true));
+ Bpl.Formal result = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, Bpl.TypedIdent.NoName, ty), false);
+ ff = new Bpl.Function(f.tok, f.FullCompileName, args, result);
+ fieldFunctions.Add(f, ff);
+ // treat certain fields specially
+ if (f.EnclosingClass is ArrayClassDecl) {
+ // add non-negative-range axioms for array Length fields
+ // 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 Bpl.ExprSeq(o)));
+ Bpl.Expr qq = new Bpl.ForallExpr(f.tok, new Bpl.VariableSeq(oVar), body);
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, qq));
+ }
+ }
+ return ff;
+ }
+
+ Bpl.Expr GetField(FieldSelectExpr fse)
+ {
+ Contract.Requires(fse != null);
+ Contract.Requires(fse.Field != null && fse.Field.IsMutable);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ return new Bpl.IdentifierExpr(fse.tok, GetField(fse.Field));
+ }
+
+ /// <summary>
+ /// This method is expected to be called just once for each function in the program.
+ /// </summary>
+ void AddFunction(Function f)
+ {
+ Contract.Requires(f != null);
+ Contract.Requires(predef != null && sink != null);
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
+ Bpl.VariableSeq args = new Bpl.VariableSeq();
+ args.Add(new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "$heap", predef.HeapType), true));
+ if (!f.IsStatic) {
+ args.Add(new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType), true));
+ }
+ foreach (Formal p in f.Formals) {
+ args.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)), true));
+ }
+ Bpl.Formal res = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, Bpl.TypedIdent.NoName, TrType(f.ResultType)), false);
+ Bpl.Function func = new Bpl.Function(f.tok, f.FullCompileName, typeParams, args, res);
+ sink.TopLevelDeclarations.Add(func);
+
+ if (f.IsRecursive) {
+ sink.TopLevelDeclarations.Add(new Bpl.Function(f.tok, FunctionName(f, 0), args, res));
+ sink.TopLevelDeclarations.Add(new Bpl.Function(f.tok, FunctionName(f, 2), args, res));
+ }
+
+ res = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, Bpl.TypedIdent.NoName, Bpl.Type.Bool), false);
+ Bpl.Function canCallF = new Bpl.Function(f.tok, f.FullCompileName + "#canCall", args, res);
+ sink.TopLevelDeclarations.Add(canCallF);
+ }
+
+ /// <summary>
+ /// This method is expected to be called at most 4 times for each procedure in the program:
+ /// * once with kind==0, which says to create a procedure for the wellformedness check of the
+ /// method's specification
+ /// * once with kind==1, which says to create the ordinary procedure for the method, always
+ /// suitable for inter-module callers, and for non-refinement methods also suitable for
+ /// the implementation and intra-module callers of the method
+ /// * possibly once with kind==2 (allowed only if isRefinementMethod), which says to create
+ /// a procedure suitable for intra-module callers of a refinement method
+ /// * possibly once with kind==3 (allowed only if isRefinementMethod), which says to create
+ /// a procedure suitable for the implementation of a refinement method
+ /// </summary>
+ Bpl.Procedure AddMethod(Method m, int kind, bool isRefinementMethod)
+ {
+ Contract.Requires(m != null);
+ Contract.Requires(m.EnclosingClass != null);
+ Contract.Requires(0 <= kind && kind < 4);
+ Contract.Requires(isRefinementMethod || kind < 2);
+ Contract.Requires(predef != null);
+ Contract.Requires(currentModule == null && codeContext == null);
+ Contract.Ensures(currentModule == null && codeContext == null);
+ Contract.Ensures(Contract.Result<Bpl.Procedure>() != null);
+
+ currentModule = m.EnclosingClass.Module;
+ codeContext = m;
+
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, m.tok);
+
+ Bpl.VariableSeq inParams, outParams;
+ GenerateMethodParameters(m.tok, m, etran, out inParams, out outParams);
+
+ Bpl.RequiresSeq req = new Bpl.RequiresSeq();
+ Bpl.IdentifierExprSeq mod = new Bpl.IdentifierExprSeq();
+ Bpl.EnsuresSeq ens = new Bpl.EnsuresSeq();
+ if (kind == 0 || (kind == 1 && !isRefinementMethod) || kind == 3) { // the other cases have no need for a free precondition
+ // free requires mh == ModuleContextHeight && InMethodContext;
+ Bpl.Expr context = Bpl.Expr.And(
+ Bpl.Expr.Eq(Bpl.Expr.Literal(m.EnclosingClass.Module.Height), etran.ModuleContextHeight()),
+ etran.InMethodContext());
+ req.Add(Requires(m.tok, true, context, null, null));
+ }
+ mod.Add((Bpl.IdentifierExpr/*TODO: this cast is rather dubious*/)etran.HeapExpr);
+ mod.Add(etran.Tick());
+
+ if (kind != 0) {
+ string comment = "user-defined preconditions";
+ foreach (MaybeFreeExpression p in m.Req) {
+ if ((p.IsFree && !DafnyOptions.O.DisallowSoundnessCheating) || kind == 3) { // kind==3 never has callers, so no reason to bother splitting
+ req.Add(Requires(p.E.tok, true, etran.TrExpr(p.E), null, comment));
+ } else {
+ bool splitHappened; // we actually don't care
+ foreach (var s in TrSplitExpr(p.E, etran, out splitHappened)) {
+ if (kind == 2 && RefinementToken.IsInherited(s.E.tok, currentModule)) {
+ // this precondition was inherited into this module, so just ignore it
+ } else {
+ req.Add(Requires(s.E.tok, s.IsFree, s.E, null, null));
+ // the free here is not linked to the free on the original expression (this is free things generated in the splitting.)
+ }
+ }
+ }
+ comment = null;
+ }
+ comment = "user-defined postconditions";
+ foreach (MaybeFreeExpression p in m.Ens) {
+ if ((p.IsFree && !DafnyOptions.O.DisallowSoundnessCheating) || (kind == 1 && isRefinementMethod) || kind == 2) { // for refinement methods, kind==1 has no implementations, and kind==2 never has implementations
+ ens.Add(Ensures(p.E.tok, true, etran.TrExpr(p.E), null, comment));
+ } else {
+ bool splitHappened; // we actually don't care
+ foreach (var s in TrSplitExpr(p.E, etran, out splitHappened)) {
+ if (kind == 3 && RefinementToken.IsInherited(s.E.tok, currentModule)) {
+ // this postcondition was inherited into this module, so just ignore it
+ } else {
+ ens.Add(Ensures(s.E.tok, s.IsFree, s.E, null, null));
+ }
+ }
+ }
+ comment = null;
+ }
+ foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(m.tok, m.Mod.Expressions, etran.Old, etran, etran.Old)) {
+ ens.Add(Ensures(tri.tok, tri.IsFree, tri.Expr, tri.ErrorMessage, tri.Comment));
+ }
+ }
+
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(m.TypeArgs);
+ string name;
+ switch (kind) {
+ case 0: name = "CheckWellformed$$" + m.FullCompileName; break;
+ case 1: name = m.FullCompileName; break;
+ case 2: name = string.Format("RefinementCall_{0}$${1}", m.EnclosingClass.Module.Name, m.FullCompileName); break;
+ case 3: name = string.Format("RefinementImpl_{0}$${1}", m.EnclosingClass.Module.Name, m.FullCompileName); break;
+ default: Contract.Assert(false); throw new cce.UnreachableException(); // unexpected kind
+ }
+ Bpl.Procedure proc = new Bpl.Procedure(m.tok, name, typeParams, inParams, outParams, req, mod, ens);
+
+ currentModule = null;
+ codeContext = null;
+
+ return proc;
+ }
+
+ private void AddMethodRefinementCheck(MethodCheck methodCheck) {
+
+ // First, we generate the declaration of the procedure. This procedure has the same
+ // pre and post conditions as the refined method. The body implementation will be a call
+ // to the refining method.
+ Method m = methodCheck.Refined;
+ currentModule = m.EnclosingClass.Module;
+ codeContext = m;
+
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, m.tok);
+
+ Bpl.VariableSeq inParams, outParams;
+ GenerateMethodParameters(m.tok, m, etran, out inParams, out outParams);
+
+ Bpl.RequiresSeq req = new Bpl.RequiresSeq();
+ Bpl.IdentifierExprSeq mod = new Bpl.IdentifierExprSeq();
+ Bpl.EnsuresSeq ens = new Bpl.EnsuresSeq();
+
+ Bpl.Expr context = Bpl.Expr.And(
+ Bpl.Expr.Eq(Bpl.Expr.Literal(m.EnclosingClass.Module.Height), etran.ModuleContextHeight()),
+ etran.InMethodContext());
+ req.Add(Requires(m.tok, true, context, null, null));
+
+ mod.Add((Bpl.IdentifierExpr/*TODO: this cast is rather dubious*/)etran.HeapExpr);
+ mod.Add(etran.Tick());
+
+ foreach (MaybeFreeExpression p in m.Req) {
+ if ((p.IsFree && !DafnyOptions.O.DisallowSoundnessCheating)) {
+ req.Add(Requires(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, out splitHappened)) {
+ req.Add(Requires(s.E.tok, s.IsFree, s.E, null, null));
+ }
+ }
+ }
+ foreach (MaybeFreeExpression p in m.Ens) {
+ bool splitHappened; // we actually don't care
+ foreach (var s in TrSplitExpr(p.E, etran, out splitHappened)) {
+ ens.Add(Ensures(s.E.tok, s.IsFree, s.E, "Error: postcondition of refined method may be violated", null));
+ }
+ }
+ foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(m.tok, m.Mod.Expressions, etran.Old, etran, etran.Old)) {
+ ens.Add(Ensures(tri.tok, tri.IsFree, tri.Expr, tri.ErrorMessage, tri.Comment));
+ }
+
+ // Generate procedure, and then add it to the sink
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(m.TypeArgs);
+ string name = "CheckRefinement$$" + m.FullCompileName + "$" + methodCheck.Refining.FullCompileName;
+ Bpl.Procedure proc = new Bpl.Procedure(m.tok, name, typeParams, inParams, outParams, req, mod, new Bpl.EnsuresSeq());
+
+ sink.TopLevelDeclarations.Add(proc);
+
+
+ // Generate the implementation
+ typeParams = TrTypeParamDecls(m.TypeArgs);
+ inParams = Bpl.Formal.StripWhereClauses(proc.InParams);
+ outParams = Bpl.Formal.StripWhereClauses(proc.OutParams);
+
+ Bpl.StmtListBuilder builder = new Bpl.StmtListBuilder();
+ Bpl.VariableSeq localVariables = new Bpl.VariableSeq();
+ GenerateImplPrelude(m, inParams, outParams, builder, localVariables);
+
+ // Generate the call to the refining method
+ Method method = methodCheck.Refining;
+ Expression receiver = new ThisExpr(Token.NoToken);
+ Bpl.ExprSeq ins = new Bpl.ExprSeq();
+ if (!method.IsStatic) {
+ ins.Add(etran.TrExpr(receiver));
+ }
+
+ // Ideally, the modifies and decreases checks would be done after the precondition check,
+ // but Boogie doesn't give us a hook for that. So, we set up our own local variables here to
+ // store the actual parameters.
+ // Create a local variable for each formal parameter, and assign each actual parameter to the corresponding local
+ Dictionary<IVariable, Expression> substMap = new Dictionary<IVariable, Expression>();
+ for (int i = 0; i < method.Ins.Count; i++) {
+ Formal p = method.Ins[i];
+ VarDecl local = new VarDecl(p.tok, p.Name + "#", p.Type, p.IsGhost);
+ local.type = local.OptionalType; // resolve local here
+ IdentifierExpr ie = new IdentifierExpr(local.Tok, local.UniqueName);
+ ie.Var = local; ie.Type = ie.Var.Type; // resolve ie here
+ substMap.Add(p, ie);
+ localVariables.Add(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.UniqueName, TrType(local.Type))));
+
+ Bpl.IdentifierExpr param = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified?
+ var bActual = new Bpl.IdentifierExpr(Token.NoToken, m.Ins[i].UniqueName, TrType(m.Ins[i].Type));
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(p.tok, param, etran.CondApplyUnbox(Token.NoToken, bActual, cce.NonNull( m.Ins[i].Type),p.Type));
+ builder.Add(cmd);
+ ins.Add(param);
+ }
+
+ // Check modifies clause of a subcall is a subset of the current frame.
+ CheckFrameSubset(method.tok, method.Mod.Expressions, receiver, substMap, etran, builder, "call may modify locations not in the refined method's modifies clause", null);
+
+ // Create variables to hold the output parameters of the call, so that appropriate unboxes can be introduced.
+ Bpl.IdentifierExprSeq outs = new Bpl.IdentifierExprSeq();
+ List<Bpl.IdentifierExpr> tmpOuts = new List<Bpl.IdentifierExpr>();
+ for (int i = 0; i < m.Outs.Count; i++) {
+ var bLhs = m.Outs[i];
+ if (!ExpressionTranslator.ModeledAsBoxType(method.Outs[i].Type) && ExpressionTranslator.ModeledAsBoxType(bLhs.Type)) {
+ // we need an Box
+ Bpl.LocalVariable var = new Bpl.LocalVariable(bLhs.tok, new Bpl.TypedIdent(bLhs.tok, "$tmp##" + otherTmpVarCount, TrType(method.Outs[i].Type)));
+ otherTmpVarCount++;
+ localVariables.Add(var);
+ Bpl.IdentifierExpr varIdE = new Bpl.IdentifierExpr(bLhs.tok, var.Name, TrType(method.Outs[i].Type));
+ tmpOuts.Add(varIdE);
+ outs.Add(varIdE);
+ } else {
+ tmpOuts.Add(null);
+ outs.Add(new Bpl.IdentifierExpr(Token.NoToken, bLhs.UniqueName, TrType(bLhs.Type)));
+ }
+ }
+
+ // Make the call
+ Bpl.CallCmd call = new Bpl.CallCmd(method.tok, method.FullCompileName, ins, outs);
+ builder.Add(call);
+
+ for (int i = 0; i < m.Outs.Count; i++) {
+ var bLhs = m.Outs[i];
+ var tmpVarIdE = tmpOuts[i];
+ if (tmpVarIdE != null) {
+ // e := Box(tmpVar);
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(Token.NoToken, new Bpl.IdentifierExpr(Token.NoToken, bLhs.UniqueName, TrType(bLhs.Type)), FunctionCall(Token.NoToken, BuiltinFunction.Box, null, tmpVarIdE));
+ builder.Add(cmd);
+ }
+ }
+
+ foreach (MaybeFreeExpression p in m.Ens) {
+ bool splitHappened; // we actually don't care
+ foreach (var s in TrSplitExpr(p.E, etran, out splitHappened)) {
+ var assert = new Bpl.AssertCmd(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);
+ }
+ }
+ Bpl.StmtList stmts = builder.Collect(method.tok); // this token is for the implict return, which should be for the refining method,
+ // as this is where the error is.
+
+ Bpl.Implementation impl = new Bpl.Implementation(m.tok, proc.Name,
+ typeParams, inParams, outParams,
+ localVariables, stmts, etran.TrAttributes(m.Attributes, null));
+ sink.TopLevelDeclarations.Add(impl);
+
+ // Clean up
+ currentModule = null;
+ codeContext = null;
+ otherTmpVarCount = 0;
+ _tmpIEs.Clear();
+ }
+
+ private static QKeyValue ErrorMessageAttribute(IToken t, string error) {
+ var l = new List<object>(1);
+ l.Add(error);
+ return new QKeyValue(t, "msg", l, null);
+ }
+ private static QKeyValue ErrorMessageAttribute(IToken t, string error, QKeyValue qv) {
+ var l = new List<object>(1);
+ l.Add(error);
+ return new QKeyValue(t, "msg", l, qv);
+ }
+
+ private void AddFunctionRefinementCheck(FunctionCheck functionCheck) {
+ Contract.Requires(sink != null && predef != null);
+ Contract.Requires(currentModule == null);
+ Contract.Ensures(currentModule == null);
+
+ Function f = functionCheck.Refined;
+ Function function = functionCheck.Refining;
+ currentModule = function.EnclosingClass.Module;
+
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, f.tok);
+ // parameters of the procedure
+ Bpl.VariableSeq inParams = new Bpl.VariableSeq();
+ if (!f.IsStatic) {
+ Bpl.Expr wh = Bpl.Expr.And(
+ Bpl.Expr.Neq(new Bpl.IdentifierExpr(f.tok, "this", predef.RefType), predef.Null),
+ etran.GoodRef(f.tok, new Bpl.IdentifierExpr(f.tok, "this", predef.RefType), Resolver.GetReceiverType(f.tok, f)));
+ Bpl.Formal thVar = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType, wh), true);
+ inParams.Add(thVar);
+ }
+ foreach (Formal p in f.Formals) {
+ Bpl.Type varType = TrType(p.Type);
+ Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.UniqueName, varType), p.Type, etran);
+ inParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, varType, wh), true));
+ }
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
+ // the procedure itself
+ Bpl.RequiresSeq req = new Bpl.RequiresSeq();
+ // free requires mh == ModuleContextHeight && fh == FunctionContextHeight;
+ ModuleDefinition mod = function.EnclosingClass.Module;
+ Bpl.Expr context = Bpl.Expr.And(
+ Bpl.Expr.Eq(Bpl.Expr.Literal(mod.Height), etran.ModuleContextHeight()),
+ Bpl.Expr.Eq(Bpl.Expr.Literal(mod.CallGraph.GetSCCRepresentativeId(function)), etran.FunctionContextHeight()));
+ req.Add(Requires(f.tok, true, context, null, null));
+
+ foreach (Expression p in f.Req) {
+ req.Add(Requires(p.tok, true, etran.TrExpr(p), null, null));
+ }
+
+ // check that postconditions hold
+ var ens = new Bpl.EnsuresSeq();
+ foreach (Expression p in f.Ens) {
+ bool splitHappened; // we actually don't care
+ foreach (var s in TrSplitExpr(p, etran, out splitHappened)) {
+ if (!s.IsFree) {
+ ens.Add(Ensures(s.E.tok, s.IsFree, s.E, null, null));
+ }
+ }
+ }
+ Bpl.Procedure proc = new Bpl.Procedure(function.tok, "CheckIsRefinement$$" + f.FullCompileName + "$" + functionCheck.Refining.FullCompileName, typeParams, inParams, new Bpl.VariableSeq(),
+ req, new Bpl.IdentifierExprSeq(), ens, etran.TrAttributes(f.Attributes, null));
+ sink.TopLevelDeclarations.Add(proc);
+
+ VariableSeq implInParams = Bpl.Formal.StripWhereClauses(proc.InParams);
+ Bpl.VariableSeq locals = new Bpl.VariableSeq();
+ Bpl.StmtListBuilder builder = new Bpl.StmtListBuilder();
+
+ Bpl.FunctionCall funcOriginal = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullCompileName, TrType(f.ResultType)));
+ Bpl.FunctionCall funcRefining = new Bpl.FunctionCall(new Bpl.IdentifierExpr(functionCheck.Refining.tok, functionCheck.Refining.FullCompileName, TrType(f.ResultType)));
+ Bpl.ExprSeq args = new Bpl.ExprSeq();
+ args.Add(etran.HeapExpr);
+ foreach (Variable p in implInParams) {
+ args.Add(new Bpl.IdentifierExpr(f.tok, p));
+ }
+ Bpl.Expr funcAppl = new Bpl.NAryExpr(f.tok, funcOriginal, args);
+ Bpl.Expr funcAppl2 = new Bpl.NAryExpr(f.tok, funcRefining, args);
+
+ Dictionary<IVariable, Expression> substMap = new Dictionary<IVariable, Expression>();
+ for (int i = 0; i < function.Formals.Count; i++) {
+ Formal p = function.Formals[i];
+ IdentifierExpr ie = new IdentifierExpr(f.Formals[i].tok, f.Formals[i].UniqueName);
+ ie.Var = f.Formals[i]; ie.Type = ie.Var.Type; // resolve ie here
+ substMap.Add(p, ie);
+ }
+ // add canCall
+ Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(Token.NoToken, function.FullCompileName + "#canCall", Bpl.Type.Bool);
+ Bpl.Expr canCall = new Bpl.NAryExpr(Token.NoToken, new Bpl.FunctionCall(canCallFuncID), args);
+ builder.Add(new AssumeCmd(function.tok, canCall));
+
+ // check that the preconditions for the call hold
+ foreach (Expression p in function.Req) {
+ Expression precond = Substitute(p, new ThisExpr(Token.NoToken), substMap);
+ var assert = new AssertCmd(p.tok, etran.TrExpr(precond));
+ assert.ErrorData = "Error: the refining function is not allowed to add preconditions";
+ builder.Add(assert);
+ }
+ builder.Add(new AssumeCmd(f.tok, Bpl.Expr.Eq(funcAppl, funcAppl2)));
+
+ foreach (Expression p in f.Ens) {
+ var s = new FunctionCallSubstituter(new ThisExpr(Token.NoToken), substMap, f, function);
+ Expression precond = s.Substitute(p);
+ var assert = new AssertCmd(p.tok, etran.TrExpr(precond));
+ assert.ErrorData = "Error: A postcondition of the refined function may not hold";
+ builder.Add(assert);
+ }
+ Bpl.Implementation impl = new Bpl.Implementation(function.tok, proc.Name,
+ typeParams, implInParams, new Bpl.VariableSeq(),
+ locals, builder.Collect(function.tok));
+ sink.TopLevelDeclarations.Add(impl);
+
+ Contract.Assert(currentModule == function.EnclosingClass.Module);
+ currentModule = null;
+ }
+
+ private void GenerateMethodParameters(IToken tok, ICodeContext m, ExpressionTranslator etran, out Bpl.VariableSeq inParams, out Bpl.VariableSeq outParams) {
+ GenerateMethodParametersChoose(tok, m, !m.IsStatic, true, true, etran, out inParams, out outParams);
+ }
+
+ private void GenerateMethodParametersChoose(IToken tok, ICodeContext m, bool includeReceiver, bool includeInParams, bool includeOutParams,
+ ExpressionTranslator etran, out Bpl.VariableSeq inParams, out Bpl.VariableSeq outParams) {
+ inParams = new Bpl.VariableSeq();
+ outParams = new Bpl.VariableSeq();
+ if (includeReceiver) {
+ var receiverType = m is MemberDecl ? Resolver.GetReceiverType(tok, (MemberDecl)m) : Resolver.GetThisType(tok, (IteratorDecl)m);
+ Bpl.Expr wh = Bpl.Expr.And(
+ Bpl.Expr.Neq(new Bpl.IdentifierExpr(tok, "this", predef.RefType), predef.Null),
+ etran.GoodRef(tok, new Bpl.IdentifierExpr(tok, "this", predef.RefType), receiverType));
+ Bpl.Formal thVar = new Bpl.Formal(tok, new Bpl.TypedIdent(tok, "this", predef.RefType, wh), true);
+ inParams.Add(thVar);
+ }
+ if (includeInParams) {
+ foreach (Formal p in m.Ins) {
+ Bpl.Type varType = TrType(p.Type);
+ Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.UniqueName, varType), p.Type, etran);
+ inParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, varType, wh), true));
+ }
+ }
+ if (includeOutParams) {
+ foreach (Formal p in m.Outs) {
+ Bpl.Type varType = TrType(p.Type);
+ Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.UniqueName, varType), p.Type, etran);
+ outParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, varType, wh), false));
+ }
+ }
+ }
+
+ class BoilerplateTriple
+ { // a triple that is now a quintuple
+ [ContractInvariantMethod]
+ void ObjectInvariant() {
+ Contract.Invariant(tok != null);
+ Contract.Invariant(Expr != null);
+ Contract.Invariant(IsFree || ErrorMessage != null);
+ }
+
+ public readonly IToken tok;
+ public readonly bool IsFree;
+ public readonly Bpl.Expr Expr;
+ public readonly string ErrorMessage;
+ public readonly string Comment;
+
+
+ public BoilerplateTriple(IToken tok, bool isFree, Bpl.Expr expr, string errorMessage, string comment)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(expr != null);
+ Contract.Requires(isFree || errorMessage != null);
+ this.tok = tok;
+ IsFree = isFree;
+ Expr = expr;
+ ErrorMessage = errorMessage;
+ Comment = comment;
+ }
+ }
+
+ /// <summary>
+ /// There are 3 states of interest when generating two-state boilerplate:
+ /// S0. the beginning of the method or loop, which is where the modifies clause is interpreted
+ /// S1. the pre-state of the two-state interval
+ /// S2. the post-state of the two-state interval
+ /// This method assumes that etranPre denotes S1, etran denotes S2, and that etranMod denotes S0.
+ /// </summary>
+ List<BoilerplateTriple/*!*/>/*!*/ GetTwoStateBoilerplate(IToken/*!*/ tok, List<FrameExpression/*!*/>/*!*/ modifiesClause, ExpressionTranslator/*!*/ etranPre, ExpressionTranslator/*!*/ etran, ExpressionTranslator/*!*/ etranMod)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(modifiesClause != null);
+ Contract.Requires(etranPre != null);
+ Contract.Requires(etran != null);
+ Contract.Ensures(cce.NonNullElements(Contract.Result<List<BoilerplateTriple>>()));
+
+ List<BoilerplateTriple> boilerplate = new List<BoilerplateTriple>();
+
+ // 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, etranPre, etran, etranMod), null, "frame condition"));
+
+ // HeapSucc(S1, S2)
+ Bpl.Expr heapSucc = FunctionCall(tok, BuiltinFunction.HeapSucc, null, etranPre.HeapExpr, etran.HeapExpr);
+ boilerplate.Add(new BoilerplateTriple(tok, true, heapSucc, null, "boilerplate"));
+
+ return boilerplate;
+ }
+
+ /// <summary>
+ /// There are 3 states of interest when generating a frame condition:
+ /// S0. the beginning of the method/loop, which is where the modifies clause is interpreted
+ /// S1. the pre-state of the two-state interval
+ /// S2. the post-state of the two-state interval
+ /// This method assumes that etranPre denotes S1, etran denotes S2, and that etranMod denotes S0.
+ /// </summary>
+ Bpl.Expr/*!*/ FrameCondition(IToken/*!*/ tok, List<FrameExpression/*!*/>/*!*/ modifiesClause, ExpressionTranslator/*!*/ etranPre, ExpressionTranslator/*!*/ etran, ExpressionTranslator/*!*/ etranMod)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(etranPre != null);
+ Contract.Requires(cce.NonNullElements(modifiesClause));
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ // generate:
+ // (forall<alpha> o: ref, f: Field alpha :: { $Heap[o,f] }
+ // o != null && old($Heap)[o,alloc] ==>
+ // $Heap[o,f] == PreHeap[o,f] ||
+ // (o,f) in modifiesClause)
+ Bpl.TypeVariable alpha = new Bpl.TypeVariable(tok, "alpha");
+ Bpl.BoundVariable oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType));
+ Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(tok, oVar);
+ Bpl.BoundVariable fVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$f", predef.FieldName(tok, alpha)));
+ Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(tok, fVar);
+
+ Bpl.Expr heapOF = ExpressionTranslator.ReadHeap(tok, etran.HeapExpr, o, f);
+ Bpl.Expr preHeapOF = ExpressionTranslator.ReadHeap(tok, etranPre.HeapExpr, o, f);
+ Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.Neq(o, predef.Null), etranMod.IsAlloced(tok, o));
+ Bpl.Expr consequent = Bpl.Expr.Eq(heapOF, preHeapOF);
+
+ consequent = Bpl.Expr.Or(consequent, InRWClause(tok, o, f, modifiesClause, etranMod, null, null));
+
+ Bpl.Trigger tr = new Bpl.Trigger(tok, true, new Bpl.ExprSeq(heapOF));
+ return new Bpl.ForallExpr(tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fVar), null, tr, Bpl.Expr.Imp(ante, consequent));
+ }
+ Bpl.Expr/*!*/ FrameConditionUsingDefinedFrame(IToken/*!*/ tok, ExpressionTranslator/*!*/ etranPre, ExpressionTranslator/*!*/ etran, ExpressionTranslator/*!*/ etranMod)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(etranPre != null);
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ // generate:
+ // (forall<alpha> o: ref, f: Field alpha :: { $Heap[o,f] }
+ // o != null && old($Heap)[o,alloc] ==>
+ // $Heap[o,f] == PreHeap[o,f] ||
+ // $_Frame[o,f])
+ Bpl.TypeVariable alpha = new Bpl.TypeVariable(tok, "alpha");
+ Bpl.BoundVariable oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType));
+ Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(tok, oVar);
+ Bpl.BoundVariable fVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$f", predef.FieldName(tok, alpha)));
+ Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(tok, fVar);
+
+ Bpl.Expr heapOF = ExpressionTranslator.ReadHeap(tok, etran.HeapExpr, o, f);
+ Bpl.Expr preHeapOF = ExpressionTranslator.ReadHeap(tok, etranPre.HeapExpr, o, f);
+ Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.Neq(o, predef.Null), etranPre.IsAlloced(tok, o));
+ Bpl.Expr consequent = Bpl.Expr.Eq(heapOF, preHeapOF);
+
+ consequent = Bpl.Expr.Or(consequent, Bpl.Expr.SelectTok(tok, etranMod.TheFrame(tok), o, f));
+
+ Bpl.Trigger tr = new Bpl.Trigger(tok, true, new Bpl.ExprSeq(heapOF));
+ return new Bpl.ForallExpr(tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fVar), null, tr, Bpl.Expr.Imp(ante, consequent));
+ }
+ // ----- Type ---------------------------------------------------------------------------------
+
+ Bpl.Type TrType(Type type)
+ {
+ Contract.Requires(type != null);
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.Result<Bpl.Type>() != null);
+
+ while (true) {
+ TypeProxy tp = type as TypeProxy;
+ if (tp == null) {
+ break;
+ } else if (tp.T == null) {
+ // unresolved proxy; just treat as ref, since no particular type information is apparently needed for this type
+ return predef.RefType;
+ } else {
+ type = tp.T;
+ }
+ }
+
+ if (type is BoolType) {
+ return Bpl.Type.Bool;
+ } else if (type is IntType) {
+ return Bpl.Type.Int;
+ } else if (type is EverIncreasingType) {
+ return Bpl.Type.Int;
+ } else if (type.IsTypeParameter) {
+ return predef.BoxType;
+ } else if (type.IsRefType) {
+ // object and class types translate to ref
+ return predef.RefType;
+ } else if (type.IsDatatype || type is DatatypeProxy) {
+ return predef.DatatypeType;
+ } else if (type is SetType) {
+ return predef.SetType(Token.NoToken, predef.BoxType);
+ } else if (type is MultiSetType) {
+ return predef.MultiSetType(Token.NoToken, predef.BoxType);
+ } else if (type is MapType) {
+ return predef.MapType(Token.NoToken, predef.BoxType, predef.BoxType);
+ } else if (type is SeqType) {
+ return predef.SeqType(Token.NoToken, predef.BoxType);
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
+ }
+ }
+
+ Bpl.TypeVariableSeq TrTypeParamDecls(List<TypeParameter/*!*/>/*!*/ tps)
+ {
+ Contract.Requires(cce.NonNullElements(tps));
+ Contract.Ensures(Contract.Result<Bpl.TypeVariableSeq>() != null);
+
+ Bpl.TypeVariableSeq typeParams = new Bpl.TypeVariableSeq();
+ return typeParams;
+ }
+
+ // ----- Statement ----------------------------------------------------------------------------
+
+ /// <summary>
+ /// A ForceCheckToken is a token wrapper whose purpose is to hide inheritance.
+ /// </summary>
+ public class ForceCheckToken : TokenWrapper
+ {
+ public ForceCheckToken(IToken tok)
+ : base(tok) {
+ Contract.Requires(tok != null);
+ }
+ public static IToken Unwrap(IToken tok) {
+ Contract.Requires(tok != null);
+ Contract.Ensures(Contract.Result<IToken>() != null);
+ var ftok = tok as ForceCheckToken;
+ return ftok != null ? ftok.WrappedToken : tok;
+ }
+ }
+
+ Bpl.PredicateCmd Assert(Bpl.IToken tok, Bpl.Expr condition, string errorMessage) {
+ return Assert(tok, condition, errorMessage, tok);
+ }
+
+ Bpl.PredicateCmd Assert(Bpl.IToken tok, Bpl.Expr condition, string errorMessage, Bpl.IToken refinesToken) {
+ Contract.Requires(tok != null);
+ Contract.Requires(condition != null);
+ Contract.Requires(errorMessage != null);
+ Contract.Ensures(Contract.Result<Bpl.PredicateCmd>() != null);
+
+ if (RefinementToken.IsInherited(refinesToken, currentModule) && (codeContext == null || !codeContext.MustReverify)) {
+ // produce an assume instead
+ return new Bpl.AssumeCmd(tok, condition);
+ } else {
+ var cmd = new Bpl.AssertCmd(ForceCheckToken.Unwrap(tok), condition);
+ cmd.ErrorData = "Error: " + errorMessage;
+ return cmd;
+ }
+ }
+ Bpl.PredicateCmd AssertNS(Bpl.IToken tok, Bpl.Expr condition, string errorMessage) {
+ return AssertNS(tok, condition, errorMessage, tok);
+ }
+ Bpl.PredicateCmd AssertNS(Bpl.IToken tok, Bpl.Expr condition, string errorMessage, Bpl.IToken refinesTok)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(errorMessage != null);
+ Contract.Requires(condition != null);
+ Contract.Ensures(Contract.Result<Bpl.PredicateCmd>() != null);
+
+ if (RefinementToken.IsInherited(refinesTok, currentModule) && (codeContext == null || !codeContext.MustReverify)) {
+ // produce a "skip" instead
+ return new Bpl.AssumeCmd(tok, Bpl.Expr.True);
+ } else {
+ tok = ForceCheckToken.Unwrap(tok);
+ var args = new List<object>();
+ args.Add(Bpl.Expr.Literal(0));
+ Bpl.QKeyValue kv = new Bpl.QKeyValue(tok, "subsumption", args, null);
+ Bpl.AssertCmd cmd = new Bpl.AssertCmd(tok, condition, kv);
+ cmd.ErrorData = "Error: " + errorMessage;
+ return cmd;
+ }
+ }
+
+ Bpl.PredicateCmd Assert(Bpl.IToken tok, Bpl.Expr condition, string errorMessage, Bpl.QKeyValue kv) {
+ Contract.Requires(tok != null);
+ Contract.Requires(errorMessage != null);
+ Contract.Requires(condition != null);
+ Contract.Ensures(Contract.Result<Bpl.PredicateCmd>() != null);
+
+ if (RefinementToken.IsInherited(tok, currentModule) && (codeContext == null || !codeContext.MustReverify)) {
+ // produce an assume instead
+ return new Bpl.AssumeCmd(tok, condition, kv);
+ } else {
+ var cmd = new Bpl.AssertCmd(ForceCheckToken.Unwrap(tok), condition, kv);
+ cmd.ErrorData = "Error: " + errorMessage;
+ return cmd;
+ }
+ }
+
+ Bpl.Ensures Ensures(IToken tok, bool free, Bpl.Expr condition, string errorMessage, string comment)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(condition != null);
+ Contract.Ensures(Contract.Result<Bpl.Ensures>() != null);
+
+ Bpl.Ensures ens = new Bpl.Ensures(ForceCheckToken.Unwrap(tok), free, condition, comment);
+ if (errorMessage != null) {
+ ens.ErrorData = errorMessage;
+ }
+ return ens;
+ }
+
+ Bpl.Requires Requires(IToken tok, bool free, Bpl.Expr condition, string errorMessage, string comment)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(condition != null);
+ Bpl.Requires req = new Bpl.Requires(ForceCheckToken.Unwrap(tok), free, condition, comment);
+ if (errorMessage != null) {
+ req.ErrorData = errorMessage;
+ }
+ return req;
+ }
+
+ Bpl.StmtList TrStmt2StmtList(Statement block, Bpl.VariableSeq locals, ExpressionTranslator etran)
+ {
+ Contract.Requires(block != null);
+ Contract.Requires(locals != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(codeContext != null && predef != null);
+ Contract.Ensures(Contract.Result<Bpl.StmtList>() != null);
+
+ return TrStmt2StmtList(new Bpl.StmtListBuilder(), block, locals, etran);
+ }
+
+ Bpl.StmtList TrStmt2StmtList(Bpl.StmtListBuilder builder, Statement block, Bpl.VariableSeq locals, ExpressionTranslator etran)
+ {
+ Contract.Requires(builder != null);
+ Contract.Requires(block != null);
+ Contract.Requires(locals != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(codeContext != null && predef != null);
+ Contract.Ensures(Contract.Result<Bpl.StmtList>() != null);
+
+ TrStmt(block, builder, locals, etran);
+ return builder.Collect(block.Tok); // TODO: would be nice to have an end-curly location for "block"
+ }
+
+ void TrStmt(Statement stmt, Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran)
+ {
+ Contract.Requires(stmt != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(locals != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(codeContext != null && predef != null);
+ if (stmt is PredicateStmt) {
+ if (stmt is AssertStmt || DafnyOptions.O.DisallowSoundnessCheating) {
+ AddComment(builder, stmt, "assert statement");
+ PredicateStmt s = (PredicateStmt)stmt;
+ TrStmt_CheckWellformed(s.Expr, builder, locals, etran, false);
+ IToken enclosingToken = null;
+ if (Attributes.Contains(stmt.Attributes, "prependAssertToken")) {
+ enclosingToken = stmt.Tok;
+ }
+ bool splitHappened;
+ var ss = TrSplitExpr(s.Expr, etran, 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));
+ } else {
+ foreach (var split in ss) {
+ if (!split.IsFree) {
+ var tok = enclosingToken == null ? split.E.tok : new NestedToken(enclosingToken, split.E.tok);
+ builder.Add(AssertNS(tok, split.E, "assertion violation", stmt.Tok));
+ }
+ }
+ builder.Add(new Bpl.AssumeCmd(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)));
+ }
+ } else if (stmt is PrintStmt) {
+ AddComment(builder, stmt, "print statement");
+ PrintStmt s = (PrintStmt)stmt;
+ foreach (Attributes.Argument arg in s.Args) {
+ if (arg.E != null) {
+ TrStmt_CheckWellformed(arg.E, builder, locals, etran, false);
+ }
+ }
+
+ } else if (stmt is BreakStmt) {
+ AddComment(builder, stmt, "break statement");
+ var s = (BreakStmt)stmt;
+ builder.Add(new GotoCmd(s.Tok, new StringSeq("after_" + s.TargetStmt.Labels.Data.UniqueId)));
+ } else if (stmt is ReturnStmt) {
+ var s = (ReturnStmt)stmt;
+ AddComment(builder, stmt, "return statement");
+ if (s.hiddenUpdate != null) {
+ TrStmt(s.hiddenUpdate, builder, locals, etran);
+ }
+ builder.Add(new Bpl.ReturnCmd(stmt.Tok));
+ } else if (stmt is YieldStmt) {
+ var s = (YieldStmt)stmt;
+ AddComment(builder, s, "yield statement");
+ Contract.Assert(codeContext is IteratorDecl);
+ var iter = (IteratorDecl)codeContext;
+ // this.ys := this.ys + [this.y];
+ var th = new ThisExpr(iter.tok);
+ th.Type = Resolver.GetThisType(iter.tok, iter); // resolve here
+ Contract.Assert(iter.OutsFields.Count == iter.OutsHistoryFields.Count);
+ for (int i = 0; i < iter.OutsFields.Count; i++) {
+ var y = iter.OutsFields[i];
+ var dafnyY = new FieldSelectExpr(s.Tok, th, y.Name);
+ dafnyY.Field = y; dafnyY.Type = y.Type; // resolve here
+ var ys = iter.OutsHistoryFields[i];
+ var dafnyYs = new FieldSelectExpr(s.Tok, th, ys.Name);
+ dafnyYs.Field = ys; dafnyYs.Type = ys.Type; // resolve here
+ var dafnySingletonY = new SeqDisplayExpr(s.Tok, new List<Expression>() { dafnyY });
+ dafnySingletonY.Type = ys.Type; // resolve here
+ var rhs = new BinaryExpr(s.Tok, BinaryExpr.Opcode.Add, dafnyYs, dafnySingletonY);
+ rhs.ResolvedOp = BinaryExpr.ResolvedOpcode.Concat;
+ rhs.Type = ys.Type; // resolve here
+ var cmd = Bpl.Cmd.SimpleAssign(s.Tok, (Bpl.IdentifierExpr/*TODO: this cast is rather dubious*/)etran.HeapExpr,
+ ExpressionTranslator.UpdateHeap(s.Tok, etran.HeapExpr, etran.TrExpr(th), new Bpl.IdentifierExpr(s.Tok, GetField(ys)), etran.TrExpr(rhs)));
+ builder.Add(cmd);
+ }
+ // yieldCount := yieldCount + 1; assume yieldCount == |ys|;
+ 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)));
+ // assert YieldEnsures[subst]; // where 'subst' replaces "old(E)" with "E" being evaluated in $_OldIterHeap
+ var yeEtran = new ExpressionTranslator(this, predef, etran.HeapExpr, new Bpl.IdentifierExpr(s.Tok, "$_OldIterHeap", predef.HeapType));
+ foreach (var p in iter.YieldEnsures) {
+ if (p.IsFree && !DafnyOptions.O.DisallowSoundnessCheating) {
+ // do nothing
+ } else {
+ bool splitHappened; // actually, we don't care
+ var ss = TrSplitExpr(p.E, yeEtran, out splitHappened);
+ foreach (var split in ss) {
+ if (RefinementToken.IsInherited(split.E.tok, currentModule)) {
+ // this postcondition was inherited into this module, so just ignore it
+ } else if (!split.IsFree) {
+ var yieldToken = new NestedToken(s.Tok, split.E.tok);
+ builder.Add(AssertNS(yieldToken, split.E, "possible violation of yield-ensures condition", stmt.Tok));
+ }
+ }
+ builder.Add(new Bpl.AssumeCmd(stmt.Tok, yeEtran.TrExpr(p.E)));
+ }
+ }
+ YieldHavoc(iter.tok, iter, builder, etran);
+ builder.Add(CaptureState(s.Tok));
+
+ } else if (stmt is AssignSuchThatStmt) {
+ var s = (AssignSuchThatStmt)stmt;
+ AddComment(builder, s, "assign-such-that statement");
+ // Essentially, treat like an assert, a parallel havoc, and an assume. However, we also need to check
+ // the well-formedness of the expression, which is easiest to do after the havoc. So, we do the havoc
+ // first, then the well-formedness check, then the assert (unless the whole statement is an assume), and
+ // finally the assume.
+
+ // Here comes the havoc part
+ var lhss = new List<Expression>();
+ var havocRhss = new List<AssignmentRhs>();
+ foreach (var lhs in s.Lhss) {
+ lhss.Add(lhs.Resolved);
+ havocRhss.Add(new HavocRhs(lhs.tok)); // note, a HavocRhs is constructed as already resolved
+ }
+ List<AssignToLhs> lhsBuilder;
+ List<Bpl.IdentifierExpr> bLhss;
+ Bpl.Expr[] ignore1, ignore2;
+ string[] ignore3;
+ ProcessLhss(lhss, false, true, builder, locals, etran, out lhsBuilder, out bLhss, out ignore1, out ignore2, out ignore3);
+ ProcessRhss(lhsBuilder, bLhss, lhss, havocRhss, builder, locals, etran);
+ // Here comes the well-formedness check
+ TrStmt_CheckWellformed(s.Expr, builder, locals, etran, false);
+ // Here comes the assert part
+ if (s.AssumeToken == null) {
+ var substMap = new Dictionary<IVariable, Expression>();
+ var bvars = new List<BoundVar>();
+ foreach (var lhs in s.Lhss) {
+ var l = lhs.Resolved;
+ if (l is IdentifierExpr) {
+ var x = (IdentifierExpr)l;
+ BoundVar bv;
+ IdentifierExpr ie;
+ CloneVariableAsBoundVar(x.tok, x.Var, "$as#" + x.Name, out bv, out ie);
+ bvars.Add(bv);
+ substMap.Add(x.Var, ie);
+ } else {
+ // other forms of LHSs have been ruled out by the resolver (it would be possible to
+ // handle them, but it would involve heap-update expressions--the translation would take
+ // effort, and it's not certain that the existential would be successful in verification).
+ Contract.Assume(false); // unexpected case
+ }
+ }
+
+ List<Tuple<List<BoundVar>, Expression>> partialGuesses = GeneratePartialGuesses(bvars, Substitute(s.Expr, null, substMap));
+ Bpl.Expr w = Bpl.Expr.False;
+ foreach (var tup in partialGuesses) {
+ var body = etran.TrExpr(tup.Item2);
+ if (tup.Item1.Count != 0) {
+ var bvs = new VariableSeq();
+ var typeAntecedent = etran.TrBoundVariables(tup.Item1, bvs);
+ body = new Bpl.ExistsExpr(s.Tok, bvs, BplAnd(typeAntecedent, body));
+ }
+ w = BplOr(body, w);
+ }
+ 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(CaptureState(s.Tok)); // just do one capture state--here, at the very end (that is, don't do one before the assume)
+
+ } else if (stmt is UpdateStmt) {
+ var s = (UpdateStmt)stmt;
+ // This UpdateStmt can be single-target assignment, a multi-assignment, a call statement, or
+ // an array-range update. Handle the multi-assignment here and handle the others as for .ResolvedStatements.
+ var resolved = s.ResolvedStatements;
+ if (resolved.Count == 1) {
+ TrStmt(resolved[0], builder, locals, etran);
+ } else {
+ AddComment(builder, s, "update statement");
+ var lhss = new List<Expression>();
+ foreach (var lhs in s.Lhss) {
+ lhss.Add(lhs.Resolved);
+ }
+ List<AssignToLhs> lhsBuilder;
+ List<Bpl.IdentifierExpr> bLhss;
+ // note: because we have more than one expression, we always must assign to Boogie locals in a two
+ // phase operation. Thus rhssCanAffectPreviouslyKnownExpressions is just true.
+ Contract.Assert(1 < lhss.Count);
+
+ Bpl.Expr[] lhsObjs, lhsFields;
+ string[] lhsNames;
+ ProcessLhss(lhss, true, false, builder, locals, etran, out lhsBuilder, out bLhss, out lhsObjs, out lhsFields, out lhsNames);
+ // We know that, because the translation saves to a local variable, that the RHS always need to
+ // generate a new local, i.e. bLhss is just all nulls.
+ Contract.Assert(Contract.ForAll(bLhss, lhs => lhs == null));
+ // This generates the assignments, and gives them to us as finalRhss.
+ var finalRhss = ProcessUpdateAssignRhss(lhss, s.Rhss, builder, locals, etran);
+ // ProcessLhss has laid down framing conditions and the ProcessUpdateAssignRhss will check subranges (nats),
+ // but we need to generate the distinctness condition (two LHS are equal only when the RHS is also
+ // equal). We need both the LHS and the RHS to do this, which is why we need to do it here.
+ CheckLhssDistinctness(finalRhss, lhss, builder, etran, lhsObjs, lhsFields, lhsNames);
+ // Now actually perform the assignments to the LHS.
+ for (int i = 0; i < lhss.Count; i++) {
+ lhsBuilder[i](finalRhss[i], builder, etran);
+ }
+ builder.Add(CaptureState(s.Tok));
+ }
+
+ } else if (stmt is AssignStmt) {
+ AddComment(builder, stmt, "assignment statement");
+ AssignStmt s = (AssignStmt)stmt;
+ TrAssignment(stmt.Tok, s.Lhs.Resolved, s.Rhs, builder, locals, etran);
+ } else if (stmt is VarDecl) {
+ AddComment(builder, stmt, "var-declaration statement");
+ VarDecl s = (VarDecl)stmt;
+ Bpl.Type varType = TrType(s.Type);
+ Bpl.Expr wh = GetWhereClause(stmt.Tok, new Bpl.IdentifierExpr(stmt.Tok, s.UniqueName, varType), s.Type, etran);
+ Bpl.LocalVariable var = new Bpl.LocalVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, s.UniqueName, varType, wh));
+ locals.Add(var);
+
+ } else if (stmt is CallStmt) {
+ AddComment(builder, stmt, "call statement");
+ TrCallStmt((CallStmt)stmt, builder, locals, etran, null);
+
+ } else if (stmt is BlockStmt) {
+ foreach (Statement ss in ((BlockStmt)stmt).Body) {
+ TrStmt(ss, builder, locals, etran);
+ if (ss.Labels != null) {
+ builder.AddLabelCmd("after_" + ss.Labels.Data.UniqueId);
+ }
+ }
+ } else if (stmt is IfStmt) {
+ AddComment(builder, stmt, "if statement");
+ IfStmt s = (IfStmt)stmt;
+ Bpl.Expr guard;
+ if (s.Guard == null) {
+ guard = null;
+ } else {
+ TrStmt_CheckWellformed(s.Guard, builder, locals, etran, true);
+ guard = etran.TrExpr(s.Guard);
+ }
+ Bpl.StmtListBuilder b = new Bpl.StmtListBuilder();
+ Bpl.StmtList thn = TrStmt2StmtList(b, s.Thn, locals, etran);
+ Bpl.StmtList els;
+ Bpl.IfCmd elsIf = null;
+ 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];
+ if (bb.LabelName == null && bb.simpleCmds.Length == 0 && bb.ec is Bpl.IfCmd) {
+ elsIf = (Bpl.IfCmd)bb.ec;
+ els = null;
+ }
+ }
+ }
+ builder.Add(new Bpl.IfCmd(stmt.Tok, guard, thn, elsIf, els));
+
+ } else if (stmt is AlternativeStmt) {
+ AddComment(builder, stmt, "alternative statement");
+ var s = (AlternativeStmt)stmt;
+ var elseCase = Assert(s.Tok, Bpl.Expr.False, "alternative cases fail to cover all possibilties");
+ TrAlternatives(s.Alternatives, elseCase, null, builder, locals, etran);
+
+ } else if (stmt is WhileStmt) {
+ AddComment(builder, stmt, "while statement");
+ var s = (WhileStmt)stmt;
+ TrLoop(s, s.Guard,
+ delegate(Bpl.StmtListBuilder bld, ExpressionTranslator e) { TrStmt(s.Body, bld, locals, e); },
+ builder, locals, etran);
+
+ } else if (stmt is AlternativeLoopStmt) {
+ AddComment(builder, stmt, "alternative loop statement");
+ var s = (AlternativeLoopStmt)stmt;
+ var tru = new LiteralExpr(s.Tok, true);
+ tru.Type = Type.Bool; // resolve here
+ TrLoop(s, tru,
+ delegate(Bpl.StmtListBuilder bld, ExpressionTranslator e) { TrAlternatives(s.Alternatives, null, new Bpl.BreakCmd(s.Tok, null), bld, locals, e); },
+ builder, locals, etran);
+
+ } else if (stmt is ParallelStmt) {
+ AddComment(builder, stmt, "parallel statement");
+ var s = (ParallelStmt)stmt;
+ if (s.Kind == ParallelStmt.ParBodyKind.Assign) {
+ Contract.Assert(s.Ens.Count == 0);
+ if (s.BoundVars.Count == 0) {
+ TrStmt(s.Body, builder, locals, etran);
+ } else {
+ var s0 = (AssignStmt)s.S0;
+ var definedness = new Bpl.StmtListBuilder();
+ var updater = new Bpl.StmtListBuilder();
+ TrParallelAssign(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)));
+ builder.Add(CaptureState(stmt.Tok));
+ }
+
+ } else if (s.Kind == ParallelStmt.ParBodyKind.Call) {
+ Contract.Assert(s.Ens.Count == 0);
+ if (s.BoundVars.Count == 0) {
+ TrStmt(s.Body, builder, locals, etran);
+ } else {
+ var s0 = (CallStmt)s.S0;
+ var definedness = new Bpl.StmtListBuilder();
+ var exporter = new Bpl.StmtListBuilder();
+ TrParallelCall(s.Tok, s.BoundVars, s.Range, null, 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.Tok));
+ }
+
+ } else if (s.Kind == ParallelStmt.ParBodyKind.Proof) {
+ var definedness = new Bpl.StmtListBuilder();
+ var exporter = new Bpl.StmtListBuilder();
+ TrParallelProof(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)));
+ builder.Add(CaptureState(stmt.Tok));
+
+ } else {
+ Contract.Assert(false); // unexpected kind
+ }
+
+ } else if (stmt is CalcStmt) {
+ /* Translate into:
+ if (*) {
+ // line well-formedness checks;
+ } else if (*) {
+ hint0;
+ assert t0 op t1;
+ assume false;
+ } else if (*) { ...
+ } else if (*) {
+ hint<n-1>;
+ assert t<n-1> op tn;
+ assume false;
+ }
+ assume t0 op tn;
+ */
+ var s = (CalcStmt)stmt;
+ Contract.Assert(s.Steps.Count == s.Hints.Count); // established by the resolver
+ AddComment(builder, stmt, "calc statement");
+ if (s.Lines.Count > 0) {
+ Bpl.IfCmd ifCmd = null;
+ Bpl.StmtListBuilder b;
+ // check steps:
+ for (int i = s.Steps.Count; 0 <= --i; ) {
+ b = new Bpl.StmtListBuilder();
+ TrStmt(s.Hints[i], b, locals, etran);
+ bool splitHappened;
+ var ss = TrSplitExpr(s.Steps[i], etran, out splitHappened);
+ if (!splitHappened) {
+ b.Add(AssertNS(s.Lines[i + 1].tok, etran.TrExpr(s.Steps[i]), "the calculation step between the previous line and this line might not hold"));
+ } else {
+ foreach (var split in ss) {
+ if (!split.IsFree) {
+ b.Add(AssertNS(s.Lines[i + 1].tok, split.E, "the calculation step between the previous line and this line might not hold"));
+ }
+ }
+ }
+ b.Add(new Bpl.AssumeCmd(s.Tok, Bpl.Expr.False));
+ ifCmd = new Bpl.IfCmd(s.Tok, null, b.Collect(s.Tok), ifCmd, null);
+ }
+ // check well-formedness of lines:
+ b = new Bpl.StmtListBuilder();
+ foreach (var e in s.Lines) {
+ TrStmt_CheckWellformed(e, b, locals, etran, false);
+ }
+ b.Add(new Bpl.AssumeCmd(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 > 0) {
+ Contract.Assert(s.Result != null); // established by the resolver
+ builder.Add(new Bpl.AssumeCmd(s.Tok, etran.TrExpr(s.Result)));
+ }
+ }
+
+ } 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));
+ Bpl.StmtList els = b.Collect(stmt.Tok);
+ Bpl.IfCmd ifCmd = null;
+ foreach (var missingCtor in s.MissingCases) {
+ // havoc all bound variables
+ b = new Bpl.StmtListBuilder();
+ VariableSeq newLocals = new VariableSeq();
+ Bpl.Expr r = CtorInvocation(s.Tok, missingCtor, etran, newLocals, b);
+ locals.AddRange(newLocals);
+
+ if (newLocals.Length != 0) {
+ Bpl.IdentifierExprSeq havocIds = new Bpl.IdentifierExprSeq();
+ foreach (Variable local in newLocals) {
+ havocIds.Add(new Bpl.IdentifierExpr(local.tok, local));
+ }
+ builder.Add(new Bpl.HavocCmd(s.Tok, havocIds));
+ }
+ b.Add(Assert(s.Tok, Bpl.Expr.False, "missing case in case statement: " + missingCtor.Name));
+
+ Bpl.Expr guard = Bpl.Expr.Eq(source, r);
+ ifCmd = new Bpl.IfCmd(s.Tok, guard, b.Collect(s.Tok), ifCmd, els);
+ els = null;
+ }
+ for (int i = s.Cases.Count; 0 <= --i; ) {
+ var mc = (MatchCaseStmt)s.Cases[i];
+ // havoc all bound variables
+ b = new Bpl.StmtListBuilder();
+ VariableSeq newLocals = new VariableSeq();
+ Bpl.Expr r = CtorInvocation(mc, etran, newLocals, b);
+ locals.AddRange(newLocals);
+
+ if (newLocals.Length != 0) {
+ Bpl.IdentifierExprSeq havocIds = new Bpl.IdentifierExprSeq();
+ foreach (Variable local in newLocals) {
+ havocIds.Add(new Bpl.IdentifierExpr(local.tok, local));
+ }
+ builder.Add(new Bpl.HavocCmd(mc.tok, havocIds));
+ }
+
+ // translate the body into b
+ foreach (Statement ss in mc.Body) {
+ TrStmt(ss, b, locals, etran);
+ }
+
+ Bpl.Expr guard = Bpl.Expr.Eq(source, r);
+ ifCmd = new Bpl.IfCmd(mc.tok, guard, b.Collect(mc.tok), ifCmd, els);
+ els = null;
+ }
+ Contract.Assert(ifCmd != null); // follows from the fact that s.Cases.Count + s.MissingCases.Count != 0.
+ builder.Add(ifCmd);
+
+ } else if (stmt is ConcreteSyntaxStatement) {
+ var s = (ConcreteSyntaxStatement)stmt;
+ foreach (var ss in s.ResolvedStatements) {
+ TrStmt(ss, builder, locals, etran);
+ }
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement
+ }
+ }
+
+ /// <summary>
+ /// Generate:
+ /// havoc Heap \ {this} \ _reads \ _new;
+ /// assume this.Valid();
+ /// assume YieldRequires;
+ /// $_OldIterHeap := Heap;
+ /// </summary>
+ void YieldHavoc(IToken tok, IteratorDecl iter, StmtListBuilder builder, ExpressionTranslator etran) {
+ Contract.Requires(tok != null);
+ Contract.Requires(iter != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(etran != null);
+ // havoc Heap \ {this} \ _reads \ _new;
+ var th = new ThisExpr(tok);
+ th.Type = Resolver.GetThisType(tok, iter); // resolve here
+ var rds = new FieldSelectExpr(tok, th, iter.Member_Reads.Name);
+ rds.Field = iter.Member_Reads; // resolve here
+ rds.Type = iter.Member_Reads.Type; // resolve here
+ var nw = new FieldSelectExpr(tok, th, iter.Member_New.Name);
+ nw.Field = iter.Member_New; // resolve here
+ nw.Type = iter.Member_New.Type; // resolve here
+ builder.Add(new Bpl.CallCmd(tok, "$YieldHavoc",
+ new List<Bpl.Expr>() { etran.TrExpr(th), etran.TrExpr(rds), etran.TrExpr(nw) },
+ new List<Bpl.IdentifierExpr>()));
+ // assume YieldRequires;
+ foreach (var p in iter.YieldRequires) {
+ builder.Add(new Bpl.AssumeCmd(tok, etran.TrExpr(p.E)));
+ }
+ // $_OldIterHeap := Heap;
+ builder.Add(Bpl.Cmd.SimpleAssign(tok, new Bpl.IdentifierExpr(tok, "$_OldIterHeap", predef.HeapType), etran.HeapExpr));
+ }
+
+ List<Tuple<List<BoundVar>, Expression>> GeneratePartialGuesses(List<BoundVar> bvars, Expression expression) {
+ if (bvars.Count == 0) {
+ var tup = new Tuple<List<BoundVar>, Expression>(new List<BoundVar>(), expression);
+ return new List<Tuple<List<BoundVar>, Expression>>() { tup };
+ }
+ var result = new List<Tuple<List<BoundVar>, Expression>>();
+ var x = bvars[0];
+ var otherBvars = bvars.GetRange(1, bvars.Count - 1);
+ foreach (var tup in GeneratePartialGuesses(otherBvars, expression)) {
+ // in the special case that x does not even occur in expression, we can just ignore x
+ if (!ContainsFreeVariable(tup.Item2, false, x)) {
+ result.Add(tup);
+ continue;
+ }
+ // one possible result is to quantify over all the variables
+ var vs = new List<BoundVar>() { x };
+ vs.AddRange(tup.Item1);
+ result.Add(new Tuple<List<BoundVar>, Expression>(vs, tup.Item2));
+ // other possibilities involve guessing a value for x
+ foreach (var guess in GuessWitnesses(x, tup.Item2)) {
+ var substMap = new Dictionary<IVariable, Expression>();
+ substMap.Add(x, guess);
+ result.Add(new Tuple<List<BoundVar>, Expression>(tup.Item1, Substitute(tup.Item2, null, substMap)));
+ }
+ }
+ return result;
+ }
+
+ IEnumerable<Expression> GuessWitnesses(BoundVar x, Expression expr) {
+ Contract.Requires(x != null);
+ Contract.Requires(expr != null);
+ var lookForBounds = false;
+ if (x.Type is BoolType) {
+ var lit = new LiteralExpr(x.tok, false);
+ lit.Type = Type.Bool; // resolve here
+ yield return lit;
+ lit = new LiteralExpr(x.tok, true);
+ lit.Type = Type.Bool; // resolve here
+ yield return lit;
+ } else if (x.Type.IsRefType) {
+ var lit = new LiteralExpr(x.tok);
+ lit.Type = x.Type;
+ yield return lit;
+ } else if (x.Type.IsIndDatatype) {
+ var dt = x.Type.AsIndDatatype;
+ Expression zero = Zero(x.tok, x.Type);
+ if (zero != null) {
+ yield return zero;
+ }
+ } else if (x.Type is SetType) {
+ var empty = new SetDisplayExpr(x.tok, new List<Expression>());
+ empty.Type = x.Type;
+ yield return empty;
+ lookForBounds = true;
+ } else if (x.Type is MultiSetType) {
+ var empty = new MultiSetDisplayExpr(x.tok, new List<Expression>());
+ empty.Type = x.Type;
+ yield return empty;
+ lookForBounds = true;
+ } else if (x.Type is SeqType) {
+ var empty = new SeqDisplayExpr(x.tok, new List<Expression>());
+ empty.Type = x.Type;
+ yield return empty;
+ lookForBounds = true;
+ } else if (x.Type is IntType) {
+ var lit = new LiteralExpr(x.tok, 0);
+ lit.Type = Type.Int; // resolve here
+ yield return lit;
+ lookForBounds = true;
+ }
+ if (lookForBounds) {
+ 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 Resolver.Minus(bnd.UpperBound, 1);
+ } else if (bound is ComprehensionExpr.SuperSetBoundedPool) {
+ var bnd = (ComprehensionExpr.SuperSetBoundedPool)bound;
+ yield return bnd.LowerBound;
+ }
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Return a zero-equivalent value for "typ", or return null (for any reason whatsoever).
+ /// </summary>
+ Expression Zero(Bpl.IToken tok, Type typ) {
+ Contract.Requires(tok != null);
+ Contract.Requires(typ != null);
+ return null; // TODO: this can be improved
+ }
+
+ void TrParallelAssign(ParallelStmt s, AssignStmt s0,
+ Bpl.StmtListBuilder definedness, Bpl.StmtListBuilder updater, Bpl.VariableSeq locals, ExpressionTranslator etran) {
+ // The statement:
+ // parallel (x,y | Range(x,y)) {
+ // (a) E(x,y) . f := G(x,y);
+ // (b) A(x,y) [ I0(x,y), I1(x,y), ... ] := G(x,y);
+ // }
+ // translate into:
+ // if (*) {
+ // // check definedness of Range
+ // var x,y;
+ // havoc x,y;
+ // CheckWellformed( Range );
+ // assume Range;
+ // // check definedness of the other expressions
+ // (a)
+ // CheckWellformed( E.F );
+ // check that E.f is in the modifies frame;
+ // CheckWellformed( G );
+ // check nat restrictions for the RHS
+ // (b)
+ // CheckWellformed( A[I0,I1,...] );
+ // check that A[I0,I1,...] is in the modifies frame;
+ // CheckWellformed( G );
+ // check nat restrictions for the RHS
+ // // check for duplicate LHSs
+ // var x', y';
+ // havoc x', y';
+ // assume Range[x,y := x',y'];
+ // assume !(x == x' && y == y');
+ // (a)
+ // assert E(x,y) != E(x',y');
+ // (b)
+ // assert !( A(x,y)==A(x',y') && I0(x,y)==I0(x',y') && I1(x,y)==I1(x',y') && ... );
+ //
+ // assume false;
+ //
+ // } else {
+ // var oldHeap := $Heap;
+ // havoc $Heap;
+ // assume $HeapSucc(oldHeap, $Heap);
+ // (a)
+ // assume (forall<alpha> o: ref, F: Field alpha ::
+ // $Heap[o,F] = oldHeap[o,F] ||
+ // (exists x,y :: Range(x,y) && o == E(x,y) && F = f));
+ // assume (forall x,y :: Range ==> $Heap[ E[$Heap:=oldHeap], F] == G[$Heap:=oldHeap]);
+ // (b)
+ // assume (forall<alpha> o: ref, F: Field alpha ::
+ // $Heap[o,F] = oldHeap[o,F] ||
+ // (exists x,y :: Range(x,y) && o == A(x,y) && F = Index(I0,I1,...)));
+ // assume (forall x,y :: Range ==> $Heap[ A[$Heap:=oldHeap], Index(I0,I1,...)] == G[$Heap:=oldHeap]);
+ // }
+
+ 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)));
+
+ var lhs = Substitute(s0.Lhs.Resolved, null, substMap);
+ TrStmt_CheckWellformed(lhs, definedness, locals, etran, false);
+ Bpl.Expr obj, F;
+ string description = GetObjFieldDetails(lhs, etran, out obj, out F);
+ definedness.Add(Assert(lhs.tok, Bpl.Expr.SelectTok(lhs.tok, etran.TheFrame(lhs.tok), obj, F),
+ "assignment may update " + description + " not in the enclosing context's modifies clause"));
+ if (s0.Rhs is ExprRhs) {
+ var r = (ExprRhs)s0.Rhs;
+ var rhs = Substitute(r.Expr, null, substMap);
+ TrStmt_CheckWellformed(rhs, definedness, locals, etran, false);
+ // check nat restrictions for the RHS
+ Type lhsType;
+ if (lhs is FieldSelectExpr) {
+ lhsType = ((FieldSelectExpr)lhs).Type;
+ } else if (lhs is SeqSelectExpr) {
+ lhsType = ((SeqSelectExpr)lhs).Type;
+ } else {
+ lhsType = ((MultiSelectExpr)lhs).Type;
+ }
+ var translatedRhs = etran.TrExpr(rhs);
+ CheckSubrange(r.Tok, translatedRhs, lhsType, definedness);
+ if (lhs is FieldSelectExpr) {
+ var fse = (FieldSelectExpr)lhs;
+ Check_NewRestrictions(fse.tok, obj, fse.Field, translatedRhs, definedness, etran);
+ }
+ }
+
+ // check for duplicate LHSs
+ 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)));
+ // assume !(x == x' && y == y');
+ Bpl.Expr eqs = Bpl.Expr.True;
+ foreach (var bv in s.BoundVars) {
+ var x = substMap[bv];
+ var xPrime = substMapPrime[bv];
+ // 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)));
+ Bpl.Expr objPrime, FPrime;
+ GetObjFieldDetails(lhsPrime, etran, out objPrime, out FPrime);
+ definedness.Add(Assert(s0.Tok,
+ Bpl.Expr.Or(Bpl.Expr.Neq(obj, objPrime), Bpl.Expr.Neq(F, FPrime)),
+ "left-hand sides for different parallel-statement bound variables may refer to the same location"));
+
+ definedness.Add(new Bpl.AssumeCmd(s.Tok, Bpl.Expr.False));
+
+ // Now for the translation of the update itself
+
+ Bpl.IdentifierExpr prevHeap = GetPrevHeapVar_IdExpr(s.Tok, locals);
+ 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 Bpl.IdentifierExprSeq((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)));
+
+ // Here comes:
+ // assume (forall<alpha> o: ref, f: Field alpha ::
+ // $Heap[o,f] = oldHeap[o,f] ||
+ // (exists x,y :: Range(x,y)[$Heap:=oldHeap] &&
+ // o == Object(x,y)[$Heap:=oldHeap] && f == Field(x,y)[$Heap:=oldHeap]));
+ Bpl.TypeVariable alpha = new Bpl.TypeVariable(s.Tok, "alpha");
+ Bpl.BoundVariable oVar = new Bpl.BoundVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$o", predef.RefType));
+ Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(s.Tok, oVar);
+ Bpl.BoundVariable fVar = new Bpl.BoundVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$f", predef.FieldName(s.Tok, alpha)));
+ Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(s.Tok, fVar);
+ Bpl.Expr heapOF = ExpressionTranslator.ReadHeap(s.Tok, etran.HeapExpr, o, f);
+ Bpl.Expr oldHeapOF = ExpressionTranslator.ReadHeap(s.Tok, prevHeap, o, f);
+ Bpl.VariableSeq xBvars = new Bpl.VariableSeq();
+ var xBody = etran.TrBoundVariables(s.BoundVars, xBvars);
+ xBody = BplAnd(xBody, prevEtran.TrExpr(s.Range));
+ Bpl.Expr xObj, xField;
+ 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);
+ Bpl.Expr body = Bpl.Expr.Or(Bpl.Expr.Eq(heapOF, oldHeapOF), xObjField);
+ Bpl.Expr qq = new Bpl.ForallExpr(s.Tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fVar), body);
+ updater.Add(new Bpl.AssumeCmd(s.Tok, qq));
+
+ if (s0.Rhs is ExprRhs) {
+ // assume (forall x,y :: Range(x,y)[$Heap:=oldHeap] ==>
+ // $Heap[ Object(x,y)[$Heap:=oldHeap], Field(x,y)[$Heap:=oldHeap] ] == G[$Heap:=oldHeap] ));
+ xBvars = new Bpl.VariableSeq();
+ Bpl.Expr xAnte = etran.TrBoundVariables(s.BoundVars, xBvars);
+ xAnte = BplAnd(xAnte, prevEtran.TrExpr(s.Range));
+ var rhs = ((ExprRhs)s0.Rhs).Expr;
+ var g = prevEtran.TrExpr(rhs);
+ GetObjFieldDetails(s0.Lhs.Resolved, prevEtran, out xObj, out xField);
+ var xHeapOF = ExpressionTranslator.ReadHeap(s.Tok, etran.HeapExpr, xObj, xField);
+
+ Type lhsType;
+ if (lhs is FieldSelectExpr) {
+ lhsType = ((FieldSelectExpr)lhs).Type;
+ } else {
+ lhsType = null;
+ }
+ g = etran.CondApplyBox(rhs.tok, g, rhs.Type, lhsType);
+
+ qq = new Bpl.ForallExpr(s.Tok, xBvars, Bpl.Expr.Imp(xAnte, Bpl.Expr.Eq(xHeapOF, g)));
+ updater.Add(new Bpl.AssumeCmd(s.Tok, qq));
+ }
+ }
+
+ delegate Bpl.Expr ExpressionConverter(Dictionary<IVariable, Expression> substMap, ExpressionTranslator etran);
+
+ void TrParallelCall(IToken tok, List<BoundVar> boundVars, Expression range, ExpressionConverter additionalRange, CallStmt s0,
+ Bpl.StmtListBuilder definedness, Bpl.StmtListBuilder exporter, Bpl.VariableSeq locals, ExpressionTranslator etran) {
+ Contract.Requires(tok != null);
+ Contract.Requires(boundVars != null);
+ Contract.Requires(range != null);
+ // additionalRange is allowed to be null
+ Contract.Requires(s0 != null);
+ // definedness is allowed to be null
+ Contract.Requires(exporter != null);
+ Contract.Requires(locals != null);
+ Contract.Requires(etran != null);
+
+ // Translate:
+ // parallel (x,y | Range(x,y)) {
+ // E(x,y) . M( Args(x,y) );
+ // }
+ // as:
+ // if (*) {
+ // var x,y;
+ // havoc x,y;
+ // CheckWellformed( Range );
+ // assume Range(x,y);
+ // assume additionalRange;
+ // Tr( Call );
+ // assume false;
+ // } else {
+ // initHeap := $Heap;
+ // advance $Heap, Tick;
+ // assume (forall x,y :: (Range(x,y) && additionalRange)[INIT] &&
+ // ==> Post[old($Heap) := initHeap]( E(x,y)[INIT], Args(x,y)[INIT] ));
+ // }
+ // where Post(this,args) is the postcondition of method M and
+ // INIT is the substitution [old($Heap),$Heap := old($Heap),initHeap].
+
+ if (definedness != null) {
+ // Note, it would be nicer (and arguably more appropriate) to do a SetupBoundVarsAsLocals
+ // here (rather than a TrBoundVariables). However, there is currently no way to apply
+ // a substMap to a statement (in particular, to s.Body), so that doesn't work here.
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ var ante = etran.TrBoundVariables(boundVars, bvars, true);
+ locals.AddRange(bvars);
+ var havocIds = new Bpl.IdentifierExprSeq();
+ foreach (Bpl.Variable bv in bvars) {
+ havocIds.Add(new Bpl.IdentifierExpr(tok, bv));
+ }
+ definedness.Add(new Bpl.HavocCmd(tok, havocIds));
+ definedness.Add(new Bpl.AssumeCmd(tok, ante));
+ TrStmt_CheckWellformed(range, definedness, locals, etran, false);
+ definedness.Add(new Bpl.AssumeCmd(range.tok, etran.TrExpr(range)));
+ if (additionalRange != null) {
+ var es = additionalRange(new Dictionary<IVariable, Expression>(), etran);
+ definedness.Add(new Bpl.AssumeCmd(es.tok, es));
+ }
+
+ TrStmt(s0, definedness, locals, etran);
+
+ definedness.Add(new Bpl.AssumeCmd(tok, Bpl.Expr.False));
+ }
+
+ // Now for the other branch, where the postcondition of the call is exported.
+ {
+ var initHeapVar = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, "$initHeapParallelStmt#" + otherTmpVarCount, predef.HeapType));
+ otherTmpVarCount++;
+ locals.Add(initHeapVar);
+ var initHeap = new Bpl.IdentifierExpr(tok, initHeapVar);
+ var initEtran = new ExpressionTranslator(this, predef, initHeap, etran.Old.HeapExpr);
+ // initHeap := $Heap;
+ exporter.Add(Bpl.Cmd.SimpleAssign(tok, initHeap, etran.HeapExpr));
+ if (s0.Method.Ens.Exists(ens => MentionsOldState(ens.E))) {
+ var heapIdExpr = (Bpl.IdentifierExpr/*TODO: this cast is rather dubious*/)etran.HeapExpr;
+ // advance $Heap, Tick;
+ exporter.Add(new Bpl.HavocCmd(tok, new Bpl.IdentifierExprSeq(heapIdExpr, etran.Tick())));
+ foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(tok, new List<FrameExpression>(), initEtran, etran, initEtran)) {
+ if (tri.IsFree) {
+ exporter.Add(new Bpl.AssumeCmd(tok, tri.Expr));
+ }
+ }
+ if (codeContext is IteratorDecl) {
+ var iter = (IteratorDecl)codeContext;
+ RecordNewObjectsIn_New(tok, iter, initHeap, heapIdExpr, exporter, locals, etran);
+ }
+ } else {
+ // As an optimization, create the illusion that the $Heap is unchanged by the parallel body.
+ exporter.Add(new Bpl.HavocCmd(tok, new Bpl.IdentifierExprSeq(etran.Tick())));
+ }
+
+ var bvars = new Bpl.VariableSeq();
+ 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
+ // 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); // 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.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); // 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));
+ }
+ }
+
+ void RecordNewObjectsIn_New(IToken tok, IteratorDecl iter, Bpl.Expr initHeap, Bpl.IdentifierExpr currentHeap,
+ Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran) {
+ Contract.Requires(tok != null);
+ Contract.Requires(iter != null);
+ Contract.Requires(initHeap != null);
+ Contract.Requires(currentHeap != null);
+ Contract.Requires(builder != null);
+ 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, "$iter_newUpdate" + otherTmpVarCount, predef.SetType(iter.tok, predef.BoxType)));
+ otherTmpVarCount++;
+ locals.Add(updatedSet);
+ var updatedSetIE = new Bpl.IdentifierExpr(iter.tok, updatedSet);
+ // call $iter_newUpdate := $IterCollectNewObjects(initHeap, $Heap, this, _new);
+ var th = new Bpl.IdentifierExpr(iter.tok, etran.This, predef.RefType);
+ var nwField = new Bpl.IdentifierExpr(tok, GetField(iter.Member_New));
+ Bpl.Cmd cmd = new CallCmd(iter.tok, "$IterCollectNewObjects",
+ new List<Bpl.Expr>() { initHeap, etran.HeapExpr, th, nwField },
+ new List<Bpl.IdentifierExpr>() { updatedSetIE });
+ builder.Add(cmd);
+ // $Heap[this, _new] := $iter_newUpdate;
+ cmd = Bpl.Cmd.SimpleAssign(iter.tok, currentHeap, ExpressionTranslator.UpdateHeap(iter.tok, currentHeap, th, nwField, updatedSetIE));
+ builder.Add(cmd);
+ }
+
+ void TrParallelProof(ParallelStmt s, Bpl.StmtListBuilder definedness, Bpl.StmtListBuilder exporter, Bpl.VariableSeq locals, ExpressionTranslator etran) {
+ // Translate:
+ // parallel (x,y | Range(x,y))
+ // ensures Post(x,y);
+ // {
+ // Body;
+ // }
+ // as:
+ // if (*) {
+ // var x,y;
+ // havoc x,y;
+ // CheckWellformed( Range );
+ // assume Range(x,y);
+ // Tr( Body );
+ // CheckWellformed( Post );
+ // assert Post;
+ // assume false;
+ // } else {
+ // initHeap := $Heap;
+ // advance $Heap, Tick;
+ // assume (forall x,y :: Range(x,y)[old($Heap),$Heap := old($Heap),initHeap] ==> Post(x,y));
+ // }
+
+ // Note, it would be nicer (and arguably more appropriate) to do a SetupBoundVarsAsLocals
+ // here (rather than a TrBoundVariables). However, there is currently no way to apply
+ // a substMap to a statement (in particular, to s.Body), so that doesn't work here.
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ var ante = etran.TrBoundVariables(s.BoundVars, bvars, true);
+ locals.AddRange(bvars);
+ var havocIds = new Bpl.IdentifierExprSeq();
+ foreach (Bpl.Variable bv in bvars) {
+ havocIds.Add(new Bpl.IdentifierExpr(s.Tok, bv));
+ }
+ definedness.Add(new Bpl.HavocCmd(s.Tok, havocIds));
+ definedness.Add(new Bpl.AssumeCmd(s.Tok, ante));
+ TrStmt_CheckWellformed(s.Range, definedness, locals, etran, false);
+ definedness.Add(new Bpl.AssumeCmd(s.Range.tok, etran.TrExpr(s.Range)));
+
+ TrStmt(s.Body, definedness, locals, etran);
+
+ // check that postconditions hold
+ foreach (var ens in s.Ens) {
+ TrStmt_CheckWellformed(ens.E, definedness, locals, etran, false);
+ if (!ens.IsFree) {
+ bool splitHappened; // we actually don't care
+ foreach (var split in TrSplitExpr(ens.E, etran, out splitHappened)) {
+ if (!split.IsFree) {
+ definedness.Add(Assert(split.E.tok, split.E, "possible violation of postcondition of parallel statement"));
+ }
+ }
+ }
+ }
+
+ definedness.Add(new Bpl.AssumeCmd(s.Tok, Bpl.Expr.False));
+
+ // Now for the other branch, where the ensures clauses are exported.
+
+ var initHeapVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$initHeapParallelStmt#" + otherTmpVarCount, predef.HeapType));
+ otherTmpVarCount++;
+ locals.Add(initHeapVar);
+ var initHeap = new Bpl.IdentifierExpr(s.Tok, initHeapVar);
+ var initEtran = new ExpressionTranslator(this, predef, initHeap, etran.Old.HeapExpr);
+ // initHeap := $Heap;
+ exporter.Add(Bpl.Cmd.SimpleAssign(s.Tok, initHeap, etran.HeapExpr));
+ if (s.Ens.Exists(ens => MentionsOldState(ens.E))) {
+ // advance $Heap;
+ exporter.Add(new Bpl.HavocCmd(s.Tok, new Bpl.IdentifierExprSeq((Bpl.IdentifierExpr/*TODO: this cast is rather dubious*/)etran.HeapExpr, etran.Tick())));
+ foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(s.Tok, new List<FrameExpression>(), initEtran, etran, initEtran)) {
+ if (tri.IsFree) {
+ exporter.Add(new Bpl.AssumeCmd(s.Tok, tri.Expr));
+ }
+ }
+ } else {
+ // As an optimization, create the illusion that the $Heap is unchanged by the parallel body.
+ exporter.Add(new Bpl.HavocCmd(s.Tok, new Bpl.IdentifierExprSeq(etran.Tick())));
+ }
+
+ bvars = new Bpl.VariableSeq();
+ Dictionary<IVariable, Expression> substMap;
+ 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.Length != 0) {
+ qq = new Bpl.ForallExpr(s.Tok, bvars, qq);
+ }
+ exporter.Add(new Bpl.AssumeCmd(s.Tok, qq));
+ }
+
+ private string GetObjFieldDetails(Expression lhs, ExpressionTranslator etran, out Bpl.Expr obj, out Bpl.Expr F) {
+ string description;
+ if (lhs is FieldSelectExpr) {
+ var fse = (FieldSelectExpr)lhs;
+ obj = etran.TrExpr(fse.Obj);
+ F = GetField(fse);
+ description = "an object field";
+ } else if (lhs is SeqSelectExpr) {
+ var sel = (SeqSelectExpr)lhs;
+ obj = etran.TrExpr(sel.Seq);
+ F = FunctionCall(sel.tok, BuiltinFunction.IndexField, null, etran.TrExpr(sel.E0));
+ description = "an array element";
+ } else {
+ MultiSelectExpr mse = (MultiSelectExpr)lhs;
+ obj = etran.TrExpr(mse.Array);
+ F = etran.GetArrayIndexFieldName(mse.tok, mse.Indices);
+ description = "an array element";
+ }
+ return description;
+ }
+
+
+ delegate void BodyTranslator(Bpl.StmtListBuilder builder, ExpressionTranslator etran);
+
+
+ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator bodyTr,
+ Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran) {
+ Contract.Requires(s != null);
+ Contract.Requires(bodyTr != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(locals != null);
+ Contract.Requires(etran != null);
+
+ int loopId = loopHeapVarCount;
+ loopHeapVarCount++;
+
+ // use simple heuristics to create a default decreases clause, if none is given
+ bool inferredDecreases;
+ List<Expression> theDecreases = LoopDecreasesWithDefault(s.Tok, Guard, s.Decreases.Expressions, out inferredDecreases);
+
+ Bpl.LocalVariable preLoopHeapVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$PreLoopHeap" + loopId, predef.HeapType));
+ locals.Add(preLoopHeapVar);
+ Bpl.IdentifierExpr preLoopHeap = new Bpl.IdentifierExpr(s.Tok, preLoopHeapVar);
+ ExpressionTranslator etranPreLoop = new ExpressionTranslator(this, predef, preLoopHeap);
+ ExpressionTranslator updatedFrameEtran;
+ string loopFrameName = "#_Frame#" + loopId;
+ if(s.Mod.Expressions != null)
+ updatedFrameEtran = new ExpressionTranslator(etran, loopFrameName);
+ else
+ updatedFrameEtran = etran;
+
+ if (s.Mod.Expressions != null) { // check that the modifies is a strict subset
+ CheckFrameSubset(s.Tok, s.Mod.Expressions, null, null, etran, builder, "loop modifies clause may violate context's modifies clause", null);
+ DefineFrame(s.Tok, s.Mod.Expressions, builder, locals, loopFrameName);
+ }
+ builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, preLoopHeap, etran.HeapExpr));
+
+ List<Bpl.Expr> initDecr = null;
+ if (!Contract.Exists(theDecreases, e => e is WildcardExpr)) {
+ initDecr = RecordDecreasesValue(theDecreases, builder, locals, etran, "$decr" + loopId + "$init$");
+ }
+
+ // the variable w is used to coordinate the definedness checking of the loop invariant
+ Bpl.LocalVariable wVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$w" + loopId, Bpl.Type.Bool));
+ Bpl.IdentifierExpr w = new Bpl.IdentifierExpr(s.Tok, wVar);
+ locals.Add(wVar);
+ // havoc w;
+ builder.Add(new Bpl.HavocCmd(s.Tok, new Bpl.IdentifierExprSeq(w)));
+
+ List<Bpl.PredicateCmd> invariants = new List<Bpl.PredicateCmd>();
+ 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)));
+
+ invariants.Add(new Bpl.AssumeCmd(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))));
+ } else {
+ bool splitHappened;
+ var ss = TrSplitExpr(loopInv.E, etran, out splitHappened);
+ if (!splitHappened) {
+ var wInv = Bpl.Expr.Imp(w, etran.TrExpr(loopInv.E));
+ invariants.Add(Assert(loopInv.E.tok, wInv, "loop invariant violation"));
+ } else {
+ foreach (var split in ss) {
+ var wInv = Bpl.Expr.Binary(split.E.tok, BinaryOperator.Opcode.Imp, w, split.E);
+ if (split.IsFree) {
+ invariants.Add(new Bpl.AssumeCmd(split.E.tok, wInv));
+ } else {
+ invariants.Add(Assert(split.E.tok, wInv, "loop invariant violation")); // TODO: it would be fine to have this use {:subsumption 0}
+ }
+ }
+ }
+ }
+ }
+ // check definedness of decreases clause
+ // TODO: can this check be omitted if the decreases clause is inferred?
+ foreach (Expression e in theDecreases) {
+ TrStmt_CheckWellformed(e, invDefinednessBuilder, locals, etran, true);
+ }
+ // include boilerplate invariants
+ foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(s.Tok, codeContext.Modifies.Expressions, etranPreLoop, etran, etran.Old))
+ {
+ if (tri.IsFree) {
+ invariants.Add(new Bpl.AssumeCmd(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)));
+
+ // include a free invariant that says that all completed iterations so far have only decreased the termination metric
+ if (initDecr != null) {
+ List<IToken> toks = new List<IToken>();
+ List<Type> types = new List<Type>();
+ List<Bpl.Expr> decrs = new List<Bpl.Expr>();
+ foreach (Expression e in theDecreases) {
+ toks.Add(e.tok);
+ types.Add(cce.NonNull(e.Type));
+ decrs.Add(etran.TrExpr(e));
+ }
+ Bpl.Expr decrCheck = DecreasesCheck(toks, types, decrs, initDecr, etran, null, null, true, false);
+ invariants.Add(new Bpl.AssumeCmd(s.Tok, decrCheck));
+ }
+
+ Bpl.StmtListBuilder loopBodyBuilder = new Bpl.StmtListBuilder();
+ // as the first thing inside the loop, generate: if (!w) { assert IsTotal(inv); assume false; }
+ invDefinednessBuilder.Add(new Bpl.AssumeCmd(s.Tok, Bpl.Expr.False));
+ loopBodyBuilder.Add(new Bpl.IfCmd(s.Tok, Bpl.Expr.Not(w), invDefinednessBuilder.Collect(s.Tok), null, null));
+ // generate: assert IsTotal(guard); if (!guard) { break; }
+ Bpl.Expr guard = null;
+ if (Guard != null) {
+ TrStmt_CheckWellformed(Guard, loopBodyBuilder, locals, etran, true);
+ guard = Bpl.Expr.Not(etran.TrExpr(Guard));
+ }
+ Bpl.StmtListBuilder guardBreak = new Bpl.StmtListBuilder();
+ guardBreak.Add(new Bpl.BreakCmd(s.Tok, null));
+ loopBodyBuilder.Add(new Bpl.IfCmd(s.Tok, guard, guardBreak.Collect(s.Tok), null, null));
+
+ loopBodyBuilder.Add(CaptureState(s.Tok, "loop entered"));
+ // termination checking
+ if (Contract.Exists(theDecreases, e => e is WildcardExpr)) {
+ // omit termination checking for this loop
+ bodyTr(loopBodyBuilder, updatedFrameEtran);
+ } else {
+ List<Bpl.Expr> oldBfs = RecordDecreasesValue(theDecreases, loopBodyBuilder, locals, etran, "$decr" + loopId + "$");
+ // time for the actual loop body
+ bodyTr(loopBodyBuilder, updatedFrameEtran);
+ // check definedness of decreases expressions
+ List<IToken> toks = new List<IToken>();
+ List<Type> types = new List<Type>();
+ List<Bpl.Expr> decrs = new List<Bpl.Expr>();
+ foreach (Expression e in theDecreases) {
+ toks.Add(e.tok);
+ types.Add(cce.NonNull(e.Type));
+ decrs.Add(etran.TrExpr(e));
+ }
+ Bpl.Expr decrCheck = DecreasesCheck(toks, types, decrs, oldBfs, etran, loopBodyBuilder, " at end of loop iteration", false, false);
+ string msg;
+ if (inferredDecreases) {
+ msg = "cannot prove termination; try supplying a decreases clause for the loop";
+ if (s is RefinedWhileStmt) {
+ msg += " (note that a refined loop does not inherit 'decreases *' from the refined loop)";
+ }
+ } else {
+ msg = "decreases expression might not decrease";
+ }
+ loopBodyBuilder.Add(Assert(s.Tok, decrCheck, msg));
+ }
+ // 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)));
+ }
+ Bpl.StmtList body = loopBodyBuilder.Collect(s.Tok);
+
+ builder.Add(new Bpl.WhileCmd(s.Tok, Bpl.Expr.True, invariants, body));
+ builder.Add(CaptureState(s.Tok, "loop exit"));
+ }
+
+ void TrAlternatives(List<GuardedAlternative> alternatives, Bpl.Cmd elseCase0, Bpl.StructuredCmd elseCase1,
+ Bpl.StmtListBuilder builder, VariableSeq locals, ExpressionTranslator etran) {
+ Contract.Requires(alternatives != null);
+ 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);
+
+ if (alternatives.Count == 0) {
+ if (elseCase0 != null) {
+ builder.Add(elseCase0);
+ } else {
+ builder.Add(elseCase1);
+ }
+ return;
+ }
+
+ // 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)));
+ }
+
+ var b = new Bpl.StmtListBuilder();
+ var elseTok = elseCase0 != null ? elseCase0.tok : elseCase1.tok;
+ b.Add(new Bpl.AssumeCmd(elseTok, noGuard));
+ if (elseCase0 != null) {
+ b.Add(elseCase0);
+ } else {
+ b.Add(elseCase1);
+ }
+ Bpl.StmtList els = b.Collect(elseTok);
+
+ Bpl.IfCmd elsIf = null;
+ for (int i = alternatives.Count; 0 <= --i; ) {
+ Contract.Assert(elsIf == null || els == null); // loop invariant
+ 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)));
+ foreach (var s in alternative.Body) {
+ TrStmt(s, b, locals, etran);
+ }
+ Bpl.StmtList thn = b.Collect(alternative.Tok);
+ elsIf = new Bpl.IfCmd(alternative.Tok, null, thn, elsIf, els);
+ els = null;
+ }
+ Contract.Assert(elsIf != null && els == null); // follows from loop invariant and the fact that there's more than one alternative
+ builder.Add(elsIf);
+ }
+
+ void TrCallStmt(CallStmt s, Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran, Bpl.Expr actualReceiver) {
+ List<AssignToLhs> lhsBuilders;
+ List<Bpl.IdentifierExpr> bLhss;
+ Bpl.Expr[] ignore1, ignore2;
+ string[] ignore3;
+ ProcessLhss(s.Lhs, true, true, builder, locals, etran, out lhsBuilders, out bLhss, out ignore1, out ignore2, out ignore3);
+ Contract.Assert(s.Lhs.Count == lhsBuilders.Count);
+ Contract.Assert(s.Lhs.Count == bLhss.Count);
+ var lhsTypes = new List<Type>();
+ for (int i = 0; i < s.Lhs.Count; i++) {
+ var lhs = s.Lhs[i];
+ lhsTypes.Add(lhs.Type);
+ if (bLhss[i] == null) { // (in the current implementation, the second parameter "true" to ProcessLhss implies that all bLhss[*] will be null)
+ // create temporary local and assign it to bLhss[i]
+ string nm = "$rhs##" + otherTmpVarCount;
+ otherTmpVarCount++;
+ var ty = TrType(lhs.Type);
+ Bpl.Expr wh = GetWhereClause(lhs.tok, new Bpl.IdentifierExpr(lhs.tok, nm, ty), lhs.Type, etran);
+ Bpl.LocalVariable var = new Bpl.LocalVariable(lhs.tok, new Bpl.TypedIdent(lhs.tok, nm, ty, wh));
+ locals.Add(var);
+ bLhss[i] = new Bpl.IdentifierExpr(lhs.tok, var.Name, ty);
+ }
+ }
+ Bpl.IdentifierExpr initHeap = null;
+ if (codeContext is IteratorDecl) {
+ // var initHeap := $Heap;
+ var initHeapVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$initHeapCallStmt#" + otherTmpVarCount, predef.HeapType));
+ otherTmpVarCount++;
+ locals.Add(initHeapVar);
+ initHeap = new Bpl.IdentifierExpr(s.Tok, initHeapVar);
+ // initHeap := $Heap;
+ builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, initHeap, etran.HeapExpr));
+ }
+ ProcessCallStmt(s.Tok, s.Receiver, actualReceiver, s.Method, s.Args, bLhss, lhsTypes, builder, locals, etran);
+ for (int i = 0; i < lhsBuilders.Count; i++) {
+ var lhs = s.Lhs[i];
+ Type lhsType = null;
+ if (lhs is IdentifierExpr) {
+ lhsType = lhs.Type;
+ } else if (lhs is FieldSelectExpr) {
+ var fse = (FieldSelectExpr)lhs;
+ lhsType = fse.Field.Type;
+ }
+
+ Bpl.Expr bRhs = bLhss[i]; // the RHS (bRhs) of the assignment to the actual call-LHS (lhs) was a LHS (bLhss[i]) in the Boogie call statement
+ if (lhsType != null) {
+ CheckSubrange(lhs.tok, bRhs, lhsType, builder);
+ }
+ bRhs = etran.CondApplyBox(lhs.tok, bRhs, lhs.Type, lhsType);
+
+ lhsBuilders[i](bRhs, builder, etran);
+ }
+ if (codeContext is IteratorDecl) {
+ var iter = (IteratorDecl)codeContext;
+ Contract.Assert(initHeap != null);
+ RecordNewObjectsIn_New(s.Tok, iter, initHeap, (Bpl.IdentifierExpr/*TODO: this cast is dubious*/)etran.HeapExpr, builder, locals, etran);
+ }
+ builder.Add(CaptureState(s.Tok));
+ }
+
+ void ProcessCallStmt(IToken tok,
+ Expression dafnyReceiver, Bpl.Expr bReceiver,
+ Method method, List<Expression> Args,
+ List<Bpl.IdentifierExpr> Lhss, List<Type> LhsTypes,
+ Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran) {
+
+ Contract.Requires(tok != null);
+ Contract.Requires(dafnyReceiver != null || bReceiver != null);
+ Contract.Requires(method != null);
+ Contract.Requires(cce.NonNullElements(Args));
+ Contract.Requires(cce.NonNullElements(Lhss));
+ Contract.Requires(cce.NonNullElements(LhsTypes));
+ Contract.Requires(method.Outs.Count == Lhss.Count);
+ Contract.Requires(method.Outs.Count == LhsTypes.Count);
+ Contract.Requires(builder != null);
+ Contract.Requires(locals != null);
+ Contract.Requires(etran != null);
+
+ Expression receiver = bReceiver == null ? dafnyReceiver : new BoogieWrapper(bReceiver, dafnyReceiver.Type);
+ Bpl.ExprSeq ins = new Bpl.ExprSeq();
+ if (!method.IsStatic) {
+ if (bReceiver == null) {
+ if (!(dafnyReceiver is ThisExpr)) {
+ CheckNonNull(dafnyReceiver.tok, dafnyReceiver, builder, etran, null);
+ }
+ }
+ ins.Add(etran.TrExpr(receiver));
+ }
+
+ // Ideally, the modifies and decreases checks would be done after the precondition check,
+ // but Boogie doesn't give us a hook for that. So, we set up our own local variables here to
+ // store the actual parameters.
+ // Create a local variable for each formal parameter, and assign each actual parameter to the corresponding local
+ Dictionary<IVariable, Expression> substMap = new Dictionary<IVariable, Expression>();
+ for (int i = 0; i < method.Ins.Count; i++) {
+ Formal p = method.Ins[i];
+ VarDecl local = new VarDecl(p.tok, p.Name + "#", p.Type, p.IsGhost);
+ local.type = local.OptionalType; // resolve local here
+ IdentifierExpr ie = new IdentifierExpr(local.Tok, local.UniqueName);
+ ie.Var = local; ie.Type = ie.Var.Type; // resolve ie here
+ substMap.Add(p, ie);
+ locals.Add(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.UniqueName, TrType(local.Type))));
+
+ Bpl.IdentifierExpr param = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified?
+ Expression actual = Args[i];
+ TrStmt_CheckWellformed(actual, builder, locals, etran, true);
+ var bActual = etran.TrExpr(actual);
+ CheckSubrange(actual.tok, bActual, p.Type, builder);
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(p.tok, param, etran.CondApplyBox(actual.tok, bActual, cce.NonNull(actual.Type), method.Ins[i].Type));
+ builder.Add(cmd);
+ ins.Add(param);
+ }
+
+ // Check modifies clause of a subcall is a subset of the current frame.
+ CheckFrameSubset(tok, method.Mod.Expressions, receiver, substMap, etran, builder, "call may violate context's modifies clause", null);
+
+ // Check termination
+ ModuleDefinition module = method.EnclosingClass.Module;
+ if (module == currentModule) {
+ var caller = codeContext is Method ? (Method)codeContext : ((IteratorDecl)codeContext).Member_MoveNext;
+ if (module.CallGraph.GetSCCRepresentative(method) == module.CallGraph.GetSCCRepresentative(caller)) {
+ bool contextDecrInferred, calleeDecrInferred;
+ List<Expression> contextDecreases = MethodDecreasesWithDefault(caller, out contextDecrInferred);
+ List<Expression> calleeDecreases = MethodDecreasesWithDefault(method, out calleeDecrInferred);
+ CheckCallTermination(tok, contextDecreases, calleeDecreases, null, receiver, substMap, etran, builder, contextDecrInferred, null);
+ }
+ }
+
+ // Create variables to hold the output parameters of the call, so that appropriate unboxes can be introduced.
+ Bpl.IdentifierExprSeq outs = new Bpl.IdentifierExprSeq();
+ List<Bpl.IdentifierExpr> tmpOuts = new List<Bpl.IdentifierExpr>();
+ for (int i = 0; i < Lhss.Count; i++) {
+ var bLhs = Lhss[i];
+ if (ExpressionTranslator.ModeledAsBoxType(method.Outs[i].Type) && !ExpressionTranslator.ModeledAsBoxType(LhsTypes[i])) {
+ // we need an Unbox
+ Bpl.LocalVariable var = new Bpl.LocalVariable(bLhs.tok, new Bpl.TypedIdent(bLhs.tok, "$tmp##" + otherTmpVarCount, predef.BoxType));
+ otherTmpVarCount++;
+ locals.Add(var);
+ Bpl.IdentifierExpr varIdE = new Bpl.IdentifierExpr(bLhs.tok, var.Name, predef.BoxType);
+ tmpOuts.Add(varIdE);
+ outs.Add(varIdE);
+ } else {
+ tmpOuts.Add(null);
+ outs.Add(bLhs);
+ }
+ }
+
+ // Make the call
+ string name;
+ if (RefinementToken.IsInherited(method.tok, currentModule) && (codeContext == null || !codeContext.MustReverify)) {
+ name = string.Format("RefinementCall_{0}$${1}", currentModule.Name, method.FullCompileName);
+ } else {
+ name = method.FullCompileName;
+ }
+ Bpl.CallCmd call = new Bpl.CallCmd(tok, name, ins, outs);
+ builder.Add(call);
+
+ // Unbox results as needed
+ for (int i = 0; i < Lhss.Count; i++) {
+ Bpl.IdentifierExpr bLhs = Lhss[i];
+ Bpl.IdentifierExpr tmpVarIdE = tmpOuts[i];
+ if (tmpVarIdE != null) {
+ // Instead of an assignment:
+ // e := UnBox(tmpVar);
+ // we use:
+ // havoc e; assume e == UnBox(tmpVar);
+ // because that will reap the benefits of e's where clause, so that some additional type information will be known about
+ // the out-parameter.
+ Bpl.Cmd cmd = new Bpl.HavocCmd(bLhs.tok, new IdentifierExprSeq(bLhs));
+ builder.Add(cmd);
+ cmd = new Bpl.AssumeCmd(bLhs.tok, Bpl.Expr.Eq(bLhs, FunctionCall(bLhs.tok, BuiltinFunction.Unbox, TrType(LhsTypes[i]), tmpVarIdE)));
+ builder.Add(cmd);
+ }
+ }
+ }
+
+ static Expression CreateIntLiteral(IToken tok, int n)
+ {
+ Contract.Requires(tok != null);
+ Contract.Ensures(Contract.Result<Expression>() != null);
+
+ if (0 <= n) {
+ Expression lit = new LiteralExpr(tok, n);
+ lit.Type = Type.Int; // resolve here
+ return lit;
+ } else {
+ return CreateIntSub(tok, CreateIntLiteral(tok, 0), CreateIntLiteral(tok, -n));
+ }
+ }
+
+ static Expression CreateIntSub(IToken tok, Expression e0, Expression e1)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(e0 != null);
+ Contract.Requires(e1 != null);
+ Contract.Requires(e0.Type is IntType && e1.Type is IntType);
+ Contract.Ensures(Contract.Result<Expression>() != null);
+ BinaryExpr s = new BinaryExpr(tok, BinaryExpr.Opcode.Sub, e0, e1);
+ s.ResolvedOp = BinaryExpr.ResolvedOpcode.Sub; // resolve here
+ s.Type = Type.Int; // resolve here
+ return s;
+ }
+
+ static Expression CreateIntITE(IToken tok, Expression test, Expression e0, Expression e1)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(test != null);
+ Contract.Requires(e0 != null);
+ Contract.Requires(e1 != null);
+ Contract.Requires(test.Type is BoolType && e0.Type is IntType && e1.Type is IntType);
+ Contract.Ensures(Contract.Result<Expression>() != null);
+
+ ITEExpr ite = new ITEExpr(tok, test, e0, e1);
+ ite.Type = Type.Int; // resolve here
+ return ite;
+ }
+
+ public IEnumerable<Expression> Conjuncts(Expression expr)
+ {
+ Contract.Requires(expr != null);
+ Contract.Requires(expr.Type is BoolType);
+ Contract.Ensures(cce.NonNullElements(Contract.Result<IEnumerable<Expression>>()));
+
+ var bin = expr as BinaryExpr;
+ if (bin != null && bin.ResolvedOp == BinaryExpr.ResolvedOpcode.And) {
+ foreach (Expression e in Conjuncts(bin.E0)) {
+ yield return e;
+ }
+ foreach (Expression e in Conjuncts(bin.E1)) {
+ yield return e;
+ }
+ yield break;
+ }
+ yield return expr;
+ }
+
+ Dictionary<IVariable, Expression> SetupBoundVarsAsLocals(List<BoundVar> boundVars, StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran) {
+ Contract.Requires(boundVars != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(locals != null);
+
+ var substMap = new Dictionary<IVariable, Expression>();
+ foreach (BoundVar bv in boundVars) {
+ VarDecl local = new VarDecl(bv.tok, bv.Name, bv.Type, bv.IsGhost);
+ local.type = local.OptionalType; // resolve local here
+ IdentifierExpr ie = new IdentifierExpr(local.Tok, local.UniqueName);
+ ie.Var = local; ie.Type = ie.Var.Type; // resolve ie here
+ substMap.Add(bv, ie);
+ Bpl.LocalVariable bvar = new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.UniqueName, TrType(local.Type)));
+ locals.Add(bvar);
+ var bIe = new Bpl.IdentifierExpr(bvar.tok, bvar);
+ builder.Add(new Bpl.HavocCmd(bv.tok, new IdentifierExprSeq(bIe)));
+ Bpl.Expr wh = GetWhereClause(bv.tok, bIe, local.Type, etran);
+ if (wh != null) {
+ builder.Add(new Bpl.AssumeCmd(bv.tok, wh));
+ }
+ }
+ return substMap;
+ }
+
+ List<Bpl.Expr> RecordDecreasesValue(List<Expression> decreases, Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran, string varPrefix)
+ {
+ Contract.Requires(locals != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(varPrefix != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(decreases != null);
+ List<Bpl.Expr> oldBfs = new List<Bpl.Expr>();
+ int c = 0;
+ foreach (Expression e in decreases) {
+ Contract.Assert(e != null);
+ Bpl.LocalVariable bfVar = new Bpl.LocalVariable(e.tok, new Bpl.TypedIdent(e.tok, varPrefix + c, TrType(cce.NonNull(e.Type))));
+ locals.Add(bfVar);
+ Bpl.IdentifierExpr bf = new Bpl.IdentifierExpr(e.tok, bfVar);
+ oldBfs.Add(bf);
+ // record value of each decreases expression at beginning of the loop iteration
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(e.tok, bf, etran.TrExpr(e));
+ builder.Add(cmd);
+
+ c++;
+ }
+ return oldBfs;
+ }
+
+ /// <summary>
+ /// Emit to "builder" a check that calleeDecreases is less than contextDecreases. More precisely,
+ /// the check is:
+ /// allowance || (calleeDecreases LESS contextDecreases).
+ /// </summary>
+ void CheckCallTermination(IToken/*!*/ tok, List<Expression/*!*/>/*!*/ contextDecreases, List<Expression/*!*/>/*!*/ calleeDecreases,
+ Bpl.Expr allowance,
+ Expression receiverReplacement, Dictionary<IVariable,Expression/*!*/>/*!*/ substMap,
+ ExpressionTranslator/*!*/ etran, Bpl.StmtListBuilder/*!*/ builder, bool inferredDecreases, string hint) {
+ Contract.Requires(tok != null);
+ Contract.Requires(cce.NonNullElements(contextDecreases));
+ Contract.Requires(cce.NonNullElements(calleeDecreases));
+ Contract.Requires(cce.NonNullDictionaryAndValues(substMap));
+ Contract.Requires(etran != null);
+ Contract.Requires(builder != null);
+
+ // The interpretation of the given decreases-clause expression tuples is as a lexicographic tuple, extended into
+ // an infinite tuple by appending TOP elements. The TOP element is strictly larger than any other value given
+ // by a Dafny expression. Each Dafny types has its own ordering, and these orderings are combined into a partial
+ // order where elements from different Dafny types are incomparable. Thus, as an optimization below, if two
+ // components from different types are compared, the answer is taken to be false.
+
+ if (Contract.Exists(calleeDecreases, e => e is WildcardExpr)) {
+ // no check needed
+ return;
+ }
+
+ int N = Math.Min(contextDecreases.Count, calleeDecreases.Count);
+ List<IToken> toks = new List<IToken>();
+ List<Type> types = new List<Type>();
+ List<Bpl.Expr> callee = new List<Bpl.Expr>();
+ List<Bpl.Expr> caller = new List<Bpl.Expr>();
+ for (int i = 0; i < N; i++) {
+ Expression e0 = Substitute(calleeDecreases[i], receiverReplacement, substMap);
+ Expression e1 = contextDecreases[i];
+ if (!CompatibleDecreasesTypes(cce.NonNull(e0.Type), cce.NonNull(e1.Type))) {
+ N = i;
+ break;
+ }
+ toks.Add(tok);
+ types.Add(e0.Type);
+ callee.Add(etran.TrExpr(e0));
+ caller.Add(etran.TrExpr(e1));
+ }
+ bool endsWithWinningTopComparison = N == contextDecreases.Count && N < calleeDecreases.Count;
+ Bpl.Expr decrExpr = DecreasesCheck(toks, types, callee, caller, etran, builder, "", endsWithWinningTopComparison, false);
+ if (allowance != null) {
+ decrExpr = Bpl.Expr.Or(allowance, decrExpr);
+ }
+ string msg = inferredDecreases ? "cannot prove termination; try supplying a decreases clause" : "failure to decrease termination measure";
+ if (hint != null) {
+ msg += " (" + hint + ")";
+ }
+ builder.Add(Assert(tok, decrExpr, msg));
+ }
+
+ /// <summary>
+ /// Returns the expression that says whether or not the decreases function has gone down (if !allowNoChange)
+ /// or has gone down or stayed the same (if allowNoChange).
+ /// ee0 represents the new values and ee1 represents old values.
+ /// If builder is non-null, then the check '0 ATMOST decr' is generated to builder.
+ /// </summary>
+ Bpl.Expr DecreasesCheck(List<IToken/*!*/>/*!*/ toks, List<Type/*!*/>/*!*/ types, List<Bpl.Expr/*!*/>/*!*/ ee0, List<Bpl.Expr/*!*/>/*!*/ ee1,
+ ExpressionTranslator/*!*/ etran, Bpl.StmtListBuilder builder, string suffixMsg, bool allowNoChange, bool includeLowerBound)
+ {
+ Contract.Requires(cce.NonNullElements(toks));
+ Contract.Requires(cce.NonNullElements(types));
+ Contract.Requires(cce.NonNullElements(ee0));
+ Contract.Requires(cce.NonNullElements(ee1));
+ Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
+ Contract.Requires(types.Count == ee0.Count && ee0.Count == ee1.Count);
+ Contract.Requires(builder == null || suffixMsg != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ int N = types.Count;
+
+ // compute eq and less for each component of the lexicographic pair
+ List<Bpl.Expr> Eq = new List<Bpl.Expr>(N);
+ List<Bpl.Expr> Less = new List<Bpl.Expr>(N);
+ for (int i = 0; i < N; i++) {
+ Bpl.Expr less, atmost, eq;
+ ComputeLessEq(toks[i], types[i], ee0[i], ee1[i], out less, out atmost, out eq, etran, includeLowerBound);
+ Eq.Add(eq);
+ Less.Add(allowNoChange ? atmost : less);
+ }
+ if (builder != null) {
+ // check: 0 <= ee1
+ // more precisely, for component k of the lexicographic decreases function, check:
+ // ee0[0] < ee1[0] || ee0[1] < ee1[1] || ... || ee0[k-1] < ee1[k-1] || ee0[k] == ee1[k] || 0 <= ee1[k]
+ for (int k = 0; k < N; k++) {
+ // we only need to check lower bound for integers--sets, sequences, booleans, references, and datatypes all have natural lower bounds
+ Bpl.Expr prefixIsLess = Bpl.Expr.False;
+ for (int i = 0; i < k; i++) {
+ prefixIsLess = Bpl.Expr.Or(prefixIsLess, Less[i]);
+ }
+ if (types[k] is IntType) {
+ Bpl.Expr bounded = Bpl.Expr.Le(Bpl.Expr.Literal(0), ee1[k]);
+ for (int i = 0; i < k; i++) {
+ bounded = Bpl.Expr.Or(bounded, Less[i]);
+ }
+ string component = N == 1 ? "" : " (component " + k + ")";
+ Bpl.Cmd cmd = Assert(toks[k], Bpl.Expr.Or(bounded, Eq[k]), "decreases expression" + component + " must be bounded below by 0" + suffixMsg);
+ builder.Add(cmd);
+ }
+ }
+ }
+ // check: ee0 < ee1 (or ee0 <= ee1, if allowNoChange)
+ Bpl.Expr decrCheck = allowNoChange ? Bpl.Expr.True : Bpl.Expr.False;
+ for (int i = N; 0 <= --i; ) {
+ Bpl.Expr less = Less[i];
+ Bpl.Expr eq = Eq[i];
+ if (allowNoChange) {
+ // decrCheck = atmost && (eq ==> decrCheck)
+ decrCheck = Bpl.Expr.And(less, Bpl.Expr.Imp(eq, decrCheck));
+ } else {
+ // decrCheck = less || (eq && decrCheck)
+ decrCheck = Bpl.Expr.Or(less, Bpl.Expr.And(eq, decrCheck));
+ }
+ }
+ return decrCheck;
+ }
+
+ bool CompatibleDecreasesTypes(Type t, Type u) {
+ Contract.Requires(t != null);
+ Contract.Requires(u != null);
+ if (t is BoolType) {
+ return u is BoolType;
+ } else if (t is IntType) {
+ return u is IntType;
+ } else if (t is SetType) {
+ return u is SetType;
+ } else if (t is SeqType) {
+ return u is SeqType;
+ } else if (t.IsDatatype) {
+ return u.IsDatatype;
+ } else if (t.IsRefType) {
+ return u.IsRefType;
+ } else if (t is MapType) {
+ return false;
+ } else {
+ Contract.Assert(t.IsTypeParameter);
+ return false; // don't consider any type parameters to be the same (since we have no comparison function for them anyway)
+ }
+ }
+
+ void ComputeLessEq(IToken/*!*/ tok, Type/*!*/ ty, Bpl.Expr/*!*/ e0, Bpl.Expr/*!*/ e1, out Bpl.Expr/*!*/ less, out Bpl.Expr/*!*/ atmost, out Bpl.Expr/*!*/ eq,
+ ExpressionTranslator/*!*/ etran, bool includeLowerBound)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(ty != null);
+ Contract.Requires(e0 != null);
+ Contract.Requires(e1 != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.ValueAtReturn(out less)!=null);
+ Contract.Ensures(Contract.ValueAtReturn(out atmost)!=null);
+ Contract.Ensures(Contract.ValueAtReturn(out eq)!=null);
+
+ if (ty is BoolType) {
+ eq = Bpl.Expr.Iff(e0, e1);
+ less = Bpl.Expr.And(Bpl.Expr.Not(e0), e1);
+ atmost = Bpl.Expr.Imp(e0, e1);
+ } else if (ty is IntType) {
+ eq = Bpl.Expr.Eq(e0, e1);
+ less = Bpl.Expr.Lt(e0, e1);
+ atmost = Bpl.Expr.Le(e0, e1);
+ if (includeLowerBound) {
+ less = Bpl.Expr.And(Bpl.Expr.Le(Bpl.Expr.Literal(0), e0), less);
+ atmost = Bpl.Expr.And(Bpl.Expr.Le(Bpl.Expr.Literal(0), e0), atmost);
+ }
+ } else if (ty is EverIncreasingType) {
+ eq = Bpl.Expr.Eq(e0, e1);
+ less = Bpl.Expr.Gt(e0, e1);
+ atmost = Bpl.Expr.Ge(e0, e1);
+ } else if (ty is SetType) {
+ eq = FunctionCall(tok, BuiltinFunction.SetEqual, null, e0, e1);
+ less = etran.ProperSubset(tok, e0, e1);
+ atmost = FunctionCall(tok, BuiltinFunction.SetSubset, null, e0, e1);
+ } else if (ty is SeqType) {
+ Bpl.Expr b0 = FunctionCall(tok, BuiltinFunction.SeqLength, null, e0);
+ Bpl.Expr b1 = FunctionCall(tok, BuiltinFunction.SeqLength, null, e1);
+ eq = Bpl.Expr.Eq(b0, b1);
+ less = Bpl.Expr.Lt(b0, b1);
+ atmost = Bpl.Expr.Le(b0, b1);
+ } else if (ty.IsDatatype) {
+ Bpl.Expr b0 = FunctionCall(tok, BuiltinFunction.DtRank, null, e0);
+ Bpl.Expr b1 = FunctionCall(tok, BuiltinFunction.DtRank, null, e1);
+ eq = Bpl.Expr.Eq(b0, b1);
+ less = Bpl.Expr.Lt(b0, b1);
+ atmost = Bpl.Expr.Le(b0, b1);
+
+ } else {
+ // reference type
+ Bpl.Expr b0 = Bpl.Expr.Neq(e0, predef.Null);
+ Bpl.Expr b1 = Bpl.Expr.Neq(e1, predef.Null);
+ eq = Bpl.Expr.Iff(b0, b1);
+ less = Bpl.Expr.And(Bpl.Expr.Not(b0), b1);
+ atmost = Bpl.Expr.Imp(b0, b1);
+ }
+ }
+
+ void AddComment(Bpl.StmtListBuilder builder, Statement stmt, string comment) {
+ Contract.Requires(builder != null);
+ Contract.Requires(stmt != null);
+ Contract.Requires(comment != null);
+ builder.Add(new Bpl.CommentCmd(string.Format("----- {0} ----- {1}({2},{3})", comment, stmt.Tok.filename, stmt.Tok.line, stmt.Tok.col)));
+ }
+
+ Bpl.Expr GetWhereClause(IToken tok, Bpl.Expr x, Type type, ExpressionTranslator etran)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(x != null);
+ Contract.Requires(type != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
+ while (true) {
+ TypeProxy proxy = type as TypeProxy;
+ if (proxy == null) {
+ break;
+ } else if (proxy.T == null) {
+ // 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;
+ } else {
+ type = proxy.T;
+ }
+ }
+
+ if (type is NatType) {
+ // nat:
+ // 0 <= x
+ return Bpl.Expr.Le(Bpl.Expr.Literal(0), x);
+
+ } else if (type is BoolType || type is IntType) {
+ // nothing to do
+
+ } else if (type is SetType) {
+ SetType st = (SetType)type;
+ // (forall t: BoxType :: { x[t] } x[t] ==> Unbox(t)-has-the-expected-type)
+ Bpl.BoundVariable tVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$t#" + otherTmpVarCount, predef.BoxType));
+ otherTmpVarCount++;
+ Bpl.Expr t = new Bpl.IdentifierExpr(tok, tVar);
+ Bpl.Expr xSubT = Bpl.Expr.SelectTok(tok, x, t);
+ Bpl.Expr unboxT = ExpressionTranslator.ModeledAsBoxType(st.Arg) ? t : FunctionCall(tok, BuiltinFunction.Unbox, TrType(st.Arg), t);
+
+ Bpl.Expr wh = GetWhereClause(tok, unboxT, st.Arg, etran);
+ if (wh != null) {
+ Bpl.Trigger tr = new Bpl.Trigger(tok, true, new Bpl.ExprSeq(xSubT));
+ return new Bpl.ForallExpr(tok, new Bpl.VariableSeq(tVar), tr, Bpl.Expr.Imp(xSubT, wh));
+ }
+
+ } else if (type is MultiSetType) {
+ MultiSetType st = (MultiSetType)type;
+ // $IsGoodMultiSet(x) && (forall t: BoxType :: { x[t] } 0 < x[t] ==> Unbox(t)-has-the-expected-type)
+ Bpl.BoundVariable tVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$t#" + otherTmpVarCount, predef.BoxType));
+ otherTmpVarCount++;
+ Bpl.Expr t = new Bpl.IdentifierExpr(tok, tVar);
+ Bpl.Expr xSubT = Bpl.Expr.Gt(Bpl.Expr.SelectTok(tok, x, t), Bpl.Expr.Literal(0));
+ Bpl.Expr unboxT = ExpressionTranslator.ModeledAsBoxType(st.Arg) ? t : FunctionCall(tok, BuiltinFunction.Unbox, TrType(st.Arg), t);
+
+ Bpl.Expr isGoodMultiset = FunctionCall(tok, BuiltinFunction.IsGoodMultiSet, null, x);
+ Bpl.Expr wh = GetWhereClause(tok, unboxT, st.Arg, etran);
+ if (wh != null) {
+ Bpl.Trigger tr = new Bpl.Trigger(tok, true, new Bpl.ExprSeq(xSubT));
+ var q = new Bpl.ForallExpr(tok, new Bpl.VariableSeq(tVar), tr, Bpl.Expr.Imp(xSubT, wh));
+ isGoodMultiset = Bpl.Expr.And(isGoodMultiset, q);
+ }
+ return isGoodMultiset;
+ } else if (type is SeqType) {
+ SeqType st = (SeqType)type;
+ // (forall i: int :: { Seq#Index(x,i) }
+ // 0 <= i && i < Seq#Length(x) ==> Unbox(Seq#Index(x,i))-has-the-expected-type)
+ Bpl.BoundVariable iVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$i#" + otherTmpVarCount, Bpl.Type.Int));
+ otherTmpVarCount++;
+ Bpl.Expr i = new Bpl.IdentifierExpr(tok, iVar);
+ Bpl.Expr xSubI = FunctionCall(tok, BuiltinFunction.SeqIndex, predef.BoxType, x, i);
+ Bpl.Expr unbox = ExpressionTranslator.ModeledAsBoxType(st.Arg) ? xSubI : FunctionCall(tok, BuiltinFunction.Unbox, TrType(st.Arg), xSubI);
+
+ Bpl.Expr c = GetBoolBoxCondition(xSubI, st.Arg);
+ Bpl.Expr wh = GetWhereClause(tok, unbox, st.Arg, etran);
+ if (wh != null) {
+ c = BplAnd(c, wh);
+ }
+ if (c != Bpl.Expr.True) {
+ Bpl.Expr range = InSeqRange(tok, i, x, true, null, false);
+ Bpl.Trigger tr = new Bpl.Trigger(tok, true, new Bpl.ExprSeq(xSubI));
+ return new Bpl.ForallExpr(tok, new Bpl.VariableSeq(iVar), tr, Bpl.Expr.Imp(range, c));
+ }
+
+ } else if (type is MapType) {
+ MapType mt = (MapType)type;
+ Bpl.Type maptype = predef.MapType(tok, predef.BoxType, predef.BoxType);
+ Bpl.Expr clause = null;
+ // (forall i: BoxType :: { Map#Domain(x)[i] }
+ // Map#Domain(x)[i] ==> Unbox(i)-has-the-expected-type)
+ // (forall i: BoxType :: { Map#Elements(x)[i] }
+ // Map#Domain(x)[i] ==> Unbox(Map#Elements(x)[i])-has-the-expected-type)
+ Bpl.BoundVariable tVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$t#" + otherTmpVarCount, predef.BoxType));
+ otherTmpVarCount++;
+ Bpl.Expr t = new Bpl.IdentifierExpr(tok, tVar);
+ Bpl.Expr xSubT = Bpl.Expr.SelectTok(tok, FunctionCall(tok, BuiltinFunction.MapDomain, maptype, x), t);
+ Bpl.Expr xElemSubT = Bpl.Expr.SelectTok(tok, FunctionCall(tok, BuiltinFunction.MapElements, maptype, x), t);
+ Bpl.Expr unboxT = ExpressionTranslator.ModeledAsBoxType(mt.Domain) ? t : FunctionCall(tok, BuiltinFunction.Unbox, TrType(mt.Domain), t);
+ Bpl.Expr unboxElemT = ExpressionTranslator.ModeledAsBoxType(mt.Domain) ? xElemSubT : FunctionCall(tok, BuiltinFunction.Unbox, TrType(mt.Domain), xElemSubT);
+
+ Bpl.Expr wh = GetWhereClause(tok, unboxT, mt.Domain, etran);
+ if (wh != null) {
+ Bpl.Trigger tr = new Bpl.Trigger(tok, true, new Bpl.ExprSeq(xSubT));
+ clause = new Bpl.ForallExpr(tok, new Bpl.VariableSeq(tVar), tr, Bpl.Expr.Imp(xSubT, wh));
+ }
+
+ Bpl.Expr wh2 = GetWhereClause(tok, xElemSubT, mt.Range, etran);
+ if (wh2 != null) {
+ Bpl.Trigger tr = new Bpl.Trigger(tok, true, new Bpl.ExprSeq(xElemSubT));
+ Bpl.Expr forall = new Bpl.ForallExpr(tok, new Bpl.VariableSeq(tVar), tr, Bpl.Expr.Imp(xSubT, wh));
+ if (clause == null) {
+ clause = forall;
+ } else {
+ clause = Bpl.Expr.And(clause, forall);
+ }
+ }
+ return clause;
+ } else if (type.IsRefType) {
+ // reference type:
+ // x == null || ($Heap[x,alloc] && dtype(x) == ...)
+ return Bpl.Expr.Or(Bpl.Expr.Eq(x, predef.Null), etran.GoodRef(tok, x, type));
+
+ } else if (type.IsDatatype) {
+ UserDefinedType udt = (UserDefinedType)type;
+
+ // DtAlloc(e, heap) && e-has-the-expected-type
+ Bpl.Expr alloc = FunctionCall(tok, BuiltinFunction.DtAlloc, null, x, etran.HeapExpr);
+ Bpl.Expr goodType = etran.Good_Datatype(tok, x, udt.ResolvedClass, udt.TypeArgs);
+ return Bpl.Expr.And(alloc, goodType);
+
+ } else if (type.IsTypeParameter) {
+ return FunctionCall(tok, BuiltinFunction.GenericAlloc, null, x, etran.HeapExpr);
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
+ }
+
+ return null;
+ }
+
+ Bpl.Expr GetBoolBoxCondition(Expr box, Type type) {
+ Contract.Requires(box != null);
+ Contract.Requires(type != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ if (type.Normalize() is BoolType) {
+ return FunctionCall(box.tok, BuiltinFunction.IsCanonicalBoolBox, null, box);
+ } else {
+ return Bpl.Expr.True;
+ }
+ }
+
+ /// <summary>
+ /// "lhs" is expected to be a resolved form of an expression, i.e., not a conrete-syntax expression.
+ /// </summary>
+ void TrAssignment(IToken tok, Expression lhs, AssignmentRhs rhs,
+ Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(lhs != null);
+ Contract.Requires(!(lhs is ConcreteSyntaxExpression));
+ Contract.Requires(!(lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne)); // these were once allowed, but their functionality is now provided by 'parallel' statements
+ Contract.Requires(rhs != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(cce.NonNullElements(locals));
+ Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
+
+ List<AssignToLhs> lhsBuilder;
+ List<Bpl.IdentifierExpr> bLhss;
+ var lhss = new List<Expression>() { lhs };
+ Bpl.Expr[] ignore1, ignore2;
+ string[] ignore3;
+ ProcessLhss(lhss, rhs.CanAffectPreviouslyKnownExpressions, true, builder, locals, etran,
+ out lhsBuilder, out bLhss, out ignore1, out ignore2, out ignore3);
+ Contract.Assert(lhsBuilder.Count == 1 && bLhss.Count == 1); // guaranteed by postcondition of ProcessLhss
+
+ var rhss = new List<AssignmentRhs>() { rhs };
+ ProcessRhss(lhsBuilder, bLhss, lhss, rhss, builder, locals, etran);
+ builder.Add(CaptureState(tok));
+ }
+
+ void ProcessRhss(List<AssignToLhs> lhsBuilder, List<Bpl.IdentifierExpr/*may be null*/> bLhss,
+ List<Expression> lhss, List<AssignmentRhs> rhss,
+ Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran) {
+ Contract.Requires(lhsBuilder != null);
+ Contract.Requires(bLhss != null);
+ Contract.Requires(cce.NonNullElements(lhss));
+ Contract.Requires(cce.NonNullElements(rhss));
+ Contract.Requires(builder != null);
+ Contract.Requires(cce.NonNullElements(locals));
+ Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
+
+ var finalRhss = new List<Bpl.IdentifierExpr>();
+ for (int i = 0; i < lhss.Count; i++) {
+ var lhs = lhss[i];
+ // the following assumes are part of the precondition, really
+ Contract.Assume(!(lhs is ConcreteSyntaxExpression));
+ Contract.Assume(!(lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne)); // array-range assignments are not allowed
+
+ Type lhsType = null;
+ if (lhs is IdentifierExpr) {
+ lhsType = lhs.Type;
+ } else if (lhs is FieldSelectExpr) {
+ var fse = (FieldSelectExpr)lhs;
+ lhsType = fse.Field.Type;
+ }
+ var bRhs = TrAssignmentRhs(rhss[i].Tok, bLhss[i], lhsType, rhss[i], lhs.Type, builder, locals, etran);
+ if (bRhs != bLhss[i]) {
+ finalRhss.Add(bRhs);
+ } else {
+ // assignment has already been done by by TrAssignmentRhs
+ finalRhss.Add(null);
+ }
+ }
+ for (int i = 0; i < lhss.Count; i++) {
+ if (finalRhss[i] != null) {
+ lhsBuilder[i](finalRhss[i], builder, etran);
+ }
+ }
+ }
+
+ List<Bpl.IdentifierExpr> ProcessUpdateAssignRhss(List<Expression> lhss, List<AssignmentRhs> rhss,
+ Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran) {
+ Contract.Requires(cce.NonNullElements(lhss));
+ Contract.Requires(cce.NonNullElements(rhss));
+ Contract.Requires(builder != null);
+ Contract.Requires(cce.NonNullElements(locals));
+ Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.ForAll(Contract.Result<List<Bpl.IdentifierExpr>>(), i => i != null));
+
+ var finalRhss = new List<Bpl.IdentifierExpr>();
+ for (int i = 0; i < lhss.Count; i++) {
+ var lhs = lhss[i];
+ // the following assumes are part of the precondition, really
+ Contract.Assume(!(lhs is ConcreteSyntaxExpression));
+ Contract.Assume(!(lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne)); // array-range assignments are not allowed
+
+ Type lhsType = null;
+ if (lhs is IdentifierExpr) {
+ lhsType = lhs.Type;
+ } else if (lhs is FieldSelectExpr) {
+ var fse = (FieldSelectExpr)lhs;
+ lhsType = fse.Field.Type;
+ }
+ var bRhs = TrAssignmentRhs(rhss[i].Tok, null, lhsType, rhss[i], lhs.Type, builder, locals, etran);
+ finalRhss.Add(bRhs);
+ }
+ return finalRhss;
+ }
+
+
+ private void CheckLhssDistinctness(List<Bpl.IdentifierExpr> rhs, List<Expression> lhss, StmtListBuilder builder, ExpressionTranslator etran,
+ Bpl.Expr[] objs, Bpl.Expr[] fields, string[] names) {
+ Contract.Requires(cce.NonNullElements(lhss));
+ Contract.Requires(builder != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
+
+ for (int i = 0; i < lhss.Count; i++) {
+ var lhs = lhss[i];
+ Contract.Assume(!(lhs is ConcreteSyntaxExpression));
+ IToken tok = lhs.tok;
+
+ if (lhs is IdentifierExpr) {
+ for (int j = 0; j < i; j++) {
+ var prev = lhss[j] as IdentifierExpr;
+ if (prev != null && names[i] == names[j]) {
+ builder.Add(Assert(tok, Bpl.Expr.Imp(Bpl.Expr.True, Bpl.Expr.Eq(rhs[i],rhs[j])), string.Format("when left-hand sides {0} and {1} refer to the same location, they must have the same value", j, i)));
+ }
+ }
+ } else if (lhs is FieldSelectExpr) {
+ var fse = (FieldSelectExpr)lhs;
+ // check that this LHS is not the same as any previous LHSs
+ for (int j = 0; j < i; j++) {
+ var prev = lhss[j] as FieldSelectExpr;
+ if (prev != null && prev.Field == fse.Field) {
+ builder.Add(Assert(tok, Bpl.Expr.Imp(Bpl.Expr.Eq(objs[j], objs[i]), Bpl.Expr.Eq(rhs[i], rhs[j])), string.Format("when left-hand sides {0} and {1} refer to the same location, they must have the same value", j, i)));
+ }
+ }
+ } else if (lhs is SeqSelectExpr) {
+ SeqSelectExpr sel = (SeqSelectExpr)lhs;
+ // check that this LHS is not the same as any previous LHSs
+ for (int j = 0; j < i; j++) {
+ var prev = lhss[j] as SeqSelectExpr;
+ if (prev != null) {
+ builder.Add(Assert(tok,
+ Bpl.Expr.Imp(Bpl.Expr.And(Bpl.Expr.Eq(objs[j], objs[i]), Bpl.Expr.Eq(fields[j], fields[i])), Bpl.Expr.Eq(rhs[i], rhs[j])),
+ string.Format("when left-hand sides {0} and {1} may refer to the same location, they must have the same value", j, i)));
+ }
+ }
+ } else {
+ MultiSelectExpr mse = (MultiSelectExpr)lhs;
+ // check that this LHS is not the same as any previous LHSs
+ for (int j = 0; j < i; j++) {
+ var prev = lhss[j] as MultiSelectExpr;
+ if (prev != null) {
+ builder.Add(Assert(tok,
+ Bpl.Expr.Imp(Bpl.Expr.And(Bpl.Expr.Eq(objs[j], objs[i]), Bpl.Expr.Eq(fields[j], fields[i])), Bpl.Expr.Eq(rhs[i], rhs[j])),
+ string.Format("when left-hand sides {0} and {1} refer to the same location, they must have the same value", j, i)));
+ }
+ }
+
+ }
+ }
+ }
+
+ delegate void AssignToLhs(Bpl.Expr rhs, Bpl.StmtListBuilder builder, ExpressionTranslator etran);
+
+ /// <summary>
+ /// Creates a list of protected Boogie LHSs for the given Dafny LHSs. Along the way,
+ /// builds code that checks that the LHSs are well-defined,
+ /// and are allowed by the enclosing modifies clause.
+ /// Checks that they denote different locations iff checkDistinctness is true.
+ /// </summary>
+ void ProcessLhss(List<Expression> lhss, bool rhsCanAffectPreviouslyKnownExpressions, bool checkDistinctness,
+ Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran,
+ out List<AssignToLhs> lhsBuilders, out List<Bpl.IdentifierExpr/*may be null*/> bLhss,
+ out Bpl.Expr[] prevObj, out Bpl.Expr[] prevIndex, out string[] prevNames) {
+
+ Contract.Requires(cce.NonNullElements(lhss));
+ Contract.Requires(builder != null);
+ Contract.Requires(cce.NonNullElements(locals));
+ Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.ValueAtReturn(out lhsBuilders).Count == lhss.Count);
+ Contract.Ensures(Contract.ValueAtReturn(out lhsBuilders).Count == Contract.ValueAtReturn(out bLhss).Count);
+
+ rhsCanAffectPreviouslyKnownExpressions = rhsCanAffectPreviouslyKnownExpressions || lhss.Count != 1;
+
+ // for each Dafny LHS, build a protected Boogie LHS for the eventual assignment
+ lhsBuilders = new List<AssignToLhs>();
+ bLhss = new List<Bpl.IdentifierExpr>();
+ prevObj = new Bpl.Expr[lhss.Count];
+ prevIndex = new Bpl.Expr[lhss.Count];
+ prevNames = new string[lhss.Count];
+ int i = 0;
+
+ var lhsNameSet = new Dictionary<string, object>();
+
+ foreach (var lhs in lhss) {
+ Contract.Assume(!(lhs is ConcreteSyntaxExpression));
+ IToken tok = lhs.tok;
+ TrStmt_CheckWellformed(lhs, builder, locals, etran, true);
+
+ if (lhs is IdentifierExpr) {
+ var ie = (IdentifierExpr)lhs;
+ // Note, the resolver does not check for duplicate IdentifierExpr's in LHSs, so do it here.
+ if (checkDistinctness) {
+ for (int j = 0; j < i; j++) {
+ var prev = lhss[j] as IdentifierExpr;
+ if (prev != null && ie.Name == prev.Name) {
+ builder.Add(Assert(tok, Bpl.Expr.False, string.Format("left-hand sides {0} and {1} refer to the same location", j, i)));
+ }
+ }
+ }
+ prevNames[i] = ie.Name;
+ var bLhs = (Bpl.IdentifierExpr)etran.TrExpr(lhs); // TODO: is this cast always justified?
+ bLhss.Add(rhsCanAffectPreviouslyKnownExpressions ? null : bLhs);
+ lhsBuilders.Add(delegate(Bpl.Expr rhs, Bpl.StmtListBuilder bldr, ExpressionTranslator et) {
+ bldr.Add(Bpl.Cmd.SimpleAssign(tok, bLhs, rhs));
+ });
+
+ } else if (lhs is FieldSelectExpr) {
+ var fse = (FieldSelectExpr)lhs;
+ Contract.Assert(fse.Field != null);
+ var obj = SaveInTemp(etran.TrExpr(fse.Obj), rhsCanAffectPreviouslyKnownExpressions,
+ "$obj" + i, predef.RefType, builder, locals);
+ prevObj[i] = obj;
+ // check that the enclosing modifies clause allows this object to be written: assert $_Frame[obj]);
+ builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.TheFrame(tok), obj, GetField(fse)), "assignment may update an object not in the enclosing context's modifies clause"));
+
+ if (checkDistinctness) {
+ // check that this LHS is not the same as any previous LHSs
+ for (int j = 0; j < i; j++) {
+ var prev = lhss[j] as FieldSelectExpr;
+ if (prev != null && prev.Field == fse.Field) {
+ builder.Add(Assert(tok, Bpl.Expr.Neq(prevObj[j], obj), string.Format("left-hand sides {0} and {1} may refer to the same location", j, i)));
+ }
+ }
+ }
+
+ bLhss.Add(null);
+ lhsBuilders.Add(delegate(Bpl.Expr rhs, Bpl.StmtListBuilder bldr, ExpressionTranslator et) {
+ Check_NewRestrictions(tok, obj, fse.Field, rhs, bldr, et);
+ var h = (Bpl.IdentifierExpr)et.HeapExpr; // TODO: is this cast always justified?
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(tok, h, ExpressionTranslator.UpdateHeap(tok, h, obj, new Bpl.IdentifierExpr(tok, GetField(fse.Field)), rhs));
+ bldr.Add(cmd);
+ // assume $IsGoodHeap($Heap);
+ bldr.Add(AssumeGoodHeap(tok, et));
+ });
+
+ } else if (lhs is SeqSelectExpr) {
+ SeqSelectExpr sel = (SeqSelectExpr)lhs;
+ Contract.Assert(sel.SelectOne); // array-range assignments are not allowed
+ Contract.Assert(sel.Seq.Type != null && sel.Seq.Type.IsArrayType);
+ Contract.Assert(sel.E0 != null);
+ var obj = SaveInTemp(etran.TrExpr(sel.Seq), rhsCanAffectPreviouslyKnownExpressions,
+ "$obj" + i, predef.RefType, builder, locals);
+ var fieldName = SaveInTemp(FunctionCall(tok, BuiltinFunction.IndexField, null, etran.TrExpr(sel.E0)), rhsCanAffectPreviouslyKnownExpressions,
+ "$index" + i, predef.FieldName(tok, predef.BoxType), builder, locals);
+ prevObj[i] = obj;
+ prevIndex[i] = fieldName;
+ // check that the enclosing modifies clause allows this object to be written: assert $_Frame[obj,index]);
+ builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.TheFrame(tok), obj, fieldName), "assignment may update an array element not in the enclosing context's modifies clause"));
+
+ if (checkDistinctness) {
+ // check that this LHS is not the same as any previous LHSs
+ for (int j = 0; j < i; j++) {
+ var prev = lhss[j] as SeqSelectExpr;
+ if (prev != null) {
+ builder.Add(Assert(tok,
+ Bpl.Expr.Or(Bpl.Expr.Neq(prevObj[j], obj), Bpl.Expr.Neq(prevIndex[j], fieldName)),
+ string.Format("left-hand sides {0} and {1} may refer to the same location", j, i)));
+ }
+ }
+ }
+ bLhss.Add(null);
+ lhsBuilders.Add(delegate(Bpl.Expr rhs, Bpl.StmtListBuilder bldr, ExpressionTranslator et) {
+ var h = (Bpl.IdentifierExpr)et.HeapExpr; // TODO: is this cast always justified?
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(tok, h, ExpressionTranslator.UpdateHeap(tok, h, obj, fieldName, rhs));
+ bldr.Add(cmd);
+ // assume $IsGoodHeap($Heap);
+ bldr.Add(AssumeGoodHeap(tok, et));
+ });
+
+ } else {
+ MultiSelectExpr mse = (MultiSelectExpr)lhs;
+ Contract.Assert(mse.Array.Type != null && mse.Array.Type.IsArrayType);
+
+ var obj = SaveInTemp(etran.TrExpr(mse.Array), rhsCanAffectPreviouslyKnownExpressions,
+ "$obj" + i, predef.RefType, builder, locals);
+ var fieldName = SaveInTemp(etran.GetArrayIndexFieldName(mse.tok, mse.Indices), rhsCanAffectPreviouslyKnownExpressions,
+ "$index" + i, predef.FieldName(mse.tok, predef.BoxType), builder, locals);
+ prevObj[i] = obj;
+ prevIndex[i] = fieldName;
+ builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.TheFrame(tok), obj, fieldName), "assignment may update an array element not in the enclosing context's modifies clause"));
+
+ if (checkDistinctness) {
+ // check that this LHS is not the same as any previous LHSs
+ for (int j = 0; j < i; j++) {
+ var prev = lhss[j] as MultiSelectExpr;
+ if (prev != null) {
+ builder.Add(Assert(tok,
+ Bpl.Expr.Or(Bpl.Expr.Neq(prevObj[j], obj), Bpl.Expr.Neq(prevIndex[j], fieldName)),
+ string.Format("left-hand sides {0} and {1} may refer to the same location", j, i)));
+ }
+ }
+ }
+ bLhss.Add(null);
+ lhsBuilders.Add(delegate(Bpl.Expr rhs, Bpl.StmtListBuilder bldr, ExpressionTranslator et) {
+ var h = (Bpl.IdentifierExpr)et.HeapExpr; // TODO: is this cast always justified?
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(tok, h, ExpressionTranslator.UpdateHeap(tok, h, obj, fieldName, rhs));
+ bldr.Add(cmd);
+ // assume $IsGoodHeap($Heap);
+ bldr.Add(AssumeGoodHeap(tok, etran));
+ });
+ }
+
+ i++;
+ }
+ }
+
+ /// <summary>
+ /// Generates an assignment of the translation of "rhs" to "bLhs" and then return "bLhs". If "bLhs" is
+ /// passed in as "null", this method will create a new temporary Boogie variable to hold the result.
+ /// Before the assignment, the generated code will check that "rhs" obeys any subrange requirements
+ /// entailed by "checkSubrangeType".
+ /// </summary>
+ Bpl.IdentifierExpr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bLhs, Type lhsType, AssignmentRhs rhs, Type checkSubrangeType,
+ Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran) {
+ Contract.Requires(tok != null);
+ Contract.Requires(rhs != null);
+ Contract.Requires(!(rhs is CallRhs)); // calls are handled in a different translation method
+ Contract.Requires(builder != null);
+ Contract.Requires(cce.NonNullElements(locals));
+ Contract.Requires(etran != null);
+ Contract.Requires(predef != null);
+
+ if (bLhs == null) {
+ var nm = string.Format("$rhs#{0}", otherTmpVarCount);
+ otherTmpVarCount++;
+ var v = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, nm, lhsType == null ? predef.BoxType : TrType(lhsType)));
+ locals.Add(v);
+ bLhs = new Bpl.IdentifierExpr(tok, v);
+ }
+
+ if (rhs is ExprRhs) {
+ var e = (ExprRhs)rhs;
+
+ TrStmt_CheckWellformed(e.Expr, builder, locals, etran, true);
+
+ Bpl.Expr bRhs = etran.TrExpr(e.Expr);
+ CheckSubrange(tok, bRhs, checkSubrangeType, builder);
+ bRhs = etran.CondApplyBox(tok, bRhs, e.Expr.Type, lhsType);
+
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(tok, bLhs, bRhs);
+ builder.Add(cmd);
+ var ch = e.Expr as UnaryExpr;
+ if (ch != null && ch.Op == UnaryExpr.Opcode.SetChoose) {
+ // havoc $Tick;
+ builder.Add(new Bpl.HavocCmd(ch.tok, new Bpl.IdentifierExprSeq(etran.Tick())));
+ }
+
+ } else if (rhs is HavocRhs) {
+ builder.Add(new Bpl.HavocCmd(tok, new Bpl.IdentifierExprSeq(bLhs)));
+ var isNat = CheckSubrange_Expr(tok, bLhs, checkSubrangeType);
+ if (isNat != null) {
+ builder.Add(new Bpl.AssumeCmd(tok, isNat));
+ }
+ } else {
+ Contract.Assert(rhs is TypeRhs); // otherwise, an unexpected AssignmentRhs
+ TypeRhs tRhs = (TypeRhs)rhs;
+
+ if (tRhs.ArrayDimensions != null) {
+ int i = 0;
+ foreach (Expression dim in tRhs.ArrayDimensions) {
+ CheckWellformed(dim, new WFOptions(), locals, builder, etran);
+ if (tRhs.ArrayDimensions.Count == 1) {
+ builder.Add(Assert(tok, Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(dim)),
+ tRhs.ArrayDimensions.Count == 1 ? "array size might be negative" : string.Format("array size (dimension {0}) might be negative", i)));
+ }
+ i++;
+ }
+ }
+
+ Bpl.IdentifierExpr nw = GetNewVar_IdExpr(tok, locals);
+ builder.Add(new Bpl.HavocCmd(tok, new Bpl.IdentifierExprSeq(nw)));
+ // assume $nw != null && !$Heap[$nw, alloc] && dtype($nw) == RHS;
+ Bpl.Expr nwNotNull = Bpl.Expr.Neq(nw, predef.Null);
+ Bpl.Expr rightType;
+ if (tRhs.ArrayDimensions != null) {
+ // array allocation
+ List<Type> typeArgs = new List<Type>();
+ typeArgs.Add(tRhs.EType);
+ rightType = etran.GoodRef_Ref(tok, nw, new Bpl.IdentifierExpr(tok, "class._System." + BuiltIns.ArrayClassName(tRhs.ArrayDimensions.Count), predef.ClassNameType), typeArgs, true);
+ } else if (tRhs.EType is ObjectType) {
+ rightType = etran.GoodRef_Ref(tok, nw, new Bpl.IdentifierExpr(Token.NoToken, GetClass(program.BuiltIns.ObjectDecl)), new List<Type>(), true);
+ } else {
+ rightType = etran.GoodRef_Class(tok, nw, (UserDefinedType)tRhs.EType, true);
+ }
+ builder.Add(new Bpl.AssumeCmd(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))));
+ i++;
+ }
+ }
+ // $Heap[$nw, alloc] := true;
+ Bpl.Expr alloc = predef.Alloc(tok);
+ Bpl.IdentifierExpr heap = (Bpl.IdentifierExpr/*TODO: this cast is dubious*/)etran.HeapExpr;
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(tok, heap, ExpressionTranslator.UpdateHeap(tok, heap, nw, alloc, Bpl.Expr.True));
+ builder.Add(cmd);
+ if (codeContext is IteratorDecl) {
+ var iter = (IteratorDecl)codeContext;
+ // $Heap[this, _new] := Set#UnionOne<BoxType>($Heap[this, _new], $Box($nw));
+ var th = new Bpl.IdentifierExpr(tok, etran.This, predef.RefType);
+ var nwField = new Bpl.IdentifierExpr(tok, GetField(iter.Member_New));
+ var thisDotNew = ExpressionTranslator.ReadHeap(tok, etran.HeapExpr, th, nwField);
+ var unionOne = FunctionCall(tok, BuiltinFunction.SetUnionOne, predef.BoxType, thisDotNew, FunctionCall(tok, BuiltinFunction.Box, null, nw));
+ var heapRhs = ExpressionTranslator.UpdateHeap(tok, etran.HeapExpr, th, nwField, unionOne);
+ builder.Add(Bpl.Cmd.SimpleAssign(tok, heap, heapRhs));
+ }
+ // assume $IsGoodHeap($Heap);
+ builder.Add(AssumeGoodHeap(tok, etran));
+ if (tRhs.InitCall != null) {
+ AddComment(builder, tRhs.InitCall, "init call statement");
+ TrCallStmt(tRhs.InitCall, builder, locals, etran, nw);
+ }
+ // bLhs := $nw;
+ builder.Add(Bpl.Cmd.SimpleAssign(tok, bLhs, etran.CondApplyBox(tok, nw, tRhs.Type, lhsType)));
+ }
+ return bLhs;
+ }
+
+ void CheckSubrange(IToken tok, Expr bRhs, Type tp, StmtListBuilder builder) {
+ Contract.Requires(tok != null);
+ Contract.Requires(bRhs != null);
+ Contract.Requires(tp != null);
+ Contract.Requires(builder != null);
+
+ var isNat = CheckSubrange_Expr(tok, bRhs, tp);
+ if (isNat != null) {
+ builder.Add(Assert(tok, isNat, "value assigned to a nat must be non-negative"));
+ }
+ }
+
+ Bpl.Expr CheckSubrange_Expr(IToken tok, Expr bRhs, Type tp) {
+ Contract.Requires(tok != null);
+ Contract.Requires(bRhs != null);
+ Contract.Requires(tp != null);
+
+ if (tp is NatType) {
+ return Bpl.Expr.Le(Bpl.Expr.Literal(0), bRhs);
+ }
+ return null;
+ }
+
+ void Check_NewRestrictions(IToken tok, Bpl.Expr obj, Field f, Bpl.Expr rhs, StmtListBuilder builder, ExpressionTranslator etran) {
+ Contract.Requires(tok != null);
+ Contract.Requires(obj != null);
+ Contract.Requires(f != null);
+ Contract.Requires(rhs != null);
+ Contract.Requires(builder != null);
+ Contract.Requires(etran != null);
+ var iter = f.EnclosingClass as IteratorDecl;
+ if (iter != null && f == iter.Member_New) {
+ // Assignments to an iterator _new field is only allowed to shrink the set, so:
+ // assert Set#Subset(rhs, obj._new);
+ var fId = new Bpl.IdentifierExpr(tok, GetField(f));
+ var subset = FunctionCall(tok, BuiltinFunction.SetSubset, null, rhs, ExpressionTranslator.ReadHeap(tok, etran.HeapExpr, obj, fId));
+ builder.Add(Assert(tok, subset, "an assignment to " + f.Name + " is only allowed to shrink the set"));
+ }
+ }
+
+ Bpl.AssumeCmd AssumeGoodHeap(IToken tok, ExpressionTranslator etran) {
+ Contract.Requires(tok != null);
+ Contract.Requires(etran != null);
+ Contract.Ensures(Contract.Result<AssumeCmd>() != null);
+
+ return new Bpl.AssumeCmd(tok, FunctionCall(tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr));
+ }
+
+ // ----- Expression ---------------------------------------------------------------------------
+
+ /// <summary>
+ /// This class gives a way to represent a Boogie translation target as if it were still a Dafny expression.
+ /// </summary>
+ internal class BoogieWrapper : Expression
+ {
+ public readonly Bpl.Expr Expr;
+ public BoogieWrapper(Bpl.Expr expr, Type dafnyType)
+ : base(expr.tok)
+ {
+ Contract.Requires(expr != null);
+ Contract.Requires(dafnyType != null);
+ Expr = expr;
+ Type = dafnyType; // resolve immediately
+ }
+ }
+
+ internal class ExpressionTranslator
+ {
+ public readonly Bpl.Expr HeapExpr;
+ public readonly PredefinedDecls predef;
+ public readonly Translator translator;
+ public readonly string This;
+ public readonly string modifiesFrame; // the name of the context's frame variable.
+ readonly Function applyLimited_CurrentFunction;
+ public readonly int layerOffset = 0;
+ public int Statistics_CustomLayerFunctionCount = 0;
+ [ContractInvariantMethod]
+ void ObjectInvariant()
+ {
+ Contract.Invariant(HeapExpr != null);
+ Contract.Invariant(HeapExpr is Bpl.OldExpr || HeapExpr is Bpl.IdentifierExpr);
+ Contract.Invariant(predef != null);
+ Contract.Invariant(translator != null);
+ Contract.Invariant(This != null);
+ Contract.Invariant(modifiesFrame != null);
+ Contract.Invariant(layerOffset == 0 || layerOffset == 1);
+ Contract.Invariant(0 <= Statistics_CustomLayerFunctionCount);
+ }
+
+ /// <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, int layerOffset, string modifiesFrame) {
+
+ Contract.Requires(translator != null);
+ Contract.Requires(predef != null);
+ Contract.Requires(heap != null);
+ Contract.Requires(thisVar != null);
+ Contract.Requires(layerOffset == 0 || layerOffset == 1);
+ Contract.Requires(modifiesFrame != null);
+
+ this.translator = translator;
+ this.predef = predef;
+ this.HeapExpr = heap;
+ this.This = thisVar;
+ this.applyLimited_CurrentFunction = applyLimited_CurrentFunction;
+ this.layerOffset = layerOffset;
+ this.modifiesFrame = modifiesFrame;
+ }
+
+ public ExpressionTranslator(Translator translator, PredefinedDecls predef, IToken heapToken)
+ : this(translator, predef, new Bpl.IdentifierExpr(heapToken, predef.HeapVarName, predef.HeapType)) {
+ Contract.Requires(translator != null);
+ Contract.Requires(predef != null);
+ Contract.Requires(heapToken != null);
+ }
+
+ public ExpressionTranslator(Translator translator, PredefinedDecls predef, Bpl.Expr heap)
+ : this(translator, predef, heap, "this") {
+ Contract.Requires(translator != null);
+ Contract.Requires(predef != null);
+ Contract.Requires(heap != null);
+ }
+
+ public ExpressionTranslator(Translator translator, PredefinedDecls predef, Bpl.Expr heap, Bpl.Expr oldHeap)
+ : this(translator, predef, heap, "this") {
+ Contract.Requires(translator != null);
+ Contract.Requires(predef != null);
+ Contract.Requires(heap != null);
+ Contract.Requires(oldHeap != null);
+
+ var old = new ExpressionTranslator(translator, predef, oldHeap);
+ old.oldEtran = old;
+ this.oldEtran = old;
+
+ }
+
+ public ExpressionTranslator(Translator translator, PredefinedDecls predef, Bpl.Expr heap, string thisVar)
+ : this(translator, predef, heap, thisVar, null, 0, "$_Frame") {
+ Contract.Requires(translator != null);
+ Contract.Requires(predef != null);
+ Contract.Requires(heap != null);
+ Contract.Requires(thisVar != null);
+ }
+
+ public ExpressionTranslator(ExpressionTranslator etran, string modifiesFrame)
+ : this(etran.translator, etran.predef, etran.HeapExpr, etran.This, etran.applyLimited_CurrentFunction, etran.layerOffset, modifiesFrame) {
+ Contract.Requires(etran != null);
+ Contract.Requires(modifiesFrame != null);
+ }
+
+ ExpressionTranslator oldEtran;
+ public ExpressionTranslator Old {
+ get {
+ Contract.Ensures(Contract.Result<ExpressionTranslator>() != null);
+
+ if (oldEtran == null) {
+ oldEtran = new ExpressionTranslator(translator, predef, new Bpl.OldExpr(HeapExpr.tok, HeapExpr), This, applyLimited_CurrentFunction, layerOffset, modifiesFrame);
+ oldEtran.oldEtran = oldEtran;
+ }
+ return oldEtran;
+ }
+ }
+
+ public bool UsesOldHeap {
+ get {
+ return HeapExpr is Bpl.OldExpr;
+ }
+ }
+
+ public ExpressionTranslator LimitedFunctions(Function applyLimited_CurrentFunction) {
+ Contract.Requires(applyLimited_CurrentFunction != null);
+ Contract.Ensures(Contract.Result<ExpressionTranslator>() != null);
+
+ return new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, layerOffset, modifiesFrame);
+ }
+
+ public ExpressionTranslator LayerOffset(int offset) {
+ Contract.Requires(0 <= offset);
+ Contract.Requires(layerOffset + offset <= 1);
+ Contract.Ensures(Contract.Result<ExpressionTranslator>() != null);
+
+ var et = new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, layerOffset + offset, modifiesFrame);
+ if (this.oldEtran != null) {
+ var etOld = new ExpressionTranslator(translator, predef, Old.HeapExpr, This, applyLimited_CurrentFunction, layerOffset + offset, modifiesFrame);
+ etOld.oldEtran = etOld;
+ et.oldEtran = etOld;
+ }
+ return et;
+ }
+
+ public Bpl.IdentifierExpr TheFrame(IToken tok)
+ {
+ Contract.Requires(tok != null);
+ Contract.Ensures(Contract.Result<Bpl.IdentifierExpr>() != null);
+ Contract.Ensures(Contract.Result<Bpl.IdentifierExpr>().Type != null);
+
+ Bpl.TypeVariable alpha = new Bpl.TypeVariable(tok, "beta");
+ Bpl.Type fieldAlpha = predef.FieldName(tok, alpha);
+ Bpl.Type ty = new Bpl.MapType(tok, new Bpl.TypeVariableSeq(alpha), new Bpl.TypeSeq(predef.RefType, fieldAlpha), Bpl.Type.Bool);
+ return new Bpl.IdentifierExpr(tok, this.modifiesFrame, ty);
+ }
+
+ public Bpl.IdentifierExpr Tick() {
+ Contract.Ensures(Contract.Result<Bpl.IdentifierExpr>() != null);
+ Contract.Ensures(Contract.Result<Bpl.IdentifierExpr>().Type != null);
+
+ return new Bpl.IdentifierExpr(Token.NoToken, "$Tick", predef.TickType);
+ }
+
+ public Bpl.IdentifierExpr ModuleContextHeight() {
+ Contract.Ensures(Contract.Result<Bpl.IdentifierExpr>().Type != null);
+ return new Bpl.IdentifierExpr(Token.NoToken, "$ModuleContextHeight", Bpl.Type.Int);
+ }
+
+ public Bpl.IdentifierExpr FunctionContextHeight() {
+ Contract.Ensures(Contract.Result<Bpl.IdentifierExpr>().Type != null);
+ return new Bpl.IdentifierExpr(Token.NoToken, "$FunctionContextHeight", Bpl.Type.Int);
+ }
+
+ public Bpl.IdentifierExpr InMethodContext() {
+ Contract.Ensures(Contract.Result<Bpl.IdentifierExpr>().Type != null);
+ return new Bpl.IdentifierExpr(Token.NoToken, "$InMethodContext", Bpl.Type.Bool);
+ }
+
+ public Expression GetSubstitutedBody(LetExpr e) {
+ Contract.Requires(e != null);
+ var substMap = new Dictionary<IVariable, Expression>();
+ Contract.Assert(e.Vars.Count == e.RHSs.Count); // checked by resolution
+ for (int i = 0; i < e.Vars.Count; i++) {
+ Expression rhs = e.RHSs[i];
+ substMap.Add(e.Vars[i], new BoogieWrapper(TrExpr(rhs), rhs.Type));
+ }
+ return Translator.Substitute(e.Body, null, substMap);
+ }
+
+
+ /// <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)
+ {
+ Contract.Requires(expr != null);
+ Contract.Requires(predef != null);
+
+ if (expr is LiteralExpr) {
+ LiteralExpr e = (LiteralExpr)expr;
+ if (e.Value == null) {
+ return predef.Null;
+ } else if (e.Value is bool) {
+ return new Bpl.LiteralExpr(e.tok, (bool)e.Value);
+ } else if (e.Value is BigInteger) {
+ return Bpl.Expr.Literal(Microsoft.Basetypes.BigNum.FromBigInt((BigInteger)e.Value));
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected literal
+ }
+
+ } else if (expr is ThisExpr) {
+ return new Bpl.IdentifierExpr(expr.tok, This, predef.RefType);
+
+ } else if (expr is IdentifierExpr) {
+ IdentifierExpr e = (IdentifierExpr)expr;
+ return TrVar(expr.tok, cce.NonNull(e.Var));
+
+ } else if (expr is BoogieWrapper) {
+ var e = (BoogieWrapper)expr;
+ return e.Expr;
+
+ } else if (expr is SetDisplayExpr) {
+ SetDisplayExpr e = (SetDisplayExpr)expr;
+ Bpl.Expr s = translator.FunctionCall(expr.tok, BuiltinFunction.SetEmpty, 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);
+ }
+ return s;
+
+ } else if (expr is MultiSetDisplayExpr) {
+ 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));
+ s = translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetUnionOne, predef.BoxType, s, ss);
+ }
+ return s;
+
+ } else if (expr is SeqDisplayExpr) {
+ SeqDisplayExpr e = (SeqDisplayExpr)expr;
+ Bpl.Expr s = translator.FunctionCall(expr.tok, BuiltinFunction.SeqEmpty, predef.BoxType);
+ foreach (Expression ee in e.Elements) {
+ Bpl.Expr elt = BoxIfNecessary(expr.tok, TrExpr(ee), cce.NonNull(ee.Type));
+ s = translator.FunctionCall(expr.tok, BuiltinFunction.SeqBuild, predef.BoxType, s, elt);
+ }
+ return s;
+
+ } else if (expr is MapDisplayExpr) {
+ MapDisplayExpr e = (MapDisplayExpr)expr;
+ Bpl.Type maptype = predef.MapType(expr.tok, predef.BoxType, predef.BoxType);
+ Bpl.Expr s = translator.FunctionCall(expr.tok, "Map#Empty", maptype);
+ 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));
+ s = translator.FunctionCall(expr.tok, "Map#Build", maptype, s, elt, elt2);
+ }
+ return s;
+
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)expr;
+ Contract.Assert(e.Field != null);
+ Bpl.Expr obj = TrExpr(e.Obj);
+ Bpl.Expr result;
+ if (e.Field.IsMutable) {
+ result = ReadHeap(expr.tok, HeapExpr, obj, new Bpl.IdentifierExpr(expr.tok, translator.GetField(e.Field)));
+ } else {
+ result = new Bpl.NAryExpr(expr.tok, new Bpl.FunctionCall(translator.GetReadonlyField(e.Field)), new Bpl.ExprSeq(obj));
+ }
+ return CondApplyUnbox(expr.tok, result, e.Field.Type, cce.NonNull(expr.Type));
+
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ Bpl.Expr seq = TrExpr(e.Seq);
+ Type elmtType = null;
+ Type domainType = null;
+ Contract.Assert(e.Seq.Type != null); // the expression has been successfully resolved
+ if (e.Seq.Type.IsArrayType) {
+ domainType = Type.Int;
+ elmtType = UserDefinedType.ArrayElementType(e.Seq.Type);
+ } else if (e.Seq.Type is SeqType) {
+ domainType = Type.Int;
+ elmtType = ((SeqType)e.Seq.Type).Arg;
+ } else if (e.Seq.Type is MapType) {
+ domainType = ((MapType)e.Seq.Type).Domain;
+ elmtType = ((MapType)e.Seq.Type).Range;
+ } 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);
+ if (e.SelectOne) {
+ Contract.Assert(e1 == null);
+ Bpl.Expr x;
+ if (e.Seq.Type.IsArrayType) {
+ Bpl.Expr fieldName = translator.FunctionCall(expr.tok, BuiltinFunction.IndexField, null, e0);
+ x = ReadHeap(expr.tok, HeapExpr, TrExpr(e.Seq), fieldName);
+ } else if(e.Seq.Type is SeqType) {
+ x = translator.FunctionCall(expr.tok, BuiltinFunction.SeqIndex, predef.BoxType, seq, e0);
+ } else if (e.Seq.Type is MapType) {
+ x = translator.FunctionCall(expr.tok, BuiltinFunction.MapElements, predef.MapType(e.tok, predef.BoxType, predef.BoxType), seq);
+ x = Bpl.Expr.Select(x, BoxIfNecessary(e.tok, e0, domainType));
+ } else { Contract.Assert(false); x = null; }
+ if (!ModeledAsBoxType(elmtType)) {
+ x = translator.FunctionCall(expr.tok, BuiltinFunction.Unbox, elType, x);
+ }
+ return x;
+ } else {
+ if (e.Seq.Type.IsArrayType) {
+ seq = translator.FunctionCall(expr.tok, BuiltinFunction.SeqFromArray, elType, HeapExpr, seq);
+ }
+ if (e1 != null) {
+ seq = translator.FunctionCall(expr.tok, BuiltinFunction.SeqTake, elType, seq, e1);
+ }
+ if (e0 != null) {
+ seq = translator.FunctionCall(expr.tok, BuiltinFunction.SeqDrop, elType, seq, e0);
+ }
+ // if e0 == null && e1 == null, then we have the identity operation seq[..] == seq;
+ return seq;
+ }
+
+ } else if (expr is SeqUpdateExpr) {
+ SeqUpdateExpr e = (SeqUpdateExpr)expr;
+ Bpl.Expr seq = TrExpr(e.Seq);
+ if (e.Seq.Type is SeqType) {
+ Type elmtType = cce.NonNull((SeqType)e.Seq.Type).Arg;
+ Bpl.Expr index = TrExpr(e.Index);
+ Bpl.Expr val = BoxIfNecessary(expr.tok, TrExpr(e.Value), elmtType);
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SeqUpdate, predef.BoxType, seq, index, val);
+ } else {
+ Contract.Assert(e.Seq.Type is MapType);
+ MapType mt = (MapType)e.Seq.Type;
+ Bpl.Type maptype = predef.MapType(expr.tok, 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);
+ return translator.FunctionCall(expr.tok, "Map#Build", maptype, seq, index, val);
+ }
+ } else if (expr is MultiSelectExpr) {
+ MultiSelectExpr e = (MultiSelectExpr)expr;
+ Type elmtType = UserDefinedType.ArrayElementType(e.Array.Type);;
+ 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);
+ if (!ModeledAsBoxType(elmtType)) {
+ x = translator.FunctionCall(expr.tok, BuiltinFunction.Unbox, elType, x);
+ }
+ return x;
+
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ int offsetToUse = e.Function.IsRecursive ? this.layerOffset : 0;
+ if (e.Function.IsRecursive) {
+ Statistics_CustomLayerFunctionCount++;
+ }
+ string nm = FunctionName(e.Function, 1 + offsetToUse);
+ if (this.applyLimited_CurrentFunction != null && e.Function.IsRecursive) {
+ ModuleDefinition module = cce.NonNull(e.Function.EnclosingClass).Module;
+ if (module == cce.NonNull(applyLimited_CurrentFunction.EnclosingClass).Module) {
+ if (module.CallGraph.GetSCCRepresentative(e.Function) == module.CallGraph.GetSCCRepresentative(applyLimited_CurrentFunction)) {
+ nm = FunctionName(e.Function, 0 + offsetToUse);
+ }
+ }
+ }
+ Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(expr.tok, nm, translator.TrType(cce.NonNull(e.Type)));
+ Bpl.ExprSeq args = FunctionInvocationArguments(e);
+ Bpl.Expr result = new Bpl.NAryExpr(expr.tok, new Bpl.FunctionCall(id), args);
+ return CondApplyUnbox(expr.tok, result, e.Function.ResultType, expr.Type);
+
+ } else if (expr is DatatypeValue) {
+ DatatypeValue dtv = (DatatypeValue)expr;
+ Contract.Assert(dtv.Ctor != null); // since dtv has been successfully resolved
+ Bpl.ExprSeq args = new Bpl.ExprSeq();
+ 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);
+ args.Add(CondApplyBox(expr.tok, bArg, cce.NonNull(arg.Type), t));
+ }
+ Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(dtv.tok, dtv.Ctor.FullName, predef.DatatypeType);
+ return new Bpl.NAryExpr(dtv.tok, new Bpl.FunctionCall(id), args);
+
+ } else if (expr is OldExpr) {
+ OldExpr e = (OldExpr)expr;
+ return Old.TrExpr(e.E);
+
+ } else if (expr is MultiSetFormingExpr) {
+ MultiSetFormingExpr e = (MultiSetFormingExpr)expr;
+ if (e.E.Type is SetType) {
+ return translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetFromSet, translator.TrType(cce.NonNull((SetType)e.E.Type).Arg), TrExpr(e.E));
+ } else if (e.E.Type is SeqType) {
+ return translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetFromSeq, translator.TrType(cce.NonNull((SeqType)e.E.Type).Arg), TrExpr(e.E));
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException();
+ }
+
+
+ } else if (expr is FreshExpr) {
+ FreshExpr e = (FreshExpr)expr;
+ if (e.E.Type is SetType) {
+ // generate: (forall $o: ref :: $o != null && X[Box($o)] ==> !old($Heap)[$o,alloc])
+ // TODO: trigger?
+ Bpl.Variable oVar = new Bpl.BoundVariable(expr.tok, new Bpl.TypedIdent(expr.tok, "$o", predef.RefType));
+ 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)e.E.Type).Arg);
+ Bpl.Expr oIsFresh = Bpl.Expr.Not(Old.IsAlloced(expr.tok, o));
+ Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(oNotNull, oInSet), oIsFresh);
+ return new Bpl.ForallExpr(expr.tok, new Bpl.VariableSeq(oVar), body);
+ } else if (e.E.Type is SeqType) {
+ // generate: (forall $i: int :: 0 <= $i && $i < Seq#Length(X) && Unbox(Seq#Index(X,$i)) != null && !old($Heap)[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 oIsFresh = Bpl.Expr.Not(Old.IsAlloced(expr.tok, XsubI));
+ Bpl.Expr xsubiNotNull = Bpl.Expr.Neq(translator.FunctionCall(expr.tok, BuiltinFunction.Unbox, predef.RefType, XsubI), predef.Null);
+ Bpl.Expr body = Bpl.Expr.And(Bpl.Expr.And(iBounds, xsubiNotNull), oIsFresh);
+ return new Bpl.ForallExpr(expr.tok, new Bpl.VariableSeq(iVar), body);
+ } else if (e.E.Type.IsDatatype) {
+ Bpl.Expr alloc = translator.FunctionCall(e.tok, BuiltinFunction.DtAlloc, null, TrExpr(e.E), Old.HeapExpr);
+ return Bpl.Expr.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)));
+ return Bpl.Expr.And(oNull, oIsFresh);
+ }
+
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ Bpl.Expr arg = TrExpr(e.E);
+ switch (e.Op) {
+ case UnaryExpr.Opcode.Not:
+ return Bpl.Expr.Unary(expr.tok, UnaryOperator.Opcode.Not, arg);
+ case UnaryExpr.Opcode.SetChoose:
+ var x = translator.FunctionCall(expr.tok, BuiltinFunction.SetChoose, predef.BoxType, arg, Tick());
+ if (!ModeledAsBoxType(e.Type)) {
+ x = translator.FunctionCall(expr.tok, BuiltinFunction.Unbox, translator.TrType(e.Type), x);
+ }
+ return x;
+ case UnaryExpr.Opcode.SeqLength:
+ if (e.E.Type is SeqType) {
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SeqLength, null, arg);
+ } else {
+ return translator.ArrayLength(expr.tok, arg, 1, 0);
+ }
+ default:
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected unary expression
+ }
+
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ Bpl.Expr e0 = TrExpr(e.E0);
+ 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) {
+ Bpl.Expr arg = TrInSet(expr.tok, e0, e.E1, cce.NonNull(e.E0.Type)); // let TrInSet translate e.E1
+ return Bpl.Expr.Unary(expr.tok, UnaryOperator.Opcode.Not, arg);
+ } else if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.InMultiSet) {
+ return TrInMultiSet(expr.tok, e0, e.E1, cce.NonNull(e.E0.Type)); // let TrInMultiSet translate e.E1
+ } else if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.NotInMultiSet) {
+ 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);
+ BinaryOperator.Opcode bOpcode;
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.Iff:
+ bOpcode = BinaryOperator.Opcode.Iff; break;
+ case BinaryExpr.ResolvedOpcode.Imp:
+ bOpcode = BinaryOperator.Opcode.Imp; break;
+ case BinaryExpr.ResolvedOpcode.And:
+ bOpcode = BinaryOperator.Opcode.And; break;
+ case BinaryExpr.ResolvedOpcode.Or:
+ bOpcode = BinaryOperator.Opcode.Or; break;
+
+ case BinaryExpr.ResolvedOpcode.EqCommon:
+ bOpcode = BinaryOperator.Opcode.Eq; break;
+ case BinaryExpr.ResolvedOpcode.NeqCommon:
+ bOpcode = BinaryOperator.Opcode.Neq; break;
+
+ case BinaryExpr.ResolvedOpcode.Lt:
+ bOpcode = BinaryOperator.Opcode.Lt; break;
+ case BinaryExpr.ResolvedOpcode.Le:
+ bOpcode = BinaryOperator.Opcode.Le; break;
+ case BinaryExpr.ResolvedOpcode.Ge:
+ bOpcode = BinaryOperator.Opcode.Ge; break;
+ case BinaryExpr.ResolvedOpcode.Gt:
+ bOpcode = BinaryOperator.Opcode.Gt; break;
+ case BinaryExpr.ResolvedOpcode.Add:
+ bOpcode = BinaryOperator.Opcode.Add; break;
+ case BinaryExpr.ResolvedOpcode.Sub:
+ bOpcode = BinaryOperator.Opcode.Sub; break;
+ case BinaryExpr.ResolvedOpcode.Mul:
+ bOpcode = BinaryOperator.Opcode.Mul; break;
+ case BinaryExpr.ResolvedOpcode.Div:
+ bOpcode = BinaryOperator.Opcode.Div; break;
+ case BinaryExpr.ResolvedOpcode.Mod:
+ bOpcode = BinaryOperator.Opcode.Mod; break;
+
+ 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:
+ return Bpl.Expr.Binary(expr.tok, BinaryOperator.Opcode.And,
+ translator.FunctionCall(expr.tok, BuiltinFunction.SetSubset, null, e0, e1),
+ Bpl.Expr.Not(translator.FunctionCall(expr.tok, BuiltinFunction.SetEqual, null, 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.ProperSuperset:
+ return Bpl.Expr.Binary(expr.tok, BinaryOperator.Opcode.And,
+ translator.FunctionCall(expr.tok, BuiltinFunction.SetSubset, null, e1, e0),
+ Bpl.Expr.Not(translator.FunctionCall(expr.tok, BuiltinFunction.SetEqual, null, e0, e1)));
+ case BinaryExpr.ResolvedOpcode.Disjoint:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetDisjoint, 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(cce.NonNull((SetType)expr.Type).Arg), e0, e1);
+ case BinaryExpr.ResolvedOpcode.Intersection:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetIntersection, translator.TrType(cce.NonNull((SetType)expr.Type).Arg), e0, e1);
+ case BinaryExpr.ResolvedOpcode.SetDifference:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetDifference, translator.TrType(cce.NonNull((SetType)expr.Type).Arg), e0, e1);
+
+ case BinaryExpr.ResolvedOpcode.MultiSetEq:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetEqual, null, e0, e1);
+ case BinaryExpr.ResolvedOpcode.MultiSetNeq:
+ return Bpl.Expr.Unary(expr.tok, UnaryOperator.Opcode.Not, translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetEqual, null, e0, e1));
+ case BinaryExpr.ResolvedOpcode.MapEq:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.MapEqual, null, e0, e1);
+ case BinaryExpr.ResolvedOpcode.MapNeq:
+ return Bpl.Expr.Unary(expr.tok, UnaryOperator.Opcode.Not, translator.FunctionCall(expr.tok, BuiltinFunction.MapEqual, null, e0, e1));
+ case BinaryExpr.ResolvedOpcode.ProperMultiSubset:
+ return Bpl.Expr.Binary(expr.tok, BinaryOperator.Opcode.And,
+ translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetSubset, null, e0, e1),
+ Bpl.Expr.Not(translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetEqual, null, e0, e1)));
+ case BinaryExpr.ResolvedOpcode.MultiSubset:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetSubset, null, e0, e1);
+ case BinaryExpr.ResolvedOpcode.MultiSuperset:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetSubset, null, e1, e0);
+ case BinaryExpr.ResolvedOpcode.ProperMultiSuperset:
+ return Bpl.Expr.Binary(expr.tok, BinaryOperator.Opcode.And,
+ translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetSubset, null, e1, e0),
+ Bpl.Expr.Not(translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetEqual, null, e0, e1)));
+ case BinaryExpr.ResolvedOpcode.MultiSetDisjoint:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetDisjoint, null, e0, e1);
+ case BinaryExpr.ResolvedOpcode.InMultiSet:
+ Contract.Assert(false); throw new cce.UnreachableException(); // this case handled above
+ case BinaryExpr.ResolvedOpcode.NotInMultiSet:
+ Contract.Assert(false); throw new cce.UnreachableException(); // this case handled above
+ case BinaryExpr.ResolvedOpcode.MultiSetUnion:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetUnion, translator.TrType(cce.NonNull((MultiSetType)expr.Type).Arg), e0, e1);
+ case BinaryExpr.ResolvedOpcode.MultiSetIntersection:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetIntersection, translator.TrType(cce.NonNull((MultiSetType)expr.Type).Arg), e0, e1);
+ case BinaryExpr.ResolvedOpcode.MultiSetDifference:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.MultiSetDifference, translator.TrType(cce.NonNull((MultiSetType)expr.Type).Arg), e0, e1);
+
+ case BinaryExpr.ResolvedOpcode.SeqEq:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SeqEqual, null, e0, e1);
+ case BinaryExpr.ResolvedOpcode.SeqNeq:
+ return Bpl.Expr.Unary(expr.tok, UnaryOperator.Opcode.Not, translator.FunctionCall(expr.tok, BuiltinFunction.SeqEqual, null, e0, e1));
+ case BinaryExpr.ResolvedOpcode.ProperPrefix:
+ return ProperPrefix(expr.tok, e0, e1);
+ case BinaryExpr.ResolvedOpcode.Prefix:
+ {
+ Bpl.Expr len0 = translator.FunctionCall(expr.tok, BuiltinFunction.SeqLength, null, e0);
+ Bpl.Expr len1 = translator.FunctionCall(expr.tok, BuiltinFunction.SeqLength, null, e1);
+ return Bpl.Expr.Binary(expr.tok, BinaryOperator.Opcode.And,
+ Bpl.Expr.Le(len0, len1),
+ translator.FunctionCall(expr.tok, BuiltinFunction.SeqSameUntil, null, e0, e1, len0));
+ }
+ case BinaryExpr.ResolvedOpcode.Concat:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SeqAppend, translator.TrType(cce.NonNull((SeqType)expr.Type).Arg), e0, e1);
+ case BinaryExpr.ResolvedOpcode.InSeq:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SeqContains, null, e1,
+ BoxIfNecessary(expr.tok, e0, cce.NonNull(e.E0.Type)));
+ case BinaryExpr.ResolvedOpcode.NotInSeq:
+ Bpl.Expr arg = translator.FunctionCall(expr.tok, BuiltinFunction.SeqContains, null, e1,
+ BoxIfNecessary(expr.tok, e0, cce.NonNull(e.E0.Type)));
+ return Bpl.Expr.Unary(expr.tok, UnaryOperator.Opcode.Not, arg);
+ case BinaryExpr.ResolvedOpcode.InMap:
+ return Bpl.Expr.Select(translator.FunctionCall(expr.tok, BuiltinFunction.MapDomain, predef.MapType(e.tok, predef.BoxType, predef.BoxType), e1),
+ BoxIfNecessary(expr.tok, e0, e.E0.Type));
+ case BinaryExpr.ResolvedOpcode.NotInMap:
+ return Bpl.Expr.Not(Bpl.Expr.Select(translator.FunctionCall(expr.tok, BuiltinFunction.MapDomain, predef.MapType(e.tok, predef.BoxType, predef.BoxType), e1),
+ BoxIfNecessary(expr.tok, e0, e.E0.Type)));
+ case BinaryExpr.ResolvedOpcode.MapDisjoint:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.MapDisjoint, null, e0, e1);
+
+ case BinaryExpr.ResolvedOpcode.RankLt:
+ return Bpl.Expr.Binary(expr.tok, BinaryOperator.Opcode.Lt,
+ translator.FunctionCall(expr.tok, BuiltinFunction.DtRank, null, e0),
+ translator.FunctionCall(expr.tok, BuiltinFunction.DtRank, null, e1));
+ case BinaryExpr.ResolvedOpcode.RankGt:
+ return Bpl.Expr.Binary(expr.tok, BinaryOperator.Opcode.Gt,
+ translator.FunctionCall(expr.tok, BuiltinFunction.DtRank, null, e0),
+ translator.FunctionCall(expr.tok, BuiltinFunction.DtRank, null, e1));
+
+ default:
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected binary expression
+ }
+ return Bpl.Expr.Binary(expr.tok, bOpcode, e0, e1);
+
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ return TrExpr(GetSubstitutedBody(e));
+ } else if (expr is NamedExpr) {
+ return TrExpr(((NamedExpr)expr).Body);
+ } else if (expr is QuantifierExpr) {
+ QuantifierExpr e = (QuantifierExpr)expr;
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ Bpl.Expr typeAntecedent = 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") {
+ Bpl.ExprSeq tt = new Bpl.ExprSeq();
+ foreach (var arg in aa.Args) {
+ if (arg.E == null) {
+ Console.WriteLine("Warning: string argument to 'trigger' attribute ignored");
+ } else {
+ tt.Add(TrExpr(arg.E));
+ }
+ }
+ tr = new Bpl.Trigger(expr.tok, true, tt, tr);
+ }
+ }
+ var antecedent = typeAntecedent;
+ if (e.Range != null) {
+ antecedent = Bpl.Expr.And(antecedent, TrExpr(e.Range));
+ }
+ Bpl.Expr body = TrExpr(e.Term);
+
+ if (e is ForallExpr) {
+ return new Bpl.ForallExpr(expr.tok, new Bpl.TypeVariableSeq(), bvars, kv, tr, Bpl.Expr.Imp(antecedent, body));
+ } else {
+ Contract.Assert(e is ExistsExpr);
+ return new Bpl.ExistsExpr(expr.tok, new Bpl.TypeVariableSeq(), 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))".
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ Bpl.Expr typeAntecedent = TrBoundVariables(e.BoundVars, bvars);
+ Bpl.QKeyValue kv = TrAttributes(e.Attributes, null);
+
+ var yVar = new Bpl.BoundVariable(expr.tok, new Bpl.TypedIdent(expr.tok, "$y#" + translator.otherTmpVarCount, predef.BoxType));
+ translator.otherTmpVarCount++;
+ 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(translator.BplAnd(typeAntecedent, TrExpr(e.Range)), eq);
+ var exst = new Bpl.ExistsExpr(expr.tok, bvars, ebody);
+
+ return new Bpl.LambdaExpr(expr.tok, new Bpl.TypeVariableSeq(), new VariableSeq(yVar), kv, exst);
+
+ } else if (expr is MapComprehension) {
+ var e = (MapComprehension)expr;
+ // Translate "map x | R :: T" into
+ // Map#Glue(lambda y: BoxType :: [unbox(y)/x]R,
+ // lambda y: BoxType :: [unbox(y)/x]T)".
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ var bv = e.BoundVars[0];
+ TrBoundVariables(e.BoundVars, bvars);
+
+ Bpl.QKeyValue kv = TrAttributes(e.Attributes, null);
+
+ var yVar = new Bpl.BoundVariable(expr.tok, new Bpl.TypedIdent(expr.tok, "$y#" + translator.otherTmpVarCount, predef.BoxType));
+ translator.otherTmpVarCount++;
+
+ Bpl.Expr unboxy = !ModeledAsBoxType(bv.Type) ? translator.FunctionCall(e.tok, BuiltinFunction.Unbox, translator.TrType(bv.Type), new Bpl.IdentifierExpr(expr.tok, yVar))
+ : (Bpl.Expr)(new Bpl.IdentifierExpr(expr.tok, yVar));
+ Bpl.Expr typeAntecedent = translator.GetWhereClause(bv.tok, unboxy, bv.Type, this);
+
+
+ Dictionary<IVariable, Expression> subst = new Dictionary<IVariable,Expression>();
+ subst.Add(e.BoundVars[0], new BoogieWrapper(unboxy,e.BoundVars[0].Type));
+
+ var ebody = translator.BplAnd(typeAntecedent ?? Bpl.Expr.True, TrExpr(Substitute(e.Range, null, subst)));
+ Bpl.Expr l1 = new Bpl.LambdaExpr(e.tok, new Bpl.TypeVariableSeq(), new VariableSeq(yVar), kv, ebody);
+ ebody = TrExpr(Substitute(e.Term, null, subst));
+ Bpl.Expr l2 = new Bpl.LambdaExpr(e.tok, new Bpl.TypeVariableSeq(), new VariableSeq(yVar), kv, BoxIfNecessary(expr.tok, ebody, e.Term.Type));
+
+
+ return translator.FunctionCall(e.tok, BuiltinFunction.MapGlue, null, l1, l2);
+
+ } else if (expr is PredicateExpr) {
+ var e = (PredicateExpr)expr;
+ return TrExpr(e.Body);
+
+ } else if (expr is ITEExpr) {
+ ITEExpr e = (ITEExpr)expr;
+ Bpl.Expr g = TrExpr(e.Test);
+ Bpl.Expr thn = TrExpr(e.Thn);
+ Bpl.Expr els = TrExpr(e.Els);
+ return new NAryExpr(expr.tok, new IfThenElse(expr.tok), new ExprSeq(g, thn, els));
+
+ } else if (expr is ConcreteSyntaxExpression) {
+ var e = (ConcreteSyntaxExpression)expr;
+ return TrExpr(e.ResolvedExpression);
+
+ } else if (expr is BoxingCastExpr) {
+ BoxingCastExpr e = (BoxingCastExpr)expr;
+ return CondApplyBox(e.tok, TrExpr(e.E), e.FromType, e.ToType);
+
+ } else if (expr is UnboxingCastExpr) {
+ UnboxingCastExpr e = (UnboxingCastExpr)expr;
+ return CondApplyUnbox(e.tok, TrExpr(e.E), e.FromType, e.ToType);
+
+ } else {
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
+ }
+ }
+
+ public Bpl.Expr TrBoundVariables(List<BoundVar/*!*/> boundVars, Bpl.VariableSeq bvars) {
+ return TrBoundVariables(boundVars, bvars, false);
+ }
+
+ public Bpl.Expr TrBoundVariables(List<BoundVar/*!*/> boundVars, Bpl.VariableSeq bvars, bool translateAsLocals) {
+ Contract.Requires(boundVars != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ Bpl.Expr typeAntecedent = Bpl.Expr.True;
+ foreach (BoundVar bv in boundVars) {
+ var tid = new Bpl.TypedIdent(bv.tok, bv.UniqueName, translator.TrType(bv.Type));
+ Bpl.Variable bvar;
+ if (translateAsLocals) {
+ bvar = new Bpl.LocalVariable(bv.tok, tid);
+ } else {
+ bvar = new Bpl.BoundVariable(bv.tok, tid);
+ }
+ bvars.Add(bvar);
+ Bpl.Expr wh = translator.GetWhereClause(bv.tok, new Bpl.IdentifierExpr(bv.tok, bvar), bv.Type, this);
+ if (wh != null) {
+ typeAntecedent = translator.BplAnd(typeAntecedent, wh);
+ }
+ }
+ return typeAntecedent;
+ }
+
+ public Bpl.Expr TrBoundVariablesRename(List<BoundVar> boundVars, Bpl.VariableSeq bvars, out Dictionary<IVariable, Expression> substMap) {
+ Contract.Requires(boundVars != null);
+ Contract.Requires(bvars != null);
+
+ substMap = new Dictionary<IVariable, Expression>();
+ Bpl.Expr typeAntecedent = Bpl.Expr.True;
+ foreach (BoundVar bv in boundVars) {
+ var newBoundVar = new BoundVar(bv.tok, bv.Name, bv.Type);
+ IdentifierExpr ie = new IdentifierExpr(newBoundVar.tok, newBoundVar.UniqueName);
+ ie.Var = newBoundVar; ie.Type = ie.Var.Type; // resolve ie here
+ substMap.Add(bv, ie);
+ Bpl.Variable bvar = new Bpl.BoundVariable(newBoundVar.tok, new Bpl.TypedIdent(newBoundVar.tok, newBoundVar.UniqueName, translator.TrType(newBoundVar.Type)));
+ bvars.Add(bvar);
+ var bIe = new Bpl.IdentifierExpr(bvar.tok, bvar);
+ Bpl.Expr wh = translator.GetWhereClause(bv.tok, bIe, newBoundVar.Type, this);
+ if (wh != null) {
+ typeAntecedent = translator.BplAnd(typeAntecedent, wh);
+ }
+ }
+ return typeAntecedent;
+ }
+
+ public ExprSeq FunctionInvocationArguments(FunctionCallExpr e) {
+ Contract.Requires(e != null);
+ Contract.Ensures(Contract.Result<ExprSeq>() != null);
+
+ Bpl.ExprSeq args = new Bpl.ExprSeq();
+ args.Add(HeapExpr);
+ if (!e.Function.IsStatic) {
+ args.Add(TrExpr(e.Receiver));
+ }
+ for (int i = 0; i < e.Args.Count; i++) {
+ Expression ee = e.Args[i];
+ Type t = e.Function.Formals[i].Type;
+ args.Add(CondApplyBox(e.tok, TrExpr(ee), cce.NonNull(ee.Type), t));
+ }
+ return args;
+ }
+
+ public Bpl.Expr GetArrayIndexFieldName(IToken tok, List<Expression> indices) {
+ Bpl.Expr fieldName = null;
+ foreach (Expression idx in indices) {
+ Bpl.Expr index = TrExpr(idx);
+ if (fieldName == null) {
+ // the index in dimension 0: IndexField(index0)
+ fieldName = translator.FunctionCall(tok, BuiltinFunction.IndexField, null, index);
+ } else {
+ // the index in dimension n: MultiIndexField(...field name for first n indices..., index_n)
+ fieldName = translator.FunctionCall(tok, BuiltinFunction.MultiIndexField, null, fieldName, index);
+ }
+ }
+ return fieldName;
+ }
+
+ public Bpl.Expr ProperSubset(IToken tok, Bpl.Expr e0, Bpl.Expr e1) {
+ Contract.Requires(tok != null);
+ Contract.Requires(e0 != null);
+ Contract.Requires(e1 != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ return Bpl.Expr.And(
+ translator.FunctionCall(tok, BuiltinFunction.SetSubset, null, e0, e1),
+ Bpl.Expr.Not(translator.FunctionCall(tok, BuiltinFunction.SetSubset, null, e1, e0)));
+ }
+ public Bpl.Expr ProperPrefix(IToken tok, Bpl.Expr e0, Bpl.Expr e1) {
+ Contract.Requires(tok != null);
+ Contract.Requires(e0 != null);
+ Contract.Requires(e1 != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+ Bpl.Expr len0 = translator.FunctionCall(tok, BuiltinFunction.SeqLength, null, e0);
+ Bpl.Expr len1 = translator.FunctionCall(tok, BuiltinFunction.SeqLength, null, e1);
+ return Bpl.Expr.And(
+ Bpl.Expr.Lt(len0, len1),
+ translator.FunctionCall(tok, BuiltinFunction.SeqSameUntil, null, e0, e1, len0));
+ }
+
+ public Bpl.Expr CondApplyBox(IToken tok, Bpl.Expr e, Type fromType, Type toType) {
+ Contract.Requires(tok != null);
+ Contract.Requires(e != null);
+ Contract.Requires(fromType != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ if (!ModeledAsBoxType(fromType) && (toType == null || ModeledAsBoxType(toType))) {
+ return translator.FunctionCall(tok, BuiltinFunction.Box, null, e);
+ } else {
+ return e;
+ }
+ }
+
+ public Bpl.Expr BoxIfNecessary(IToken tok, Bpl.Expr e, Type fromType) {
+ Contract.Requires(tok != null);
+ Contract.Requires(e != null);
+ Contract.Requires(fromType != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ return CondApplyBox(tok, e, fromType, null);
+ }
+
+ public Bpl.Expr CondApplyUnbox(IToken tok, Bpl.Expr e, Type fromType, Type toType) {
+ Contract.Requires(tok != null);
+ Contract.Requires(e != null);
+ Contract.Requires(fromType != null);
+ Contract.Requires(toType != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ if (ModeledAsBoxType(fromType) && !ModeledAsBoxType(toType)) {
+ return translator.FunctionCall(tok, BuiltinFunction.Unbox, translator.TrType(toType), e);
+ } else {
+ return e;
+ }
+ }
+
+ public static bool ModeledAsBoxType(Type t) {
+ Contract.Requires(t != null);
+ while (true) {
+ TypeProxy tp = t as TypeProxy;
+ if (tp == null) {
+ break;
+ } else if (tp.T == null) {
+ // unresolved proxy
+ return false;
+ } else {
+ t = tp.T;
+ }
+ }
+ return t.IsTypeParameter;
+ }
+
+ public Bpl.IdentifierExpr TrVar(IToken tok, IVariable var) {
+ Contract.Requires(var != null);
+ Contract.Requires(tok != null);
+ Contract.Ensures(Contract.Result<Bpl.IdentifierExpr>() != null);
+
+ return new Bpl.IdentifierExpr(tok, var.UniqueName, translator.TrType(var.Type));
+ }
+
+ public static Bpl.NAryExpr ReadHeap(IToken tok, Expr heap, Expr r, Expr f) {
+ Contract.Requires(tok != null);
+ Contract.Requires(heap != null);
+ Contract.Requires(r != null);
+ Contract.Requires(f != null);
+ Contract.Ensures(Contract.Result<Bpl.NAryExpr>() != null);
+
+ Bpl.ExprSeq args = new Bpl.ExprSeq();
+ args.Add(heap);
+ args.Add(r);
+ args.Add(f);
+ Bpl.Type t = (f.Type != null) ? f.Type : f.ShallowType;
+ return new Bpl.NAryExpr(tok,
+ new Bpl.FunctionCall(new Bpl.IdentifierExpr(tok, "read", t.AsCtor.Arguments[0])),
+ args);
+ }
+
+ public static Bpl.NAryExpr UpdateHeap(IToken tok, Expr heap, Expr r, Expr f, Expr v) {
+ Contract.Requires(tok != null);
+ Contract.Requires(heap != null);
+ Contract.Requires(r != null);
+ Contract.Requires(f != null);
+ Contract.Requires(v != null);
+ Contract.Ensures(Contract.Result<Bpl.NAryExpr>() != null);
+
+ Bpl.ExprSeq args = new Bpl.ExprSeq();
+ args.Add(heap);
+ args.Add(r);
+ args.Add(f);
+ args.Add(v);
+ return new Bpl.NAryExpr(tok,
+ new Bpl.FunctionCall(new Bpl.IdentifierExpr(tok, "update", heap.Type)),
+ args);
+ }
+
+ /// <summary>
+ /// Translate like s[Box(elmt)], but try to avoid as many set functions as possible in the
+ /// translation, because such functions can mess up triggering.
+ /// </summary>
+ public Bpl.Expr TrInSet(IToken tok, Bpl.Expr elmt, Expression s, Type elmtType) {
+ Contract.Requires(tok != null);
+ Contract.Requires(elmt != null);
+ Contract.Requires(s != null);
+ Contract.Requires(elmtType != null);
+
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ if (s is BinaryExpr) {
+ BinaryExpr bin = (BinaryExpr)s;
+ switch (bin.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.Union:
+ return Bpl.Expr.Or(TrInSet(tok, elmt, bin.E0, elmtType), TrInSet(tok, elmt, bin.E1, elmtType));
+ case BinaryExpr.ResolvedOpcode.Intersection:
+ return Bpl.Expr.And(TrInSet(tok, elmt, bin.E0, elmtType), TrInSet(tok, elmt, bin.E1, elmtType));
+ case BinaryExpr.ResolvedOpcode.SetDifference:
+ return Bpl.Expr.And(TrInSet(tok, elmt, bin.E0, elmtType), Bpl.Expr.Not(TrInSet(tok, elmt, bin.E1, elmtType)));
+ default:
+ break;
+ }
+ } else if (s is SetDisplayExpr) {
+ SetDisplayExpr disp = (SetDisplayExpr)s;
+ Bpl.Expr disjunction = null;
+ foreach (Expression a in disp.Elements) {
+ Bpl.Expr disjunct = Bpl.Expr.Eq(elmt, TrExpr(a));
+ if (disjunction == null) {
+ disjunction = disjunct;
+ } else {
+ disjunction = Bpl.Expr.Or(disjunction, disjunct);
+ }
+ }
+ if (disjunction == null) {
+ return Bpl.Expr.False;
+ } else {
+ return disjunction;
+ }
+ }
+ return Bpl.Expr.SelectTok(tok, TrExpr(s), BoxIfNecessary(tok, elmt, elmtType));
+ }
+
+ /// <summary>
+ /// Translate like 0 < s[Box(elmt)], but try to avoid as many set functions as possible in the
+ /// translation, because such functions can mess up triggering.
+ /// </summary>
+ public Bpl.Expr TrInMultiSet(IToken tok, Bpl.Expr elmt, Expression s, Type elmtType) {
+ Contract.Requires(tok != null);
+ Contract.Requires(elmt != null);
+ Contract.Requires(s != null);
+ Contract.Requires(elmtType != null);
+
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ if (s is BinaryExpr) {
+ BinaryExpr bin = (BinaryExpr)s;
+ switch (bin.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.MultiSetUnion:
+ return Bpl.Expr.Or(TrInMultiSet(tok, elmt, bin.E0, elmtType), TrInMultiSet(tok, elmt, bin.E1, elmtType));
+ case BinaryExpr.ResolvedOpcode.MultiSetIntersection:
+ return Bpl.Expr.And(TrInMultiSet(tok, elmt, bin.E0, elmtType), TrInMultiSet(tok, elmt, bin.E1, elmtType));
+ default:
+ break;
+ }
+ } else if (s is MultiSetDisplayExpr) {
+ MultiSetDisplayExpr disp = (MultiSetDisplayExpr)s;
+ Bpl.Expr disjunction = null;
+ foreach (Expression a in disp.Elements) {
+ Bpl.Expr disjunct = Bpl.Expr.Eq(elmt, TrExpr(a));
+ if (disjunction == null) {
+ disjunction = disjunct;
+ } else {
+ disjunction = Bpl.Expr.Or(disjunction, disjunct);
+ }
+ }
+ if (disjunction == null) {
+ return Bpl.Expr.False;
+ } else {
+ return disjunction;
+ }
+ }
+ return Bpl.Expr.Gt(Bpl.Expr.SelectTok(tok, TrExpr(s), BoxIfNecessary(tok, elmt, elmtType)), Bpl.Expr.Literal(0));
+ }
+
+ public Bpl.QKeyValue TrAttributes(Attributes attrs, string skipThisAttribute) {
+ Bpl.QKeyValue kv = null;
+ for ( ; attrs != null; attrs = attrs.Prev) {
+ if (attrs.Name == skipThisAttribute) { continue; }
+ List<object> parms = new List<object>();
+ foreach (Attributes.Argument arg in attrs.Args) {
+ if (arg.E != null) {
+ parms.Add(TrExpr(arg.E));
+ } else {
+ parms.Add(cce.NonNull(arg.S));
+ }
+ }
+ kv = new Bpl.QKeyValue(Token.NoToken, attrs.Name, parms, kv);
+ }
+ return kv;
+ }
+
+ // --------------- help routines ---------------
+
+ public Bpl.Expr IsAlloced(IToken tok, Bpl.Expr e) {
+ Contract.Requires(tok != null);
+ Contract.Requires(e != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ return ReadHeap(tok, HeapExpr, e, predef.Alloc(tok));
+ }
+
+ public Bpl.Expr GoodRef(IToken tok, Bpl.Expr e, Type type) {
+ Contract.Requires(tok != null);
+ Contract.Requires(e != null);
+ Contract.Requires(type != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ if (type is UserDefinedType && ((UserDefinedType)type).ResolvedClass != null) {
+ // Heap[e, alloc] && dtype(e) == T
+ return GoodRef_Class(tok, e, (UserDefinedType)type, false);
+ } else {
+ // Heap[e, alloc]
+ return IsAlloced(tok, e);
+ }
+ }
+
+ public Bpl.Expr GoodRef_Class(IToken tok, Bpl.Expr e, UserDefinedType type, bool isNew)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(e != null);
+ Contract.Requires(type != null);
+ Contract.Requires(type.ResolvedClass is ClassDecl);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+ return GoodRef_Ref(tok, e, new Bpl.IdentifierExpr(tok, translator.GetClass(type.ResolvedClass)), type.TypeArgs, isNew);
+ }
+
+ public Bpl.Expr GoodRef_Ref(IToken tok, Bpl.Expr e, Bpl.Expr type, List<Type/*!*/>/*!*/ typeArgs, bool isNew) {
+ Contract.Requires(tok != null);
+ Contract.Requires(e != null);
+ Contract.Requires(type != null);
+ Contract.Requires(cce.NonNullElements(typeArgs));
+
+ // Heap[e, alloc]
+ Bpl.Expr r = IsAlloced(tok, e);
+ if (isNew) {
+ r = Bpl.Expr.Not(r); // use the conjunct: !Heap[e, alloc]
+ }
+
+ // dtype(e) == C
+ Bpl.Expr dtypeFunc = translator.FunctionCall(tok, BuiltinFunction.DynamicType, null, e);
+ Bpl.Expr dtype = Bpl.Expr.Eq(dtypeFunc, type);
+ r = r == null ? dtype : Bpl.Expr.And(r, dtype);
+
+ // TypeParams(e, #) == T
+ int n = 0;
+ foreach (Type arg in typeArgs) {
+ Bpl.Expr tpFunc = translator.FunctionCall(tok, BuiltinFunction.TypeParams, null, e, Bpl.Expr.Literal(n));
+ Bpl.Expr ta = translator.GetTypeExpr(tok, arg);
+ if (ta != null) {
+ r = Bpl.Expr.And(r, Bpl.Expr.Eq(tpFunc, ta));
+ }
+ n++;
+ }
+
+ return r;
+ }
+
+ public Bpl.Expr Good_Datatype(IToken tok, Bpl.Expr e, TopLevelDecl resolvedClass, List<Type> typeArgs) {
+ Contract.Requires(tok != null);
+ Contract.Requires(e != null);
+ Contract.Requires(resolvedClass != null);
+ Contract.Requires(typeArgs != null);
+
+ // DtType(e) == C
+ Bpl.Expr dttypeFunc = translator.FunctionCall(tok, BuiltinFunction.DtType, null, e);
+ Bpl.Expr r = Bpl.Expr.Eq(dttypeFunc, new Bpl.IdentifierExpr(tok, translator.GetClass(resolvedClass)));
+
+ // DtTypeParams(e, #) == T
+ int n = 0;
+ foreach (Type arg in typeArgs) {
+ Bpl.Expr tpFunc = translator.FunctionCall(tok, BuiltinFunction.DtTypeParams, null, e, Bpl.Expr.Literal(n));
+ Bpl.Expr ta = translator.GetTypeExpr(tok, arg);
+ if (ta != null) {
+ r = Bpl.Expr.And(r, Bpl.Expr.Eq(tpFunc, ta));
+ }
+ n++;
+ }
+
+ return r;
+ }
+ }
+
+ enum BuiltinFunction
+ {
+ SetEmpty,
+ SetUnionOne,
+ SetUnion,
+ SetIntersection,
+ SetDifference,
+ SetEqual,
+ SetSubset,
+ SetDisjoint,
+ SetChoose,
+
+ MultiSetEmpty,
+ MultiSetUnionOne,
+ MultiSetUnion,
+ MultiSetIntersection,
+ MultiSetDifference,
+ MultiSetEqual,
+ MultiSetSubset,
+ MultiSetDisjoint,
+ MultiSetFromSet,
+ MultiSetFromSeq,
+ IsGoodMultiSet,
+
+ SeqLength,
+ SeqEmpty,
+ SeqBuild,
+ SeqAppend,
+ SeqIndex,
+ SeqUpdate,
+ SeqContains,
+ SeqDrop,
+ SeqTake,
+ SeqEqual,
+ SeqSameUntil,
+ SeqFromArray,
+
+ MapDomain,
+ MapElements,
+ MapEqual,
+ MapBuild,
+ MapDisjoint,
+ MapUnion,
+ MapGlue,
+
+ IndexField,
+ MultiIndexField,
+
+ Box,
+ Unbox,
+ IsCanonicalBoolBox,
+
+ IsGoodHeap,
+ HeapSucc,
+
+ DynamicType, // allocated type (of object reference)
+ DtType, // type of datatype value
+ TypeParams, // type parameters of allocated type
+ DtTypeParams, // type parameters of datatype
+ TypeTuple,
+ DeclType,
+ FieldOfDecl,
+ FDim, // field dimension (0 - named, 1 or more - indexed)
+
+ DatatypeCtorId,
+ DtRank,
+ DtAlloc,
+
+ GenericAlloc
+ }
+
+ // The "typeInstantiation" argument is passed in to help construct the result type of the function.
+ Bpl.NAryExpr FunctionCall(IToken tok, BuiltinFunction f, Bpl.Type typeInstantiation, params Bpl.Expr[] args)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(args != null);
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.Result<Bpl.NAryExpr>() != null);
+
+ switch (f) {
+ case BuiltinFunction.SetEmpty: {
+ Contract.Assert(args.Length == 0);
+ Contract.Assert(typeInstantiation != null);
+ Bpl.Type resultType = predef.SetType(tok, 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);
+ case BuiltinFunction.SetUnion:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "Set#Union", predef.SetType(tok, typeInstantiation), args);
+ case BuiltinFunction.SetIntersection:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "Set#Intersection", predef.SetType(tok, typeInstantiation), args);
+ case BuiltinFunction.SetDifference:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "Set#Difference", predef.SetType(tok, typeInstantiation), args);
+ case BuiltinFunction.SetEqual:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "Set#Equal", Bpl.Type.Bool, args);
+ case BuiltinFunction.SetSubset:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "Set#Subset", Bpl.Type.Bool, args);
+ case BuiltinFunction.SetDisjoint:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "Set#Disjoint", Bpl.Type.Bool, args);
+ case BuiltinFunction.SetChoose:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "Set#Choose", typeInstantiation, args);
+
+
+ case BuiltinFunction.MultiSetEmpty: {
+ Contract.Assert(args.Length == 0);
+ Contract.Assert(typeInstantiation != null);
+ Bpl.Type resultType = predef.MultiSetType(tok, typeInstantiation);
+ return Bpl.Expr.CoerceType(tok, FunctionCall(tok, "MultiSet#Empty", resultType, args), resultType);
+ }
+ case BuiltinFunction.MultiSetUnionOne:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "MultiSet#UnionOne", predef.MultiSetType(tok, typeInstantiation), args);
+ case BuiltinFunction.MultiSetUnion:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "MultiSet#Union", predef.MultiSetType(tok, typeInstantiation), args);
+ case BuiltinFunction.MultiSetIntersection:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "MultiSet#Intersection", predef.MultiSetType(tok, typeInstantiation), args);
+ case BuiltinFunction.MultiSetDifference:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "MultiSet#Difference", predef.MultiSetType(tok, typeInstantiation), args);
+ case BuiltinFunction.MultiSetEqual:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "MultiSet#Equal", Bpl.Type.Bool, args);
+ case BuiltinFunction.MultiSetSubset:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "MultiSet#Subset", Bpl.Type.Bool, args);
+ case BuiltinFunction.MultiSetDisjoint:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "MultiSet#Disjoint", Bpl.Type.Bool, args);
+ case BuiltinFunction.MultiSetFromSet:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "MultiSet#FromSet", predef.MultiSetType(tok, typeInstantiation), args);
+ case BuiltinFunction.MultiSetFromSeq:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "MultiSet#FromSeq", predef.MultiSetType(tok, typeInstantiation), args);
+ case BuiltinFunction.IsGoodMultiSet:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "$IsGoodMultiSet", Bpl.Type.Bool, args);
+ // avoiding this for now
+ /*case BuiltinFunction.SetChoose:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "Set#Choose", typeInstantiation, args);*/
+
+ case BuiltinFunction.SeqLength:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "Seq#Length", Bpl.Type.Int, args);
+ case BuiltinFunction.SeqEmpty: {
+ Contract.Assert(args.Length == 0);
+ Contract.Assert(typeInstantiation != null);
+ Bpl.Type resultType = predef.SeqType(tok, typeInstantiation);
+ return Bpl.Expr.CoerceType(tok, FunctionCall(tok, "Seq#Empty", resultType, args), resultType);
+ }
+ case BuiltinFunction.SeqBuild:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "Seq#Build", predef.SeqType(tok, typeInstantiation), args);
+ case BuiltinFunction.SeqAppend:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "Seq#Append", predef.SeqType(tok, typeInstantiation), args);
+ case BuiltinFunction.SeqIndex:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "Seq#Index", typeInstantiation, args);
+ case BuiltinFunction.SeqUpdate:
+ Contract.Assert(args.Length == 3);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "Seq#Update", predef.SeqType(tok, typeInstantiation), args);
+ case BuiltinFunction.SeqContains:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "Seq#Contains", Bpl.Type.Bool, args);
+ case BuiltinFunction.SeqDrop:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "Seq#Drop", predef.SeqType(tok, typeInstantiation), args);
+ case BuiltinFunction.SeqTake:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "Seq#Take", predef.SeqType(tok, typeInstantiation), args);
+ case BuiltinFunction.SeqEqual:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "Seq#Equal", Bpl.Type.Bool, args);
+ case BuiltinFunction.SeqSameUntil:
+ Contract.Assert(args.Length == 3);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "Seq#SameUntil", Bpl.Type.Bool, args);
+ case BuiltinFunction.SeqFromArray:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "Seq#FromArray", typeInstantiation, args);
+
+ case BuiltinFunction.MapDomain:
+ Contract.Assert(args.Length == 1);
+ return FunctionCall(tok, "Map#Domain", typeInstantiation, args);
+ case BuiltinFunction.MapElements:
+ Contract.Assert(args.Length == 1);
+ return FunctionCall(tok, "Map#Elements", typeInstantiation, args);
+ case BuiltinFunction.MapGlue:
+ Contract.Assert(args.Length == 2);
+ return FunctionCall(tok, "Map#Glue", predef.MapType(tok, predef.BoxType, predef.BoxType), args);
+ case BuiltinFunction.MapEqual:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "Map#Equal", Bpl.Type.Bool, args);
+ case BuiltinFunction.MapDisjoint:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "Map#Disjoint", Bpl.Type.Bool, args);
+ case BuiltinFunction.MapUnion:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "Map#Disjoint", typeInstantiation, args);
+
+ case BuiltinFunction.IndexField:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "IndexField", predef.FieldName(tok, predef.BoxType), args);
+ case BuiltinFunction.MultiIndexField:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "MultiIndexField", predef.FieldName(tok, predef.BoxType), args);
+
+ case BuiltinFunction.Box:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "$Box", predef.BoxType, args);
+ case BuiltinFunction.Unbox:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation != null);
+ return Bpl.Expr.CoerceType(tok, FunctionCall(tok, "$Unbox", typeInstantiation, args), typeInstantiation);
+ case BuiltinFunction.IsCanonicalBoolBox:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "$IsCanonicalBoolBox", Bpl.Type.Bool, args);
+
+ case BuiltinFunction.IsGoodHeap:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "$IsGoodHeap", Bpl.Type.Bool, args);
+ case BuiltinFunction.HeapSucc:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "$HeapSucc", Bpl.Type.Bool, args);
+
+ case BuiltinFunction.DynamicType:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "dtype", predef.ClassNameType, args);
+ case BuiltinFunction.DtType:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "DtType", predef.ClassNameType, args);
+ case BuiltinFunction.TypeParams:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "TypeParams", predef.ClassNameType, args);
+ case BuiltinFunction.DtTypeParams:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "DtTypeParams", predef.ClassNameType, args);
+ case BuiltinFunction.TypeTuple:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "TypeTuple", predef.ClassNameType, args);
+ case BuiltinFunction.DeclType:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "DeclType", predef.ClassNameType, args);
+ case BuiltinFunction.FieldOfDecl:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "FieldOfDecl", predef.FieldName(tok, typeInstantiation) , args);
+ case BuiltinFunction.FDim:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation != null);
+ return FunctionCall(tok, "FDim", Bpl.Type.Int, args);
+
+ case BuiltinFunction.DatatypeCtorId:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "DatatypeCtorId", predef.DtCtorId, args);
+ case BuiltinFunction.DtRank:
+ Contract.Assert(args.Length == 1);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "DtRank", Bpl.Type.Int, args);
+ case BuiltinFunction.DtAlloc:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "DtAlloc", Bpl.Type.Bool, args);
+
+ case BuiltinFunction.GenericAlloc:
+ Contract.Assert(args.Length == 2);
+ Contract.Assert(typeInstantiation == null);
+ return FunctionCall(tok, "GenericAlloc", Bpl.Type.Bool, args);
+
+ default:
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected built-in function
+ }
+ }
+
+ Bpl.NAryExpr FunctionCall(IToken tok, string function, Bpl.Type returnType, params Bpl.Expr[] args)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(function != null);
+ Contract.Requires(args != null);
+ Contract.Ensures(Contract.Result<Bpl.NAryExpr>() != null);
+
+ return new Bpl.NAryExpr(tok, new Bpl.FunctionCall(new Bpl.IdentifierExpr(tok, function, returnType)), new Bpl.ExprSeq(args));
+ }
+
+ Bpl.NAryExpr FunctionCall(IToken tok, string function, Bpl.Type returnType, List<Bpl.Expr> args)
+ {
+ Contract.Requires(tok != null);
+ Contract.Requires(function != null);
+ Contract.Requires(returnType != null);
+ Contract.Requires(cce.NonNullElements(args));
+ Contract.Ensures(Contract.Result<Bpl.NAryExpr>() != null);
+
+ Bpl.ExprSeq aa = new Bpl.ExprSeq();
+ foreach (Bpl.Expr arg in args) {
+ aa.Add(arg);
+ }
+ return new Bpl.NAryExpr(tok, new Bpl.FunctionCall(new Bpl.IdentifierExpr(tok, function, returnType)), aa);
+ }
+
+ Bpl.Expr ArrayLength(IToken tok, Bpl.Expr arr, int totalDims, int dim) {
+ Contract.Requires(tok != null);
+ Contract.Requires(arr != null);
+ Contract.Requires(1 <= totalDims);
+ Contract.Requires(0 <= dim && dim < totalDims);
+
+ string name = "_System." + BuiltIns.ArrayClassName(totalDims) + ".Length";
+ if (totalDims != 1) {
+ name += dim;
+ }
+ return new Bpl.NAryExpr(tok, new Bpl.FunctionCall(new Bpl.IdentifierExpr(tok, name, Bpl.Type.Int)), new Bpl.ExprSeq(arr));
+ }
+
+ public class SplitExprInfo
+ {
+ public readonly bool IsFree;
+ public readonly Bpl.Expr E;
+ public SplitExprInfo(bool isFree, Bpl.Expr e) {
+ Contract.Requires(e != null);
+ IsFree = isFree;
+ E = e;
+ }
+ }
+
+ internal List<SplitExprInfo/*!*/>/*!*/ TrSplitExpr(Expression expr, ExpressionTranslator etran, out bool splitHappened) {
+ 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, int.MaxValue, etran);
+ return splits;
+ }
+
+ /// <summary>
+ /// Tries to split the expression into tactical conjuncts (if "position") or disjuncts (if "!position").
+ /// If a (necessarily boolean) function call appears as a top-level conjunct, then inline the function if
+ /// if it declared in the current module and its height is less than "heightLimit".
+ /// </summary>
+ internal bool TrSplitExpr(Expression expr, List<SplitExprInfo/*!*/>/*!*/ splits, bool position, int heightLimit, ExpressionTranslator etran) {
+ Contract.Requires(expr != null);
+ Contract.Requires(expr.Type is BoolType || (expr is BoxingCastExpr && ((BoxingCastExpr)expr).E.Type is BoolType));
+ Contract.Requires(splits != null);
+ Contract.Requires(etran != null);
+
+ if (expr is BoxingCastExpr) {
+ var bce = (BoxingCastExpr)expr;
+ var ss = new List<SplitExprInfo>();
+ if (TrSplitExpr(bce.E, ss, position, heightLimit, etran)) {
+ foreach (var s in ss) {
+ splits.Add(new SplitExprInfo(s.IsFree, etran.CondApplyBox(s.E.tok, s.E, bce.FromType, bce.ToType)));
+ }
+ return true;
+ }
+
+ } else if (expr is ConcreteSyntaxExpression) {
+ var e = (ConcreteSyntaxExpression)expr;
+ return TrSplitExpr(e.ResolvedExpression, splits, position, heightLimit, etran);
+
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ return TrSplitExpr(etran.GetSubstitutedBody(e), splits, position, heightLimit, etran);
+
+ } else if (expr is UnaryExpr) {
+ var e = (UnaryExpr)expr;
+ if (e.Op == UnaryExpr.Opcode.Not) {
+ var ss = new List<SplitExprInfo>();
+ if (TrSplitExpr(e.E, ss, !position, heightLimit, etran)) {
+ foreach (var s in ss) {
+ splits.Add(new SplitExprInfo(s.IsFree, Bpl.Expr.Unary(s.E.tok, UnaryOperator.Opcode.Not, s.E)));
+ }
+ return true;
+ }
+ }
+
+ } else if (expr is BinaryExpr) {
+ var bin = (BinaryExpr)expr;
+ if (position && bin.ResolvedOp == BinaryExpr.ResolvedOpcode.And) {
+ TrSplitExpr(bin.E0, splits, position, heightLimit, etran);
+ TrSplitExpr(bin.E1, splits, position, heightLimit, etran);
+ return true;
+
+ } else if (!position && bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Or) {
+ TrSplitExpr(bin.E0, splits, position, heightLimit, etran);
+ TrSplitExpr(bin.E1, splits, position, heightLimit, etran);
+ return true;
+
+ } else if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Imp) {
+ // non-conditionally split these, so we get the source location to point to a subexpression
+ if (position) {
+ var lhs = etran.TrExpr(bin.E0);
+ var ss = new List<SplitExprInfo>();
+ TrSplitExpr(bin.E1, ss, position, heightLimit, 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.IsFree, Bpl.Expr.Binary(s.E.tok, BinaryOperator.Opcode.Imp, lhs, s.E)));
+ }
+ } else {
+ var ss = new List<SplitExprInfo>();
+ TrSplitExpr(bin.E0, ss, !position, heightLimit, etran);
+ var lhs = etran.TrExpr(bin.E1);
+ foreach (var s in ss) {
+ // as the source location in the following implication, use that of the translated "s"
+ splits.Add(new SplitExprInfo(s.IsFree, Bpl.Expr.Binary(s.E.tok, BinaryOperator.Opcode.Imp, s.E, lhs)));
+ }
+ }
+ return true;
+ }
+
+ } else if (expr is ITEExpr) {
+ var ite = (ITEExpr)expr;
+
+ var ssThen = new List<SplitExprInfo>();
+ var ssElse = new List<SplitExprInfo>();
+ // Note: The following lines intentionally uses | instead of ||, because we need both calls to TrSplitExpr
+ if (TrSplitExpr(ite.Thn, ssThen, position, heightLimit, etran) | TrSplitExpr(ite.Els, ssElse, position, heightLimit, etran)) {
+ var op = position ? BinaryOperator.Opcode.Imp : BinaryOperator.Opcode.And;
+ var test = etran.TrExpr(ite.Test);
+ foreach (var s in ssThen) {
+ // as the source location in the following implication, use that of the translated "s"
+ splits.Add(new SplitExprInfo(s.IsFree, Bpl.Expr.Binary(s.E.tok, op, test, s.E)));
+ }
+
+ var negatedTest = Bpl.Expr.Not(test);
+ foreach (var s in ssElse) {
+ // as the source location in the following implication, use that of the translated "s"
+ splits.Add(new SplitExprInfo(s.IsFree, Bpl.Expr.Binary(s.E.tok, op, negatedTest, s.E)));
+ }
+
+ return true;
+ }
+
+ } else if (expr is PredicateExpr) {
+ var e = (PredicateExpr)expr;
+ // For a predicate expression in split position, the predicate can be used as an assumption. Unfortunately,
+ // this assumption is not generated in non-split positions (because I don't know how.)
+ // So, treat "assert/assume P; E" like "P ==> E".
+ if (position) {
+ var guard = etran.TrExpr(e.Guard);
+ var ss = new List<SplitExprInfo>();
+ TrSplitExpr(e.Body, ss, position, heightLimit, 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.IsFree, Bpl.Expr.Binary(s.E.tok, BinaryOperator.Opcode.Imp, guard, s.E)));
+ }
+ } else {
+ var ss = new List<SplitExprInfo>();
+ TrSplitExpr(e.Guard, ss, !position, heightLimit, etran);
+ var rhs = etran.TrExpr(e.Body);
+ foreach (var s in ss) {
+ // as the source location in the following implication, use that of the translated "s"
+ splits.Add(new SplitExprInfo(s.IsFree, Bpl.Expr.Binary(s.E.tok, BinaryOperator.Opcode.Imp, s.E, rhs)));
+ }
+ }
+ return true;
+
+ } else if (expr is OldExpr) {
+ var e = (OldExpr)expr;
+ return TrSplitExpr(e.E, splits, position, heightLimit, etran.Old);
+
+ } else if (expr is FunctionCallExpr) {
+ if (position) {
+ var fexp = (FunctionCallExpr)expr;
+ var f = fexp.Function;
+ Contract.Assert(f != null); // filled in during resolution
+ var module = f.EnclosingClass.Module;
+ var functionHeight = module.CallGraph.GetSCCRepresentativeId(f);
+
+ if (module == currentModule && functionHeight < heightLimit) {
+ if (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 {
+ // inline this body
+ Dictionary<IVariable, Expression> 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];
+ Expression arg = fexp.Args[i];
+ arg = new BoxingCastExpr(arg, cce.NonNull(arg.Type), p.Type);
+ arg.Type = p.Type; // resolve here
+ substMap.Add(p, arg);
+ }
+ Expression body = Substitute(f.Body, fexp.Receiver, substMap);
+
+ // Produce, for a "body" split into b0, b1, b2:
+ // free F#canCall(args) && F(args) && (b0 && b1 && b2)
+ // checked F#canCall(args) ==> F(args) || b0
+ // checked F#canCall(args) ==> F(args) || b1
+ // checked F#canCall(args) ==> F(args) || b2
+ // Note that "body" does not contain limited calls.
+
+ // F#canCall(args)
+ Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(expr.tok, f.FullCompileName + "#canCall", Bpl.Type.Bool);
+ ExprSeq args = etran.FunctionInvocationArguments(fexp);
+ Bpl.Expr canCall = new Bpl.NAryExpr(expr.tok, new Bpl.FunctionCall(canCallFuncID), args);
+
+ // F(args)
+ Bpl.Expr fargs = etran.TrExpr(fexp);
+
+ // body
+ Bpl.Expr trBody = etran.TrExpr(body);
+ trBody = etran.CondApplyUnbox(trBody.tok, trBody, f.ResultType, expr.Type);
+
+ // here goes the free piece:
+ splits.Add(new SplitExprInfo(true, Bpl.Expr.Binary(trBody.tok, BinaryOperator.Opcode.And, canCall, BplAnd(fargs, trBody))));
+
+ // recurse on body
+ var ss = new List<SplitExprInfo>();
+ TrSplitExpr(body, ss, position, functionHeight, etran);
+ foreach (var s in ss) {
+ var unboxedConjunct = etran.CondApplyUnbox(s.E.tok, s.E, f.ResultType, expr.Type);
+ var bodyOrConjunct = Bpl.Expr.Or(fargs, unboxedConjunct);
+ var p = Bpl.Expr.Binary(new NestedToken(fexp.tok, s.E.tok), BinaryOperator.Opcode.Imp, canCall, bodyOrConjunct);
+ splits.Add(new SplitExprInfo(s.IsFree, p));
+ }
+
+ return true;
+ }
+ }
+ }
+ }
+
+ } else if ((position && expr is ForallExpr) || (!position && expr is ExistsExpr)) {
+ var e = (QuantifierExpr)expr;
+ var inductionVariables = ApplyInduction(e);
+ if (2 <= DafnyOptions.O.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
+ // (exists n :: (forall k :: k < n ==> !P(k)) && P(n))
+ // ^^^^^^ ^ ^^ <--- note these 3 differences
+ var kvars = new List<BoundVar>();
+ var kk = new List<Bpl.Expr>();
+ var nn = new List<Bpl.Expr>();
+ var toks = new List<IToken>();
+ var types = new List<Type>();
+ var substMap = new Dictionary<IVariable, Expression>();
+ foreach (var n in inductionVariables) {
+ toks.Add(n.tok);
+ types.Add(n.Type);
+ BoundVar k = new BoundVar(n.tok, n.Name + "$ih#" + otherTmpVarCount, n.Type);
+ otherTmpVarCount++;
+ kvars.Add(k);
+
+ IdentifierExpr ieK = new IdentifierExpr(k.tok, k.UniqueName);
+ ieK.Var = k; ieK.Type = ieK.Var.Type; // resolve it here
+ kk.Add(etran.TrExpr(ieK));
+
+ IdentifierExpr ieN = new IdentifierExpr(n.tok, n.UniqueName);
+ ieN.Var = n; ieN.Type = ieN.Var.Type; // resolve it here
+ nn.Add(etran.TrExpr(ieN));
+
+ substMap.Add(n, ieK);
+ }
+ Expression bodyK = Substitute(e.LogicalBody(), null, substMap);
+
+ Bpl.Expr less = DecreasesCheck(toks, types, kk, nn, etran, null, null, false, true);
+
+ Bpl.Expr ihBody = etran.TrExpr(bodyK);
+ if (!position) {
+ ihBody = Bpl.Expr.Not(ihBody);
+ }
+ ihBody = Bpl.Expr.Imp(less, ihBody);
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ Bpl.Expr typeAntecedent = etran.TrBoundVariables(kvars, bvars);
+ Bpl.Expr ih = new Bpl.ForallExpr(expr.tok, bvars, Bpl.Expr.Imp(typeAntecedent, ihBody));
+
+ // More precisely now:
+ // (forall n :: n-has-expected-type && (forall k :: k < n ==> P(k)) && case0(n) ==> P(n))
+ // (forall n :: n-has-expected-type && (forall k :: k < n ==> P(k)) && case...(n) ==> P(n))
+ // or similar for existentials.
+ var caseProduct = new List<Bpl.Expr>() { new Bpl.LiteralExpr(expr.tok, true) }; // make sure to include the correct token information
+ var i = 0;
+ foreach (var n in inductionVariables) {
+ var newCases = new List<Bpl.Expr>();
+ foreach (var kase in InductionCases(n.Type, nn[i], etran)) {
+ foreach (var cs in caseProduct) {
+ if (kase != Bpl.Expr.True) { // if there's no case, don't add anything to the token
+ newCases.Add(Bpl.Expr.Binary(new NestedToken(cs.tok, kase.tok), Bpl.BinaryOperator.Opcode.And, cs, kase));
+ } else {
+ newCases.Add(cs);
+ }
+ }
+ }
+ caseProduct = newCases;
+ i++;
+ }
+ bvars = new Bpl.VariableSeq();
+ 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());
+ Bpl.Expr q;
+ if (position) {
+ q = new Bpl.ForallExpr(kase.tok, bvars, Bpl.Expr.Imp(ante, bdy));
+ } else {
+ q = new Bpl.ExistsExpr(kase.tok, bvars, Bpl.Expr.And(ante, bdy));
+ }
+ splits.Add(new SplitExprInfo(false, q));
+ }
+
+ // Finally, assume the original quantifier (forall/exists n :: P(n))
+ splits.Add(new SplitExprInfo(true, etran.TrExpr(expr)));
+ return true;
+ }
+ }
+
+ Bpl.Expr translatedExpression;
+ bool splitHappened;
+ if ((position && expr is ExistsExpr) || (!position && expr is ForallExpr)) {
+ translatedExpression = etran.TrExpr(expr);
+ splitHappened = false;
+ } else {
+ etran = etran.LayerOffset(1);
+ translatedExpression = etran.TrExpr(expr);
+ splitHappened = etran.Statistics_CustomLayerFunctionCount != 0; // return true if the LayerOffset(1) came into play
+ }
+ // TODO: Is the the following call to ContainsChange expensive? It's linear in the size of "expr", but we get here many times in TrSpliExpr, so wouldn't the total
+ // time in the size of the expression passed to the first TrSpliExpr be quadratic?
+ if (RefinementToken.IsInherited(expr.tok, currentModule) && (codeContext == null || !codeContext.MustReverify) && RefinementTransformer.ContainsChange(expr, currentModule)) {
+ // If "expr" contains a subexpression that has changed from the inherited expression, we'll destructively
+ // change the token of the translated expression to make it look like it's not inherited. This will cause "e" to
+ // be verified again in the refining module.
+ translatedExpression.tok = new ForceCheckToken(expr.tok);
+ splitHappened = true; // count this as a split, so this translated expression is not ignored
+ }
+ splits.Add(new SplitExprInfo(false, translatedExpression));
+ return splitHappened;
+ }
+
+ List<BoundVar> ApplyInduction(QuantifierExpr e) {
+ return ApplyInduction(e.BoundVars, e.Attributes, new List<Expression>() { e.LogicalBody() },
+ delegate(System.IO.TextWriter wr) { new Printer(Console.Out).PrintExpression(e); });
+ }
+
+ 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".
+ /// </summary>
+ List<VarType> ApplyInduction<VarType>(List<VarType> boundVars, Attributes attributes, List<Expression> searchExprs, TracePrinter tracePrinter) 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].E 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.E.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) {
+ 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' 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;
+ bool inferredDecreases; // we don't actually care
+ var decr = FunctionDecreasesWithDefault(e.Function, out inferredDecreases);
+ 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 {
+ // The argument position is considered to be "variant" if the function is recursive and the argument participates
+ // in the effective decreases clause of the function. The argument participates if it's a free variable
+ // of a term in the explicit decreases clause.
+ variantArgument = rec && decr.Exists(ee => ContainsFreeVariable(ee, false, f));
+ }
+ if (VarOccursInArgumentToRecursiveFunction(exp, n, variantArgument || subExprIsProminent)) {
+ return true;
+ }
+ }
+ return false;
+ } 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 PredicateExpr) {
+ var e = (PredicateExpr)expr;
+ // ignore the guard
+ return VarOccursInArgumentToRecursiveFunction(e.Body, 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;
+ }
+ }
+
+ IEnumerable<Bpl.Expr> InductionCases(Type ty, Bpl.Expr expr, ExpressionTranslator etran) {
+ IndDatatypeDecl dt = ty.AsIndDatatype;
+ if (dt == null) {
+ yield return Bpl.Expr.True;
+ } else {
+ UserDefinedType instantiatedType = (UserDefinedType)ty; // correctness of cast follows from the non-null return of ty.AsDatatype
+ var subst = new Dictionary<TypeParameter, Type>();
+ for (int i = 0; i < dt.TypeArgs.Count; i++) {
+ subst.Add(dt.TypeArgs[i], instantiatedType.TypeArgs[i]);
+ }
+
+ foreach (DatatypeCtor ctor in dt.Ctors) {
+ Bpl.VariableSeq bvs;
+ List<Bpl.Expr> args;
+ CreateBoundVariables(ctor.Formals, out bvs, out args);
+ Bpl.Expr ct = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
+ // (exists args :: args-have-the-expected-types && ct(args) == expr)
+ Bpl.Expr q = Bpl.Expr.Binary(ctor.tok, BinaryOperator.Opcode.Eq, ct, expr);
+ if (bvs.Length != 0) {
+ int i = 0;
+ Bpl.Expr typeAntecedent = Bpl.Expr.True;
+ foreach (Formal arg in ctor.Formals) {
+ var instantiatedArgType = Resolver.SubstType(arg.Type, subst);
+ Bpl.Expr wh = GetWhereClause(arg.tok, etran.CondApplyUnbox(arg.tok, args[i], arg.Type, instantiatedArgType), instantiatedArgType, etran);
+ if (wh != null) {
+ typeAntecedent = BplAnd(typeAntecedent, wh);
+ }
+ i++;
+ }
+ q = new Bpl.ExistsExpr(ctor.tok, bvs, BplAnd(typeAntecedent, q));
+ }
+ yield return q;
+ }
+ }
+ }
+
+ void AddCoinductionPrinciple(CoPredicate coPredicate) {
+ Contract.Requires(coPredicate != null);
+ if (coPredicate.Body == null) {
+ return; // we can't generate any useful coinduction principle if the copredicate doesn't have a body
+ } else if (2 <= coPredicate.EnclosingClass.Module.CallGraph.GetSCCSize(coPredicate)) {
+ return; // this copredicate involves mutually recursive calls; for now, we don't handle those
+ }
+ var n = (coPredicate.IsStatic ? 0 : 1) + coPredicate.Formals.Count;
+ var templates = new List<FunctionCallExpr[/*of length n*/]>();
+ foreach (var fce in coPredicate.Uses) {
+ // Compute a template of instantiation for this use: for each actual argument, record
+ // a function F if the argument is a call to F, or record null otherwise.
+ var t = new FunctionCallExpr[n];
+ var i = 0;
+ if (!coPredicate.IsStatic) {
+ t[i] = fce.Receiver.Resolved as FunctionCallExpr;
+ i++;
+ }
+ foreach (var a in fce.Args) {
+ t[i] = a.Resolved as FunctionCallExpr;
+ i++;
+ }
+ Contract.Assert(i == t.Length);
+ // See if this template has been done before (for now, this is a slow check; it would be nice to speed it up)
+ foreach (var prev in templates) {
+ var j = 0;
+ for (; j < n; j++) {
+ if (prev[j] == null && t[j] == null) {
+ // things are equal
+ } else if (prev[j] != null && t[j] != null && prev[j].Function == t[j].Function) {
+ // things are equal
+ } else {
+ // we found an unequal part of the template
+ break;
+ }
+ }
+ if (j == n) {
+ // template 't' matches 'prev', so we've already spilled out an axiom for this template
+ // TODO: this is not the complete test, because of possible type parameters
+ goto Next_Use;
+ }
+ // 't' does not match this 'prev'; try the next 'prev'
+ }
+ // No 'prev' matches the current template 't'. So, generate an axiom for 't'.
+ string comment = string.Format("Coinduction principle for copredicate {0} instantiated like:", coPredicate.FullName);
+ foreach (var tf in t) {
+ comment += tf == null ? " ." : " " + tf.Name;
+ }
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(fce.tok, CoinductionPrinciple(coPredicate, fce, t), comment));
+ templates.Add(t);
+ Next_Use: ;
+ }
+ }
+
+ /// <summary>
+ /// It's the "callOfInterest" that determines the (type-parameter instantiated) types of the arguments
+ /// </summary>
+ Bpl.Expr CoinductionPrinciple(CoPredicate coPredicate, FunctionCallExpr callOfInterest, FunctionCallExpr[] t) {
+ Contract.Requires(coPredicate != null);
+ Contract.Requires(coPredicate.Body != null);
+ Contract.Requires(t != null);
+ Contract.Requires(t.Length == (coPredicate.IsStatic ? 0 : 1) + coPredicate.Formals.Count);
+ // Let C be the name of the coPredicate, and suppose it is defined by:
+ // copredicate C(x)
+ // requires PreC(x);
+ // {
+ // Body[C]
+ // }
+ // where the notation Body[_] means an expression Body with holes and in particular one whole
+ // for every recursive use of C.
+ // The general form of the coinduction principle of which we will generate an instance is:
+ // forall P ::
+ // forall s :: // where s is a list of arguments to coPredicate
+ // P(s) &&
+ // (forall s' :: P(s') ==> Body[P][x := s'])
+ // ==>
+ // C(s)
+ // We will pick particular P's, and will therefore instead generate the axiom once for each
+ // such P. We will also redistribute the terms as follows:
+ // (forall s :: P(s) ==> Body[P][x := s])
+ // ==>
+ // (forall s :: P(s) ==> C(s))
+ // Furthermore, if the C of interest has actual parameters that are function-call expressions,
+ // then the P will be chosen to fit that pattern. As a specific example, suppose C takes 3
+ // parameters (that is, 's' is a triple (s0,s1,s2)) and that the actual arguments for these, in
+ // the C use of interest, are of the form C(E0,F(E1),G(E2,E3)), where E0 denotes an expression that
+ // is not a function-call expression and E1,E2,E3 are any expressions. Also, suppose the
+ // preconditions of C,F,G are PreC(s0,s1,s2), PreF(f), PreG(g0,g1).
+ // Then, we pick P(s0,s1,s2) :=
+ // exists a,b,c,d :: PreF(b) && PreG(c,d) && PredC(a,F(b),G(c,d)) && (s0,s1,s2) == (a,F(b),G(c,d))
+ // So, the axiom looks like:
+ // (forall a,b,c,d :: PreF(b) && PreG(c,d) && PredC(a,F(b),G(c,d))
+ // ==> Body[P][x0,x1,x2 := a,F(b),G(c,d)])
+ // ==>
+ // (forall a,b,c,d { C(a,F(b),G(c,d)) } ::
+ // PreF(b) && PreG(c,d) && PredC(a,F(b),G(c,d))
+ // ==> C(a,F(b),G(c,d)))
+ //
+ // To be usable, we need to do more. In particular, we need to do some preprocessing of the expressions
+ // of the form C(E0,E1,E2) in Body, which are turned into P(E0,E1,E2) in Body[P] (where these are any
+ // expressions E0,E1,E2, not necessarily the ones as above). We know that such C(E0,E1,E2) occurrences
+ // sit only in positive positions. Hence, we can soundly replace each (exists a,b,c,d :: Q(a,b,c,d))
+ // expressions in Body[P] in the axiom with Q(A,B,C,D) for any A,B,C,D. For this to be useful, we need
+ // good guesses for A,B,C,D.
+ //
+ // TODO: also need a module/function-height antecedent for the axiom!
+ var tok = coPredicate.tok;
+ var bvs = new Bpl.VariableSeq();
+ var boundVars = new List<BoundVar>();
+ Bpl.BoundVariable heapVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$h", predef.HeapType));
+ bvs.Add(heapVar);
+ var bHeap = new Bpl.IdentifierExpr(tok, heapVar);
+ Bpl.Expr typeAntecedents = FunctionCall(tok, BuiltinFunction.IsGoodHeap, null, bHeap);
+ var etran = new ExpressionTranslator(this, predef, bHeap);
+ Expression pre = new LiteralExpr(tok, true);
+ var i = 0;
+ Expression receiverReplacement = null;
+ if (!coPredicate.IsStatic) {
+ Expression preF;
+ receiverReplacement = FG(tok, callOfInterest.Receiver.Type, t[i], boundVars, bvs, ref typeAntecedents, out preF, etran);
+ typeAntecedents = BplAnd(typeAntecedents, Bpl.Expr.Neq(etran.TrExpr(receiverReplacement), predef.Null));
+ if (preF != null) {
+ pre = AutoContractsRewriter.BinBoolExpr(receiverReplacement.tok, BinaryExpr.ResolvedOpcode.And, pre, preF);
+ }
+ i++;
+ }
+ var substMap = new Dictionary<IVariable, Expression>();
+ var j = 0;
+ foreach (var p in coPredicate.Formals) {
+ Expression preF;
+ var e = FG(tok, callOfInterest.Args[j].Type, t[i], boundVars, bvs, ref typeAntecedents, out preF, etran);
+ substMap.Add(p, e);
+ if (preF != null) {
+ pre = AutoContractsRewriter.BinBoolExpr(e.tok, BinaryExpr.ResolvedOpcode.And, pre, preF);
+ }
+ i++; j++;
+ }
+ // conjoin subst(preC) to pre
+ foreach (var req in coPredicate.Req) {
+ var preC = Substitute(req, receiverReplacement, substMap);
+ pre = AutoContractsRewriter.BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.And, pre, preC);
+ }
+ // build up the term C(a,F(b),G(c,d))
+ var args = new List<Expression>();
+ foreach (var p in coPredicate.Formals) {
+ args.Add(substMap[p]);
+ }
+ var C = new FunctionCallExpr(tok, coPredicate.Name, receiverReplacement, tok, args);
+ C.Function = coPredicate;
+ C.Type = coPredicate.ResultType;
+ // We are now ready to produce the Conclusion of the axiom
+ var preStuff = Bpl.Expr.And(typeAntecedents, etran.TrExpr(pre));
+ Bpl.Expr Conclusion = new Bpl.ForallExpr(tok, bvs, new Bpl.Trigger(tok, true, new ExprSeq(etran.TrExpr(C))),
+ Bpl.Expr.Imp(preStuff, etran.TrExpr(C)));
+ // Now for the antecedent of the axiom
+ if (coPredicate.Body is MatchExpr) {
+ return Bpl.Expr.True; // TODO: if coPredicate.Body uses a 'match' expression, first desugar it into an ordinary expression
+ }
+ var s = new CoinductionSubstituter(coPredicate, receiverReplacement, substMap, pre, boundVars, receiverReplacement, args);
+ var body = s.Substitute(coPredicate.Body);
+ Bpl.Expr Antecedent = new Bpl.ForallExpr(tok, bvs, Bpl.Expr.Imp(preStuff, etran.TrExpr(body)));
+ // Put it all together and we're ready to go
+ return Bpl.Expr.Imp(Antecedent, Conclusion);
+ }
+
+ /// <summary>
+ /// "templateFunc" is passed in as "null", then "precondition" is guaranteed to come out as "null".
+ /// </summary>
+ Expression FG(IToken tok, Type argumentType, FunctionCallExpr templateFunc, List<BoundVar> boundVars, Bpl.VariableSeq bvs, ref Bpl.Expr typeAntecedents, out Expression precondition, ExpressionTranslator etran) {
+ Contract.Requires(tok != null);
+ Contract.Requires(argumentType != null);
+ Contract.Requires(boundVars != null);
+ Contract.Requires(bvs != null);
+ Contract.Requires(etran != null);
+ Contract.Ensures(Contract.Result<Expression>() != null);
+ Contract.Ensures(Contract.ValueAtReturn<Expression>(out precondition) != null);
+ // TODO: also need to return type antecedents
+ precondition = new LiteralExpr(tok, true);
+ if (templateFunc == null) {
+ // generate a fresh variable of type "argumentType"
+ var k = new BoundVar(tok, "_coind" + otherTmpVarCount, argumentType);
+ otherTmpVarCount++;
+ boundVars.Add(k);
+ // convert it to a Boogie variable and add it to "bvs"
+ var bvar = new Bpl.BoundVariable(k.tok, new Bpl.TypedIdent(k.tok, k.UniqueName, TrType(k.Type)));
+ bvs.Add(bvar);
+ // update typeAntecedents
+ var wh = GetWhereClause(k.tok, new Bpl.IdentifierExpr(k.tok, bvar), k.Type, etran);
+ if (wh != null) {
+ typeAntecedents = BplAnd(typeAntecedents, wh);
+ }
+ // make an IdentifierExpr out of it and return it
+ IdentifierExpr ie = new IdentifierExpr(k.tok, k.UniqueName);
+ ie.Var = k; ie.Type = ie.Var.Type; // resolve it here
+ return ie;
+
+ } else {
+ // for each formal parameter to "templateFunc", generate a fresh variable
+ // for each one, convert it to a Boogie variable and add it to "bvs"
+ // make a FunctionCallExpr, passing in these variables as arguments, and then returning the FunctionCallExpr
+ Expression preIgnore;
+ Expression receiver = null;
+ if (!templateFunc.Function.IsStatic) {
+ receiver = FG(tok, templateFunc.Receiver.Type, null, boundVars, bvs, ref typeAntecedents, out preIgnore, etran);
+ typeAntecedents = BplAnd(typeAntecedents, Bpl.Expr.Neq(etran.TrExpr(receiver), predef.Null));
+ }
+ var args = new List<Expression>();
+ var i = 0;
+ var substMap = new Dictionary<IVariable, Expression>();
+ foreach (var arg in templateFunc.Args) {
+ var e = FG(tok, arg.Type, null, boundVars, bvs, ref typeAntecedents, out preIgnore, etran);
+ args.Add(e);
+ substMap.Add(templateFunc.Function.Formals[i], e);
+ i++;
+ }
+ var F = new FunctionCallExpr(tok, templateFunc.Name, receiver, tok, args);
+ F.Function = templateFunc.Function;
+ F.Type = templateFunc.Function.ResultType;
+
+ foreach (var req in templateFunc.Function.Req) {
+ var pre = Substitute(req, receiver, substMap);
+ precondition = AutoContractsRewriter.BinBoolExpr(tok, BinaryExpr.ResolvedOpcode.And, precondition, pre);
+ }
+ return F;
+ }
+ }
+
+ /// <summary>
+ /// Returns true iff 'v' occurs as a free variable in 'expr'.
+ /// 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) {
+ Contract.Requires(expr != null);
+ Contract.Requires(lookForReceiver || v != null);
+
+ if (expr is ThisExpr) {
+ return lookForReceiver || v is ThisSurrogate;
+ } else if (expr is IdentifierExpr) {
+ IdentifierExpr e = (IdentifierExpr)expr;
+ return e.Var == v;
+ } else {
+ foreach (var ee in expr.SubExpressions) {
+ if (ContainsFreeVariable(ee, lookForReceiver, v)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Returns true iff 'expr' is a two-state expression, that is, if it mentions "old(...)" or "fresh(...)".
+ /// </summary>
+ public static bool MentionsOldState(Expression expr) {
+ Contract.Requires(expr != null);
+ if (expr is OldExpr || expr is FreshExpr) {
+ return true;
+ }
+ foreach (var ee in expr.SubExpressions) {
+ if (MentionsOldState(ee)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static Expression Substitute(Expression expr, Expression receiverReplacement, Dictionary<IVariable, Expression/*!*/>/*!*/ substMap) {
+ Contract.Requires(expr != null);
+ Contract.Requires(cce.NonNullDictionaryAndValues(substMap));
+ Contract.Ensures(Contract.Result<Expression>() != null);
+ var s = new Substituter(receiverReplacement, substMap);
+ return s.Substitute(expr);
+ }
+
+ public class CoinductionSubstituter : Substituter
+ {
+ CoPredicate coPredicate;
+ Expression existentialAntecedent;
+ List<BoundVar> existentialVariables;
+ Expression existentialReceiver;
+ List<Expression> existentialTemplates;
+
+ public CoinductionSubstituter(CoPredicate coPredicate, Expression receiverReplacement, Dictionary<IVariable, Expression> substMap,
+ Expression existentialAntecedent, List<BoundVar> existentialVariables, Expression existentialReceiver, List<Expression> existentialTemplates)
+ : base(receiverReplacement, substMap)
+ {
+ Contract.Requires(coPredicate != null);
+ Contract.Requires(substMap != null);
+ Contract.Requires(existentialAntecedent != null);
+ Contract.Requires(existentialVariables != null);
+ Contract.Requires(existentialTemplates != null);
+ this.coPredicate = coPredicate;
+ this.existentialAntecedent = existentialAntecedent;
+ this.existentialVariables = existentialVariables;
+ this.existentialReceiver = existentialReceiver;
+ this.existentialTemplates = existentialTemplates;
+ }
+ public override Expression Substitute(Expression expr) {
+ expr = base.Substitute(expr);
+ var e = expr as FunctionCallExpr;
+ if (e == null || e.Function != coPredicate) {
+ return expr;
+ }
+ // We found a call coPredicate(args). Replace it with P(args), where P is the predicate we're
+ // using in the coinduction principle. As described in method CoinductionPrinciple above, the
+ // predicate P has the form:
+ // exists a,b,c,d :: PreF(b) && PreG(c,d) && PredC(a,F(b),G(c,d)) && (s0,s1,s2) == (a,F(b),G(c,d))
+ // where s0,s1,s2 are shows as the actual arguments to coPredicate (called "args" four lines
+ // above). This existential corresponds to the parameters passed to this CoinductionSubstituter
+ // as follows:
+ // exists existentialVariables :: existentialAntecedent && (s0,s1,s2) == (existentialTemplate)
+ // The idea is now to build up a substitution for a,b,c,d, such that the last conjunct
+ // in the existential will be true in any context. We can then replace coPredicate(s0,s1,s2) with
+ // existentialAntecedent[a,b,c,d := A,B,C,D]
+ var substMap = new Dictionary<IVariable, Expression>();
+ var j = 0;
+ for (int i = -1; i < existentialTemplates.Count; i++) {
+ Expression templ;
+ Expression si;
+ if (0 <= i) {
+ templ = existentialTemplates[i].Resolved;
+ si = e.Args[i];
+ } else if (existentialReceiver != null) {
+ templ = existentialReceiver.Resolved;
+ si = e.Receiver;
+ } else {
+ continue;
+ }
+ templ = PossiblyInline(templ);
+ if (templ is IdentifierExpr) {
+ // this one is easy--use the instantiation a:=si
+ Contract.Assert(((IdentifierExpr)templ).Var == existentialVariables[j]);
+ substMap.Add(existentialVariables[j], si);
+ j++;
+ } else {
+ var templFce = (FunctionCallExpr)templ;
+ var psi = PartiallyEvaluate(si, 1);
+ var fce = psi as FunctionCallExpr;
+ if (fce == null || fce.Function != templFce.Function) {
+ // This is not what we were hoping for. What can we do?
+ // One option would be to existentially quantify over the variable for which we didn't get an instantiation.
+ // Another option would be to guess some arbitrary but value.
+ // A third option, which is what we'll do, is to return false.
+ return new LiteralExpr(e.tok, false);
+ } else {
+ // We're in luck. Pick the parameters of the partially evaluated call
+ if (templFce.Receiver != null) {
+ Contract.Assert(templFce.Receiver is IdentifierExpr && ((IdentifierExpr)templFce.Receiver).Var == existentialVariables[j]);
+ substMap.Add(existentialVariables[j], fce.Receiver);
+ j++;
+ }
+ var k = 0;
+ foreach (var arg in fce.Args) {
+ Contract.Assert(templFce.Args[k] is IdentifierExpr && ((IdentifierExpr)templFce.Args[k]).Var == existentialVariables[j]);
+ substMap.Add(existentialVariables[j], arg);
+ j++;
+ k++;
+ }
+ }
+ }
+ }
+ Contract.Assert(j == existentialVariables.Count);
+ return Translator.Substitute(existentialAntecedent, null, substMap);
+ }
+
+ Expression PossiblyInline(Expression expr) {
+ Contract.Requires(expr != null);
+ Contract.Ensures(Contract.Result<Expression>() != null);
+ if (expr is FunctionCallExpr) {
+ var e = (FunctionCallExpr)expr;
+ if (e.Function.Body != null && !e.Function.IsRecursive) {
+ var inlinedBody = InlineFunctionCall(e);
+ // use the inlinedBody if it consists of only IdentifierExpr's and FunctionCallExpr's
+ if (IsTemplateLike(inlinedBody)) {
+ return PossiblyInline(inlinedBody);
+ }
+ }
+ }
+ return expr;
+ }
+
+ bool IsTemplateLike(Expression expr) {
+ Contract.Requires(expr != null);
+ expr = expr.Resolved;
+ if (expr is IdentifierExpr) {
+ return true;
+ } else if (expr is FunctionCallExpr) {
+ var e = (FunctionCallExpr)expr;
+ if (e.Receiver == null || IsTemplateLike(e.Receiver)) {
+ return e.Args.TrueForAll(IsTemplateLike);
+ }
+ }
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Returns a (resolved) expression that evaluates to the same value as "expr".
+ /// Assumes "expr" to be well defined.
+ /// </summary>
+ public static Expression PartiallyEvaluate(Expression expr, int recursiveInlineDepth) {
+ Contract.Requires(expr != null);
+ Contract.Requires(0 <= recursiveInlineDepth);
+ if (expr is ConcreteSyntaxExpression) {
+ var e = (ConcreteSyntaxExpression)expr;
+ return PartiallyEvaluate(e.ResolvedExpression, recursiveInlineDepth);
+ } else if (expr is FieldSelectExpr) {
+ var e = (FieldSelectExpr)expr;
+ // check if it's a destructor around a constructor
+ var dtor = e.Field as DatatypeDestructor;
+ if (dtor != null) {
+ var obj = PartiallyEvaluate(e.Obj, recursiveInlineDepth);
+ var dtv = obj as DatatypeValue;
+ if (dtv != null) {
+ for (int i = 0; i < dtv.Ctor.Formals.Count; i++) {
+ if (dtv.Ctor.Formals[i] == dtor.CorrespondingFormal) {
+ return dtv.Arguments[i];
+ }
+ }
+ Contract.Assert(false); // we expect to have found the destructor among the constructor's formals
+ } else if (obj != e.Obj) {
+ var peE = new FieldSelectExpr(e.tok, obj, e.FieldName);
+ peE.Field = e.Field; // resolve here
+ peE.Type = e.Type; // resolve here
+ return peE;
+ }
+ }
+ } else if (expr is FunctionCallExpr) {
+ var e = (FunctionCallExpr)expr;
+ if (e.Function.Body != null && (!e.Function.IsRecursive || 0 < recursiveInlineDepth)) {
+ var inlinedBody = InlineFunctionCall(e);
+ return PartiallyEvaluate(inlinedBody, recursiveInlineDepth - (e.Function.IsRecursive ? 1 : 0));
+ }
+ }
+ return expr;
+ }
+
+ static Expression InlineFunctionCall(FunctionCallExpr fce) {
+ Contract.Requires(fce != null);
+ var substMap = new Dictionary<IVariable, Expression>();
+ for (int i = 0; i < fce.Args.Count; i++) {
+ substMap.Add(fce.Function.Formals[i], fce.Args[i]);
+ }
+ // TODO: in the following substitution, it may be that we also need to update the types of the resulting subexpressions (is this true for all Substitute calls?)
+ return Substitute(fce.Function.Body, fce.Receiver, substMap);
+ }
+ public class FunctionCallSubstituter : Substituter
+ {
+ public readonly Function A, B;
+ public FunctionCallSubstituter(Expression receiverReplacement, Dictionary<IVariable, Expression/*!*/>/*!*/ substMap, Function a, Function b)
+ : base(receiverReplacement, substMap) {
+ A = a;
+ B = b;
+ }
+ public override Expression Substitute(Expression expr) {
+ if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ Expression receiver = Substitute(e.Receiver);
+ List<Expression> newArgs = SubstituteExprList(e.Args);
+ FunctionCallExpr newFce = new FunctionCallExpr(expr.tok, e.Name, receiver, e.OpenParen, newArgs);
+ if (e.Function == A) {
+ newFce.Function = B;
+ newFce.Type = e.Type; // TODO: this may not work with type parameters.
+ } else {
+ newFce.Function = e.Function;
+ newFce.Type = e.Type;
+ }
+ return newFce;
+ }
+ return base.Substitute(expr);
+ }
+ }
+ public class Substituter
+ {
+ public readonly Expression receiverReplacement;
+ public readonly Dictionary<IVariable, Expression/*!*/>/*!*/ substMap;
+ public Substituter(Expression receiverReplacement, Dictionary<IVariable, Expression/*!*/>/*!*/ substMap) {
+ Contract.Requires(substMap != null);
+ this.receiverReplacement = receiverReplacement;
+ this.substMap = substMap;
+ }
+ public virtual Expression Substitute(Expression expr) {
+ Contract.Requires(expr != null);
+ Contract.Ensures(Contract.Result<Expression>() != null);
+
+ Expression newExpr = null; // set to non-null value only if substitution has any effect; if non-null, newExpr will be resolved at end
+
+ if (expr is LiteralExpr || expr is WildcardExpr || expr is BoogieWrapper) {
+ // nothing to substitute
+ } else if (expr is ThisExpr) {
+ return receiverReplacement == null ? expr : receiverReplacement;
+ } else if (expr is IdentifierExpr) {
+ IdentifierExpr e = (IdentifierExpr)expr;
+ Expression substExpr;
+ if (substMap.TryGetValue(e.Var, out substExpr)) {
+ return cce.NonNull(substExpr);
+ }
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ List<Expression> newElements = SubstituteExprList(e.Elements);
+ if (newElements != e.Elements) {
+ if (expr is SetDisplayExpr) {
+ newExpr = new SetDisplayExpr(expr.tok, newElements);
+ } else if (expr is MultiSetDisplayExpr) {
+ newExpr = new MultiSetDisplayExpr(expr.tok, newElements);
+ } else {
+ newExpr = new SeqDisplayExpr(expr.tok, newElements);
+ }
+ }
+
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr fse = (FieldSelectExpr)expr;
+ Expression substE = Substitute(fse.Obj);
+ if (substE != fse.Obj) {
+ FieldSelectExpr fseNew = new FieldSelectExpr(fse.tok, substE, fse.FieldName);
+ fseNew.Field = fse.Field; // resolve on the fly (and fseExpr.Type is set at end of method)
+ newExpr = fseNew;
+ }
+
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr sse = (SeqSelectExpr)expr;
+ Expression seq = Substitute(sse.Seq);
+ Expression e0 = sse.E0 == null ? null : Substitute(sse.E0);
+ Expression e1 = sse.E1 == null ? null : Substitute(sse.E1);
+ if (seq != sse.Seq || e0 != sse.E0 || e1 != sse.E1) {
+ newExpr = new SeqSelectExpr(sse.tok, sse.SelectOne, seq, e0, e1);
+ }
+
+ } else if (expr is SeqUpdateExpr) {
+ SeqUpdateExpr sse = (SeqUpdateExpr)expr;
+ Expression seq = Substitute(sse.Seq);
+ Expression index = Substitute(sse.Index);
+ Expression val = Substitute(sse.Value);
+ if (seq != sse.Seq || index != sse.Index || val != sse.Value) {
+ newExpr = new SeqUpdateExpr(sse.tok, seq, index, val);
+ }
+
+ } else if (expr is MultiSelectExpr) {
+ MultiSelectExpr mse = (MultiSelectExpr)expr;
+ Expression array = Substitute(mse.Array);
+ List<Expression> newArgs = SubstituteExprList(mse.Indices);
+ if (array != mse.Array || newArgs != mse.Indices) {
+ newExpr = new MultiSelectExpr(mse.tok, array, newArgs);
+ }
+
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ Expression receiver = Substitute(e.Receiver);
+ List<Expression> newArgs = SubstituteExprList(e.Args);
+ if (receiver != e.Receiver || newArgs != e.Args) {
+ FunctionCallExpr newFce = new FunctionCallExpr(expr.tok, e.Name, receiver, e.OpenParen, newArgs);
+ newFce.Function = e.Function; // resolve on the fly (and set newFce.Type below, at end)
+ newExpr = newFce;
+ }
+
+ } else if (expr is DatatypeValue) {
+ DatatypeValue dtv = (DatatypeValue)expr;
+ List<Expression> newArgs = SubstituteExprList(dtv.Arguments);
+ if (newArgs != dtv.Arguments) {
+ DatatypeValue newDtv = new DatatypeValue(dtv.tok, dtv.DatatypeName, dtv.MemberName, newArgs);
+ newDtv.Ctor = dtv.Ctor; // resolve on the fly (and set newDtv.Type below, at end)
+ newExpr = newDtv;
+ }
+
+ } else if (expr is OldExpr) {
+ OldExpr e = (OldExpr)expr;
+ // Note, it is up to the caller to avoid variable capture. In most cases, this is not a
+ // problem, since variables have unique declarations. However, it is an issue if the substitution
+ // takes place inside an OldExpr. In those cases (see LetExpr), the caller can use a
+ // BoogieWrapper before calling Substitute.
+ Expression se = Substitute(e.E);
+ if (se != e.E) {
+ newExpr = new OldExpr(expr.tok, se);
+ }
+ } else if (expr is FreshExpr) {
+ FreshExpr e = (FreshExpr)expr;
+ Expression se = Substitute(e.E);
+ if (se != e.E) {
+ newExpr = new FreshExpr(expr.tok, se);
+ }
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ Expression se = Substitute(e.E);
+ if (se != e.E) {
+ newExpr = new UnaryExpr(expr.tok, e.Op, se);
+ }
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ Expression e0 = Substitute(e.E0);
+ Expression e1 = Substitute(e.E1);
+ if (e0 != e.E0 || e1 != e.E1) {
+ BinaryExpr newBin = new BinaryExpr(expr.tok, e.Op, e0, e1);
+ newBin.ResolvedOp = e.ResolvedOp; // part of what needs to be done to resolve on the fly (newBin.Type is set below, at end)
+ newExpr = newBin;
+ }
+
+ } else if (expr is LetExpr) {
+ var e = (LetExpr)expr;
+ var rhss = new List<Expression>();
+ bool anythingChanged = false;
+ foreach (var rhs in e.RHSs) {
+ var r = Substitute(rhs);
+ if (r != rhs) {
+ anythingChanged = true;
+ }
+ rhss.Add(r);
+ }
+ var body = Substitute(e.Body);
+ if (anythingChanged || body != e.Body) {
+ newExpr = new LetExpr(e.tok, e.Vars, rhss, body);
+ }
+
+ } else if (expr is NamedExpr) {
+ var e = (NamedExpr)expr;
+ var body = Substitute(e.Body);
+ var contract = e.Contract == null ? null : Substitute(e.Contract);
+ newExpr = new NamedExpr(e.tok, e.Name, body, contract, e.ReplacerToken);
+ } else if (expr is ComprehensionExpr) {
+ var e = (ComprehensionExpr)expr;
+ Expression newRange = e.Range == null ? null : Substitute(e.Range);
+ Expression newTerm = Substitute(e.Term);
+ Attributes newAttrs = SubstAttributes(e.Attributes);
+ if (e is SetComprehension) {
+ if (newRange != e.Range || newTerm != e.Term || newAttrs != e.Attributes) {
+ newExpr = new SetComprehension(expr.tok, e.BoundVars, newRange, newTerm);
+ }
+ } else if (e is MapComprehension) {
+ if (newRange != e.Range || newTerm != e.Term || newAttrs != e.Attributes) {
+ newExpr = new MapComprehension(expr.tok, e.BoundVars, newRange, newTerm);
+ }
+ } else if (e is QuantifierExpr) {
+ var q = (QuantifierExpr)e;
+ if (newRange != e.Range || newTerm != e.Term || newAttrs != e.Attributes) {
+ if (expr is ForallExpr) {
+ newExpr = new ForallExpr(expr.tok, e.BoundVars, newRange, newTerm, newAttrs);
+ } else {
+ newExpr = new ExistsExpr(expr.tok, e.BoundVars, newRange, newTerm, newAttrs);
+ }
+ }
+ } else {
+ Contract.Assert(false); // unexpected ComprehensionExpr
+ }
+
+ } else if (expr is PredicateExpr) {
+ var e = (PredicateExpr)expr;
+ Expression g = Substitute(e.Guard);
+ Expression b = Substitute(e.Body);
+ if (g != e.Guard || b != e.Body) {
+ if (expr is AssertExpr) {
+ newExpr = new AssertExpr(e.tok, g, b);
+ } else {
+ newExpr = new AssumeExpr(e.tok, g, b);
+ }
+ }
+
+ } else if (expr is ITEExpr) {
+ ITEExpr e = (ITEExpr)expr;
+ Expression test = Substitute(e.Test);
+ Expression thn = Substitute(e.Thn);
+ Expression els = Substitute(e.Els);
+ if (test != e.Test || thn != e.Thn || els != e.Els) {
+ newExpr = new ITEExpr(expr.tok, test, thn, els);
+ }
+
+ } else if (expr is ConcreteSyntaxExpression) {
+ var e = (ConcreteSyntaxExpression)expr;
+ return Substitute(e.ResolvedExpression);
+ }
+
+ if (newExpr == null) {
+ return expr;
+ } else {
+ newExpr.Type = expr.Type; // resolve on the fly (any additional resolution must be done above)
+ return newExpr;
+ }
+ }
+
+ protected List<Expression/*!*/>/*!*/ SubstituteExprList(List<Expression/*!*/>/*!*/ elist) {
+ Contract.Requires(cce.NonNullElements(elist));
+ Contract.Ensures(cce.NonNullElements(Contract.Result<List<Expression>>()));
+
+ List<Expression> newElist = null; // initialized lazily
+ for (int i = 0; i < elist.Count; i++) {
+ cce.LoopInvariant(newElist == null || newElist.Count == i);
+
+ Expression substE = Substitute(elist[i]);
+ if (substE != elist[i] && newElist == null) {
+ newElist = new List<Expression>();
+ for (int j = 0; j < i; j++) {
+ newElist.Add(elist[j]);
+ }
+ }
+ if (newElist != null) {
+ newElist.Add(substE);
+ }
+ }
+ if (newElist == null) {
+ return elist;
+ } else {
+ return newElist;
+ }
+ }
+
+ public Attributes SubstAttributes(Attributes attrs) {
+ Contract.Requires(cce.NonNullDictionaryAndValues(substMap));
+ if (attrs != null) {
+ List<Attributes.Argument> newArgs = new List<Attributes.Argument>(); // allocate it eagerly, what the heck, it doesn't seem worth the extra complexity in the code to do it lazily for the infrequently occurring attributes
+ bool anyArgSubst = false;
+ foreach (Attributes.Argument arg in attrs.Args) {
+ Attributes.Argument newArg = arg;
+ if (arg.E != null) {
+ Expression newE = Substitute(arg.E);
+ if (newE != arg.E) {
+ newArg = new Attributes.Argument(arg.Tok, newE);
+ anyArgSubst = true;
+ }
+ }
+ newArgs.Add(newArg);
+ }
+ if (!anyArgSubst) {
+ newArgs = attrs.Args;
+ }
+
+ Attributes prev = SubstAttributes(attrs.Prev);
+ if (newArgs != attrs.Args || prev != attrs.Prev) {
+ return new Attributes(attrs.Name, newArgs, prev);
+ }
+ }
+ return attrs;
+ }
+ }
+
+ }
+}
diff --git a/Source/Dafny/Util.cs b/Source/Dafny/Util.cs
new file mode 100644
index 00000000..8db3ebc8
--- /dev/null
+++ b/Source/Dafny/Util.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Dafny {
+ class Util
+ {
+ public delegate string ToString<T>(T t);
+ public static string Comma<T>(string comma, IEnumerable<T> l, ToString<T> f) {
+ string res = "";
+ string c = "";
+ foreach(var t in l) {
+ res += c + f(t);
+ c = comma;
+ }
+ return res;
+ }
+ }
+}
diff --git a/Source/Dafny/cce.cs b/Source/Dafny/cce.cs
new file mode 100644
index 00000000..ff5152d0
--- /dev/null
+++ b/Source/Dafny/cce.cs
@@ -0,0 +1,112 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Text;
+using Microsoft.Boogie;
+
+ /// <summary>
+ /// A class containing static methods to extend the functionality of Code Contracts
+ /// </summary>
+
+public static class cce {
+ [Pure]
+ public static T NonNull<T>(T t) where T : class {
+ Contract.Assert(t != null);
+ return t;
+ }
+ [Pure]
+ public static bool NonNullElements<T>(IEnumerable<T> collection) where T : class {
+ return collection != null && Contract.ForAll(collection, c => c != null);
+ }
+ [Pure]
+ public static bool NonNullDictionaryAndValues<TKey, TValue>(IDictionary<TKey, TValue> collection) where TValue : class {
+ return collection != null && NonNullElements(collection.Values);
+ }
+ [Pure]
+ public static bool NonNullElements(VariableSeq collection) {
+ return collection != null && Contract.ForAll(0, collection.Length, i => collection[i] != null);
+ }
+ [Pure]
+ public static bool NonNullElements<T>(Microsoft.Dafny.Graph<T> collection) where T : class {
+ return collection != null && cce.NonNullElements(collection.TopologicallySortedComponents());
+ }
+ [Pure]
+ public static void BeginExpose(object o) {
+ }
+ [Pure]
+ public static void EndExpose() {
+ }
+ [Pure]
+ public static bool IsPeerConsistent(object o) {
+ return true;
+ }
+ [Pure]
+ public static bool IsConsistent(object o) {
+ return true;
+ }
+ [Pure]
+ public static bool IsExposable(object o) {
+ return true;
+ }
+ [Pure]
+ public static bool IsExposed(object o) {
+ return true;
+ }
+ public static class Owner {
+ [Pure]
+ public static bool Same(object o, object p) {
+ return true;
+ }
+ [Pure]
+ public static void AssignSame(object o, object p) {
+ }
+ [Pure]
+ public static object ElementProxy(object o) {
+ return o;
+ }
+ [Pure]
+ public static bool None(object o) {
+ return true;
+ }
+ }
+ [Pure]
+ public static void LoopInvariant(bool p) {
+ Contract.Assert(p);
+ }
+
+ public class UnreachableException : Exception {
+ public UnreachableException() {
+ }
+ }
+ [Pure]
+ public static bool IsValid(Microsoft.Dafny.Expression expression) {
+ return true;
+ }
+}
+
+public class PeerAttribute : System.Attribute {
+}
+public class RepAttribute : System.Attribute {
+}
+public class CapturedAttribute : System.Attribute {
+}
+public class NotDelayedAttribute : System.Attribute {
+}
+public class NoDefaultContractAttribute : System.Attribute {
+}
+public class VerifyAttribute : System.Attribute {
+ public VerifyAttribute(bool b) {
+
+ }
+}
+public class StrictReadonlyAttribute : System.Attribute {
+ }
+public class AdditiveAttribute : System.Attribute {
+}
+public class ReadsAttribute : System.Attribute {
+ public enum Reads {
+ Nothing,
+ };
+ public ReadsAttribute(object o) {
+ }
+} \ No newline at end of file
diff --git a/Source/DafnyDriver/DafnyDriver.cs b/Source/DafnyDriver/DafnyDriver.cs
new file mode 100644
index 00000000..80f78d16
--- /dev/null
+++ b/Source/DafnyDriver/DafnyDriver.cs
@@ -0,0 +1,796 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------------
+// DafnyDriver
+// - main program for taking a Dafny program and verifying it
+//---------------------------------------------------------------------------------------------
+
+namespace Microsoft.Dafny
+{
+ using System;
+ using System.IO;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using PureCollections;
+ using Microsoft.Boogie;
+ using Bpl = Microsoft.Boogie;
+ using Microsoft.Boogie.AbstractInterpretation;
+ using System.Diagnostics;
+ using VC;
+ using System.CodeDom.Compiler;
+
+ public class DafnyDriver
+ {
+ // ------------------------------------------------------------------------
+ // Main
+
+ public static int Main(string[] args)
+ {
+ Contract.Requires(cce.NonNullElements(args));
+
+ DafnyOptions.Install(new DafnyOptions());
+
+ //assert forall{int i in (0:args.Length); args[i] != null};
+ ExitValue exitValue = ExitValue.VERIFIED;
+ CommandLineOptions.Clo.RunningBoogieFromCommandLine = true;
+ if (!CommandLineOptions.Clo.Parse(args)) {
+ exitValue = ExitValue.PREPROCESSING_ERROR;
+ goto END;
+ }
+ if (CommandLineOptions.Clo.Files.Count == 0)
+ {
+ ErrorWriteLine("*** 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) {
+ ErrorWriteLine("*** Error: " + errMsg);
+ exitValue = ExitValue.PREPROCESSING_ERROR;
+ goto END;
+ }
+ }
+ if (!CommandLineOptions.Clo.DontShowLogo)
+ {
+ Console.WriteLine(CommandLineOptions.Clo.Version);
+ }
+ if (CommandLineOptions.Clo.ShowEnv == CommandLineOptions.ShowEnvironment.Always)
+ {
+ Console.WriteLine("---Command arguments");
+ foreach (string arg in args)
+ {Contract.Assert(arg != null);
+ Console.WriteLine(arg);
+ }
+ Console.WriteLine("--------------------");
+ }
+
+ foreach (string file in CommandLineOptions.Clo.Files)
+ {Contract.Assert(file != null);
+ string extension = Path.GetExtension(file);
+ if (extension != null) { extension = extension.ToLower(); }
+ if (extension != ".dfy")
+ {
+ ErrorWriteLine("*** 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;
+ }
+ }
+ exitValue = ProcessFiles(CommandLineOptions.Clo.Files);
+
+ END:
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.Close();
+ }
+ if (CommandLineOptions.Clo.Wait)
+ {
+ Console.WriteLine("Press Enter to exit.");
+ Console.ReadLine();
+ }
+ return (int)exitValue;
+ }
+
+ public static void ErrorWriteLine(string s) {Contract.Requires(s != null);
+ if (!s.Contains("Error: ") && !s.Contains("Error BP")) {
+ Console.WriteLine(s);
+ return;
+ }
+
+ // split the string up into its first line and the remaining lines
+ string remaining = null;
+ int i = s.IndexOf('\r');
+ if (0 <= i) {
+ remaining = s.Substring(i+1);
+ if (remaining.StartsWith("\n")) {
+ remaining = remaining.Substring(1);
+ }
+ s = s.Substring(0, i);
+ }
+
+ ConsoleColor col = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine(s);
+ Console.ForegroundColor = col;
+
+ if (remaining != null) {
+ Console.WriteLine(remaining);
+ }
+ }
+
+ public static void ErrorWriteLine(string format, params object[] args) {
+ Contract.Requires(format != null);
+ string s = string.Format(format, args);
+ ErrorWriteLine(s);
+ }
+
+ public static void AdvisoryWriteLine(string format, params object[] args) {
+ Contract.Requires(format != null);
+ ConsoleColor col = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.WriteLine(format, args);
+ Console.ForegroundColor = col;
+ }
+
+ enum FileType { Unknown, Cil, Bpl, Dafny };
+
+ static ExitValue ProcessFiles (List<string/*!*/>/*!*/ fileNames)
+ {
+ Contract.Requires(cce.NonNullElements(fileNames));
+ ExitValue exitValue = ExitValue.VERIFIED;
+ using (XmlFileScope xf = new XmlFileScope(CommandLineOptions.Clo.XmlSink, fileNames[fileNames.Count-1])) {
+ Dafny.Program dafnyProgram;
+ string programName = fileNames.Count == 1 ? fileNames[0] : "the program";
+ string err = Dafny.Main.ParseCheck(fileNames, programName, out dafnyProgram);
+ if (err != null) {
+ exitValue = ExitValue.DAFNY_ERROR;
+ ErrorWriteLine(err);
+ } else if (dafnyProgram != null && !CommandLineOptions.Clo.NoResolve && !CommandLineOptions.Clo.NoTypecheck) {
+ Dafny.Translator translator = new Dafny.Translator();
+ Bpl.Program boogieProgram = translator.Translate(dafnyProgram);
+ if (CommandLineOptions.Clo.PrintFile != null)
+ {
+ PrintBplFile(CommandLineOptions.Clo.PrintFile, boogieProgram, false);
+ }
+
+ string bplFilename;
+ if (CommandLineOptions.Clo.PrintFile != null) {
+ bplFilename = CommandLineOptions.Clo.PrintFile;
+ } else {
+ string baseName = cce.NonNull(Path.GetFileName(fileNames[fileNames.Count-1]));
+ baseName = cce.NonNull(Path.ChangeExtension(baseName, "bpl"));
+ bplFilename = Path.Combine(Path.GetTempPath(), baseName);
+ }
+
+ int errorCount, verified, inconclusives, timeOuts, outOfMemories;
+ PipelineOutcome oc = BoogiePipelineWithRerun(boogieProgram, bplFilename, out errorCount, out verified, out inconclusives, out timeOuts, out outOfMemories);
+ bool allOk = errorCount == 0 && inconclusives == 0 && timeOuts == 0 && outOfMemories == 0;
+ switch (oc) {
+ case PipelineOutcome.VerificationCompleted:
+ WriteTrailer(verified, errorCount, inconclusives, timeOuts, outOfMemories);
+ if ((DafnyOptions.O.Compile && allOk && CommandLineOptions.Clo.ProcsToCheck == null) || DafnyOptions.O.ForceCompile)
+ CompileDafnyProgram(dafnyProgram, fileNames[0]);
+ break;
+ case PipelineOutcome.Done:
+ WriteTrailer(verified, errorCount, inconclusives, timeOuts, outOfMemories);
+ if (DafnyOptions.O.ForceCompile)
+ CompileDafnyProgram(dafnyProgram, fileNames[0]);
+ break;
+ default:
+ // error has already been reported to user
+ break;
+ }
+ exitValue = allOk ? ExitValue.VERIFIED : ExitValue.NOT_VERIFIED;
+ }
+ }
+ return exitValue;
+ }
+
+ private static void CompileDafnyProgram(Dafny.Program dafnyProgram, string dafnyProgramName)
+ {
+ // Compile the Dafny program into a string that contains the C# program
+ StringWriter sw = new StringWriter();
+ Dafny.Compiler compiler = new Dafny.Compiler(sw);
+ compiler.Compile(dafnyProgram);
+ var csharpProgram = sw.ToString();
+ bool completeProgram = compiler.ErrorCount == 0;
+
+ // blurt out the code to a file
+ if (DafnyOptions.O.SpillTargetCode) {
+ string targetFilename = Path.ChangeExtension(dafnyProgramName, "cs");
+ using (TextWriter target = new StreamWriter(new FileStream(targetFilename, System.IO.FileMode.Create))) {
+ target.Write(csharpProgram);
+ if (completeProgram) {
+ Console.WriteLine("Compiled program written to {0}", targetFilename);
+ } else {
+ Console.WriteLine("File {0} contains the partially compiled program", targetFilename);
+ }
+ }
+ }
+
+ // compile the program into an assembly
+ if (!completeProgram) {
+ // don't compile
+ } else if (!CodeDomProvider.IsDefinedLanguage("CSharp")) {
+ Console.WriteLine("Error: cannot compile, because there is no provider configured for input language CSharp");
+ } else {
+ var provider = CodeDomProvider.CreateProvider("CSharp");
+ var cp = new System.CodeDom.Compiler.CompilerParameters();
+ cp.GenerateExecutable = true;
+ if (compiler.HasMain(dafnyProgram)) {
+ cp.OutputAssembly = Path.ChangeExtension(dafnyProgramName, "exe");
+ cp.CompilerOptions = "/debug";
+ } else {
+ cp.OutputAssembly = Path.ChangeExtension(dafnyProgramName, "dll");
+ cp.CompilerOptions = "/debug /target:library";
+ }
+ cp.GenerateInMemory = false;
+ cp.ReferencedAssemblies.Add("System.Numerics.dll");
+
+ var cr = provider.CompileAssemblyFromSource(cp, csharpProgram);
+ if (cr.Errors.Count == 0) {
+ Console.WriteLine("Compiled assembly into {0}", cr.PathToAssembly);
+ } else {
+ Console.WriteLine("Errors compiling program into {0}", cr.PathToAssembly);
+ foreach (var ce in cr.Errors) {
+ Console.WriteLine(ce.ToString());
+ Console.WriteLine();
+ }
+ }
+ }
+ }
+
+ static void PrintBplFile (string filename, Bpl.Program program, bool allowPrintDesugaring)
+ {
+ Contract.Requires(filename != null);
+ Contract.Requires(program != null);
+ bool oldPrintDesugaring = CommandLineOptions.Clo.PrintDesugarings;
+ if (!allowPrintDesugaring) {
+ CommandLineOptions.Clo.PrintDesugarings = false;
+ }
+ using (TokenTextWriter writer = filename == "-" ?
+ new TokenTextWriter("<console>", Console.Out, false) :
+ new TokenTextWriter(filename, false))
+ {
+ writer.WriteLine("// " + CommandLineOptions.Clo.Version);
+ writer.WriteLine("// " + CommandLineOptions.Clo.Environment);
+ writer.WriteLine();
+ program.Emit(writer);
+ }
+ CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaring;
+ }
+
+
+ static bool ProgramHasDebugInfo (Bpl.Program program)
+ {
+ Contract.Requires(program != null);
+ // We inspect the last declaration because the first comes from the prelude and therefore always has source context.
+ return program.TopLevelDeclarations.Count > 0 &&
+ cce.NonNull(program.TopLevelDeclarations[program.TopLevelDeclarations.Count - 1]).tok.IsValid;
+ }
+
+
+ /// <summary>
+ /// Inform the user about something and proceed with translation normally.
+ /// Print newline after the message.
+ /// </summary>
+ public static void Inform(string s) {
+ if (CommandLineOptions.Clo.Trace || CommandLineOptions.Clo.TraceProofObligations) {
+ Console.WriteLine(s);
+ }
+ }
+
+ static void WriteTrailer(int verified, int errors, int inconclusives, int timeOuts, int outOfMemories){
+ Contract.Requires(0 <= errors && 0 <= inconclusives && 0 <= timeOuts && 0 <= outOfMemories);
+ Console.WriteLine();
+ Console.Write("{0} finished with {1} verified, {2} error{3}", CommandLineOptions.Clo.DescriptiveToolName, verified, errors, errors == 1 ? "" : "s");
+ if (inconclusives != 0) {
+ Console.Write(", {0} inconclusive{1}", inconclusives, inconclusives == 1 ? "" : "s");
+ }
+ if (timeOuts != 0) {
+ Console.Write(", {0} time out{1}", timeOuts, timeOuts == 1 ? "" : "s");
+ }
+ if (outOfMemories != 0) {
+ Console.Write(", {0} out of memory", outOfMemories);
+ }
+ Console.WriteLine();
+ Console.Out.Flush();
+ }
+
+
+
+ static void ReportBplError(IToken tok, string message, bool error)
+ {
+ Contract.Requires(message != null);
+ string s;
+ if (tok == null) {
+ s = message;
+ } else {
+ s = string.Format("{0}({1},{2}): {3}", tok.filename, tok.line, tok.col, message);
+ }
+ if (error) {
+ ErrorWriteLine(s);
+ } else {
+ Console.WriteLine(s);
+ }
+ if (tok is Dafny.NestedToken) {
+ var nt = (Dafny.NestedToken)tok;
+ ReportBplError(nt.Inner, "Related location: Related location", false);
+ }
+ }
+
+ /// <summary>
+ /// Parse the given files into one Boogie program. If an I/O or parse error occurs, an error will be printed
+ /// and null will be returned. On success, a non-null program is returned.
+ /// </summary>
+ static Bpl.Program ParseBoogieProgram(List<string/*!*/>/*!*/ fileNames, bool suppressTraceOutput)
+ {
+ Contract.Requires(cce.NonNullElements(fileNames));
+ //BoogiePL.Errors.count = 0;
+ Bpl.Program program = null;
+ bool okay = true;
+ foreach (string bplFileName in fileNames) {
+ if (!suppressTraceOutput) {
+ if (CommandLineOptions.Clo.XmlSink != null) {
+ CommandLineOptions.Clo.XmlSink.WriteFileFragment(bplFileName);
+ }
+ if (CommandLineOptions.Clo.Trace) {
+ Console.WriteLine("Parsing " + bplFileName);
+ }
+ }
+
+ Bpl.Program programSnippet;
+ int errorCount;
+ try {
+ errorCount = Microsoft.Boogie.Parser.Parse(bplFileName, (List<string>)null, out programSnippet);
+ if (programSnippet == null || errorCount != 0) {
+ Console.WriteLine("{0} parse errors detected in {1}", errorCount, bplFileName);
+ okay = false;
+ continue;
+ }
+ } catch (IOException e) {
+ ErrorWriteLine("Error opening file \"{0}\": {1}", bplFileName, e.Message);
+ okay = false;
+ continue;
+ }
+ if (program == null) {
+ program = programSnippet;
+ } else if (programSnippet != null) {
+ program.TopLevelDeclarations.AddRange(programSnippet.TopLevelDeclarations);
+ }
+ }
+ if (!okay) {
+ return null;
+ } else if (program == null) {
+ return new Bpl.Program();
+ } else {
+ return program;
+ }
+ }
+
+ /// <summary>
+ /// Resolve, type check, infer invariants for, and verify the given Boogie program.
+ /// The intention is that this Boogie program has been produced by translation from something
+ /// else. Hence, any resolution errors and type checking errors are due to errors in
+ /// the translation.
+ /// The method prints errors for resolution and type checking errors, but still returns
+ /// their error code.
+ /// </summary>
+ static PipelineOutcome BoogiePipelineWithRerun (Bpl.Program/*!*/ program, string/*!*/ bplFileName,
+ out int errorCount, out int verified, out int inconclusives, out int timeOuts, out int outOfMemories)
+ {
+ Contract.Requires(program != null);
+ Contract.Requires(bplFileName != null);
+ Contract.Ensures(0 <= Contract.ValueAtReturn(out inconclusives) && 0 <= Contract.ValueAtReturn(out timeOuts));
+
+ errorCount = verified = inconclusives = timeOuts = outOfMemories = 0;
+ PipelineOutcome oc = ResolveAndTypecheck(program, bplFileName);
+ switch (oc) {
+ case PipelineOutcome.Done:
+ return oc;
+
+ case PipelineOutcome.ResolutionError:
+ case PipelineOutcome.TypeCheckingError:
+ {
+ PrintBplFile(bplFileName, program, false);
+ Console.WriteLine();
+ Console.WriteLine("*** Encountered internal translation error - re-running Boogie to get better debug information");
+ Console.WriteLine();
+
+ List<string/*!*/>/*!*/ fileNames = new List<string/*!*/>();
+ fileNames.Add(bplFileName);
+ Bpl.Program reparsedProgram = ParseBoogieProgram(fileNames, true);
+ if (reparsedProgram != null) {
+ ResolveAndTypecheck(reparsedProgram, bplFileName);
+ }
+ }
+ return oc;
+
+ case PipelineOutcome.ResolvedAndTypeChecked:
+ EliminateDeadVariablesAndInline(program);
+ return InferAndVerify(program, out errorCount, out verified, out inconclusives, out timeOuts, out outOfMemories);
+
+ default:
+ Contract.Assert(false);throw new cce.UnreachableException(); // unexpected outcome
+ }
+ }
+
+ static void EliminateDeadVariablesAndInline(Bpl.Program program) {
+ Contract.Requires(program != null);
+ // Eliminate dead variables
+ Microsoft.Boogie.UnusedVarEliminator.Eliminate(program);
+
+ // Collect mod sets
+ if (CommandLineOptions.Clo.DoModSetAnalysis) {
+ Microsoft.Boogie.ModSetCollector.DoModSetAnalysis(program);
+ }
+
+ // Coalesce blocks
+ if (CommandLineOptions.Clo.CoalesceBlocks) {
+ Microsoft.Boogie.BlockCoalescer.CoalesceBlocks(program);
+ }
+
+ // Inline
+ var TopLevelDeclarations = program.TopLevelDeclarations;
+
+ if (CommandLineOptions.Clo.ProcedureInlining != CommandLineOptions.Inlining.None) {
+ bool inline = false;
+ foreach (var d in TopLevelDeclarations) {
+ if (d.FindExprAttribute("inline") != null) {
+ inline = true;
+ }
+ }
+ if (inline && CommandLineOptions.Clo.StratifiedInlining == 0) {
+ foreach (var d in TopLevelDeclarations) {
+ var impl = d as Implementation;
+ if (impl != null) {
+ impl.OriginalBlocks = impl.Blocks;
+ impl.OriginalLocVars = impl.LocVars;
+ }
+ }
+ foreach (var d in TopLevelDeclarations) {
+ var impl = d as Implementation;
+ if (impl != null && !impl.SkipVerification) {
+ Inliner.ProcessImplementation(program, impl);
+ }
+ }
+ foreach (var d in TopLevelDeclarations) {
+ var impl = d as Implementation;
+ if (impl != null) {
+ impl.OriginalBlocks = null;
+ impl.OriginalLocVars = null;
+ }
+ }
+ }
+ }
+ }
+
+ enum PipelineOutcome { Done, ResolutionError, TypeCheckingError, ResolvedAndTypeChecked, FatalError, VerificationCompleted }
+ enum ExitValue { VERIFIED = 0, PREPROCESSING_ERROR, DAFNY_ERROR, NOT_VERIFIED }
+
+ /// <summary>
+ /// Resolves and type checks the given Boogie program. Any errors are reported to the
+ /// console. Returns:
+ /// - Done if no errors occurred, and command line specified no resolution or no type checking.
+ /// - ResolutionError if a resolution error occurred
+ /// - TypeCheckingError if a type checking error occurred
+ /// - ResolvedAndTypeChecked if both resolution and type checking succeeded
+ /// </summary>
+ static PipelineOutcome ResolveAndTypecheck (Bpl.Program program, string bplFileName)
+ {
+ Contract.Requires(program != null);
+ Contract.Requires(bplFileName != null);
+ // ---------- Resolve ------------------------------------------------------------
+
+ if (CommandLineOptions.Clo.NoResolve) { return PipelineOutcome.Done; }
+
+ int errorCount = program.Resolve();
+ if (errorCount != 0) {
+ Console.WriteLine("{0} name resolution errors detected in {1}", errorCount, bplFileName);
+ return PipelineOutcome.ResolutionError;
+ }
+
+ // ---------- Type check ------------------------------------------------------------
+
+ if (CommandLineOptions.Clo.NoTypecheck) { return PipelineOutcome.Done; }
+
+ errorCount = program.Typecheck();
+ if (errorCount != 0) {
+ Console.WriteLine("{0} type checking errors detected in {1}", errorCount, bplFileName);
+ return PipelineOutcome.TypeCheckingError;
+ }
+
+ if (CommandLineOptions.Clo.PrintFile != null && CommandLineOptions.Clo.PrintDesugarings)
+ {
+ // if PrintDesugaring option is engaged, print the file here, after resolution and type checking
+ PrintBplFile(CommandLineOptions.Clo.PrintFile, program, true);
+ }
+
+ return PipelineOutcome.ResolvedAndTypeChecked;
+ }
+
+ /// <summary>
+ /// Given a resolved and type checked Boogie program, infers invariants for the program
+ /// and then attempts to verify it. Returns:
+ /// - Done if command line specified no verification
+ /// - FatalError if a fatal error occurred, in which case an error has been printed to console
+ /// - VerificationCompleted if inference and verification completed, in which the out
+ /// parameters contain meaningful values
+ /// </summary>
+ static PipelineOutcome InferAndVerify (Bpl.Program program,
+ out int errorCount, out int verified, out int inconclusives, out int timeOuts, out int outOfMemories)
+ {
+ Contract.Requires(program != null);
+ Contract.Ensures(0 <= Contract.ValueAtReturn(out inconclusives) && 0 <= Contract.ValueAtReturn(out timeOuts));
+
+ errorCount = verified = inconclusives = timeOuts = outOfMemories = 0;
+
+ // ---------- Infer invariants --------------------------------------------------------
+
+ // Abstract interpretation -> Always use (at least) intervals, if not specified otherwise (e.g. with the "/noinfer" switch)
+ if (CommandLineOptions.Clo.UseAbstractInterpretation) {
+ if (CommandLineOptions.Clo.Ai.J_Intervals || CommandLineOptions.Clo.Ai.J_Trivial) {
+ Microsoft.Boogie.AbstractInterpretation.NativeAbstractInterpretation.RunAbstractInterpretation(program);
+ } else {
+ // use /infer:j as the default
+ CommandLineOptions.Clo.Ai.J_Intervals = true;
+ Microsoft.Boogie.AbstractInterpretation.NativeAbstractInterpretation.RunAbstractInterpretation(program);
+ }
+ }
+
+ if (CommandLineOptions.Clo.LoopUnrollCount != -1) {
+ program.UnrollLoops(CommandLineOptions.Clo.LoopUnrollCount);
+ }
+
+ if (CommandLineOptions.Clo.PrintInstrumented) {
+ program.Emit(new TokenTextWriter(Console.Out));
+ }
+
+ if (CommandLineOptions.Clo.ExpandLambdas) {
+ LambdaHelper.ExpandLambdas(program);
+ //PrintBplFile ("-", program, true);
+ }
+
+ // ---------- Verify ------------------------------------------------------------
+
+ if (!CommandLineOptions.Clo.Verify) { return PipelineOutcome.Done; }
+
+ #region Verify each implementation
+
+#if ROB_DEBUG
+ string now = DateTime.Now.ToString().Replace(' ','-').Replace('/','-').Replace(':','-');
+ System.IO.StreamWriter w = new System.IO.StreamWriter(@"\temp\batch_"+now+".bpl");
+ program.Emit(new TokenTextWriter(w));
+ w.Close();
+#endif
+
+ ConditionGeneration vcgen = null;
+ try
+ {
+ vcgen = new VCGen(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend);
+ }
+ catch (ProverException e)
+ {
+ ErrorWriteLine("Fatal Error: ProverException: {0}", e);
+ return PipelineOutcome.FatalError;
+ }
+
+ var decls = program.TopLevelDeclarations.ToArray();
+ foreach (var decl in decls )
+ {
+ Contract.Assert(decl != null);
+ Implementation impl = decl as Implementation;
+ if (impl != null && CommandLineOptions.Clo.UserWantsToCheckRoutine(cce.NonNull(impl.Name)) && !impl.SkipVerification)
+ {
+ List<Counterexample>/*?*/ errors;
+
+ DateTime start = new DateTime(); // to please compiler's definite assignment rules
+ if (CommandLineOptions.Clo.Trace || CommandLineOptions.Clo.TraceProofObligations || CommandLineOptions.Clo.XmlSink != null)
+ {
+ start = DateTime.Now;
+ if (CommandLineOptions.Clo.Trace || CommandLineOptions.Clo.TraceProofObligations)
+ {
+ Console.WriteLine();
+ Console.WriteLine("Verifying {0} ...", impl.Name);
+ }
+ if (CommandLineOptions.Clo.XmlSink != null)
+ {
+ CommandLineOptions.Clo.XmlSink.WriteStartMethod(impl.Name, start);
+ }
+ }
+
+ ConditionGeneration.Outcome outcome;
+ int prevAssertionCount = vcgen.CumulativeAssertionCount;
+ try
+ {
+ outcome = vcgen.VerifyImplementation(impl, out errors);
+ }
+ catch (VCGenException e)
+ {
+ ReportBplError(impl.tok, String.Format("Error BP5010: {0} Encountered in implementation {1}.", e.Message, impl.Name), true);
+ errors = null;
+ outcome = VCGen.Outcome.Inconclusive;
+ }
+ catch (UnexpectedProverOutputException upo)
+ {
+ AdvisoryWriteLine("Advisory: {0} SKIPPED because of internal error: unexpected prover output: {1}", impl.Name, upo.Message);
+ errors = null;
+ outcome = VCGen.Outcome.Inconclusive;
+ }
+
+ string timeIndication = "";
+ DateTime end = DateTime.Now;
+ TimeSpan elapsed = end - start;
+ if (CommandLineOptions.Clo.Trace) {
+ int poCount = vcgen.CumulativeAssertionCount - prevAssertionCount;
+ timeIndication = string.Format(" [{0:F3} s, {1} proof obligation{2}] ", elapsed.TotalSeconds, poCount, poCount == 1 ? "" : "s");
+ } else if (CommandLineOptions.Clo.TraceProofObligations) {
+ int poCount = vcgen.CumulativeAssertionCount - prevAssertionCount;
+ timeIndication = string.Format(" [{0} proof obligation{1}] ", poCount, poCount == 1 ? "" : "s");
+ }
+
+ switch (outcome)
+ {
+ default:
+ Contract.Assert(false); throw new cce.UnreachableException(); // unexpected outcome
+ case VCGen.Outcome.Correct:
+ Inform(String.Format("{0}verified", timeIndication));
+ verified++;
+ break;
+ case VCGen.Outcome.TimedOut:
+ timeOuts++;
+ Inform(String.Format("{0}timed out", timeIndication));
+ break;
+ case VCGen.Outcome.OutOfMemory:
+ outOfMemories++;
+ Inform(String.Format("{0}out of memory", timeIndication));
+ break;
+ case VCGen.Outcome.Inconclusive:
+ inconclusives++;
+ Inform(String.Format("{0}inconclusive", timeIndication));
+ break;
+ case VCGen.Outcome.Errors:
+ Contract.Assert(errors != null); // guaranteed by postcondition of VerifyImplementation
+ // BP1xxx: Parsing errors
+ // BP2xxx: Name resolution errors
+ // BP3xxx: Typechecking errors
+ // BP4xxx: Abstract interpretation errors (Is there such a thing?)
+ // BP5xxx: Verification errors
+
+ errors.Sort(new CounterexampleComparer());
+ foreach (Counterexample error in errors)
+ {
+ if (error is CallCounterexample)
+ {
+ CallCounterexample err = (CallCounterexample)error;
+ ReportBplError(err.FailingCall.tok, (err.FailingCall.ErrorData as string) ?? "Error BP5002: A precondition for this call might not hold.", true);
+ ReportBplError(err.FailingRequires.tok, (err.FailingRequires.ErrorData as string) ?? "Related location: This is the precondition that might not hold.", false);
+ if (CommandLineOptions.Clo.XmlSink != null)
+ {
+ CommandLineOptions.Clo.XmlSink.WriteError("precondition violation", err.FailingCall.tok, err.FailingRequires.tok, error.Trace);
+ }
+ }
+ else if (error is ReturnCounterexample)
+ {
+ ReturnCounterexample err = (ReturnCounterexample)error;
+ ReportBplError(err.FailingReturn.tok, "Error BP5003: A postcondition might not hold on this return path.", true);
+ ReportBplError(err.FailingEnsures.tok, (err.FailingEnsures.ErrorData as string) ?? "Related location: This is the postcondition that might not hold.", false);
+ ReportAllBplErrors(err.FailingEnsures.Attributes);
+ if (CommandLineOptions.Clo.XmlSink != null)
+ {
+ CommandLineOptions.Clo.XmlSink.WriteError("postcondition violation", err.FailingReturn.tok, err.FailingEnsures.tok, error.Trace);
+ }
+ }
+ else // error is AssertCounterexample
+ {
+ AssertCounterexample err = (AssertCounterexample)error;
+ if (err.FailingAssert is LoopInitAssertCmd)
+ {
+ ReportBplError(err.FailingAssert.tok, "Error BP5004: This loop invariant might not hold on entry.", true);
+ if (CommandLineOptions.Clo.XmlSink != null)
+ {
+ CommandLineOptions.Clo.XmlSink.WriteError("loop invariant entry violation", err.FailingAssert.tok, null, error.Trace);
+ }
+ }
+ else if (err.FailingAssert is LoopInvMaintainedAssertCmd)
+ {
+ // this assertion is a loop invariant which is not maintained
+ ReportBplError(err.FailingAssert.tok, "Error BP5005: This loop invariant might not be maintained by the loop.", true);
+ if (CommandLineOptions.Clo.XmlSink != null)
+ {
+ CommandLineOptions.Clo.XmlSink.WriteError("loop invariant maintenance violation", err.FailingAssert.tok, null, error.Trace);
+ }
+ }
+ else
+ {
+ string msg = err.FailingAssert.ErrorData as string;
+ if (msg == null)
+ {
+ msg = "Error BP5001: This assertion might not hold.";
+ }
+ ReportBplError(err.FailingAssert.tok, msg, true);
+ var attr = err.FailingAssert.Attributes;
+ ReportAllBplErrors(attr);
+ if (CommandLineOptions.Clo.XmlSink != null)
+ {
+ CommandLineOptions.Clo.XmlSink.WriteError("assertion violation", err.FailingAssert.tok, null, error.Trace);
+ }
+ }
+ }
+ if (CommandLineOptions.Clo.EnhancedErrorMessages == 1)
+ {
+ foreach (string info in error.relatedInformation)
+ {
+ Contract.Assert(info != null);
+ Console.WriteLine(" " + info);
+ }
+ }
+ if (CommandLineOptions.Clo.ErrorTrace > 0)
+ {
+ Console.WriteLine("Execution trace:");
+ foreach (Block b in error.Trace)
+ {
+ Contract.Assert(b != null);
+ if (b.tok == null)
+ {
+ Console.WriteLine(" <intermediate block>");
+ }
+ else
+ {
+ // for ErrorTrace == 1 restrict the output;
+ // do not print tokens with -17:-4 as their location because they have been
+ // introduced in the translation and do not give any useful feedback to the user
+ if (!(CommandLineOptions.Clo.ErrorTrace == 1 && b.tok.line == -17 && b.tok.col == -4))
+ {
+ Console.WriteLine(" {0}({1},{2}): {3}", b.tok.filename, b.tok.line, b.tok.col, b.Label);
+ }
+ }
+ }
+ }
+ if (CommandLineOptions.Clo.ModelViewFile != null)
+ {
+ error.PrintModel();
+ }
+ errorCount++;
+ }
+
+ Inform(String.Format("{0}error{1}", timeIndication, errors.Count == 1 ? "" : "s"));
+ break;
+ }
+
+ if (CommandLineOptions.Clo.XmlSink != null)
+ {
+ CommandLineOptions.Clo.XmlSink.WriteEndMethod(outcome.ToString().ToLowerInvariant(), end, elapsed);
+ }
+ if (outcome == VCGen.Outcome.Errors || CommandLineOptions.Clo.Trace)
+ {
+ Console.Out.Flush();
+ }
+ }
+ }
+ vcgen.Close();
+ cce.NonNull(CommandLineOptions.Clo.TheProverFactory).Close();
+
+ #endregion
+
+ return PipelineOutcome.VerificationCompleted;
+ }
+
+ private static void ReportAllBplErrors(QKeyValue attr) {
+ while (attr != null) {
+ if (attr.Key == "msg" && attr.Params.Count == 1) {
+ var str = attr.Params[0] as string;
+ if (str != null) {
+ ReportBplError(attr.tok, "Error: "+str, false);
+ }
+ }
+ attr = attr.Next;
+ }
+ }
+
+ }
+}
diff --git a/Source/DafnyDriver/DafnyDriver.csproj b/Source/DafnyDriver/DafnyDriver.csproj
new file mode 100644
index 00000000..6f32302d
--- /dev/null
+++ b/Source/DafnyDriver/DafnyDriver.csproj
@@ -0,0 +1,182 @@
+<?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>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{63400D1F-05B2-453E-9592-1EAB74B2C9CC}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Dafny</RootNamespace>
+ <AssemblyName>Dafny</AssemblyName>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <CodeContractsAssemblyMode>0</CodeContractsAssemblyMode>
+ <SignAssembly>true</SignAssembly>
+ <AssemblyOriginatorKeyFile>..\InterimKey.snk</AssemblyOriginatorKeyFile>
+ <FileUpgradeFlags>
+ </FileUpgradeFlags>
+ <OldToolsVersion>3.5</OldToolsVersion>
+ <UpgradeBackupLocation />
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <TargetFrameworkProfile />
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <ApplicationRevision>0</ApplicationRevision>
+ <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ </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>
+ <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking>
+ <CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
+ <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
+ <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
+ <CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis>
+ <CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
+ <CodeContractsBoundsObligations>False</CodeContractsBoundsObligations>
+ <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
+ <CodeContractsPointerObligations>False</CodeContractsPointerObligations>
+ <CodeContractsContainerAnalysis>False</CodeContractsContainerAnalysis>
+ <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions>
+ <CodeContractsRunInBackground>True</CodeContractsRunInBackground>
+ <CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
+ <CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
+ <CodeContractsEmitXMLDocs>False</CodeContractsEmitXMLDocs>
+ <CodeContractsCustomRewriterAssembly>
+ </CodeContractsCustomRewriterAssembly>
+ <CodeContractsCustomRewriterClass>
+ </CodeContractsCustomRewriterClass>
+ <CodeContractsLibPaths>
+ </CodeContractsLibPaths>
+ <CodeContractsExtraRewriteOptions>
+ </CodeContractsExtraRewriteOptions>
+ <CodeContractsExtraAnalysisOptions>
+ </CodeContractsExtraAnalysisOptions>
+ <CodeContractsBaseLineFile>
+ </CodeContractsBaseLineFile>
+ <CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
+ <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeContractsRuntimeSkipQuantifiers>False</CodeContractsRuntimeSkipQuantifiers>
+ <CodeContractsEnumObligations>False</CodeContractsEnumObligations>
+ <CodeContractsCacheAnalysisResults>False</CodeContractsCacheAnalysisResults>
+ <CodeContractsAnalysisWarningLevel>0</CodeContractsAnalysisWarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ </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="AbsInt">
+ <HintPath>..\..\Binaries\AbsInt.dll</HintPath>
+ </Reference>
+ <Reference Include="Core">
+ <HintPath>..\..\Binaries\Core.dll</HintPath>
+ </Reference>
+ <Reference Include="ParserHelper, Version=2.2.30705.1126, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\Binaries\ParserHelper.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ <Reference Include="VCGeneration">
+ <HintPath>..\..\Binaries\VCGeneration.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="DafnyDriver.cs" />
+ <Compile Include="..\version.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Dafny\DafnyPipeline.csproj">
+ <Project>{FE44674A-1633-4917-99F4-57635E6FA740}</Project>
+ <Name>DafnyPipeline</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
+ <Visible>False</Visible>
+ <ProductName>Windows Installer 3.1</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="app.config" />
+ </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/DafnyDriver/app.config b/Source/DafnyDriver/app.config
new file mode 100644
index 00000000..cb2586be
--- /dev/null
+++ b/Source/DafnyDriver/app.config
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<configuration>
+<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
diff --git a/Source/DafnyExtension.sln b/Source/DafnyExtension.sln
new file mode 100644
index 00000000..fd450cc8
--- /dev/null
+++ b/Source/DafnyExtension.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyExtension", "DafnyExtension\DafnyExtension.csproj", "{6E9A5E14-0763-471C-A129-80A879D9E7BA}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6E9A5E14-0763-471C-A129-80A879D9E7BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6E9A5E14-0763-471C-A129-80A879D9E7BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6E9A5E14-0763-471C-A129-80A879D9E7BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6E9A5E14-0763-471C-A129-80A879D9E7BA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Source/DafnyExtension/BraceMatching.cs b/Source/DafnyExtension/BraceMatching.cs
new file mode 100644
index 00000000..44b1affe
--- /dev/null
+++ b/Source/DafnyExtension/BraceMatching.cs
@@ -0,0 +1,253 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Tagging;
+using Microsoft.VisualStudio.Utilities;
+
+namespace DafnyLanguage
+{
+ [Export(typeof(IViewTaggerProvider))]
+ [ContentType("dafny")]
+ [TagType(typeof(TextMarkerTag))]
+ internal class BraceMatchingTaggerProvider : IViewTaggerProvider
+ {
+ [Import]
+ internal IBufferTagAggregatorFactoryService AggregatorFactory = null;
+
+ public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag {
+ if (textView == null)
+ return null;
+
+ //provide highlighting only on the top-level buffer
+ if (textView.TextBuffer != buffer)
+ return null;
+
+ ITagAggregator<DafnyTokenTag> tagAggregator = AggregatorFactory.CreateTagAggregator<DafnyTokenTag>(buffer);
+ return new BraceMatchingTagger(textView, buffer, tagAggregator) as ITagger<T>;
+ }
+ }
+
+ internal abstract class TokenBasedTagger
+ {
+ ITagAggregator<DafnyTokenTag> _aggregator;
+
+ internal TokenBasedTagger(ITagAggregator<DafnyTokenTag> tagAggregator) {
+ _aggregator = tagAggregator;
+ }
+
+ public bool InsideComment(SnapshotPoint pt) {
+ SnapshotSpan span = new SnapshotSpan(pt, 1);
+ foreach (var tagSpan in this._aggregator.GetTags(span)) {
+ switch (tagSpan.Tag.Kind) {
+ case DafnyTokenKinds.Comment:
+ case DafnyTokenKinds.String:
+ foreach (var s in tagSpan.Span.GetSpans(pt.Snapshot)) {
+ if (s.Contains(span))
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+ }
+
+ internal class BraceMatchingTagger : TokenBasedTagger, ITagger<TextMarkerTag>
+ {
+ ITextView View { get; set; }
+ ITextBuffer SourceBuffer { get; set; }
+ SnapshotPoint? CurrentChar { get; set; }
+ private char[] openBraces;
+ private char[] closeBraces;
+
+ static TextMarkerTag Blue = new TextMarkerTag("blue");
+
+ internal BraceMatchingTagger(ITextView view, ITextBuffer sourceBuffer, ITagAggregator<DafnyTokenTag> tagAggregator)
+ : base(tagAggregator)
+ {
+ //here the keys are the open braces, and the values are the close braces
+ openBraces = new char[] { '(', '{', '[' };
+ closeBraces = new char[] { ')', '}', ']' };
+ this.View = view;
+ this.SourceBuffer = sourceBuffer;
+ this.CurrentChar = null;
+
+ this.View.Caret.PositionChanged += CaretPositionChanged;
+ this.View.LayoutChanged += ViewLayoutChanged;
+ }
+
+ public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
+
+ void ViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) {
+ if (e.NewSnapshot != e.OldSnapshot) //make sure that there has really been a change
+ {
+ UpdateAtCaretPosition(View.Caret.Position);
+ }
+ }
+
+ void CaretPositionChanged(object sender, CaretPositionChangedEventArgs e) {
+ UpdateAtCaretPosition(e.NewPosition);
+ }
+
+ void UpdateAtCaretPosition(CaretPosition caretPosition) {
+ CurrentChar = caretPosition.Point.GetPoint(SourceBuffer, caretPosition.Affinity);
+
+ if (!CurrentChar.HasValue)
+ return;
+
+ var chngd = TagsChanged;
+ if (chngd != null)
+ chngd(this, new SnapshotSpanEventArgs(new SnapshotSpan(SourceBuffer.CurrentSnapshot, 0, SourceBuffer.CurrentSnapshot.Length)));
+ }
+
+ public IEnumerable<ITagSpan<TextMarkerTag>> GetTags(NormalizedSnapshotSpanCollection spans) {
+ if (spans.Count == 0) //there is no content in the buffer
+ yield break;
+
+ //don't do anything if the current SnapshotPoint is not initialized
+ if (!CurrentChar.HasValue)
+ yield break;
+
+ //hold on to a snapshot of the current character
+ SnapshotPoint currentChar = CurrentChar.Value;
+
+ //if the requested snapshot isn't the same as the one the brace is on, translate our spans to the expected snapshot
+ if (spans[0].Snapshot != currentChar.Snapshot) {
+ currentChar = currentChar.TranslateTo(spans[0].Snapshot, PointTrackingMode.Positive);
+ }
+
+ if (currentChar.Position < spans[0].Snapshot.Length) {
+ // Check if the current character is an open brace
+ char ch = currentChar.GetChar();
+ char closeCh;
+ if (MatchBrace(currentChar, ch, true, out closeCh)) {
+ SnapshotSpan pairSpan;
+ if (FindMatchingCloseChar(currentChar, ch, closeCh, View.TextViewLines.Count, out pairSpan)) {
+ yield return new TagSpan<TextMarkerTag>(new SnapshotSpan(currentChar, 1), Blue);
+ yield return new TagSpan<TextMarkerTag>(pairSpan, Blue);
+ }
+ }
+ }
+
+ if (0 < currentChar.Position) {
+ // Check if the previous character is a close brace (note, caret may be between a close brace and an open brace, in which case we'll tag two pairs)
+ SnapshotPoint prevChar = currentChar - 1;
+ char ch = prevChar.GetChar();
+ char openCh;
+ if (MatchBrace(prevChar, ch, false, out openCh)) {
+ SnapshotSpan pairSpan;
+ if (FindMatchingOpenChar(prevChar, openCh, ch, View.TextViewLines.Count, out pairSpan)) {
+ yield return new TagSpan<TextMarkerTag>(new SnapshotSpan(prevChar, 1), Blue);
+ yield return new TagSpan<TextMarkerTag>(pairSpan, Blue);
+ }
+ }
+ }
+ }
+
+ private bool MatchBrace(SnapshotPoint pt, char query, bool sourceIsOpen, out char match) {
+ if (!InsideComment(pt)) {
+ char[] source = sourceIsOpen ? openBraces : closeBraces;
+ int i = 0;
+ foreach (char ch in source) {
+ if (ch == query) {
+ char[] dest = sourceIsOpen ? closeBraces : openBraces;
+ match = dest[i];
+ return true;
+ }
+ i++;
+ }
+ }
+ match = query; // satisfy compiler
+ return false;
+ }
+
+ private bool FindMatchingCloseChar(SnapshotPoint startPoint, char open, char close, int linesViewed, out SnapshotSpan pairSpan) {
+ ITextSnapshotLine line = startPoint.GetContainingLine();
+ int lineNumber = line.LineNumber;
+ int offset = startPoint.Position - line.Start.Position + 1;
+
+ int lineNumberLimit = Math.Min(startPoint.Snapshot.LineCount, lineNumber + linesViewed);
+
+ int openCount = 0;
+ while (true) {
+ string lineText = line.GetText();
+
+ //walk the entire line
+ for (; offset < line.Length; offset++) {
+ char currentChar = lineText[offset];
+ if (currentChar == open || currentChar == close) {
+ if (!InsideComment(new SnapshotPoint(line.Snapshot, line.Start.Position + offset))) {
+ if (currentChar == open) {
+ openCount++;
+ } else if (0 < openCount) {
+ openCount--;
+ } else {
+ //found the matching close
+ pairSpan = new SnapshotSpan(startPoint.Snapshot, line.Start + offset, 1);
+ return true;
+ }
+ }
+ }
+ }
+
+ //move on to the next line
+ lineNumber++;
+ if (lineNumberLimit <= lineNumber)
+ break;
+
+ line = line.Snapshot.GetLineFromLineNumber(lineNumber);
+ offset = 0;
+ }
+
+ pairSpan = new SnapshotSpan(startPoint, startPoint); // satisfy the compiler
+ return false;
+ }
+
+ private bool FindMatchingOpenChar(SnapshotPoint startPoint, char open, char close, int linesViewed, out SnapshotSpan pairSpan) {
+ ITextSnapshotLine line = startPoint.GetContainingLine();
+ int lineNumber = line.LineNumber;
+ int offset = startPoint.Position - line.Start.Position - 1; //move the offset to the character before this one
+
+ int lineNumberLimit = Math.Max(0, lineNumber - linesViewed);
+
+ int closeCount = 0;
+ while (true) {
+ string lineText = line.GetText();
+
+ //walk the entire line
+ for (; 0 <= offset; offset--) {
+ char currentChar = lineText[offset];
+ if (currentChar == open || currentChar == close) {
+ if (!InsideComment(new SnapshotPoint(line.Snapshot, line.Start.Position + offset))) {
+ if (currentChar == close) {
+ closeCount++;
+ } else if (0 < closeCount) {
+ closeCount--;
+ } else {
+ // We've found the open character
+ pairSpan = new SnapshotSpan(line.Start + offset, 1); //we just want the character itself
+ return true;
+ }
+ }
+ }
+ }
+
+ // Move to the previous line
+ lineNumber--;
+ if (lineNumber < lineNumberLimit)
+ break;
+
+ line = line.Snapshot.GetLineFromLineNumber(lineNumber);
+ offset = line.Length - 1;
+ }
+
+ pairSpan = new SnapshotSpan(startPoint, startPoint); // satisfy the compiler
+ return false;
+ }
+ }
+}
diff --git a/Source/DafnyExtension/BufferIdleEventUtil.cs b/Source/DafnyExtension/BufferIdleEventUtil.cs
new file mode 100644
index 00000000..1aea1385
--- /dev/null
+++ b/Source/DafnyExtension/BufferIdleEventUtil.cs
@@ -0,0 +1,155 @@
+//***************************************************************************
+// Copyright © 2010 Microsoft Corporation. All Rights Reserved.
+// This code released under the terms of the
+// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
+//***************************************************************************
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.VisualStudio.Text;
+using System.Windows.Threading;
+
+namespace DafnyLanguage
+{
+ /// <summary>
+ /// Handy reusable utility to listen for change events on the associated buffer, but
+ /// only pass these along to a set of listeners when the user stops typing for a half second
+ /// </summary>
+ static class BufferIdleEventUtil
+ {
+ static object bufferListenersKey = new object();
+ static object bufferTimerKey = new object();
+
+ #region Public interface
+
+ public static bool AddBufferIdleEventListener(ITextBuffer buffer, EventHandler handler)
+ {
+ HashSet<EventHandler> listenersForBuffer;
+ if (!TryGetBufferListeners(buffer, out listenersForBuffer))
+ listenersForBuffer = ConnectToBuffer(buffer);
+
+ if (listenersForBuffer.Contains(handler))
+ return false;
+
+ listenersForBuffer.Add(handler);
+
+ return true;
+ }
+
+ public static bool RemoveBufferIdleEventListener(ITextBuffer buffer, EventHandler handler)
+ {
+ HashSet<EventHandler> listenersForBuffer;
+ if (!TryGetBufferListeners(buffer, out listenersForBuffer))
+ return false;
+
+ if (!listenersForBuffer.Contains(handler))
+ return false;
+
+ listenersForBuffer.Remove(handler);
+
+ if (listenersForBuffer.Count == 0)
+ DisconnectFromBuffer(buffer);
+
+ return true;
+ }
+
+ #endregion
+
+ #region Helpers
+
+ static bool TryGetBufferListeners(ITextBuffer buffer, out HashSet<EventHandler> listeners)
+ {
+ return buffer.Properties.TryGetProperty(bufferListenersKey, out listeners);
+ }
+
+ static void ClearBufferListeners(ITextBuffer buffer)
+ {
+ buffer.Properties.RemoveProperty(bufferListenersKey);
+ }
+
+ static bool TryGetBufferTimer(ITextBuffer buffer, out DispatcherTimer timer)
+ {
+ return buffer.Properties.TryGetProperty(bufferTimerKey, out timer);
+ }
+
+ static void ClearBufferTimer(ITextBuffer buffer)
+ {
+ DispatcherTimer timer;
+ if (TryGetBufferTimer(buffer, out timer))
+ {
+ if (timer != null)
+ timer.Stop();
+ buffer.Properties.RemoveProperty(bufferTimerKey);
+ }
+ }
+
+ static void DisconnectFromBuffer(ITextBuffer buffer)
+ {
+ buffer.Changed -= BufferChanged;
+
+ ClearBufferListeners(buffer);
+ ClearBufferTimer(buffer);
+
+ buffer.Properties.RemoveProperty(bufferListenersKey);
+ }
+
+ static HashSet<EventHandler> ConnectToBuffer(ITextBuffer buffer)
+ {
+ buffer.Changed += BufferChanged;
+
+ RestartTimerForBuffer(buffer);
+
+ HashSet<EventHandler> listenersForBuffer = new HashSet<EventHandler>();
+ buffer.Properties[bufferListenersKey] = listenersForBuffer;
+
+ return listenersForBuffer;
+ }
+
+ static void RestartTimerForBuffer(ITextBuffer buffer)
+ {
+ DispatcherTimer timer;
+
+ if (TryGetBufferTimer(buffer, out timer))
+ {
+ timer.Stop();
+ }
+ else
+ {
+ timer = new DispatcherTimer(DispatcherPriority.ApplicationIdle)
+ {
+ Interval = TimeSpan.FromMilliseconds(500)
+ };
+
+ timer.Tick += (s, e) =>
+ {
+ ClearBufferTimer(buffer);
+
+ HashSet<EventHandler> handlers;
+ if (TryGetBufferListeners(buffer, out handlers))
+ {
+ foreach (var handler in handlers)
+ {
+ handler(buffer, new EventArgs());
+ }
+ }
+ };
+
+ buffer.Properties[bufferTimerKey] = timer;
+ }
+
+ timer.Start();
+ }
+
+ static void BufferChanged(object sender, TextContentChangedEventArgs e)
+ {
+ ITextBuffer buffer = sender as ITextBuffer;
+ if (buffer == null)
+ return;
+
+ RestartTimerForBuffer(buffer);
+ }
+
+ #endregion
+ }
+}
diff --git a/Source/DafnyExtension/ClassificationTagger.cs b/Source/DafnyExtension/ClassificationTagger.cs
new file mode 100644
index 00000000..09835ac9
--- /dev/null
+++ b/Source/DafnyExtension/ClassificationTagger.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Windows.Media;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Classification;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Tagging;
+using Microsoft.VisualStudio.Utilities;
+
+
+namespace DafnyLanguage
+{
+ [Export(typeof(ITaggerProvider))]
+ [ContentType("dafny")]
+ [TagType(typeof(ClassificationTag))]
+ internal sealed class DafnyClassifierProvider : ITaggerProvider
+ {
+ [Import]
+ internal IBufferTagAggregatorFactoryService AggregatorFactory = null;
+
+ [Import]
+ internal IClassificationTypeRegistryService ClassificationTypeRegistry = null;
+
+ [Import]
+ internal Microsoft.VisualStudio.Language.StandardClassification.IStandardClassificationService Standards = null;
+
+ public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag {
+ ITagAggregator<DafnyTokenTag> tagAggregator = AggregatorFactory.CreateTagAggregator<DafnyTokenTag>(buffer);
+ return new DafnyClassifier(buffer, tagAggregator, ClassificationTypeRegistry, Standards) as ITagger<T>;
+ }
+ }
+
+ internal sealed class DafnyClassifier : ITagger<ClassificationTag>
+ {
+ ITextBuffer _buffer;
+ ITagAggregator<DafnyTokenTag> _aggregator;
+ IDictionary<DafnyTokenKinds, IClassificationType> _typeMap;
+
+ internal DafnyClassifier(ITextBuffer buffer,
+ ITagAggregator<DafnyTokenTag> tagAggregator,
+ IClassificationTypeRegistryService typeService, Microsoft.VisualStudio.Language.StandardClassification.IStandardClassificationService standards) {
+ _buffer = buffer;
+ _aggregator = tagAggregator;
+ _aggregator.TagsChanged += new EventHandler<TagsChangedEventArgs>(_aggregator_TagsChanged);
+ // use built-in classification types:
+ _typeMap = new Dictionary<DafnyTokenKinds, IClassificationType>();
+ _typeMap[DafnyTokenKinds.Keyword] = standards.Keyword;
+ _typeMap[DafnyTokenKinds.Number] = standards.NumberLiteral;
+ _typeMap[DafnyTokenKinds.String] = standards.StringLiteral;
+ _typeMap[DafnyTokenKinds.Comment] = standards.Comment;
+ _typeMap[DafnyTokenKinds.VariableIdentifier] = standards.Identifier;
+ _typeMap[DafnyTokenKinds.VariableIdentifierDefinition] = typeService.GetClassificationType("Dafny identifier");
+ }
+
+ public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
+
+ public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans) {
+ if (spans.Count == 0) yield break;
+ var snapshot = spans[0].Snapshot;
+ foreach (var tagSpan in this._aggregator.GetTags(spans)) {
+ IClassificationType t = _typeMap[tagSpan.Tag.Kind];
+ foreach (SnapshotSpan s in tagSpan.Span.GetSpans(snapshot)) {
+ yield return new TagSpan<ClassificationTag>(s, new ClassificationTag(t));
+ }
+ }
+ }
+
+ void _aggregator_TagsChanged(object sender, TagsChangedEventArgs e) {
+ var chng = TagsChanged;
+ if (chng != null) {
+ NormalizedSnapshotSpanCollection spans = e.Span.GetSpans(_buffer.CurrentSnapshot);
+ if (spans.Count > 0) {
+ SnapshotSpan span = new SnapshotSpan(spans[0].Start, spans[spans.Count - 1].End);
+ chng(this, new SnapshotSpanEventArgs(span));
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Defines an editor format for user-defined type.
+ /// </summary>
+ [Export(typeof(EditorFormatDefinition))]
+ [ClassificationType(ClassificationTypeNames = "Dafny identifier")]
+ [Name("Dafny identifier")]
+ [UserVisible(true)]
+ //set the priority to be after the default classifiers
+ [Order(Before = Priority.Default)]
+ internal sealed class DafnyTypeFormat : ClassificationFormatDefinition
+ {
+ public DafnyTypeFormat() {
+ this.DisplayName = "Dafny identifier"; //human readable version of the name
+ this.ForegroundColor = Colors.CornflowerBlue;
+ }
+ }
+
+ internal static class ClassificationDefinition
+ {
+ /// <summary>
+ /// Defines the "ordinary" classification type.
+ /// </summary>
+ [Export(typeof(ClassificationTypeDefinition))]
+ [Name("Dafny identifier")]
+ internal static ClassificationTypeDefinition UserType = null;
+ }
+}
diff --git a/Source/DafnyExtension/ContentType.cs b/Source/DafnyExtension/ContentType.cs
new file mode 100644
index 00000000..d8487f74
--- /dev/null
+++ b/Source/DafnyExtension/ContentType.cs
@@ -0,0 +1,18 @@
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Utilities;
+
+namespace DafnyLanguage
+{
+ class DafnyContentType
+ {
+ [Export]
+ [Name("dafny")]
+ [BaseDefinition("code")]
+ internal static ContentTypeDefinition ContentType = null;
+
+ [Export]
+ [FileExtension(".dfy")]
+ [ContentType("dafny")]
+ internal static FileExtensionToContentTypeDefinition FileType = null;
+ }
+}
diff --git a/Source/DafnyExtension/DafnyDriver.cs b/Source/DafnyExtension/DafnyDriver.cs
new file mode 100644
index 00000000..39829bb0
--- /dev/null
+++ b/Source/DafnyExtension/DafnyDriver.cs
@@ -0,0 +1,419 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+// Here come the Dafny/Boogie specific imports
+//using PureCollections;
+using Bpl = Microsoft.Boogie;
+using Dafny = Microsoft.Dafny;
+using Microsoft.Boogie.AbstractInterpretation;
+using VC;
+// using AI = Microsoft.AbstractInterpretationFramework;
+
+
+namespace DafnyLanguage
+{
+ class DafnyDriver
+ {
+ readonly string _programText;
+ readonly string _filename;
+ Dafny.Program _program;
+ List<DafnyError> _errors = new List<DafnyError>();
+ public List<DafnyError> Errors { get { return _errors; } }
+
+ public DafnyDriver(string programText, string filename) {
+ _programText = programText;
+ _filename = filename;
+ }
+
+ void RecordError(int line, int col, ErrorCategory cat, string msg) {
+ _errors.Add(new DafnyError(line, col, cat, msg));
+ }
+
+ static DafnyDriver() {
+ Initialize();
+ }
+
+ static void Initialize() {
+ if (Dafny.DafnyOptions.O == null) {
+ var options = new Dafny.DafnyOptions();
+ options.ProverKillTime = 10;
+ options.ErrorTrace = 0;
+ Dafny.DafnyOptions.Install(options);
+ options.ApplyDefaultOptions();
+ }
+ }
+
+ public Dafny.Program ProcessResolution() {
+ if (!ParseAndTypeCheck()) {
+ return null;
+ }
+ return _program;
+ }
+
+ bool ParseAndTypeCheck() {
+ Dafny.ModuleDecl module = new Dafny.LiteralModuleDecl(new Dafny.DefaultModuleDecl(), null);
+ Dafny.BuiltIns builtIns = new Dafny.BuiltIns();
+ int errorCount = Dafny.Parser.Parse(_programText, _filename, module, builtIns, new VSErrors(this));
+ if (errorCount != 0)
+ return false;
+ Dafny.Program program = new Dafny.Program(_filename, module, builtIns);
+
+ Dafny.Resolver r = new VSResolver(program, this);
+ r.ResolveProgram(program);
+ if (r.ErrorCount != 0)
+ return false;
+
+ _program = program;
+ return true; // success
+ }
+
+ class VSErrors : Dafny.Errors
+ {
+ DafnyDriver dd;
+ public VSErrors(DafnyDriver dd) {
+ this.dd = dd;
+ }
+ public override void SynErr(string filename, int line, int col, string msg) {
+ dd.RecordError(line - 1, col - 1, ErrorCategory.ParseError, msg);
+ count++;
+ }
+ public override void SemErr(string filename, int line, int col, string msg) {
+ dd.RecordError(line - 1, col - 1, ErrorCategory.ResolveError, msg);
+ count++;
+ }
+ public override void Warning(string filename, int line, int col, string msg) {
+ dd.RecordError(line - 1, col - 1, ErrorCategory.ParseWarning, msg);
+ }
+ }
+
+ class VSResolver : Dafny.Resolver
+ {
+ DafnyDriver dd;
+ public VSResolver(Dafny.Program program, DafnyDriver dd)
+ : base(program) {
+ this.dd = dd;
+ }
+ public override void Error(Bpl.IToken tok, string msg, params object[] args) {
+ string s = string.Format(msg, args);
+ dd.RecordError(tok.line - 1, tok.col - 1, ErrorCategory.ResolveError, s);
+ ErrorCount++;
+ }
+ }
+
+ public static bool Verify(Dafny.Program dafnyProgram, ErrorReporterDelegate er) {
+ Dafny.Translator translator = new Dafny.Translator();
+ Bpl.Program boogieProgram = translator.Translate(dafnyProgram);
+
+ PipelineOutcome oc = BoogiePipeline(boogieProgram, er);
+ switch (oc) {
+ case PipelineOutcome.Done:
+ case PipelineOutcome.VerificationCompleted:
+ // TODO: This would be the place to proceed to compile the program, if desired
+ return true;
+ case PipelineOutcome.FatalError:
+ default:
+ return false;
+ }
+ }
+
+ enum PipelineOutcome { Done, ResolutionError, TypeCheckingError, ResolvedAndTypeChecked, FatalError, VerificationCompleted }
+
+ /// <summary>
+ /// Resolve, type check, infer invariants for, and verify the given Boogie program.
+ /// The intention is that this Boogie program has been produced by translation from something
+ /// else. Hence, any resolution errors and type checking errors are due to errors in
+ /// the translation.
+ /// </summary>
+ static PipelineOutcome BoogiePipeline(Bpl.Program/*!*/ program, ErrorReporterDelegate er) {
+ Contract.Requires(program != null);
+
+ PipelineOutcome oc = BoogieResolveAndTypecheck(program);
+ if (oc == PipelineOutcome.ResolvedAndTypeChecked) {
+ EliminateDeadVariablesAndInline(program);
+ return BoogieInferAndVerify(program, er);
+ }
+ return oc;
+ }
+
+ static void EliminateDeadVariablesAndInline(Bpl.Program program) {
+ Contract.Requires(program != null);
+ // Eliminate dead variables
+ Microsoft.Boogie.UnusedVarEliminator.Eliminate(program);
+
+ // Collect mod sets
+ if (Bpl.CommandLineOptions.Clo.DoModSetAnalysis) {
+ Microsoft.Boogie.ModSetCollector.DoModSetAnalysis(program);
+ }
+
+ // Coalesce blocks
+ if (Bpl.CommandLineOptions.Clo.CoalesceBlocks) {
+ Microsoft.Boogie.BlockCoalescer.CoalesceBlocks(program);
+ }
+
+ // Inline
+ var TopLevelDeclarations = program.TopLevelDeclarations;
+
+ if (Bpl.CommandLineOptions.Clo.ProcedureInlining != Bpl.CommandLineOptions.Inlining.None) {
+ bool inline = false;
+ foreach (var d in TopLevelDeclarations) {
+ if (d.FindExprAttribute("inline") != null) {
+ inline = true;
+ }
+ }
+ if (inline && Bpl.CommandLineOptions.Clo.StratifiedInlining == 0) {
+ foreach (var d in TopLevelDeclarations) {
+ var impl = d as Bpl.Implementation;
+ if (impl != null) {
+ impl.OriginalBlocks = impl.Blocks;
+ impl.OriginalLocVars = impl.LocVars;
+ }
+ }
+ foreach (var d in TopLevelDeclarations) {
+ var impl = d as Bpl.Implementation;
+ if (impl != null && !impl.SkipVerification) {
+ Bpl.Inliner.ProcessImplementation(program, impl);
+ }
+ }
+ foreach (var d in TopLevelDeclarations) {
+ var impl = d as Bpl.Implementation;
+ if (impl != null) {
+ impl.OriginalBlocks = null;
+ impl.OriginalLocVars = null;
+ }
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Resolves and type checks the given Boogie program.
+ /// Returns:
+ /// - Done if no errors occurred, and command line specified no resolution or no type checking.
+ /// - ResolutionError if a resolution error occurred
+ /// - TypeCheckingError if a type checking error occurred
+ /// - ResolvedAndTypeChecked if both resolution and type checking succeeded
+ /// </summary>
+ static PipelineOutcome BoogieResolveAndTypecheck(Bpl.Program program) {
+ Contract.Requires(program != null);
+ // ---------- Resolve ------------------------------------------------------------
+ int errorCount = program.Resolve();
+ if (errorCount != 0) {
+ return PipelineOutcome.ResolutionError;
+ }
+
+ // ---------- Type check ------------------------------------------------------------
+ errorCount = program.Typecheck();
+ if (errorCount != 0) {
+ return PipelineOutcome.TypeCheckingError;
+ }
+
+ return PipelineOutcome.ResolvedAndTypeChecked;
+ }
+
+ /// <summary>
+ /// Given a resolved and type checked Boogie program, infers invariants for the program
+ /// and then attempts to verify it. Returns:
+ /// - Done if command line specified no verification
+ /// - FatalError if a fatal error occurred
+ /// - VerificationCompleted if inference and verification completed, in which the out
+ /// parameters contain meaningful values
+ /// </summary>
+ static PipelineOutcome BoogieInferAndVerify(Bpl.Program program, ErrorReporterDelegate er) {
+ Contract.Requires(program != null);
+
+ // ---------- Infer invariants --------------------------------------------------------
+
+ // Abstract interpretation -> Always use (at least) intervals, if not specified otherwise (e.g. with the "/noinfer" switch)
+ if (Bpl.CommandLineOptions.Clo.UseAbstractInterpretation) {
+ if (Bpl.CommandLineOptions.Clo.Ai.J_Intervals || Bpl.CommandLineOptions.Clo.Ai.J_Trivial) {
+ Microsoft.Boogie.AbstractInterpretation.NativeAbstractInterpretation.RunAbstractInterpretation(program);
+ } else {
+ // use /infer:j as the default
+ Bpl.CommandLineOptions.Clo.Ai.J_Intervals = true;
+ Microsoft.Boogie.AbstractInterpretation.NativeAbstractInterpretation.RunAbstractInterpretation(program);
+ }
+ }
+
+ if (Bpl.CommandLineOptions.Clo.LoopUnrollCount != -1) {
+ program.UnrollLoops(Bpl.CommandLineOptions.Clo.LoopUnrollCount);
+ }
+
+ if (Bpl.CommandLineOptions.Clo.ExpandLambdas) {
+ Bpl.LambdaHelper.ExpandLambdas(program);
+ //PrintBplFile ("-", program, true);
+ }
+
+ // ---------- Verify ------------------------------------------------------------
+
+ if (!Bpl.CommandLineOptions.Clo.Verify) { return PipelineOutcome.Done; }
+
+ #region Verify each implementation
+
+ ConditionGeneration vcgen = null;
+ try {
+ vcgen = new VCGen(program, Bpl.CommandLineOptions.Clo.SimplifyLogFilePath, Bpl.CommandLineOptions.Clo.SimplifyLogFileAppend);
+ } catch (Bpl.ProverException) {
+ return PipelineOutcome.FatalError;
+ }
+
+ var decls = program.TopLevelDeclarations.ToArray();
+ foreach (var decl in decls) {
+ Contract.Assert(decl != null);
+ Bpl.Implementation impl = decl as Bpl.Implementation;
+ if (impl != null && Bpl.CommandLineOptions.Clo.UserWantsToCheckRoutine(impl.Name) && !impl.SkipVerification) {
+ List<Bpl.Counterexample>/*?*/ errors;
+
+ ConditionGeneration.Outcome outcome;
+ int prevAssertionCount = vcgen.CumulativeAssertionCount;
+ try {
+ outcome = vcgen.VerifyImplementation(impl, out errors);
+ } catch (VCGenException) {
+ errors = null;
+ outcome = VCGen.Outcome.Inconclusive;
+ } catch (Bpl.UnexpectedProverOutputException) {
+ errors = null;
+ outcome = VCGen.Outcome.Inconclusive;
+ }
+
+ switch (outcome) {
+ default:
+ Contract.Assert(false); throw new Exception(); // unexpected outcome
+ case VCGen.Outcome.Correct:
+ break;
+ case VCGen.Outcome.TimedOut:
+ er(new DafnyErrorInformation(impl.tok, "Verification timed out (" + impl.Name + ")"));
+ break;
+ case VCGen.Outcome.OutOfMemory:
+ er(new DafnyErrorInformation(impl.tok, "Verification out of memory (" + impl.Name + ")"));
+ break;
+ case VCGen.Outcome.Inconclusive:
+ er(new DafnyErrorInformation(impl.tok, "Verification inconclusive (" + impl.Name + ")"));
+ break;
+ case VCGen.Outcome.Errors:
+ Contract.Assert(errors != null); // guaranteed by postcondition of VerifyImplementation
+
+ errors.Sort(new Bpl.CounterexampleComparer());
+ foreach (var error in errors) {
+ DafnyErrorInformation errorInfo;
+
+ if (error is Bpl.CallCounterexample) {
+ var err = (Bpl.CallCounterexample)error;
+ errorInfo = new DafnyErrorInformation(err.FailingCall.tok, err.FailingCall.ErrorData as string ?? "A precondition for this call might not hold.");
+ errorInfo.AddAuxInfo(err.FailingRequires.tok, err.FailingRequires.ErrorData as string ?? "Related location: This is the precondition that might not hold.");
+
+ } else if (error is Bpl.ReturnCounterexample) {
+ var err = (Bpl.ReturnCounterexample)error;
+ errorInfo = new DafnyErrorInformation(err.FailingReturn.tok, "A postcondition might not hold on this return path.");
+ errorInfo.AddAuxInfo(err.FailingEnsures.tok, err.FailingEnsures.ErrorData as string ?? "Related location: This is the postcondition that might not hold.");
+ errorInfo.AddAuxInfo(err.FailingEnsures.Attributes);
+
+ } else { // error is AssertCounterexample
+ var err = (Bpl.AssertCounterexample)error;
+ if (err.FailingAssert is Bpl.LoopInitAssertCmd) {
+ errorInfo = new DafnyErrorInformation(err.FailingAssert.tok, "This loop invariant might not hold on entry.");
+ } else if (err.FailingAssert is Bpl.LoopInvMaintainedAssertCmd) {
+ // this assertion is a loop invariant which is not maintained
+ errorInfo = new DafnyErrorInformation(err.FailingAssert.tok, "This loop invariant might not be maintained by the loop.");
+ } else {
+ string msg = err.FailingAssert.ErrorData as string;
+ if (msg == null) {
+ msg = "This assertion might not hold.";
+ }
+ errorInfo = new DafnyErrorInformation(err.FailingAssert.tok, msg);
+ errorInfo.AddAuxInfo(err.FailingAssert.Attributes);
+ }
+ }
+ if (Bpl.CommandLineOptions.Clo.ErrorTrace > 0) {
+ foreach (Bpl.Block b in error.Trace) {
+ // for ErrorTrace == 1 restrict the output;
+ // do not print tokens with -17:-4 as their location because they have been
+ // introduced in the translation and do not give any useful feedback to the user
+ if (!(Bpl.CommandLineOptions.Clo.ErrorTrace == 1 && b.tok.line == -17 && b.tok.col == -4) &&
+ b.tok.line != 0 && b.tok.col != 0) {
+ errorInfo.AddAuxInfo(b.tok, "Execution trace: " + b.Label);
+ }
+ }
+ }
+ // if (Bpl.CommandLineOptions.Clo.ModelViewFile != null) {
+ // error.PrintModel();
+ // }
+ er(errorInfo);
+ }
+ break;
+ }
+ }
+ }
+ vcgen.Close();
+ Bpl.CommandLineOptions.Clo.TheProverFactory.Close();
+
+ #endregion
+
+ return PipelineOutcome.VerificationCompleted;
+ }
+
+ public delegate void ErrorReporterDelegate(DafnyErrorInformation errInfo);
+
+ public class DafnyErrorInformation
+ {
+ public readonly Bpl.IToken Tok;
+ public readonly string Msg;
+ public readonly List<DafnyErrorAuxInfo> Aux = new List<DafnyErrorAuxInfo>();
+
+ public class DafnyErrorAuxInfo
+ {
+ public readonly Bpl.IToken Tok;
+ public readonly string Msg;
+ public DafnyErrorAuxInfo(Bpl.IToken tok, string msg) {
+ Tok = tok;
+ Msg = CleanUp(msg);
+ }
+ }
+
+ public DafnyErrorInformation(Bpl.IToken tok, string msg) {
+ Contract.Requires(tok != null);
+ Contract.Requires(1 <= tok.line && 1 <= tok.col);
+ Contract.Requires(msg != null);
+ Tok = tok;
+ Msg = CleanUp(msg);
+ AddNestingsAsAux(tok);
+ }
+ public void AddAuxInfo(Bpl.IToken tok, string msg) {
+ Contract.Requires(tok != null);
+ Contract.Requires(1 <= tok.line && 1 <= tok.col);
+ Contract.Requires(msg != null);
+ Aux.Add(new DafnyErrorAuxInfo(tok, msg));
+ AddNestingsAsAux(tok);
+ }
+ void AddNestingsAsAux(Bpl.IToken tok) {
+ while (tok is Dafny.NestedToken) {
+ var nt = (Dafny.NestedToken)tok;
+ tok = nt.Inner;
+ Aux.Add(new DafnyErrorAuxInfo(tok, "Related location"));
+ }
+ }
+ public void AddAuxInfo(Bpl.QKeyValue attr) {
+ while (attr != null) {
+ if (attr.Key == "msg" && attr.Params.Count == 1 && attr.tok.line != 0 && attr.tok.col != 0) {
+ var str = attr.Params[0] as string;
+ if (str != null) {
+ AddAuxInfo(attr.tok, str);
+ }
+ }
+ attr = attr.Next;
+ }
+ }
+
+ public static string CleanUp(string msg) {
+ if (msg.ToLower().StartsWith("error: ")) {
+ return msg.Substring(7);
+ } else {
+ return msg;
+ }
+ }
+ }
+ }
+}
diff --git a/Source/DafnyExtension/DafnyExtension.csproj b/Source/DafnyExtension/DafnyExtension.csproj
new file mode 100644
index 00000000..2580c396
--- /dev/null
+++ b/Source/DafnyExtension/DafnyExtension.csproj
@@ -0,0 +1,191 @@
+<?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>
+ <ProductVersion>10.0.20305</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ProjectGuid>{6E9A5E14-0763-471C-A129-80A879D9E7BA}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>DafnyLanguage</RootNamespace>
+ <AssemblyName>DafnyLanguageService</AssemblyName>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <GeneratePkgDefFile>false</GeneratePkgDefFile>
+ <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
+ <FileUpgradeFlags>
+ </FileUpgradeFlags>
+ <UpgradeBackupLocation>
+ </UpgradeBackupLocation>
+ <OldToolsVersion>4.0</OldToolsVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="AbsInt, Version=2.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\..\..\Binaries\AbsInt.dll</HintPath>
+ </Reference>
+ <Reference Include="AIFramework, Version=2.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\..\..\Binaries\AIFramework.dll</HintPath>
+ </Reference>
+ <Reference Include="Basetypes, Version=2.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\..\..\Binaries\Basetypes.dll</HintPath>
+ </Reference>
+ <Reference Include="CodeContractsExtender, Version=2.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\..\..\Binaries\CodeContractsExtender.dll</HintPath>
+ </Reference>
+ <Reference Include="Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\..\..\Binaries\Core.dll</HintPath>
+ </Reference>
+ <Reference Include="DafnyPipeline, Version=2.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\..\..\Binaries\DafnyPipeline.dll</HintPath>
+ </Reference>
+ <Reference Include="Graph">
+ <HintPath>..\..\..\..\Binaries\Graph.dll</HintPath>
+ </Reference>
+ <Reference Include="Houdini">
+ <HintPath>..\..\..\..\Binaries\Houdini.dll</HintPath>
+ </Reference>
+ <Reference Include="Model">
+ <HintPath>..\..\..\..\Binaries\Model.dll</HintPath>
+ </Reference>
+ <Reference Include="ParserHelper">
+ <HintPath>..\..\..\..\Binaries\ParserHelper.dll</HintPath>
+ </Reference>
+ <Reference Include="Provers.Z3">
+ <HintPath>..\..\..\..\Binaries\Provers.Z3.dll</HintPath>
+ </Reference>
+ <Reference Include="Provers.SMTLib">
+ <HintPath>..\..\..\..\Binaries\Provers.SMTLib.dll</HintPath>
+ </Reference>
+ <Reference Include="VCExpr">
+ <HintPath>..\..\..\..\Binaries\VCExpr.dll</HintPath>
+ </Reference>
+ <Reference Include="VCGeneration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\..\..\Binaries\VCGeneration.dll</HintPath>
+ </Reference>
+ <Reference Include="EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <EmbedInteropTypes>False</EmbedInteropTypes>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.CoreUtility">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Editor, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Language.Intellisense, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Language.StandardClassification, 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, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Immutable.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <EmbedInteropTypes>True</EmbedInteropTypes>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.8.0, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+ <Reference Include="Microsoft.VisualStudio.Text.Data">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.Logic">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.UI">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.UI.Wpf">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.TextManager.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+ <Reference Include="PresentationCore" />
+ <Reference Include="PresentationFramework" />
+ <Reference Include="System" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.ComponentModel.Composition" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ <Reference Include="System.Xaml" />
+ <Reference Include="WindowsBase" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="BufferIdleEventUtil.cs" />
+ <Compile Include="HoverText.cs" />
+ <Compile Include="IdentifierTagger.cs" />
+ <Compile Include="ProgressMargin.cs" />
+ <Compile Include="OutliningTagger.cs" />
+ <Compile Include="ResolverTagger.cs" />
+ <Compile Include="DafnyDriver.cs" />
+ <Compile Include="ContentType.cs" />
+ <Compile Include="BraceMatching.cs" />
+ <Compile Include="WordHighlighter.cs" />
+ <Compile Include="ErrorTagger.cs" />
+ <Compile Include="TokenTagger.cs" />
+ <Compile Include="ClassificationTagger.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="DafnyPrelude.bpl">
+ <IncludeInVSIX>true</IncludeInVSIX>
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
+ <None Include="source.extension.vsixmanifest">
+ <SubType>Designer</SubType>
+ </None>
+ <Content Include="UnivBackPred2.smt2">
+ <IncludeInVSIX>true</IncludeInVSIX>
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
+ </ItemGroup>
+ <ItemGroup>
+ <WCFMetadata Include="Service References\" />
+ </ItemGroup>
+ <PropertyGroup>
+ <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
+ <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+ </PropertyGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\VSSDK\Microsoft.VsSDK.targets" Condition="false" />
+ <PropertyGroup>
+ <PostBuildEvent>cd</PostBuildEvent>
+ <PostBuildEvent>
+ </PostBuildEvent>
+ </PropertyGroup>
+ <PropertyGroup>
+ <PreBuildEvent>copy ..\..\..\..\..\..\Binaries\DafnyPrelude.bpl $(ProjectDir)
+copy ..\..\..\..\..\..\Binaries\UnivBackPred2.smt2 $(ProjectDir)</PreBuildEvent>
+ </PropertyGroup>
+ <!-- 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/DafnyExtension/ErrorTagger.cs b/Source/DafnyExtension/ErrorTagger.cs
new file mode 100644
index 00000000..efd755d8
--- /dev/null
+++ b/Source/DafnyExtension/ErrorTagger.cs
@@ -0,0 +1,85 @@
+//***************************************************************************
+// Copyright © 2010 Microsoft Corporation. All Rights Reserved.
+// This code released under the terms of the
+// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
+//***************************************************************************
+using EnvDTE;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.ComponentModel.Composition;
+using System.Windows.Threading;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Classification;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Tagging;
+using Microsoft.VisualStudio.Text.Projection;
+using Microsoft.VisualStudio.Utilities;
+
+namespace DafnyLanguage
+{
+ [Export(typeof(ITaggerProvider))]
+ [ContentType("dafny")]
+ [TagType(typeof(ErrorTag))]
+ internal sealed class ErrorTaggerProvider : ITaggerProvider
+ {
+ [Import]
+ internal IBufferTagAggregatorFactoryService AggregatorFactory = null;
+
+ public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag {
+ ITagAggregator<DafnyResolverTag> tagAggregator = AggregatorFactory.CreateTagAggregator<DafnyResolverTag>(buffer);
+ // create a single tagger for each buffer.
+ Func<ITagger<T>> sc = delegate() { return new ErrorTagger(buffer, tagAggregator) as ITagger<T>; };
+ return buffer.Properties.GetOrCreateSingletonProperty<ITagger<T>>(sc);
+ }
+ }
+
+ /// <summary>
+ /// Translate PkgDefTokenTags into ErrorTags and Error List items
+ /// </summary>
+ internal sealed class ErrorTagger : ITagger<ErrorTag>
+ {
+ ITextBuffer _buffer;
+ ITagAggregator<DafnyResolverTag> _aggregator;
+
+ internal ErrorTagger(ITextBuffer buffer, ITagAggregator<DafnyResolverTag> tagAggregator) {
+ _buffer = buffer;
+ _aggregator = tagAggregator;
+ _aggregator.TagsChanged += new EventHandler<TagsChangedEventArgs>(_aggregator_TagsChanged);
+ }
+
+ /// <summary>
+ /// Find the Error tokens in the set of all tokens and create an ErrorTag for each
+ /// </summary>
+ public IEnumerable<ITagSpan<ErrorTag>> GetTags(NormalizedSnapshotSpanCollection spans) {
+ if (spans.Count == 0) yield break;
+ var snapshot = spans[0].Snapshot;
+ foreach (var tagSpan in this._aggregator.GetTags(spans)) {
+ DafnyResolverTag t = tagSpan.Tag;
+ DafnyErrorResolverTag et = t as DafnyErrorResolverTag;
+ if (et != null) {
+ foreach (SnapshotSpan s in tagSpan.Span.GetSpans(snapshot)) {
+ yield return new TagSpan<ErrorTag>(s, new ErrorTag(et.Typ, et.Msg));
+ }
+ }
+ }
+ }
+
+ // the Classifier tagger is translating buffer change events into TagsChanged events, so we don't have to
+ public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
+
+ void _aggregator_TagsChanged(object sender, TagsChangedEventArgs e) {
+ var chng = TagsChanged;
+ if (chng != null) {
+ NormalizedSnapshotSpanCollection spans = e.Span.GetSpans(_buffer.CurrentSnapshot);
+ if (spans.Count > 0) {
+ SnapshotSpan span = new SnapshotSpan(spans[0].Start, spans[spans.Count - 1].End);
+ chng(this, new SnapshotSpanEventArgs(span));
+ }
+ }
+ }
+ }
+}
diff --git a/Source/DafnyExtension/HoverText.cs b/Source/DafnyExtension/HoverText.cs
new file mode 100644
index 00000000..be806f5b
--- /dev/null
+++ b/Source/DafnyExtension/HoverText.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Input;
+using Microsoft.VisualStudio.Language.Intellisense;
+using System.Collections.ObjectModel;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Tagging;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Utilities;
+using System.Diagnostics.Contracts;
+
+
+namespace DafnyLanguage
+{
+ [Export(typeof(IQuickInfoSourceProvider))]
+ [ContentType("dafny")]
+ [Name("Dafny QuickInfo")]
+ class OokQuickInfoSourceProvider : IQuickInfoSourceProvider
+ {
+ [Import]
+ IBufferTagAggregatorFactoryService aggService = null;
+
+ public IQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer) {
+ return new DafnyQuickInfoSource(textBuffer, aggService.CreateTagAggregator<DafnyTokenTag>(textBuffer));
+ }
+ }
+
+ class DafnyQuickInfoSource : IQuickInfoSource
+ {
+ private ITagAggregator<DafnyTokenTag> _aggregator;
+ private ITextBuffer _buffer;
+
+ public DafnyQuickInfoSource(ITextBuffer buffer, ITagAggregator<DafnyTokenTag> aggregator) {
+ _aggregator = aggregator;
+ _buffer = buffer;
+ }
+
+ public void AugmentQuickInfoSession(IQuickInfoSession session, IList<object> quickInfoContent, out ITrackingSpan applicableToSpan) {
+ applicableToSpan = null;
+
+ var triggerPoint = (SnapshotPoint)session.GetTriggerPoint(_buffer.CurrentSnapshot);
+ if (triggerPoint == null)
+ return;
+
+ foreach (IMappingTagSpan<DafnyTokenTag> curTag in _aggregator.GetTags(new SnapshotSpan(triggerPoint, triggerPoint))) {
+ var s = curTag.Tag.HoverText;
+ if (s != null) {
+ var tagSpan = curTag.Span.GetSpans(_buffer).First();
+ applicableToSpan = _buffer.CurrentSnapshot.CreateTrackingSpan(tagSpan, SpanTrackingMode.EdgeExclusive);
+ quickInfoContent.Add(s);
+ }
+ }
+ }
+ public void Dispose() {
+ }
+ }
+ // --------------------------------- QuickInfo controller ------------------------------------------
+
+ [Export(typeof(IIntellisenseControllerProvider))]
+ [Name("Dafny QuickInfo controller")]
+ [ContentType("dafny")]
+ class DafnyQuickInfoControllerProvider : IIntellisenseControllerProvider
+ {
+ [Import]
+ internal IQuickInfoBroker QuickInfoBroker { get; set; }
+
+ public IIntellisenseController TryCreateIntellisenseController(ITextView textView, IList<ITextBuffer> subjectBuffers) {
+ return new DafnyQuickInfoController(textView, subjectBuffers, this);
+ }
+ }
+
+ class DafnyQuickInfoController : IIntellisenseController
+ {
+ private ITextView _textView;
+ private IList<ITextBuffer> _subjectBuffers;
+ private DafnyQuickInfoControllerProvider _componentContext;
+
+ private IQuickInfoSession _session;
+
+ internal DafnyQuickInfoController(ITextView textView, IList<ITextBuffer> subjectBuffers, DafnyQuickInfoControllerProvider componentContext) {
+ _textView = textView;
+ _subjectBuffers = subjectBuffers;
+ _componentContext = componentContext;
+
+ _textView.MouseHover += this.OnTextViewMouseHover;
+ }
+
+ public void ConnectSubjectBuffer(ITextBuffer subjectBuffer) {
+ }
+
+ public void DisconnectSubjectBuffer(ITextBuffer subjectBuffer) {
+ }
+
+ public void Detach(ITextView textView) {
+ if (_textView == textView) {
+ _textView.MouseHover -= OnTextViewMouseHover;
+ _textView = null;
+ }
+ }
+
+ void OnTextViewMouseHover(object sender, MouseHoverEventArgs e) {
+ SnapshotPoint? point = GetMousePosition(new SnapshotPoint(_textView.TextSnapshot, e.Position));
+
+ if (point != null) {
+ ITrackingPoint triggerPoint = point.Value.Snapshot.CreateTrackingPoint(point.Value.Position, PointTrackingMode.Positive);
+
+ // Find the broker for this buffer
+ if (!_componentContext.QuickInfoBroker.IsQuickInfoActive(_textView)) {
+ _session = _componentContext.QuickInfoBroker.CreateQuickInfoSession(_textView, triggerPoint, true);
+ _session.Start();
+ }
+ }
+ }
+
+ SnapshotPoint? GetMousePosition(SnapshotPoint topPosition) {
+ return _textView.BufferGraph.MapDownToFirstMatch(
+ topPosition,
+ PointTrackingMode.Positive,
+ snapshot => _subjectBuffers.Contains(snapshot.TextBuffer),
+ PositionAffinity.Predecessor);
+ }
+ }
+}
diff --git a/Source/DafnyExtension/IdentifierTagger.cs b/Source/DafnyExtension/IdentifierTagger.cs
new file mode 100644
index 00000000..5ecc8dc2
--- /dev/null
+++ b/Source/DafnyExtension/IdentifierTagger.cs
@@ -0,0 +1,344 @@
+//***************************************************************************
+// Copyright © 2010 Microsoft Corporation. All Rights Reserved.
+// This code released under the terms of the
+// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
+//***************************************************************************
+using EnvDTE;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.ComponentModel.Composition;
+using System.Windows.Threading;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Classification;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Tagging;
+using Microsoft.VisualStudio.Text.Projection;
+using Microsoft.VisualStudio.Utilities;
+using System.Diagnostics.Contracts;
+using Bpl = Microsoft.Boogie;
+using Microsoft.Dafny;
+
+namespace DafnyLanguage
+{
+ [Export(typeof(ITaggerProvider))]
+ [ContentType("dafny")]
+ [TagType(typeof(DafnyTokenTag))]
+ internal sealed class IdentifierTaggerProvider : ITaggerProvider
+ {
+ [Import]
+ internal IBufferTagAggregatorFactoryService AggregatorFactory = null;
+
+ public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag {
+ ITagAggregator<DafnyResolverTag> tagAggregator = AggregatorFactory.CreateTagAggregator<DafnyResolverTag>(buffer);
+ // create a single tagger for each buffer.
+ Func<ITagger<T>> sc = delegate() { return new IdentifierTagger(buffer, tagAggregator) as ITagger<T>; };
+ return buffer.Properties.GetOrCreateSingletonProperty<ITagger<T>>(sc);
+ }
+ }
+
+ /// <summary>
+ /// Translate DafnyResolverTag's into IOutliningRegionTag's
+ /// </summary>
+ internal sealed class IdentifierTagger : ITagger<DafnyTokenTag>
+ {
+ ITextBuffer _buffer;
+ ITextSnapshot _snapshot; // the most recent snapshot of _buffer that we have been informed about
+ Microsoft.Dafny.Program _program; // the program parsed from _snapshot
+ List<IdRegion> _regions = new List<IdRegion>(); // the regions generated from _program
+ ITagAggregator<DafnyResolverTag> _aggregator;
+
+ internal IdentifierTagger(ITextBuffer buffer, ITagAggregator<DafnyResolverTag> tagAggregator) {
+ _buffer = buffer;
+ _snapshot = _buffer.CurrentSnapshot;
+ _aggregator = tagAggregator;
+ _aggregator.TagsChanged += new EventHandler<TagsChangedEventArgs>(_aggregator_TagsChanged);
+ }
+
+ /// <summary>
+ /// Find the Error tokens in the set of all tokens and create an ErrorTag for each
+ /// </summary>
+ public IEnumerable<ITagSpan<DafnyTokenTag>> GetTags(NormalizedSnapshotSpanCollection spans) {
+ if (spans.Count == 0) yield break;
+ // (A NormalizedSnapshotSpanCollection contains spans that all come from the same snapshot.)
+ // The spans are ordered by the .Start component, and the collection contains no adjacent or abutting spans.
+ // Hence, to find a span that includes all the ones in "spans", we only need to look at the .Start for the
+ // first spand and the .End of the last span:
+ var startPoint = spans[0].Start;
+ var endPoint = spans[spans.Count - 1].End;
+
+ // Note, (startPoint,endPoint) are points in the spans for which we're being asked to provide tags. We need to translate
+ // these back into the most recent snapshot that we've computed regions for, namely _snapshot.
+ var entire = new SnapshotSpan(startPoint, endPoint).TranslateTo(_snapshot, SpanTrackingMode.EdgeExclusive);
+ int start = entire.Start;
+ int end = entire.End;
+ foreach (var r in _regions) {
+ if (0 <= r.Length && r.Start <= end && start <= r.Start + r.Length) {
+ DafnyTokenKinds kind;
+ switch (r.Kind) {
+ case IdRegion.OccurrenceKind.Use:
+ kind = DafnyTokenKinds.VariableIdentifier; break;
+ case IdRegion.OccurrenceKind.Definition:
+ kind = DafnyTokenKinds.VariableIdentifierDefinition; break;
+ case IdRegion.OccurrenceKind.WildDefinition:
+ kind = DafnyTokenKinds.Keyword; break;
+ default:
+ Contract.Assert(false); // unexpected OccurrenceKind
+ goto case IdRegion.OccurrenceKind.Use; // to please compiler
+ }
+ yield return new TagSpan<DafnyTokenTag>(
+ new SnapshotSpan(_snapshot, r.Start, r.Length),
+ new DafnyTokenTag(kind, r.HoverText));
+ }
+ }
+ }
+
+ // the Classifier tagger is translating buffer change events into TagsChanged events, so we don't have to
+ public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
+
+ void _aggregator_TagsChanged(object sender, TagsChangedEventArgs e) {
+ var r = sender as ResolverTagger;
+ if (r != null) {
+ ITextSnapshot snap;
+ Microsoft.Dafny.Program prog;
+ lock (this) {
+ snap = r._snapshot;
+ prog = r._program;
+ }
+ if (prog != null) {
+ if (!ComputeIdentifierRegions(prog, snap))
+ return; // no new regions
+
+ var chng = TagsChanged;
+ if (chng != null) {
+ NormalizedSnapshotSpanCollection spans = e.Span.GetSpans(_buffer.CurrentSnapshot);
+ if (spans.Count > 0) {
+ SnapshotSpan span = new SnapshotSpan(spans[0].Start, spans[spans.Count - 1].End);
+ chng(this, new SnapshotSpanEventArgs(span));
+ }
+ }
+ }
+ }
+ }
+
+ bool ComputeIdentifierRegions(Microsoft.Dafny.Program program, ITextSnapshot snapshot) {
+ Contract.Requires(snapshot != null);
+
+ if (program == _program)
+ return false; // no new regions
+
+ List<IdRegion> newRegions = new List<IdRegion>();
+
+ foreach (var module in program.Modules) {
+ foreach (var d in module.TopLevelDecls) {
+ if (d is DatatypeDecl) {
+ var dt = (DatatypeDecl)d;
+ foreach (var ctor in dt.Ctors) {
+ foreach (var dtor in ctor.Destructors) {
+ if (dtor != null) {
+ IdRegion.Add(newRegions, dtor.tok, dtor, null, "destructor", true, module);
+ }
+ }
+ }
+ } else if (d is ClassDecl) {
+ var cl = (ClassDecl)d;
+ foreach (var member in cl.Members) {
+ if (member is Function) {
+ var f = (Function)member;
+ foreach (var p in f.Formals) {
+ IdRegion.Add(newRegions, 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));
+ if (f.Body != null) {
+ ExprRegions(f.Body, newRegions, module);
+ }
+ } else if (member is Method) {
+ var m = (Method)member;
+ foreach (var p in m.Ins) {
+ IdRegion.Add(newRegions, p.tok, p, true, module);
+ }
+ foreach (var p in m.Outs) {
+ IdRegion.Add(newRegions, 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));
+ if (m.Body != null) {
+ StatementRegions(m.Body, newRegions, module);
+ }
+ } else if (member is Field) {
+ var fld = (Field)member;
+ IdRegion.Add(newRegions, fld.tok, fld, null, "field", true, module);
+ }
+ }
+ }
+ }
+ }
+ _snapshot = snapshot;
+ _regions = newRegions;
+ _program = program;
+ return true;
+ }
+
+ static void FrameExprRegions(FrameExpression fe, List<IdRegion> regions, bool descendIntoExpressions, ModuleDefinition module) {
+ Contract.Requires(fe != null);
+ Contract.Requires(regions != null);
+ if (descendIntoExpressions) {
+ ExprRegions(fe.E, regions, 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);
+ }
+ }
+
+ static void ExprRegions(Microsoft.Dafny.Expression expr, List<IdRegion> regions, ModuleDefinition module) {
+ Contract.Requires(expr != null);
+ Contract.Requires(regions != null);
+ if (expr is IdentifierExpr) {
+ var e = (IdentifierExpr)expr;
+ IdRegion.Add(regions, e.tok, e.Var, false, module);
+ } else if (expr is FieldSelectExpr) {
+ var e = (FieldSelectExpr)expr;
+ IdRegion.Add(regions, e.tok, e.Field, e.Type, "field", false, module);
+ } else if (expr is ComprehensionExpr) {
+ var e = (ComprehensionExpr)expr;
+ foreach (var bv in e.BoundVars) {
+ IdRegion.Add(regions, 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));
+ }
+ } 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));
+ return; // return here, so as to avoid doing the subexpressions below
+ }
+ foreach (var ee in expr.SubExpressions) {
+ ExprRegions(ee, regions, module);
+ }
+ }
+
+ static void StatementRegions(Statement stmt, List<IdRegion> regions, ModuleDefinition module) {
+ Contract.Requires(stmt != null);
+ Contract.Requires(regions != 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 lhs in s.Lhss) {
+ IdRegion.Add(regions, lhs.Tok, lhs, true, module);
+ }
+ if (s.Update == null) {
+ // the VarDeclStmt has no associated assignment
+ } else if (s.Update is UpdateStmt) {
+ var upd = (UpdateStmt)s.Update;
+ foreach (var rhs in upd.Rhss) {
+ foreach (var ee in rhs.SubExpressions) {
+ ExprRegions(ee, regions, module);
+ }
+ }
+ } else {
+ var upd = (AssignSuchThatStmt)s.Update;
+ ExprRegions(upd.Expr, regions, module);
+ }
+ // we're done, so don't do the sub-statements/expressions again
+ return;
+ } else if (stmt is VarDecl) {
+ var s = (VarDecl)stmt;
+ IdRegion.Add(regions, s.Tok, s, true, module);
+ } else if (stmt is ParallelStmt) {
+ var s = (ParallelStmt)stmt;
+ s.BoundVars.ForEach(bv => IdRegion.Add(regions, 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));
+ }
+ } else if (stmt is LoopStmt) {
+ var s = (LoopStmt)stmt;
+ if (s.Mod.Expressions != null) {
+ s.Mod.Expressions.ForEach(fe => FrameExprRegions(fe, regions, false, module));
+ }
+ }
+ foreach (var ee in stmt.SubExpressions) {
+ ExprRegions(ee, regions, module);
+ }
+ foreach (var ss in stmt.SubStatements) {
+ StatementRegions(ss, regions, module);
+ }
+ }
+
+ class IdRegion
+ {
+ public readonly int Start;
+ public readonly int Length;
+ public readonly string HoverText;
+ public enum OccurrenceKind { Use, Definition, WildDefinition }
+ public readonly OccurrenceKind Kind;
+
+ 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) {
+ Contract.Requires(regions != null);
+ Contract.Requires(tok != null);
+ Contract.Requires(v != null);
+ if (SurfaceSyntaxToken(tok)) {
+ regions.Add(new IdRegion(tok, v, isDefinition, context));
+ }
+ }
+ public static void Add(List<IdRegion> regions, Bpl.IToken tok, Field decl, Microsoft.Dafny.Type showType, string kind, bool isDefinition, ModuleDefinition context) {
+ Contract.Requires(regions != null);
+ Contract.Requires(tok != null);
+ Contract.Requires(decl != null);
+ Contract.Requires(kind != null);
+ if (SurfaceSyntaxToken(tok)) {
+ regions.Add(new IdRegion(tok, decl, showType, kind, isDefinition, context));
+ }
+ }
+
+ private IdRegion(Bpl.IToken tok, IVariable v, bool isDefinition, ModuleDefinition context) {
+ Contract.Requires(tok != null);
+ Contract.Requires(v != null);
+ Start = tok.pos;
+ Length = v.DisplayName.Length;
+ string kind;
+ if (v is VarDecl) {
+ kind = "local variable";
+ } else if (v is BoundVar) {
+ kind = "bound variable";
+ } else {
+ var formal = (Formal)v;
+ kind = formal.InParam ? "in-parameter" : "out-parameter";
+ }
+ HoverText = string.Format("({2}{3}) {0}: {1}", v.DisplayName, v.Type.TypeName(context), v.IsGhost ? "ghost " : "", kind);
+ Kind = !isDefinition ? OccurrenceKind.Use : VarDecl.HasWildcardName(v) ? OccurrenceKind.WildDefinition : OccurrenceKind.Definition;
+ }
+ private IdRegion(Bpl.IToken tok, Field decl, Microsoft.Dafny.Type showType, string kind, bool isDefinition, ModuleDefinition context) {
+ Contract.Requires(tok != null);
+ Contract.Requires(decl != null);
+ Contract.Requires(kind != null);
+ if (showType == null) {
+ showType = decl.Type;
+ }
+ Start = tok.pos;
+ Length = decl.Name.Length;
+ HoverText = string.Format("({2}{3}) {0}: {1}", decl.FullNameInContext(context), showType.TypeName(context), decl.IsGhost ? "ghost " : "", kind);
+ Kind = !isDefinition ? OccurrenceKind.Use : OccurrenceKind.Definition;
+ }
+ }
+ }
+
+}
diff --git a/Source/DafnyExtension/OutliningTagger.cs b/Source/DafnyExtension/OutliningTagger.cs
new file mode 100644
index 00000000..a47cdba7
--- /dev/null
+++ b/Source/DafnyExtension/OutliningTagger.cs
@@ -0,0 +1,185 @@
+//***************************************************************************
+// Copyright © 2010 Microsoft Corporation. All Rights Reserved.
+// This code released under the terms of the
+// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
+//***************************************************************************
+using EnvDTE;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.ComponentModel.Composition;
+using System.Windows.Threading;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Classification;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Tagging;
+using Microsoft.VisualStudio.Text.Projection;
+using Microsoft.VisualStudio.Utilities;
+using System.Diagnostics.Contracts;
+using Bpl = Microsoft.Boogie;
+using Dafny = Microsoft.Dafny;
+
+namespace DafnyLanguage
+{
+ [Export(typeof(ITaggerProvider))]
+ [ContentType("dafny")]
+ [TagType(typeof(IOutliningRegionTag))]
+ internal sealed class OutliningTaggerProvider : ITaggerProvider
+ {
+ [Import]
+ internal IBufferTagAggregatorFactoryService AggregatorFactory = null;
+
+ public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag {
+ ITagAggregator<DafnyResolverTag> tagAggregator = AggregatorFactory.CreateTagAggregator<DafnyResolverTag>(buffer);
+ // create a single tagger for each buffer.
+ Func<ITagger<T>> sc = delegate() { return new OutliningTagger(buffer, tagAggregator) as ITagger<T>; };
+ return buffer.Properties.GetOrCreateSingletonProperty<ITagger<T>>(sc);
+ }
+ }
+
+ /// <summary>
+ /// Translate DafnyResolverTag's into IOutliningRegionTag's
+ /// </summary>
+ internal sealed class OutliningTagger : ITagger<IOutliningRegionTag>
+ {
+ ITextBuffer _buffer;
+ ITextSnapshot _snapshot; // the most recent snapshot of _buffer that we have been informed about
+ Dafny.Program _program; // the program parsed from _snapshot
+ List<ORegion> _regions = new List<ORegion>(); // the regions generated from _program
+ ITagAggregator<DafnyResolverTag> _aggregator;
+
+ internal OutliningTagger(ITextBuffer buffer, ITagAggregator<DafnyResolverTag> tagAggregator) {
+ _buffer = buffer;
+ _snapshot = _buffer.CurrentSnapshot;
+ _aggregator = tagAggregator;
+ _aggregator.TagsChanged += new EventHandler<TagsChangedEventArgs>(_aggregator_TagsChanged);
+ }
+
+ /// <summary>
+ /// Find the Error tokens in the set of all tokens and create an ErrorTag for each
+ /// </summary>
+ public IEnumerable<ITagSpan<IOutliningRegionTag>> GetTags(NormalizedSnapshotSpanCollection spans) {
+ if (spans.Count == 0) yield break;
+ // (A NormalizedSnapshotSpanCollection contains spans that all come from the same snapshot.)
+ // The spans are ordered by the .Start component, and the collection contains no adjacent or abutting spans.
+ // Hence, to find a span that includes all the ones in "spans", we only need to look at the .Start for the
+ // first spand and the .End of the last span:
+ var startPoint = spans[0].Start;
+ var endPoint = spans[spans.Count - 1].End;
+
+ // Note, (startPoint,endPoint) are points in the spans for which we're being asked to provide tags. We need to translate
+ // these back into the most recent snapshot that we've computed regions for, namely _snapshot.
+ var entire = new SnapshotSpan(startPoint, endPoint).TranslateTo(_snapshot, SpanTrackingMode.EdgeExclusive);
+ int start = entire.Start;
+ int end = entire.End;
+ if (start == end) yield break;
+
+ foreach (var r in _regions) {
+ 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));
+ }
+ }
+ }
+
+ // the Classifier tagger is translating buffer change events into TagsChanged events, so we don't have to
+ public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
+
+ void _aggregator_TagsChanged(object sender, TagsChangedEventArgs e) {
+ var r = sender as ResolverTagger;
+ if (r != null) {
+ ITextSnapshot snap;
+ Microsoft.Dafny.Program prog;
+ lock (this) {
+ snap = r._snapshot;
+ prog = r._program;
+ }
+ if (prog != null) {
+ if (!ComputeOutliningRegions(prog, snap))
+ return; // no new regions
+
+ var chng = TagsChanged;
+ if (chng != null) {
+ NormalizedSnapshotSpanCollection spans = e.Span.GetSpans(_buffer.CurrentSnapshot);
+ if (spans.Count > 0) {
+ SnapshotSpan span = new SnapshotSpan(spans[0].Start, spans[spans.Count - 1].End);
+ chng(this, new SnapshotSpanEventArgs(span));
+ }
+ }
+ }
+ }
+ }
+
+ bool ComputeOutliningRegions(Dafny.Program program, ITextSnapshot snapshot) {
+ Contract.Requires(snapshot != null);
+
+ if (program == _program)
+ return false; // no new regions
+
+ List<ORegion> newRegions = new List<ORegion>();
+
+ foreach (var module in program.Modules) {
+ if (!module.IsDefaultModule) {
+ newRegions.Add(new ORegion(module, "module"));
+ }
+ foreach (Dafny.TopLevelDecl d in module.TopLevelDecls) {
+ if (!HasBodyTokens(d) && !(d is Dafny.ClassDecl)) {
+ continue;
+ }
+ if (d is Dafny.ArbitraryTypeDecl) {
+ newRegions.Add(new ORegion(d, "type"));
+ } else if (d is Dafny.CoDatatypeDecl) {
+ newRegions.Add(new ORegion(d, "codatatype"));
+ } else if (d is Dafny.DatatypeDecl) {
+ newRegions.Add(new ORegion(d, "datatype"));
+ } else if (d is Dafny.ModuleDecl) {
+ // do nothing here, since the outer loop handles modules
+ } else {
+ Dafny.ClassDecl cl = (Dafny.ClassDecl)d;
+ if (!cl.IsDefaultClass) {
+ newRegions.Add(new ORegion(cl, "class"));
+ }
+ // do the class members (in particular, functions and methods)
+ foreach (Dafny.MemberDecl m in cl.Members) {
+ if (!HasBodyTokens(m)) {
+ continue;
+ }
+ if (m is Dafny.Function && ((Dafny.Function)m).Body != null) {
+ newRegions.Add(new ORegion(m, m is Dafny.CoPredicate ? "copredicate" : m is Dafny.Predicate ? "predicate" : "function"));
+ } else if (m is Dafny.Method && ((Dafny.Method)m).Body != null) {
+ newRegions.Add(new ORegion(m, m is Dafny.Constructor ? "constructor" : "method"));
+ }
+ }
+ }
+ }
+ }
+ _snapshot = snapshot;
+ _regions = newRegions;
+ _program = program;
+ return true;
+ }
+
+ bool HasBodyTokens(Dafny.Declaration decl) {
+ Contract.Requires(decl != null);
+ return decl.BodyStartTok != Bpl.Token.NoToken && decl.BodyEndTok != Bpl.Token.NoToken;
+ }
+
+ class ORegion
+ {
+ public readonly int Start;
+ public readonly int Length;
+ public readonly string HoverText;
+ public ORegion(Dafny.Declaration decl, string kind) {
+ int startPosition = decl.BodyStartTok.pos + 1; // skip the open-curly brace itself
+ int length = decl.BodyEndTok.pos - startPosition;
+ Start = startPosition;
+ Length = length;
+ HoverText = string.Format("body of {0} {1}", kind, decl.Name);
+ }
+ }
+ }
+}
diff --git a/Source/DafnyExtension/ProgressMargin.cs b/Source/DafnyExtension/ProgressMargin.cs
new file mode 100644
index 00000000..7fdf38a6
--- /dev/null
+++ b/Source/DafnyExtension/ProgressMargin.cs
@@ -0,0 +1,260 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Windows.Threading;
+using System.Windows;
+using System.Windows.Shapes;
+using System.Windows.Media;
+using System.Windows.Controls;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Formatting;
+using Microsoft.VisualStudio.Text.Tagging;
+using Microsoft.VisualStudio.Text.Classification;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Utilities;
+using System.Diagnostics.Contracts;
+using Dafny = Microsoft.Dafny;
+using Bpl = Microsoft.Boogie;
+
+
+namespace DafnyLanguage
+{
+ #region UI stuff
+ internal class ProgressMarginGlyphFactory : IGlyphFactory
+ {
+ public UIElement GenerateGlyph(IWpfTextViewLine line, IGlyphTag tag) {
+ var dtag = tag as ProgressGlyphTag;
+ if (dtag == null) {
+ return null;
+ }
+
+ System.Windows.Shapes.Rectangle sh = new Rectangle() {
+ Fill = dtag.Val == 0 ? Brushes.Violet : Brushes.DarkOrange,
+ Height = 18.0,
+ Width = 3.0
+ };
+ return sh;
+ }
+ }
+
+ [Export(typeof(IGlyphFactoryProvider))]
+ [Name("ProgressMarginGlyph")]
+ [Order(After = "TokenTagger")]
+ [ContentType("dafny")]
+ [TagType(typeof(ProgressGlyphTag))]
+ internal sealed class ProgressMarginGlyphFactoryProvider : IGlyphFactoryProvider
+ {
+ public IGlyphFactory GetGlyphFactory(IWpfTextView view, IWpfTextViewMargin margin) {
+ return new ProgressMarginGlyphFactory();
+ }
+ }
+
+ internal class ProgressGlyphTag : IGlyphTag
+ {
+ public readonly int Val;
+ public ProgressGlyphTag(int val) {
+ Val = val;
+ }
+ }
+ #endregion
+
+ [Export(typeof(ITaggerProvider))]
+ [ContentType("dafny")]
+ [TagType(typeof(ProgressGlyphTag))]
+ class ProgressTaggerProvider : ITaggerProvider
+ {
+ [Import]
+ internal IBufferTagAggregatorFactoryService AggregatorFactory = null;
+
+ [Import(typeof(Microsoft.VisualStudio.Shell.SVsServiceProvider))]
+ internal IServiceProvider _serviceProvider = null;
+
+ public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag {
+ ITagAggregator<DafnyResolverTag> tagAggregator = AggregatorFactory.CreateTagAggregator<DafnyResolverTag>(buffer);
+ // create a single tagger for each buffer.
+ Func<ITagger<T>> sc = delegate() { return new ProgressTagger(buffer, _serviceProvider, tagAggregator) as ITagger<T>; };
+ return buffer.Properties.GetOrCreateSingletonProperty<ITagger<T>>(sc);
+ }
+ }
+
+ internal class ProgressTagger : ITagger<ProgressGlyphTag>
+ {
+ ErrorListProvider _errorProvider;
+ ITextBuffer _buffer;
+
+ readonly DispatcherTimer timer;
+
+ public ProgressTagger(ITextBuffer buffer, IServiceProvider serviceProvider, ITagAggregator<DafnyResolverTag> tagAggregator) {
+ _buffer = buffer;
+ _errorProvider = new ErrorListProvider(serviceProvider);
+
+ timer = new DispatcherTimer(DispatcherPriority.ApplicationIdle);
+ timer.Interval = TimeSpan.FromMilliseconds(500);
+ timer.Tick += new EventHandler(UponIdle);
+
+ tagAggregator.TagsChanged += new EventHandler<TagsChangedEventArgs>(_aggregator_TagsChanged);
+ buffer.Changed += new EventHandler<TextContentChangedEventArgs>(buffer_Changed);
+ bufferChangesPostVerificationStart.Add(new SnapshotSpan(buffer.CurrentSnapshot, 0, buffer.CurrentSnapshot.Length));
+ }
+
+ public void Dispose() {
+ }
+
+ // The following fields and the contents of the following two lists are protected by the lock "this".
+ List<SnapshotSpan> bufferChangesPreVerificationStart = new List<SnapshotSpan>(); // buffer changes after the last completed verification and before the currently running verification
+ List<SnapshotSpan> bufferChangesPostVerificationStart = new List<SnapshotSpan>(); // buffer changes since the start of the currently running verification
+
+ void buffer_Changed(object sender, TextContentChangedEventArgs e) {
+ lock (this) {
+ foreach (var change in e.Changes) {
+ var startLine = e.After.GetLineFromPosition(change.NewPosition);
+ var endLine = e.After.GetLineFromPosition(change.NewEnd);
+ bufferChangesPostVerificationStart.Add(new SnapshotSpan(startLine.Start, endLine.End));
+ }
+ }
+ }
+
+ // The next field is protected by "this"
+ ResolverTagger resolver;
+ // Keep track of the most recent resolution results.
+ void _aggregator_TagsChanged(object sender, TagsChangedEventArgs e) {
+ var r = sender as ResolverTagger;
+ if (r != null) {
+ lock (this) {
+ resolver = r;
+ }
+ timer.Stop();
+ timer.Start();
+ }
+ }
+
+ bool verificationInProgress; // this field is protected by "this". Invariant: !verificationInProgress ==> bufferChangesPreVerificationStart.Count == 0
+ /// <summary>
+ /// This method is invoked when the user has been idle for a little while.
+ /// Note, "sender" and "args" are allowed to be passed in as null--they are not used by this method.
+ /// </summary>
+ public void UponIdle(object sender, EventArgs args) {
+ Dafny.Program prog;
+ ITextSnapshot snap;
+ ResolverTagger r;
+ lock (this) {
+ if (verificationInProgress) {
+ // This UponIdle message came at an inopportune time--we've already kicked off a verification.
+ // Just back off.
+ return;
+ }
+
+ if (resolver == null) return;
+ lock (resolver) {
+ prog = resolver._program;
+ snap = resolver._snapshot;
+ }
+ r = resolver;
+ resolver = null;
+ if (prog == null) 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;
+ }
+
+ // at this time, we're committed to running the verifier
+ verificationInProgress = true;
+
+ // Change orange progress markers into yellow ones
+ Contract.Assert(bufferChangesPreVerificationStart.Count == 0); // follows from monitor invariant
+ var empty = bufferChangesPreVerificationStart;
+ bufferChangesPreVerificationStart = bufferChangesPostVerificationStart;
+ bufferChangesPostVerificationStart = empty;
+ // Notify to-whom-it-may-concern about the changes we just made
+ var chng = TagsChanged;
+ if (chng != null) {
+ chng(this, new SnapshotSpanEventArgs(new SnapshotSpan(snap, 0, snap.Length)));
+ }
+
+ }
+ new Thread(() => VerificationWorker(prog, snap, r)).Start();
+ }
+
+ /// <summary>
+ /// Thread entry point.
+ /// </summary>
+ void VerificationWorker(Dafny.Program program, ITextSnapshot snapshot, ResolverTagger errorListHolder) {
+ Contract.Requires(program != null);
+ Contract.Requires(snapshot != null);
+ Contract.Requires(errorListHolder != null);
+
+ // Run the verifier
+ var newErrors = new List<DafnyError>();
+ try {
+ bool success = DafnyDriver.Verify(program, errorInfo => {
+ newErrors.Add(new DafnyError(errorInfo.Tok.line - 1, errorInfo.Tok.col - 1, ErrorCategory.VerificationError, errorInfo.Msg));
+ foreach (var aux in errorInfo.Aux) {
+ newErrors.Add(new DafnyError(aux.Tok.line - 1, aux.Tok.col - 1, ErrorCategory.AuxInformation, aux.Msg));
+ }
+ });
+ if (!success) {
+ newErrors.Add(new DafnyError(0, 0, ErrorCategory.InternalError, "verification process error"));
+ }
+ } catch (Exception e) {
+ newErrors.Add(new DafnyError(0, 0, ErrorCategory.InternalError, "verification process error: " + e.Message));
+ }
+ errorListHolder.PopulateErrorList(newErrors, true, snapshot);
+
+ lock (this) {
+ bufferChangesPreVerificationStart.Clear();
+ verificationInProgress = false;
+ }
+ // Notify to-whom-it-may-concern about the cleared pre-verification changes
+ var chng = TagsChanged;
+ if (chng != null) {
+ chng(this, new SnapshotSpanEventArgs(new SnapshotSpan(snapshot, 0, snapshot.Length)));
+ }
+
+ // If new changes took place since we started the verification, we may need to kick off another verification
+ // immediately.
+ UponIdle(null, null);
+ }
+
+ public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
+ IEnumerable<ITagSpan<ProgressGlyphTag>> ITagger<ProgressGlyphTag>.GetTags(NormalizedSnapshotSpanCollection spans) {
+ if (spans.Count == 0) yield break;
+ var targetSnapshot = spans[0].Snapshot;
+
+ List<SnapshotSpan> pre;
+ List<SnapshotSpan> post;
+ lock (this) {
+ pre = bufferChangesPreVerificationStart;
+ post = bufferChangesPostVerificationStart;
+ }
+
+ // If the requested snapshot isn't the same as the one our words are on, translate our spans to the expected snapshot
+ NormalizedSnapshotSpanCollection chs;
+ chs = new NormalizedSnapshotSpanCollection(Map(pre, span => span.TranslateTo(targetSnapshot, SpanTrackingMode.EdgeExclusive)));
+ foreach (SnapshotSpan span in NormalizedSnapshotSpanCollection.Overlap(spans, chs)) {
+ yield return new TagSpan<ProgressGlyphTag>(span, new ProgressGlyphTag(0));
+ }
+ chs = new NormalizedSnapshotSpanCollection(Map(post, span => span.TranslateTo(targetSnapshot, SpanTrackingMode.EdgeExclusive)));
+ foreach (SnapshotSpan span in NormalizedSnapshotSpanCollection.Overlap(spans, chs)) {
+ yield return new TagSpan<ProgressGlyphTag>(span, new ProgressGlyphTag(1));
+ }
+ }
+
+ /// <summary>
+ /// (Why the firetruck isn't an extension method like this already in the standard library?)
+ /// </summary>
+ public static IEnumerable<TOut> Map<TIn, TOut>(IEnumerable<TIn> coll, System.Func<TIn, TOut> fn) {
+ foreach (var e in coll) {
+ yield return fn(e);
+ }
+ }
+ }
+}
diff --git a/Source/DafnyExtension/Properties/AssemblyInfo.cs b/Source/DafnyExtension/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..b73b8410
--- /dev/null
+++ b/Source/DafnyExtension/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+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("EditorClassifier1")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("EditorClassifier1")]
+[assembly: AssemblyCopyright("")]
+[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)]
+
+// 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/DafnyExtension/ResolverTagger.cs b/Source/DafnyExtension/ResolverTagger.cs
new file mode 100644
index 00000000..d1af6878
--- /dev/null
+++ b/Source/DafnyExtension/ResolverTagger.cs
@@ -0,0 +1,321 @@
+//***************************************************************************
+// Copyright © 2010 Microsoft Corporation. All Rights Reserved.
+// This code released under the terms of the
+// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
+//***************************************************************************
+using EnvDTE;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.ComponentModel.Composition;
+using System.Windows.Threading;
+using Microsoft.VisualStudio;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Classification;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Tagging;
+using Microsoft.VisualStudio.Text.Projection;
+using Microsoft.VisualStudio.TextManager.Interop;
+using Microsoft.VisualStudio.Utilities;
+using System.Diagnostics.Contracts;
+using Dafny = Microsoft.Dafny;
+
+namespace DafnyLanguage
+{
+ [Export(typeof(ITaggerProvider))]
+ [ContentType("dafny")]
+ [TagType(typeof(DafnyResolverTag))]
+ internal sealed class ResolverTaggerProvider : ITaggerProvider
+ {
+ [Import(typeof(Microsoft.VisualStudio.Shell.SVsServiceProvider))]
+ internal IServiceProvider _serviceProvider = null;
+
+ [Import]
+ ITextDocumentFactoryService _textDocumentFactory = null;
+
+ public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag {
+ // create a single tagger for each buffer.
+ Func<ITagger<T>> sc = delegate() { return new ResolverTagger(buffer, _serviceProvider, _textDocumentFactory) as ITagger<T>; };
+ return buffer.Properties.GetOrCreateSingletonProperty<ITagger<T>>(sc);
+ }
+ }
+
+ public abstract class DafnyResolverTag : ITag
+ {
+ }
+ public class DafnyErrorResolverTag : DafnyResolverTag
+ {
+ public readonly string Typ;
+ public readonly string Msg;
+ public DafnyErrorResolverTag(string typ, string msg) {
+ Typ = typ;
+ Msg = msg;
+ }
+ }
+ public class DafnySuccessResolverTag : DafnyResolverTag
+ {
+ public readonly Dafny.Program Program;
+ public DafnySuccessResolverTag(Dafny.Program program) {
+ Program = program;
+ }
+ }
+
+ /// <summary>
+ /// Translate PkgDefTokenTags into ErrorTags and Error List items
+ /// </summary>
+ internal sealed class ResolverTagger : ITagger<DafnyResolverTag>, IDisposable
+ {
+ ITextBuffer _buffer;
+ ITextDocument _document;
+ // The _snapshot and _program fields should be updated and read together, so they are protected by "this"
+ public ITextSnapshot _snapshot; // may be null
+ public Dafny.Program _program; // non-null only if the snapshot contains a Dafny program that type checks
+ List<DafnyError> _resolutionErrors = new List<DafnyError>(); // if nonempty, then _snapshot is the snapshot from which the errors were produced
+ List<DafnyError> _verificationErrors = new List<DafnyError>();
+ ErrorListProvider _errorProvider;
+
+ internal ResolverTagger(ITextBuffer buffer, IServiceProvider serviceProvider, ITextDocumentFactoryService textDocumentFactory) {
+ _buffer = buffer;
+ if (!textDocumentFactory.TryGetTextDocument(_buffer, out _document))
+ _document = null;
+ _snapshot = null; // this makes sure the next snapshot will look different
+ _errorProvider = new ErrorListProvider(serviceProvider);
+
+ BufferIdleEventUtil.AddBufferIdleEventListener(_buffer, ResolveBuffer);
+ }
+
+ public void Dispose() {
+ if (_errorProvider != null) {
+ try {
+ _errorProvider.Tasks.Clear();
+ } catch (InvalidOperationException) {
+ // this may occur if the SVsServiceProvider somehow has been uninstalled before our Dispose method is called
+ }
+ _errorProvider.Dispose();
+ }
+ BufferIdleEventUtil.RemoveBufferIdleEventListener(_buffer, ResolveBuffer);
+ }
+
+ public IEnumerable<DafnyError> AllErrors() {
+ foreach (var err in _resolutionErrors) {
+ yield return err;
+ }
+ if (_resolutionErrors.Count != 0) {
+ // we're done
+ yield break;
+ }
+ foreach (var err in _verificationErrors) {
+ yield return err;
+ }
+ }
+
+ /// <summary>
+ /// Find the Error tokens in the set of all tokens and create an ErrorTag for each
+ /// </summary>
+ public IEnumerable<ITagSpan<DafnyResolverTag>> GetTags(NormalizedSnapshotSpanCollection spans) {
+ if (spans.Count == 0) yield break;
+ var currentSnapshot = spans[0].Snapshot;
+ foreach (var err in AllErrors()) {
+ if (err.Category != ErrorCategory.ProcessError) {
+ var span = err.Span().TranslateTo(currentSnapshot, SpanTrackingMode.EdgeExclusive);
+ string ty; // the COLORs below indicate what I see on my machine
+ switch (err.Category) {
+ default: // unexpected category
+ case ErrorCategory.ParseError:
+ case ErrorCategory.ParseWarning:
+ ty = "syntax error"; break; // COLOR: red
+ case ErrorCategory.ResolveError:
+ ty = "compiler error"; break; // COLOR: blue
+ case ErrorCategory.VerificationError:
+ ty = "error"; break; // COLOR: red
+ case ErrorCategory.AuxInformation:
+ ty = "other error"; break; // COLOR: purple red
+ case ErrorCategory.InternalError:
+ ty = "error"; break; // COLOR: red
+ }
+ yield return new TagSpan<DafnyResolverTag>(span, new DafnyErrorResolverTag(ty, err.Message));
+ }
+ }
+
+ ITextSnapshot snap;
+ Dafny.Program prog;
+ lock (this) {
+ snap = _snapshot;
+ prog = _program;
+ }
+ if (prog != null) {
+ yield return new TagSpan<DafnyResolverTag>(new SnapshotSpan(snap, 0, snap.Length), new DafnySuccessResolverTag(prog));
+ }
+ }
+
+ public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
+
+ /// <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) {
+ ITextSnapshot snapshot = _buffer.CurrentSnapshot;
+ if (snapshot == _snapshot)
+ return; // we've already done this snapshot
+ NormalizedSnapshotSpanCollection spans = new NormalizedSnapshotSpanCollection(new SnapshotSpan(snapshot, 0, snapshot.Length));
+
+ var driver = new DafnyDriver(snapshot.GetText(), _document != null ? _document.FilePath : "<program>");
+ List<DafnyError> newErrors;
+ Dafny.Program program;
+ try {
+ program = driver.ProcessResolution();
+ newErrors = driver.Errors;
+ } catch (Exception e) {
+ newErrors = new List<DafnyError>();
+ newErrors.Add(new DafnyError(0, 0, ErrorCategory.InternalError, "internal Dafny error: " + e.Message));
+ program = null;
+ }
+
+ lock (this) {
+ _snapshot = snapshot;
+ _program = program;
+ }
+ PopulateErrorList(newErrors, false, snapshot);
+ }
+
+ public void PopulateErrorList(List<DafnyError> newErrors, bool verificationErrors, ITextSnapshot snapshot) {
+ Contract.Requires(newErrors != null);
+ foreach (var err in newErrors) {
+ err.FillInSnapshot(snapshot);
+ }
+ if (verificationErrors) {
+ _verificationErrors = newErrors;
+ } else {
+ _resolutionErrors = newErrors;
+ }
+
+ _errorProvider.SuspendRefresh(); // reduce flickering
+ _errorProvider.Tasks.Clear();
+ foreach (var err in AllErrors()) {
+ ErrorTask task = new ErrorTask() {
+ Category = TaskCategory.BuildCompile,
+ ErrorCategory = CategoryConversion(err.Category),
+ Text = err.Message,
+ Line = err.Line,
+ Column = err.Column
+ };
+ 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.ResumeRefresh();
+ var chng = TagsChanged;
+ if (chng != null)
+ chng(this, new SnapshotSpanEventArgs(new SnapshotSpan(snapshot, 0, snapshot.Length)));
+ }
+
+ TaskErrorCategory CategoryConversion(ErrorCategory cat) {
+ switch (cat) {
+ case ErrorCategory.ParseError:
+ case ErrorCategory.ResolveError:
+ case ErrorCategory.VerificationError:
+ case ErrorCategory.InternalError:
+ return TaskErrorCategory.Error;
+ case ErrorCategory.ParseWarning:
+ return TaskErrorCategory.Warning;
+ case ErrorCategory.AuxInformation:
+ return TaskErrorCategory.Message;
+ default:
+ Contract.Assert(false); // unexpected category
+ return TaskErrorCategory.Error; // please compiler
+ }
+ }
+
+ void NavigateHandler(object sender, EventArgs arguments) {
+ var task = sender as ErrorTask;
+ if (task == null || task.Document == null)
+ return;
+
+ // This would have been the simple way of doing things:
+ // _errorProvider.Navigate(error, new Guid(EnvDTE.Constants.vsViewKindCode));
+ // Unfortunately, it doesn't work--it seems to ignore the column position. (Moreover, it wants 1-based
+ // line/column numbers, whereas the Error Task pane wants 0-based line/column numbers.)
+ // So, instead we do all the things that follow:
+
+ var openDoc = Package.GetGlobalService(typeof(IVsUIShellOpenDocument)) as IVsUIShellOpenDocument;
+ if (openDoc == null)
+ return;
+
+ IVsWindowFrame frame;
+ Microsoft.VisualStudio.OLE.Interop.IServiceProvider sp;
+ IVsUIHierarchy hier;
+ uint itemid;
+ Guid logicalView = VSConstants.LOGVIEWID_Code;
+ if (Microsoft.VisualStudio.ErrorHandler.Failed(openDoc.OpenDocumentViaProject(task.Document, ref logicalView, out sp, out hier, out itemid, out frame)) || frame == null)
+ return;
+
+ object docData;
+ Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocData, out docData));
+
+ // Get the VsTextBuffer
+ VsTextBuffer buffer = docData as VsTextBuffer;
+ if (buffer == null) {
+ IVsTextBufferProvider bufferProvider = docData as IVsTextBufferProvider;
+ if (bufferProvider != null) {
+ IVsTextLines lines;
+ Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(bufferProvider.GetTextBuffer(out lines));
+ buffer = lines as VsTextBuffer;
+ if (buffer == null)
+ return;
+ }
+ }
+
+ VsTextManager textManager = Package.GetGlobalService(typeof(VsTextManagerClass)) as VsTextManager;
+ if (textManager == null)
+ return;
+
+ // Finally, move the cursor
+ Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(textManager.NavigateToLineAndColumn(buffer, ref logicalView, task.Line, task.Column, task.Line, task.Column));
+ }
+
+ }
+
+ public enum ErrorCategory
+ {
+ ProcessError, ParseWarning, ParseError, ResolveError, VerificationError, AuxInformation, InternalError
+ }
+
+ internal class DafnyError
+ {
+ public readonly int Line; // 0 based
+ public readonly int Column; // 0 based
+ ITextSnapshot Snapshot; // filled in during the FillInSnapshot call
+ public readonly ErrorCategory Category;
+ public readonly string Message;
+ /// <summary>
+ /// "line" and "col" are expected to be 0-based
+ /// </summary>
+ public DafnyError(int line, int col, ErrorCategory cat, string msg) {
+ Contract.Requires(0 <= line);
+ Contract.Requires(0 <= col);
+ Line = line;
+ Column = col;
+ Category = cat;
+ Message = msg;
+ }
+
+ public void FillInSnapshot(ITextSnapshot snapshot) {
+ Contract.Requires(snapshot != null);
+ Snapshot = snapshot;
+ }
+ public SnapshotSpan Span() {
+ Contract.Requires(Snapshot != null); // requires that Snapshot has been filled in
+ var line = Snapshot.GetLineFromLineNumber(Line);
+ Contract.Assume(Column <= line.Length); // this is really a precondition of the constructor + FillInSnapshot
+ var length = Math.Min(line.Length - Column, 5);
+ return new SnapshotSpan(line.Start + Column, length);
+ }
+ }
+}
diff --git a/Source/DafnyExtension/TokenTagger.cs b/Source/DafnyExtension/TokenTagger.cs
new file mode 100644
index 00000000..2f295429
--- /dev/null
+++ b/Source/DafnyExtension/TokenTagger.cs
@@ -0,0 +1,342 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Classification;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Tagging;
+using Microsoft.VisualStudio.Utilities;
+using Dafny = Microsoft.Dafny;
+
+namespace DafnyLanguage
+{
+ [Export(typeof(ITaggerProvider))]
+ [ContentType("dafny")]
+ [TagType(typeof(DafnyTokenTag))]
+ internal sealed class DafnyTokenTagProvider : ITaggerProvider
+ {
+ public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag {
+ return new DafnyTokenTagger(buffer) as ITagger<T>;
+ }
+ }
+
+ public enum DafnyTokenKinds
+ {
+ Keyword, Number, String, Comment,
+ VariableIdentifier, VariableIdentifierDefinition
+ }
+
+ public class DafnyTokenTag : ITag
+ {
+ public DafnyTokenKinds Kind { get; private set; }
+ public string HoverText { get; private set; }
+
+ public DafnyTokenTag(DafnyTokenKinds kind) {
+ this.Kind = kind;
+ }
+
+ public DafnyTokenTag(DafnyTokenKinds kind, string hoverText) {
+ this.Kind = kind;
+ this.HoverText = hoverText;
+ }
+ }
+
+ internal sealed class DafnyTokenTagger : ITagger<DafnyTokenTag>
+ {
+ ITextBuffer _buffer;
+ ITextSnapshot _snapshot;
+ List<DRegion> _regions;
+
+ internal DafnyTokenTagger(ITextBuffer buffer) {
+ _buffer = buffer;
+ _snapshot = buffer.CurrentSnapshot;
+ _regions = Rescan(_snapshot);
+
+ _buffer.Changed += new EventHandler<TextContentChangedEventArgs>(ReparseFile);
+ }
+
+ public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
+
+ public IEnumerable<ITagSpan<DafnyTokenTag>> GetTags(NormalizedSnapshotSpanCollection spans) {
+ if (spans.Count == 0)
+ yield break;
+
+ List<DRegion> currentRegions = _regions;
+ ITextSnapshot currentSnapshot = _snapshot;
+
+ // create a new SnapshotSpan for the entire region encompassed by the span collection
+ SnapshotSpan entire = new SnapshotSpan(spans[0].Start, spans[spans.Count - 1].End).TranslateTo(currentSnapshot, SpanTrackingMode.EdgeExclusive);
+
+ // return tags for any regions that fall within that span
+ // BUGBUG: depending on how GetTags gets called (e.g., once for each line in the buffer), this may produce quadratic behavior
+ foreach (var region in currentRegions) {
+ if (entire.IntersectsWith(region.Span)) {
+ yield return new TagSpan<DafnyTokenTag>(new SnapshotSpan(region.Start, region.End), new DafnyTokenTag(region.Kind));
+ }
+ }
+ }
+
+ /// <summary>
+ /// Find all of the tag regions in the document (snapshot) and notify
+ /// listeners of any that changed
+ /// </summary>
+ void ReparseFile(object sender, TextContentChangedEventArgs args) {
+ ITextSnapshot snapshot = _buffer.CurrentSnapshot;
+ if (snapshot == _snapshot)
+ return; // we've already computed the regions for this snapshot
+
+ // get all of the outline regions in the snapshot
+ List<DRegion> 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;
+
+ var chng = TagsChanged;
+ if (chng != null) {
+ foreach (var span in difference) {
+ chng(this, new SnapshotSpanEventArgs(span));
+ }
+ }
+ }
+
+ NormalizedSnapshotSpanCollection SymmetricDifference(NormalizedSnapshotSpanCollection first, NormalizedSnapshotSpanCollection second) {
+ return NormalizedSnapshotSpanCollection.Union(
+ NormalizedSnapshotSpanCollection.Difference(first, second),
+ NormalizedSnapshotSpanCollection.Difference(second, first));
+ }
+
+ private static List<DRegion> Rescan(ITextSnapshot newSnapshot) {
+ List<DRegion> newRegions = new List<DRegion>();
+
+ bool stillScanningLongComment = false;
+ SnapshotPoint commentStart = new SnapshotPoint(); // used only when stillScanningLongComment
+ SnapshotPoint commentEndAsWeKnowIt = new SnapshotPoint(); // used only when stillScanningLongComment
+ 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
+
+ if (stillScanningLongComment) {
+ if (ScanForEndOfComment(txt, ref cur)) {
+ newRegions.Add(new DRegion(commentStart, new SnapshotPoint(newSnapshot, line.Start + cur), DafnyTokenKinds.Comment));
+ stillScanningLongComment = false;
+ } else {
+ commentEndAsWeKnowIt = new SnapshotPoint(newSnapshot, line.Start + cur);
+ }
+ }
+
+ // repeatedly get the remaining tokens from this line
+ int end; // offset into the current line
+ for (; ; cur = end) {
+ // advance to the first character of a keyword or token
+ DafnyTokenKinds ty = DafnyTokenKinds.Keyword;
+ for (; ; cur++) {
+ if (N <= cur) {
+ // we've looked at everything in this line
+ goto OUTER_CONTINUE;
+ }
+ char ch = txt[cur];
+ if ('a' <= ch && ch <= 'z') break;
+ if ('A' <= ch && ch <= 'Z') break;
+ if ('0' <= ch && ch <= '9') { ty = DafnyTokenKinds.Number; break; }
+ if (ch == '"') { ty = DafnyTokenKinds.String; break; }
+ if (ch == '/') { ty = DafnyTokenKinds.Comment; break; }
+ if (ch == '\'' || ch == '_' || ch == '?' || ch == '\\') break; // parts of identifiers
+ }
+
+ // advance to the end of the token
+ end = cur + 1; // offset into the current line
+ if (ty == DafnyTokenKinds.Number) {
+ // scan the rest of this number
+ for (; end < N; end++) {
+ char ch = txt[end];
+ if ('0' <= ch && ch <= '9') {
+ } else break;
+ }
+ } else if (ty == DafnyTokenKinds.String) {
+ // scan the rest of this string, but not past the end-of-line
+ for (; end < N; end++) {
+ char ch = txt[end];
+ if (ch == '"') {
+ end++; break;
+ } else if (ch == '\\') {
+ // escape sequence
+ end++;
+ if (end == N) { break; }
+ ch = txt[end];
+ if (ch == 'u') {
+ end += 4;
+ if (N <= end) { end = N; break; }
+ }
+ }
+ }
+ } else if (ty == DafnyTokenKinds.Comment) {
+ if (end == N) continue; // this was not the start of a comment
+ char ch = txt[end];
+ if (ch == '/') {
+ // a short comment
+ end = N;
+ } else if (ch == '*') {
+ // a long comment; find the matching "*/"
+ end++;
+ commentStart = new SnapshotPoint(newSnapshot, line.Start + cur);
+ if (ScanForEndOfComment(txt, ref end)) {
+ newRegions.Add(new DRegion(commentStart, new SnapshotPoint(newSnapshot, line.Start + end), DafnyTokenKinds.Comment));
+ } else {
+ stillScanningLongComment = true;
+ commentEndAsWeKnowIt = new SnapshotPoint(newSnapshot, line.Start + end);
+ }
+ continue;
+ } else {
+ // not a comment
+ continue;
+ }
+ } else {
+ int trailingDigits = 0;
+ for (; end < N; end++) {
+ char ch = txt[end];
+ if ('a' <= ch && ch <= 'z') {
+ trailingDigits = 0;
+ } else if ('A' <= ch && ch <= 'Z') {
+ trailingDigits = 0;
+ } else if ('0' <= ch && ch <= '9') {
+ trailingDigits++;
+ } else if (ch == '\'' || ch == '_' || ch == '?' || ch == '\\') {
+ trailingDigits = 0;
+ } else break;
+ }
+ // we have a keyword or an identifier
+ string s = txt.Substring(cur, end - cur);
+ if (0 < trailingDigits && s.Length == 5 + trailingDigits && s.StartsWith("array") && s[5] != '0' && (trailingDigits != 1 || s[5] != '1')) {
+ // this is a keyword (array2, array3, ...)
+ } else {
+ switch (s) {
+ #region keywords
+ case "allocated":
+ case "array":
+ case "as":
+ case "assert":
+ case "assume":
+ case "bool":
+ case "break":
+ case "calc":
+ case "case":
+ case "choose":
+ case "class":
+ case "codatatype":
+ case "constructor":
+ case "copredicate":
+ case "datatype":
+ case "decreases":
+ case "else":
+ case "ensures":
+ case "exists":
+ case "false":
+ case "forall":
+ case "free":
+ case "fresh":
+ case "function":
+ case "ghost":
+ case "if":
+ case "import":
+ case "in":
+ case "int":
+ case "invariant":
+ case "iterator":
+ case "label":
+ case "match":
+ case "method":
+ case "modifies":
+ case "module":
+ case "multiset":
+ case "nat":
+ case "new":
+ case "null":
+ case "object":
+ case "old":
+ case "opened":
+ case "parallel":
+ case "predicate":
+ case "print":
+ case "reads":
+ case "refines":
+ case "requires":
+ case "result":
+ case "return":
+ case "returns":
+ case "seq":
+ case "set":
+ case "static":
+ case "then":
+ case "this":
+ case "true":
+ case "type":
+ case "var":
+ case "while":
+ case "yield":
+ case "yields":
+ #endregion
+ break;
+ default:
+ continue; // it was an identifier
+ }
+ }
+ }
+
+ newRegions.Add(new DRegion(new SnapshotPoint(newSnapshot, line.Start + cur), new SnapshotPoint(newSnapshot, line.Start + end), ty));
+ }
+ OUTER_CONTINUE: ;
+ }
+
+ if (stillScanningLongComment) {
+ newRegions.Add(new DRegion(commentStart, commentEndAsWeKnowIt, DafnyTokenKinds.Comment));
+ }
+
+ return newRegions;
+ }
+
+ private static bool ScanForEndOfComment(string txt, ref int end) {
+ int N = txt.Length;
+ for (; end < N; end++) {
+ char ch = txt[end];
+ if (ch == '*' && end + 1 < N) {
+ ch = txt[end + 1];
+ if (ch == '/') {
+ end += 2;
+ return true;
+ }
+ }
+ }
+ return false; // hit end-of-line without finding end-of-comment
+ }
+ }
+
+ internal class DRegion
+ {
+ public SnapshotPoint Start { get; private set; }
+ public SnapshotPoint End { get; private set; }
+ public SnapshotSpan Span {
+ get { return new SnapshotSpan(Start, End); }
+ }
+ public DafnyTokenKinds Kind { get; private set; }
+
+ public DRegion(SnapshotPoint start, SnapshotPoint end, DafnyTokenKinds kind) {
+ Start = start;
+ End = end;
+ Kind = kind;
+ }
+ }
+}
diff --git a/Source/DafnyExtension/WordHighlighter.cs b/Source/DafnyExtension/WordHighlighter.cs
new file mode 100644
index 00000000..03456c85
--- /dev/null
+++ b/Source/DafnyExtension/WordHighlighter.cs
@@ -0,0 +1,211 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Linq;
+using System.Threading;
+using System.Windows.Media;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Classification;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Operations;
+using Microsoft.VisualStudio.Text.Tagging;
+using Microsoft.VisualStudio.Utilities;
+
+namespace DafnyLanguage
+{
+#if LATER_MAYBE
+ #region // (the current annoying) word highligher
+ internal class HighlightWordTagger : ITagger<HighlightWordTag>
+ {
+ ITextView View { get; set; }
+ ITextBuffer SourceBuffer { get; set; }
+ ITextSearchService TextSearchService { get; set; }
+ ITextStructureNavigator TextStructureNavigator { get; set; }
+ NormalizedSnapshotSpanCollection WordSpans { get; set; }
+ SnapshotSpan? CurrentWord { get; set; }
+ SnapshotPoint RequestedPoint { get; set; }
+ object updateLock = new object();
+
+ public HighlightWordTagger(ITextView view, ITextBuffer sourceBuffer, ITextSearchService textSearchService,
+ ITextStructureNavigator textStructureNavigator) {
+ this.View = view;
+ this.SourceBuffer = sourceBuffer;
+ this.TextSearchService = textSearchService;
+ this.TextStructureNavigator = textStructureNavigator;
+ this.WordSpans = new NormalizedSnapshotSpanCollection();
+ this.CurrentWord = null;
+ this.View.Caret.PositionChanged += CaretPositionChanged;
+ this.View.LayoutChanged += ViewLayoutChanged;
+ }
+
+ void ViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) {
+ // If a new snapshot wasn't generated, then skip this layout
+ if (e.NewSnapshot != e.OldSnapshot) {
+ UpdateAtCaretPosition(View.Caret.Position);
+ }
+ }
+
+ void CaretPositionChanged(object sender, CaretPositionChangedEventArgs e) {
+ UpdateAtCaretPosition(e.NewPosition);
+ }
+
+ public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
+
+ void UpdateAtCaretPosition(CaretPosition caretPosition) {
+ SnapshotPoint? point = caretPosition.Point.GetPoint(SourceBuffer, caretPosition.Affinity);
+
+ if (!point.HasValue)
+ return;
+
+ // If the new caret position is still within the current word (and on the same snapshot), we don't need to check it
+ if (CurrentWord.HasValue
+ && CurrentWord.Value.Snapshot == View.TextSnapshot
+ && CurrentWord.Value.Start <= point.Value && point.Value <= CurrentWord.Value.End) {
+ return;
+ }
+
+ RequestedPoint = point.Value;
+ UpdateWordAdornments();
+ }
+
+ void UpdateWordAdornments() {
+ SnapshotPoint currentRequest = RequestedPoint;
+ List<SnapshotSpan> wordSpans = new List<SnapshotSpan>();
+ //Find all words in the buffer like the one the caret is on
+ TextExtent word = TextStructureNavigator.GetExtentOfWord(currentRequest);
+ bool foundWord = true;
+ //If we've selected something not worth highlighting, we might have missed a "word" by a little bit
+ if (!WordExtentIsValid(currentRequest, word)) {
+ //Before we retry, make sure it is worthwhile
+ if (word.Span.Start != currentRequest
+ || currentRequest == currentRequest.GetContainingLine().Start
+ || char.IsWhiteSpace((currentRequest - 1).GetChar())) {
+ foundWord = false;
+ } else {
+ // Try again, one character previous.
+ //If the caret is at the end of a word, pick up the word.
+ word = TextStructureNavigator.GetExtentOfWord(currentRequest - 1);
+
+ //If the word still isn't valid, we're done
+ if (!WordExtentIsValid(currentRequest, word))
+ foundWord = false;
+ }
+ }
+
+ if (!foundWord) {
+ //If we couldn't find a word, clear out the existing markers
+ SynchronousUpdate(currentRequest, new NormalizedSnapshotSpanCollection(), null);
+ return;
+ }
+
+ SnapshotSpan currentWord = word.Span;
+ //If this is the current word, and the caret moved within a word, we're done.
+ if (CurrentWord.HasValue && currentWord == CurrentWord)
+ return;
+
+ //Find the new spans
+ FindData findData = new FindData(currentWord.GetText(), currentWord.Snapshot);
+ findData.FindOptions = FindOptions.WholeWord | FindOptions.MatchCase;
+
+ wordSpans.AddRange(TextSearchService.FindAll(findData));
+
+ //If another change hasn't happened, do a real update
+ if (currentRequest == RequestedPoint)
+ SynchronousUpdate(currentRequest, new NormalizedSnapshotSpanCollection(wordSpans), currentWord);
+ }
+
+ static bool WordExtentIsValid(SnapshotPoint currentRequest, TextExtent word) {
+ return word.IsSignificant
+ && currentRequest.Snapshot.GetText(word.Span).Any(c => char.IsLetter(c));
+ }
+
+ void SynchronousUpdate(SnapshotPoint currentRequest, NormalizedSnapshotSpanCollection newSpans, SnapshotSpan? newCurrentWord) {
+ lock (updateLock) {
+ if (currentRequest != RequestedPoint)
+ return;
+
+ WordSpans = newSpans;
+ CurrentWord = newCurrentWord;
+
+ var chngd = TagsChanged;
+ if (chngd != null)
+ chngd(this, new SnapshotSpanEventArgs(new SnapshotSpan(SourceBuffer.CurrentSnapshot, 0, SourceBuffer.CurrentSnapshot.Length)));
+ }
+ }
+
+ public IEnumerable<ITagSpan<HighlightWordTag>> GetTags(NormalizedSnapshotSpanCollection spans) {
+ if (CurrentWord == null)
+ yield break;
+
+ // Hold on to a "snapshot" of the word spans and current word, so that we maintain the same
+ // collection throughout
+ SnapshotSpan currentWord = CurrentWord.Value;
+ NormalizedSnapshotSpanCollection wordSpans = WordSpans;
+
+ if (spans.Count == 0 || WordSpans.Count == 0)
+ yield break;
+
+ // If the requested snapshot isn't the same as the one our words are on, translate our spans to the expected snapshot
+ if (spans[0].Snapshot != wordSpans[0].Snapshot) {
+ wordSpans = new NormalizedSnapshotSpanCollection(
+ wordSpans.Select(span => span.TranslateTo(spans[0].Snapshot, SpanTrackingMode.EdgeExclusive)));
+
+ currentWord = currentWord.TranslateTo(spans[0].Snapshot, SpanTrackingMode.EdgeExclusive);
+ }
+
+ // First, yield back the word the cursor is under (if it overlaps)
+ // Note that we'll yield back the same word again in the wordspans collection;
+ // the duplication here is expected.
+ if (spans.OverlapsWith(new NormalizedSnapshotSpanCollection(currentWord)))
+ yield return new TagSpan<HighlightWordTag>(currentWord, new HighlightWordTag());
+
+ // Second, yield all the other words in the file
+ foreach (SnapshotSpan span in NormalizedSnapshotSpanCollection.Overlap(spans, wordSpans)) {
+ yield return new TagSpan<HighlightWordTag>(span, new HighlightWordTag());
+ }
+ }
+ }
+
+ internal class HighlightWordTag : TextMarkerTag
+ {
+ public HighlightWordTag() : base("MarkerFormatDefinition/HighlightWordFormatDefinition") { }
+ }
+
+ [Export(typeof(EditorFormatDefinition))]
+ [Name("MarkerFormatDefinition/HighlightWordFormatDefinition")]
+ [UserVisible(true)]
+ internal class HighlightWordFormatDefinition : MarkerFormatDefinition
+ {
+ public HighlightWordFormatDefinition() {
+ this.BackgroundColor = Colors.LightBlue;
+ this.ForegroundColor = Colors.DarkBlue;
+ this.DisplayName = "Highlight Word";
+ this.ZOrder = 5;
+ }
+ }
+
+ [Export(typeof(IViewTaggerProvider))]
+ [ContentType("text")]
+ [TagType(typeof(TextMarkerTag))]
+ internal class HighlightWordTaggerProvider : IViewTaggerProvider
+ {
+ [Import]
+ internal ITextSearchService TextSearchService { get; set; }
+
+ [Import]
+ internal ITextStructureNavigatorSelectorService TextStructureNavigatorSelector { get; set; }
+
+ public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag {
+ //provide highlighting only on the top buffer
+ if (textView.TextBuffer != buffer)
+ return null;
+
+ ITextStructureNavigator textStructureNavigator =
+ TextStructureNavigatorSelector.GetTextStructureNavigator(buffer);
+
+ return new HighlightWordTagger(textView, buffer, TextSearchService, textStructureNavigator) as ITagger<T>;
+ }
+ }
+#endregion
+#endif
+}
diff --git a/Source/DafnyExtension/source.extension.vsixmanifest b/Source/DafnyExtension/source.extension.vsixmanifest
new file mode 100644
index 00000000..ef5c1cf5
--- /dev/null
+++ b/Source/DafnyExtension/source.extension.vsixmanifest
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Vsix xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">
+ <Identifier Id="DafnyLanguageMode.Microsoft.6c7ed99a-206a-4937-9e08-b389de175f68">
+ <Name>DafnyLanguageMode</Name>
+ <Author>Microsoft Research</Author>
+ <Version>1.0</Version>
+ <Description xml:space="preserve">This is a language mode for using the Dafny language inside Visual Studio.</Description>
+ <Locale>1033</Locale>
+ <SupportedProducts>
+ <VisualStudio Version="10.0">
+ <Edition>Pro</Edition>
+ </VisualStudio>
+ <VisualStudio Version="11.0">
+ <Edition>Pro</Edition>
+ </VisualStudio>
+ </SupportedProducts>
+ <SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.0" />
+ </Identifier>
+ <References />
+ <Content>
+ <MefComponent>|%CurrentProject%|</MefComponent>
+ <CustomExtension Type="Boogie">DafnyPrelude.bpl</CustomExtension>
+ <CustomExtension Type="SMTLib 2">UnivBackPred2.smt2</CustomExtension>
+ </Content>
+</Vsix>
diff --git a/Source/Jennisys.sln b/Source/Jennisys.sln
new file mode 100644
index 00000000..3f213e27
--- /dev/null
+++ b/Source/Jennisys.sln
@@ -0,0 +1,58 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Jennisys", "Jennisys\Jennisys.fsproj", "{F2FF4B3A-2FE8-474A-88DF-6950F7D78908}"
+ ProjectSection(ProjectDependencies) = postProject
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83} = {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "..\Source\Model\Model.csproj", "{ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Checked|Any CPU = Checked|Any CPU
+ Checked|Mixed Platforms = Checked|Mixed Platforms
+ Checked|x86 = Checked|x86
+ Debug|Any CPU = Debug|Any CPU
+ Debug|Mixed Platforms = Debug|Mixed Platforms
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|Mixed Platforms = Release|Mixed Platforms
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Checked|Any CPU.ActiveCfg = Release|x86
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Checked|Mixed Platforms.ActiveCfg = Release|x86
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Checked|Mixed Platforms.Build.0 = Release|x86
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Checked|x86.ActiveCfg = Release|x86
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Checked|x86.Build.0 = Release|x86
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Debug|Mixed Platforms.Build.0 = Debug|x86
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Debug|x86.ActiveCfg = Debug|x86
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Debug|x86.Build.0 = Debug|x86
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Release|Any CPU.ActiveCfg = Release|x86
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Release|Mixed Platforms.ActiveCfg = Release|x86
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Release|Mixed Platforms.Build.0 = Release|x86
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Release|x86.ActiveCfg = Release|x86
+ {F2FF4B3A-2FE8-474A-88DF-6950F7D78908}.Release|x86.Build.0 = Release|x86
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Checked|Any CPU.ActiveCfg = Checked|Any CPU
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Checked|Any CPU.Build.0 = Checked|Any CPU
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Checked|Mixed Platforms.ActiveCfg = Checked|Any CPU
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Checked|Mixed Platforms.Build.0 = Checked|Any CPU
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Checked|x86.ActiveCfg = Checked|Any CPU
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Release|Any CPU.Build.0 = Release|Any CPU
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}.Release|x86.ActiveCfg = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Source/Jennisys/Analyzer.fs b/Source/Jennisys/Analyzer.fs
new file mode 100644
index 00000000..db4887ed
--- /dev/null
+++ b/Source/Jennisys/Analyzer.fs
@@ -0,0 +1,953 @@
+module Analyzer
+
+open Ast
+open Getters
+open AstUtils
+open CodeGen
+open DafnyModelUtils
+open DafnyPrinter
+open FixpointSolver
+open MethodUnifier
+open Modularizer
+open Options
+open PipelineUtils
+open PrintUtils
+open Resolver
+open TypeChecker
+open Utils
+
+open Microsoft.Boogie
+
+let Rename suffix vars =
+ vars |> List.map (function Var(nm,tp,old) -> nm, Var(nm + suffix, tp, old))
+
+let ReplaceName substMap nm =
+ match Map.tryFind nm substMap with
+ | Some(Var(name,_,_)) -> name
+ | None -> nm
+
+let rec Substitute substMap = function
+ | IdLiteral(s) -> IdLiteral(ReplaceName substMap s)
+ | Dot(e,f) -> Dot(Substitute substMap e, ReplaceName substMap f)
+ | UnaryExpr(op,e) -> UnaryExpr(op, Substitute substMap e)
+ | BinaryExpr(n,op,e0,e1) -> BinaryExpr(n, op, Substitute substMap e0, Substitute substMap e1)
+ | SelectExpr(e0,e1) -> SelectExpr(Substitute substMap e0, Substitute substMap e1)
+ | UpdateExpr(e0,e1,e2) -> UpdateExpr(Substitute substMap e0, Substitute substMap e1, Substitute substMap e2)
+ | SequenceExpr(ee) -> SequenceExpr(List.map (Substitute substMap) ee)
+ | SeqLength(e) -> SeqLength(Substitute substMap e)
+ | ForallExpr(vv,e) -> ForallExpr(vv, Substitute substMap e)
+ | expr -> expr
+
+let GenMethodAnalysisCode comp m assertion genOld =
+ let methodName = GetMethodName m
+ let signature = GetMethodSig m
+ let ppre,ppost = GetMethodPrePost m
+ let pre = Desugar ppre
+ let post = Desugar ppost |> RewriteOldExpr
+ let ghostPre = GetMethodGhostPrecondition m |> Desugar
+ //let sigStr = PrintSig signature
+ let sigVarsDecl =
+ match signature with
+ | Sig(ins,outs) -> ins @ outs |> List.fold (fun acc vd -> acc + (sprintf " var %s;" (PrintVarDecl vd)) + newline) ""
+
+ " method " + methodName + "()" + newline +
+ " modifies this;" + newline +
+ " {" + newline +
+ // print signature as local variables
+ sigVarsDecl +
+ " // assume precondition" + newline +
+ " assume " + (PrintExpr 0 pre) + ";" + newline +
+ " // assume ghost precondition" + newline +
+ " assume " + (PrintExpr 0 ghostPre) + ";" + newline +
+ " // assume invariant and postcondition" + newline +
+ " assume Valid();" + newline +
+ (if genOld then " assume Valid_old();" + newline else "") +
+ " assume " + (PrintExpr 0 post) + ";" + newline +
+ " // assume user defined invariant again because assuming Valid() doesn't always work" + newline +
+ (GetInvariantsAsList comp |> PrintSep newline (fun e -> " assume " + (PrintExpr 0 e) + ";")) + newline +
+ // if the following assert fails, the model hints at what code to generate; if the verification succeeds, an implementation would be infeasible
+ " // assert false to search for a model satisfying the assumed constraints" + newline +
+ " assert " + (PrintExpr 0 assertion) + ";" + newline +
+ " }" + newline
+
+let rec MethodAnalysisPrinter onlyForThese assertion genOld comp =
+ let cname = GetComponentName comp
+ match onlyForThese with
+ | (c,m) :: rest when GetComponentName c = cname ->
+ match m with
+ | Method(_) ->
+ (GenMethodAnalysisCode c m assertion genOld) + newline +
+ (MethodAnalysisPrinter rest assertion genOld comp)
+ | _ -> ""
+ | _ :: rest -> MethodAnalysisPrinter rest assertion genOld comp
+ | [] -> ""
+
+// =========================================================================
+/// For a given constant "objRefName" (which is an object, something like
+/// "gensym32"), finds a path of field references from "this" (e.g. something
+/// like "this.next.next").
+///
+/// Implements a backtracking search over the heap entries to find that
+/// path. It starts from the given object, and follows the backpointers
+/// until it reaches the root ("this")
+// =========================================================================
+// let objRef2ExprCache = new System.Collections.Generic.Dictionary<string, Expr>()
+let GetObjRefExpr objRefName (heapInst: HeapInstance) =
+ let rec __GetObjRefExpr objRefName visited =
+ if Set.contains objRefName visited then
+ None
+ else
+ let newVisited = Set.add objRefName visited
+ match objRefName with
+ | "this" -> Some(ObjLiteral("this"))
+ | _ ->
+ let rec __fff lst =
+ match lst with
+ | ((o,var),_) :: rest ->
+ match __GetObjRefExpr o.name newVisited with
+ | Some(expr) -> Some(Dot(expr, GetExtVarName var))
+ | None -> __fff rest
+ | [] -> None
+ let backPointers = heapInst.concreteValues |> List.choose (function
+ FieldAssignment (x,l) ->
+ if l = ObjLiteral(objRefName) then Some(x,l) else None
+ |_ -> None)
+ __fff backPointers
+ (* --- function body starts here --- *)
+ __GetObjRefExpr objRefName (Set.empty)
+// THIS DOESN'T WORK BECAUSE THE CACHE HAS TO BE PURGED AFTER EVERY METHOD
+// if objRef2ExprCache.ContainsKey(objRefName) then
+// Some(objRef2ExprCache.[objRefName])
+// else
+// let res = __GetObjRefExpr objRefName (Set.empty)
+// match res with
+// | Some(e) -> objRef2ExprCache.Add(objRefName, e)
+// | None -> ()
+// res
+
+// =============================================================================
+/// Returns an expression that combines the post-condition of a given method with
+/// invariants for all objects present on the heap
+// =============================================================================
+let GetHeapExpr prog mthd heapInst includePreState =
+ // get expressions to evaluate:
+ // - add post (and pre?) conditions
+ // - go through all objects on the heap and assert their invariants
+ let pre,post = GetMethodPrePost mthd
+ let prepostExpr = post //TODO: do we need the "pre" here as well?
+ let heapObjs = heapInst.assignments |> List.fold (fun acc asgn ->
+ match asgn with
+ | FieldAssignment((o,_),_) -> acc |> Set.add o
+ | _ -> acc) Set.empty
+ heapObjs |> Set.fold (fun acc o ->
+ let receiverOpt = GetObjRefExpr o.name heapInst
+ let receiver = Utils.ExtractOption receiverOpt
+ let objComp = FindComponent prog (GetTypeShortName o.objType) |> Utils.ExtractOption
+ let objInvs = GetInvariantsAsList objComp
+ let objInvsUpdated = objInvs |> List.map (ChangeThisReceiver receiver)
+ let objInvFinal = objInvsUpdated |> List.fold BinaryAnd TrueLiteral
+ let objAllInvs =
+ if includePreState then
+ let objInvPre = MakeOld objInvFinal
+ BinaryAnd objInvFinal objInvPre
+ else
+ objInvFinal
+ BinaryAnd prepostExpr objAllInvs
+ ) prepostExpr
+
+let IsUnmodConcrOnly prog (comp,meth) expr =
+ let isConstr = IsModifiableObj (ThisObj comp) (comp,meth)
+ let rec __IsUnmodOnly args expr =
+ let __IsUnmodOnlyLst elist =
+ elist |> List.fold (fun acc e -> acc && (__IsUnmodOnly args e)) true
+ match expr with
+ | IntLiteral(_)
+ | BoolLiteral(_)
+ | BoxLiteral(_)
+ | Star
+ | VarDeclExpr(_)
+ | ObjLiteral(_) -> true
+ | VarLiteral(id) -> args |> List.exists (fun var -> GetExtVarName var = id)
+ | IdLiteral("null") | IdLiteral("this") -> true
+ | IdLiteral(id) ->
+ not (isConstr || IsAbstractField comp id)
+ | Dot(e, fldName) -> //if isConstr then false else __IsUnmodOnlyLst [e]
+ if isConstr then
+ false
+ else
+ // assume it is unmodifiable, because it is a method, so just check if it's concrete
+ let lhsType = InferType prog comp (MethodArgChecker prog meth) e |> Utils.ExtractOptionMsg (sprintf "Inference failed for %s" (PrintExpr 0 e))
+ IsConcreteField lhsType fldName
+ | AssertExpr(e)
+ | AssumeExpr(e)
+ | SeqLength(e)
+ | LCIntervalExpr(e)
+ | MethodOutSelect(e,_)
+ | OldExpr(e)
+ | UnaryExpr(_,e) -> __IsUnmodOnlyLst [e]
+ | SelectExpr(e1, e2)
+ | BinaryExpr(_,_,e1,e2) -> __IsUnmodOnlyLst [e1; e2]
+ | IteExpr(e3, e1, e2)
+ | UpdateExpr(e1, e2, e3) -> __IsUnmodOnlyLst [e1; e2; e3]
+ | SequenceExpr(exprs) | SetExpr(exprs) -> __IsUnmodOnlyLst exprs
+ | MethodCall(rcv,_,_,aparams) -> __IsUnmodOnlyLst (rcv :: aparams)
+ | ForallExpr(vars,e) -> __IsUnmodOnly (args @ vars) e
+ (* --- function body starts here --- *)
+ __IsUnmodOnly (GetMethodInArgs meth) expr
+
+let AddUnif indent e v unifMap =
+ let idt = Indent indent
+ let builder = new CascadingBuilder<_>(unifMap)
+ builder {
+ let! notAlreadyAdded = Map.tryFind e unifMap |> Utils.IsNoneOption |> Utils.BoolToOption
+ Logger.DebugLine (idt + " - adding unification " + (PrintExpr 0 e) + " <--> " + (PrintConst v))
+ return Map.add e v unifMap
+ }
+
+//TODO: unifications should probably by "Expr <--> Expr" instead of "Expr <--> Const"
+let rec GetUnifications prog indent (comp,meth) heapInst unifs expr =
+ let idt = Indent indent
+ // - first looks if the give expression talks only about method arguments (args)
+ // - then it tries to evaluate it to a constant
+ // - if all of these succeed, it adds a unification rule e <--> val(e) to the given unifMap map
+ let __AddUnif e unifsAcc =
+ if IsConstExpr e then
+ unifsAcc
+ else
+ let builder = new CascadingBuilder<_>(unifsAcc)
+ builder {
+ let! argsOnly = IsUnmodConcrOnly prog (comp,meth) e |> Utils.BoolToOption
+ let! v = try Some(EvalFull heapInst e |> Expr2Const) with ex -> None
+ return AddUnif indent e v unifsAcc
+ }
+ (* --- function body starts here --- *)
+ AstUtils.DescendExpr2 __AddUnif expr unifs
+
+// =======================================================
+/// Returns a map (Expr |--> Const) containing unifications
+/// found for the given method wrt to heapInst.
+///
+/// The list of potential unifications include:
+/// (1) arg-value pairs for all method arguments,
+/// (2) field-value pairs for all unmodifiable fields,
+/// (3) expr-value pairs where expr are unmodifiable
+/// expressions found in the spec.
+// =======================================================
+let GetUnificationsForMethod indent prog comp m heapInst =
+ let idt = Indent indent
+ let rec GetArgValueUnifications args =
+ match args with
+ | var :: rest ->
+ let name = GetExtVarName var
+ match Map.tryFind name heapInst.methodArgs with
+ | Some(c) ->
+ GetArgValueUnifications rest |> AddUnif indent (VarLiteral(name)) c
+ | None -> failwith ("couldn't find value for argument " + name)
+ | [] -> Map.empty
+ let rec GetFldValueUnifications unifs =
+ heapInst.assignments |> List.fold (fun acc asgn ->
+ match asgn with
+ | FieldAssignment((obj,var), fldVal) ->
+ try
+ let vname = GetExtVarName var
+ let comp = obj.objType |> FindComponentForType prog |> Utils.ExtractOption
+ if IsConcreteField comp vname then
+ let path = GetObjRefExpr obj.name heapInst |> Utils.ExtractOption
+ let c = Expr2Const fldVal
+ AddUnif indent (Dot(path, vname)) c acc
+ else
+ acc
+ with
+ | ex ->
+ Logger.WarnLine ("[WARN]: error during getting field value unifications: " + ex.Message)
+ acc
+ | _ -> acc
+ ) unifs
+
+ (* --- function body starts here --- *)
+ let unifs = GetArgValueUnifications (GetMethodInArgs m)
+ let unifs =
+ //TODO: it should really read the "modifies" clause and figure out modifiable fields from there
+ if not (IsConstructor m) then
+ GetFldValueUnifications unifs
+ else
+ unifs
+ GetUnifications prog indent (comp,m) heapInst unifs (GetMethodPrePost m |> fun x -> BinaryAnd (fst x) (snd x))
+
+// =======================================================
+/// Applies given unifications onto a given heapInstance
+///
+/// If "conservative" is true, applies only those that
+/// can be verified to hold, otherwise applies all of them
+// =======================================================
+let rec ApplyUnifications indent prog comp mthd unifs heapInst conservative =
+ let idt = Indent indent
+ ///
+ let __CheckUnif o f e idx =
+ if not conservative || not Options.CONFIG.checkUnifications then
+ true
+ else
+ let lhs = if o = NoObj then
+ VarLiteral(GetVarName f)
+ else
+ let objRefExpr = GetObjRefExpr o.name heapInst |> Utils.ExtractOptionMsg ("Couldn't find a path from 'this' to " + o.name)
+ let fldName = GetVarName f
+ Dot(objRefExpr, fldName)
+ let assertionExpr = match GetVarType f with
+ | Some(SeqType(_)) when not (idx = -1) -> BinaryEq (SelectExpr(lhs, IntLiteral(idx))) e
+ | Some(SetType(_)) when not (idx = -1) -> BinaryIn e lhs
+ | _ -> BinaryEq lhs e
+ // check if the assertion follows and if so update the env
+ let genOld = false
+ let code = PrintDafnyCodeSkeleton prog (MethodAnalysisPrinter [comp,mthd] assertionExpr genOld) true genOld
+ Logger.Debug (idt + " - checking assertion: " + (PrintExpr 0 assertionExpr) + " ... ")
+ let ok = CheckDafnyProgram code ("unif_" + (GetMethodFullName comp mthd))
+ if ok then
+ Logger.DebugLine " HOLDS"
+ else
+ Logger.DebugLine " DOESN'T HOLD"
+ ok
+ ///
+ let __Apply (o,f) c e value=
+ if value = Const2Expr c then
+ if __CheckUnif o f e -1 then
+ // change the value to expression
+ //Logger.TraceLine (sprintf "%s - applied: %s.%s --> %s" idt (PrintConst o) (GetVarName f) (PrintExpr 0 e) )
+ e
+ else
+ value
+ else
+ let rec __UnifyOverLst lst cnt =
+ match lst with
+ | lstElem :: rest when lstElem = Const2Expr c ->
+ if __CheckUnif o f e cnt then
+ //Logger.TraceLine (sprintf "%s - applied: %s.%s[%d] --> %s" idt (PrintConst o) (GetVarName f) cnt (PrintExpr 0 e) )
+ e :: __UnifyOverLst rest (cnt+1)
+ else
+ lstElem :: __UnifyOverLst rest (cnt+1)
+ | lstElem :: rest ->
+ lstElem :: __UnifyOverLst rest (cnt+1)
+ | [] -> []
+ // see if it's a list, then try to match its elements, otherwise leave it as is
+ match value with
+ | SequenceExpr(elist) ->
+ let newExprList = __UnifyOverLst elist 0
+ SequenceExpr(newExprList)
+ | SetExpr(elist) ->
+ let newExprList = __UnifyOverLst elist 0
+ SetExpr(newExprList)
+ | _ ->
+ value
+
+ (* --- function body starts here --- *)
+ match unifs with
+ | (e,c) :: rest ->
+ let heapInst = ApplyUnifications indent prog comp mthd rest heapInst conservative
+ let newHeap = heapInst.assignments|> List.fold (fun acc asgn ->
+ match asgn with
+ | FieldAssignment((o,f),value) when heapInst.modifiableObjs |> Set.contains o ->
+ let e2 = __Apply (o,f) c e value
+ acc @ [FieldAssignment((o,f),e2)]
+ | _ -> acc @ [asgn]
+ ) []
+ let newRetVals = heapInst.methodRetVals |> Map.fold (fun acc key value ->
+ let e2 = __Apply (NoObj,Var(key, None, false)) c e value
+ acc |> Map.add key e2
+ ) Map.empty
+ {heapInst with assignments = newHeap; methodRetVals = newRetVals}
+ | [] -> heapInst
+
+// ====================================================================================
+/// Returns whether the code synthesized for the given method can be verified with Dafny
+// ====================================================================================
+let VerifySolution prog solutions genRepr =
+ // print the solution to file and try to verify it with Dafny
+ //let prog = Program(solutions |> Utils.MapKeys |> Map.ofList |> Utils.MapKeys)
+ let code = PrintImplCode prog solutions genRepr false
+ CheckDafnyProgram code dafnyVerifySuffix
+
+let rec DiscoverAliasing exprList heapInst =
+ match exprList with
+ | e1 :: rest ->
+ let eqExpr = rest |> List.fold (fun acc e ->
+ if EvalFull heapInst (BinaryEq e1 e) = TrueLiteral then
+ BinaryAnd acc (BinaryEq e1 e)
+ else
+ acc
+ ) TrueLiteral
+ BinaryAnd eqExpr (DiscoverAliasing rest heapInst)
+ | [] -> TrueLiteral
+
+//
+let DontResolveUnmodifiableStuff prog comp meth expr =
+ let methodArgs = GetMethodInArgs meth
+ let __IsMethodArg argName = methodArgs |> List.exists (fun var -> GetExtVarName var = argName)
+ let isMod = IsModifiableObj (ThisObj comp) (comp,meth)
+ match expr with
+ | VarLiteral(id) when __IsMethodArg id -> false
+ | IdLiteral(id) when id = "this" || id = "null" -> true
+ | IdLiteral(id) | Dot(_, id) ->
+ // this must be a field, so resolve it only if modifiable
+ isMod
+ | _ -> true
+
+/// Descends down a given expression and returns bunch of sub-expressions that all evaluate to true
+let FindClauses trueOnly resolverFunc heapInst expr =
+ let MyFun expr acc =
+ try
+ match expr with
+ // skip binary logical operators because we want to find smallest sub-expressions
+ | BinaryExpr(_,op,_,_) when IsLogicalOp op -> acc
+ | _ ->
+ let exprEval = Eval heapInst resolverFunc expr
+ match exprEval with
+ | _ when exprEval = TrueLiteral -> acc
+ | _ ->
+ let exprAllResolved = EvalFull heapInst expr
+ match exprAllResolved with
+ | BoolLiteral(true) -> acc @ (exprEval |> SplitIntoConjunts)
+ | BoolLiteral(false) -> acc //if trueOnly then acc else acc @ (UnaryNot exprEval |> SplitIntoConjunts)
+ | _ -> acc
+ with
+ | _ -> acc
+ (* --- function body starts here --- *)
+ DescendExpr2 MyFun expr []
+
+/// Descends down a given expression and returns all sub-expressions that evaluate to TrueLiteral
+let FindTrueClauses resolverFunc heapInst expr =
+ FindClauses true resolverFunc heapInst expr
+
+/// Returns a list of boolean expressions obtained by combining (in some way)
+/// the two given list of conditions conditions
+let GetAllPossibleConditions specConds argConds aliasingConds =
+ let __Conjoin lst = lst |> List.fold (fun acc e -> BinaryAnd acc e) TrueLiteral
+ let __Preproc lst = lst |> List.map SplitIntoConjunts |> List.concat |> Utils.ListDeduplicate
+
+ // 0. aliasing conditions
+ // 1. conjunction of spec conditions
+ // 2. individual arg conditions
+ // 3. conjunction of arg conditions
+ // 4. individual spec conditions
+ let aliasing = aliasingConds |> __Preproc
+ let specIndi = specConds |> __Preproc
+ let specConj = [__Conjoin specIndi]
+ let argsIndi = argConds |> __Preproc
+ let argsConj = [__Conjoin argsIndi]
+
+ let allConds = aliasing @ specConj @ argsIndi @ specIndi @ argsConj
+ allConds |> List.filter (fun e -> not (e = TrueLiteral))
+ |> Utils.ListDeduplicate
+
+// check whther a given solution (in the form of heapInst) verifies assuming a given guard
+let rec CheckGuard prog comp m candCond indent idt heapInst callGraph =
+ let rec __MinGuard guard idx m2 sol =
+ let conjs = SplitIntoConjunts guard
+ let len = List.length conjs
+ if idx >= 0 && idx < len && len > 1 then
+ let guard' = conjs |> Utils.ListRemoveIdx (len - idx - 1) |> List.fold BinaryAnd TrueLiteral
+ match CheckGuard prog comp m guard' indent idt heapInst callGraph with
+ | Some(x) -> x
+ | None -> __MinGuard guard (idx+1) m2 sol
+ else
+ guard, m2, sol
+
+ let m2 = AddPrecondition m candCond
+ let sol = MakeModular (indent+2) prog comp m2 candCond heapInst callGraph
+ Logger.Info (idt + " - verifying partial solution ... ")
+ let verified =
+ if Options.CONFIG.verifyPartialSolutions then
+ VerifySolution prog sol Options.CONFIG.genRepr
+ else
+ true
+ if verified then
+ if Options.CONFIG.verifyPartialSolutions then Logger.InfoLine "VERIFIED" else Logger.InfoLine "SKIPPED"
+ if Options.CONFIG.minimizeGuards then
+ Logger.InfoLine(idt + " - minimizing guard ... " + (PrintExpr 0 candCond))
+ Some(__MinGuard candCond 0 m2 sol)
+ else
+ Some(candCond,m2,sol)
+ else
+ Logger.InfoLine ("NOT VERIFIED")
+ None
+
+// iteratively tries to remove conjunts and check whether the solutions still verifies
+//let MinimizeGuard guard prog comp m heapInst callGraph indent =
+
+
+// ============================================================================
+/// Attempts to synthesize the initialization code for the given constructor "m"
+///
+/// Returns a (heap,env,ctx) tuple
+// ============================================================================
+let rec AnalyzeConstructor indent prog comp m callGraph =
+ let idt = Indent indent
+ let TryFindAndVerify m =
+ match TryFindExistingAndConvertToSolution indent comp m TrueLiteral callGraph with
+ | Some(sol) ->
+ if VerifySolution prog sol Options.CONFIG.genRepr then
+ Logger.InfoLine (idt + " ~~~ VERIFIED ~~~")
+ Some(sol)
+ else
+ Logger.InfoLine (idt + " !!! NOT VERIFIED !!!")
+ None
+ | None -> None
+
+ (* --- function body starts here --- *)
+ Logger.InfoLine (idt + "[*] Analyzing constructor")
+ Logger.InfoLine (idt + "------------------------------------------")
+ Logger.InfoLine (Printer.PrintMethodSignFull (indent + 4) comp m)
+ Logger.InfoLine (idt + "------------------------------------------")
+ match TryFindAndVerify m with
+ | Some(sol) -> sol
+ | None ->
+ let methodName = GetMethodName m
+ let pre,post = GetMethodPrePost m
+ // generate Dafny code for analysis first
+ let genOld = true
+ let code = PrintDafnyCodeSkeleton prog (MethodAnalysisPrinter [comp,m] FalseLiteral genOld) true genOld
+ Logger.Info (idt + " - searching for an instance ...")
+ let models = RunDafnyProgram code (dafnyScratchSuffix + "_" + (GetMethodFullName comp m))
+ if models.Count = 0 then
+ // no models means that the "assert false" was verified, which means that the spec is inconsistent
+ Logger.WarnLine (idt + " !!! SPEC IS INCONSISTENT !!!")
+ Map.empty
+ else
+ if models.Count > 1 then
+ Logger.WarnLine " FAILED "
+ failwith "internal error (more than one model for a single constructor analysis)"
+ Logger.InfoLine " OK "
+ let model = models.[0]
+ let hModel = ReadFieldValuesFromModel model prog comp m
+ let heapInst = ResolveModel hModel (comp,m)
+ let unifs = GetUnificationsForMethod indent prog comp m heapInst |> Map.toList
+ let heapInst = ApplyUnifications indent prog comp m unifs heapInst true
+
+ // split into method calls
+ let sol = MakeModular indent prog comp m TrueLiteral heapInst callGraph |> FixSolution comp m
+
+ if Options.CONFIG.verifySolutions then
+ Logger.InfoLine (idt + " - verifying synthesized solution ... ")
+ let verified = VerifySolution prog sol Options.CONFIG.genRepr
+ Logger.Info (idt + " ")
+ if verified then
+ Logger.InfoLine "~~~ VERIFIED ~~~"
+ sol
+ else
+ Logger.InfoLine "!!! NOT VERIFIED !!!"
+ if Options.CONFIG.inferConditionals then
+ TryRecursion (indent + 4) prog comp m unifs heapInst callGraph
+ else
+ sol
+ else
+ sol
+and TryRecursion indent prog comp m unifs heapInst callGraph =
+ let idt = Indent indent
+
+ /// checks whether an expression is ok, meaning
+ /// - only immediate concrete fields of the "this" object are used,
+ /// - no recursion on the same object with the same parameters
+ let __IsOk hInst expr =
+ let compName = GetComponentName comp
+ let methName = GetMethodName m
+ let myVisitor =
+ fun expr acc ->
+ if not acc then
+ false
+ else
+ match expr with
+ | Dot(discr, fldName) ->
+ let obj = EvalFull heapInst discr
+ match obj with
+ | ObjLiteral(id) when id = "this" ->
+ try
+ let fname = RenameFromOld fldName
+ IsConcreteField (InferType prog comp (MethodArgChecker prog m) discr |> Utils.ExtractOption) fname
+ with
+ | _ -> false
+ | ObjLiteral(id) -> false
+ | _ -> failwithf "Didn't expect the discriminator of a Dot to not be ObjLiteral"
+ | MethodCall(receiver, cn, mn, elst) when receiver = ThisLiteral && cn = compName && mn = methName ->
+ elst |> List.exists (function VarLiteral(_) -> false | _ -> true)
+ | _ -> true
+ DescendExpr2 myVisitor expr true
+
+ /// Finds all modifiable fields in a given hInst, and checks if an "ok"
+ /// expression exists for each one of them.
+ ///
+ /// Returns all possible combinations of "ok" solutions (these are not verified yet).
+ let __GetAllAssignments hInst premises =
+ let rec __IterVars vars =
+ match vars with
+ | lhs :: [] ->
+ let lhsOptions = premises |> Set.toList
+ |> List.choose (function
+ | BinaryExpr(_,"=",l,r) -> if l = lhs then Some(r) elif r = lhs then Some(l) else None
+ | _ -> None)
+ |> List.filter (__IsOk hInst)
+ |> List.map (fun e -> [lhs,e])
+ lhsOptions
+ | lhs :: rest ->
+ let lhsOptions = __IterVars [lhs]
+ if List.isEmpty lhsOptions then
+ List.empty
+ else
+ let restOptions = __IterVars rest
+ Utils.ListCombine (fun t1 t2 -> t1 @ t2) lhsOptions restOptions
+ | [] -> List.empty
+
+ let stmts = ConvertToStatements hInst true
+ let modVars = stmts |> List.choose (function
+ | Assign(lhs,_) -> Some(lhs)
+ | _ -> None)
+ __IterVars modVars
+
+ /// Print a given list of assignments
+ let rec __PrintSol indent s =
+ let idt = Indent indent
+ match s with
+ | (l,r) :: [] ->
+ sprintf "%s%s := %s" idt (PrintExpr 0 l) (PrintExpr 0 r)
+ | (l,r) :: rest ->
+ let str = __PrintSol indent [l,r]
+ str + newline + (__PrintSol indent rest)
+ | [] -> ""
+
+ /// Returns a given method's postcondition where
+ /// - all input variables are renamed so that their names start with "$" and
+ /// (so that the unifier know that it's ok to try to unify those variables)
+ /// - all output variables are rewritten as $this.<method_name>(<args>)["<out_var_name>"]
+ /// (so that it is clear that they are results of a method call)
+ let __GetMethodPostTemplate comp m =
+ let compName = GetComponentName comp
+ let methName = GetMethodName m
+ let ins = GetMethodInArgs m
+ let outs = GetMethodOutArgs m
+ let post = GetMethodPrePost m |> snd
+ post |> RewriteWithCtx (fun ctx e ->
+ match e with
+ | VarLiteral(id) when not (IsInVarList ctx id) ->
+ if IsInVarList outs id then
+ let mcall = MethodCall(ThisLiteral, compName, methName, ins |> List.map (function var -> VarLiteral("$" + (GetExtVarName var))))
+ let outSel = MethodOutSelect(mcall, id)
+ Some(outSel)
+ else
+ Some(VarLiteral("$" + id))
+ | _ -> None) []
+ |> ChangeThisReceiver (VarLiteral("$this"))
+
+ /// Merges ...
+ let __MergeSolutions hInst s =
+ let __FindRhs lhs = s |> List.choose (fun (l,r) -> if l = lhs then Some(r) else None) |> Utils.ListToOption
+ let rec __FixAssignments asgs =
+ match asgs with
+ | asg :: rest ->
+ let newAsg =
+ match asg with
+ | FieldAssignment((obj,var) as discr,valExpr) ->
+ let objPath = GetObjRefExpr obj.name hInst |> Utils.ExtractOption
+ let lhs = Dot(objPath, GetExtVarName var)
+ match __FindRhs lhs with
+ | Some(rhs) -> FieldAssignment(discr,rhs)
+ | None -> asg
+ | _ -> asg
+ newAsg :: (__FixAssignments rest)
+ | [] -> []
+ let rec __FixRetValues retVals =
+ match retVals with
+ | (varName,varExpr) :: rest ->
+ let lhs = VarLiteral(varName)
+ let newVarExpr =
+ match __FindRhs lhs with
+ | Some(rhs) -> rhs
+ | None -> varExpr
+ __FixRetValues rest |> Map.add varName newVarExpr
+ | [] -> Map.empty
+ if s = [] then
+ hInst
+ else
+ // fix assignments
+ let newAsgs = __FixAssignments hInst.assignments
+ // fix return values
+ let newRetVals = __FixRetValues (hInst.methodRetVals |> Map.toList)
+ {hInst with assignments = newAsgs;
+ methodRetVals = newRetVals}
+
+
+ /// For a given heap instance and a list of possible solutions, it iterates
+ /// trough all of them and returns whichever verifies first.
+ let rec __IterSolutions hInst premises wrongSol sList =
+ match sList with
+ | s :: rest ->
+ Logger.InfoLine (idt + "Candidate solution:")
+ Logger.InfoLine (__PrintSol (indent + 4) s)
+ let hInst' = __MergeSolutions hInst s
+ let sol = Utils.MapSingleton (comp,m) [TrueLiteral, hInst']
+ if not (hInst' = hInst) && VerifySolution prog sol Options.CONFIG.genRepr then
+ Logger.InfoLine (idt + " ~~~ VERIFIED ~~~")
+ sol
+ else
+ Logger.InfoLine (idt + " !!! NOT VERIFIED !!!")
+ match TryInferConditionals indent prog comp m unifs hInst' callGraph premises with
+ | Some(candCond,solThis) ->
+ let m' = AddPrecondition m (UnaryNot(candCond))
+ let solRest = AnalyzeConstructor (indent + 2) prog comp m' callGraph
+ MergeSolutions solThis solRest |> FixSolution comp m
+ | None ->
+ __IterSolutions hInst premises wrongSol rest
+ | [] -> wrongSol
+
+ (* --- function body starts here --- *)
+ let loggerFunc = fun e -> Logger.TraceLine (sprintf "%s --> %s" idt (PrintExpr 0 e))
+
+ //TODO
+ let expandOnlyModVarsFunc = fun e ->
+ true
+// let __CheckExpr l =
+// //TODO: FIX THIS!!!!!
+// match l with
+// | VarLiteral(vname) -> GetMethodOutArgs m |> List.exists (fun var -> GetVarName var = vname)
+// | IdLiteral(_) -> true
+// | Dot(_,_) -> true
+// | _ -> false
+// match e with
+// | BinaryExpr(_,"=",l,_) ->
+// //TODO: it should really check both lhs and rhs
+// __CheckExpr l
+// | BinaryExpr(_,op,l,_) when IsRelationalOp op ->
+// __CheckExpr l
+// | _ -> __CheckExpr e
+
+ let wrongSol = Utils.MapSingleton (comp,m) [TrueLiteral, heapInst]
+ let heapInst = ApplyUnifications indent prog comp m unifs heapInst false
+ let methodArgs = GetMethodInArgs m
+ let heapExpr = GetHeapExpr prog m heapInst true
+
+ //Logger.TraceLine (PrintExpr 0 heapExpr)
+
+ // find set of premises (don't resolve anything)
+ let premises = heapExpr |> FindClauses false (fun e -> false) heapInst
+
+ Logger.TraceLine (sprintf "%s Premises:" idt)
+ premises |> List.iter loggerFunc
+
+ // add only recursive call for now
+ let post = __GetMethodPostTemplate comp m
+
+ let premiseSet = premises |> Set.ofList |> Set.add post
+ let closedPremises = ComputeClosure heapInst expandOnlyModVarsFunc premiseSet
+
+ Logger.TraceLine (idt + "Closed premises with methods")
+ closedPremises |> Set.iter loggerFunc
+
+ let s = __GetAllAssignments heapInst closedPremises
+ if s = [] then
+ // have at least one empty sol so that the original heapInst is not missed
+ __IterSolutions heapInst closedPremises wrongSol [[]]
+ else
+ __IterSolutions heapInst closedPremises wrongSol s
+
+and TryInferConditionals indent prog comp m unifs heapInst callGraph premises =
+ let idt = Indent indent
+ let loggerFunc = fun e -> Logger.TraceLine (sprintf "%s --> %s" idt (PrintExpr 0 e))
+ let methodArgs = GetMethodInArgs m
+
+ /// Iterates through a given list of boolean conditions and checks
+ /// which one suffices. If it finds such a condition, it returns
+ /// the following three things:
+ /// - the condition itself
+ /// - the method with this condition added to its preconditions
+ /// - a solution
+ /// Otherwise returns None.
+ let rec __TryOutConditions heapInst candidateConditions =
+ let idt = Indent indent
+ match candidateConditions with
+ | [] ->
+ Logger.InfoLine (sprintf "%s - no more interesting pre-conditions" idt)
+ None
+ | candCond :: rest ->
+ Logger.InfoLine (sprintf "%s ________________________" idt)
+ Logger.InfoLine (sprintf "%s candidate pre-condition: %s" idt (PrintExpr 0 candCond))
+ Logger.InfoLine (sprintf "%s ------------------------" idt)
+ let idt = idt + " "
+ match CheckGuard prog comp m candCond indent idt heapInst callGraph with
+ | Some(guard, m2, sol) -> Some(guard, m2, sol)
+ | None -> __TryOutConditions heapInst rest
+
+ if IsSolution1stLevelOnly heapInst then
+ // try to find a non-recursive solution
+ Logger.InfoLine (idt + "Strengthening the pre-condition")
+ let expr = GetHeapExpr prog m heapInst false
+ let specConds1 = expr |> FindTrueClauses (DontResolveUnmodifiableStuff prog comp m) heapInst
+ let specConds2 = premises |> Set.toList
+
+ let isConstFunc = fun e -> try
+ EvalNone heapInst e |> Expr2Const |> ignore
+ true
+ with
+ | _ -> false
+ let unmodConcrFunc = IsUnmodConcrOnly prog (comp,m)
+ let is1stLevelFunc = __Is1stLevelExpr false heapInst
+
+ let specConds = (specConds1 @ specConds2)
+ |> List.map SimplifyExpr
+ |> List.filter (fun e -> is1stLevelFunc e && unmodConcrFunc e && not (isConstFunc e))
+
+ let aliasingCond = lazy(DiscoverAliasing (methodArgs |> List.map (function var -> VarLiteral(GetExtVarName var))) heapInst)
+ let argConds = heapInst.methodArgs |> Map.fold (fun acc name value -> acc @ [BinaryEq (VarLiteral(name)) (Const2Expr value)]) []
+ let allConds = GetAllPossibleConditions specConds argConds [aliasingCond.Force()]
+ allConds |> List.iter loggerFunc
+
+ match __TryOutConditions heapInst allConds with
+ | Some(candCond,m2,sol) ->
+ Logger.InfoLine (idt + " - guard found: " + (PrintExpr 0 candCond))
+ let solThis = match TryFindExistingAndConvertToSolution indent comp m2 candCond callGraph with
+ | Some(sol2) -> sol2
+ | None -> sol
+ let solThis = solThis |> FixSolution comp m
+ Some(candCond,solThis)
+ | None ->
+ Logger.InfoLine (idt + "!!! Giving up !!!")
+ None
+ else
+ // the solution is not immediate
+ None
+
+
+
+// ===========================================================
+/// Reads CONFIG.methodToSynth to return a list of methods
+/// that Jennisys should attempt to synthesize.
+// ===========================================================
+let GetMethodsToAnalyze prog =
+ let __ReadMethodsParam =
+ let mOpt = Options.CONFIG.methodToSynth;
+ if mOpt = "*" then
+ (* all *)
+ FilterMembers prog FilterMethodMembers
+ else
+ let allMethods,neg =
+ if mOpt.StartsWith("~") then
+ mOpt.Substring(1), true
+ else
+ mOpt, false
+ (* exact list *)
+ let methods = allMethods.Split([|','|])
+ let lst = methods |> Array.fold (fun acc m ->
+ let idx = m.LastIndexOf(".")
+ if idx = -1 || idx = m.Length - 1 then
+ raise (InvalidCmdLineArg("Invalid method full name: " + m))
+ let compName = m.Substring(0, idx)
+ let methName = m.Substring(idx + 1)
+ let c = FindComponent prog compName |> Utils.ExtractOptionMsg ("Cannot find component " + compName)
+ let mthd = FindMethod c methName |> Utils.ExtractOptionMsg ("Cannot find method " + methName + " in component " + compName)
+ (c,mthd) :: acc
+ ) []
+ if neg then
+ FilterMembers prog FilterMethodMembers |> List.filter (fun e -> not (Utils.ListContains e lst))
+ else
+ lst
+ (* --- function body starts here --- *)
+ let meths = __ReadMethodsParam
+ if Options.CONFIG.constructorsOnly then
+ meths |> List.filter (fun (c,m) -> IsConstructor m)
+ else
+ meths
+
+// ============================================================================
+/// Goes through a given list of methods of the given program and attempts to
+/// synthesize code for each one of them.
+///
+/// Returns a map from (component * method) |--> Expr * HeapInstance
+// ============================================================================
+let rec AnalyzeMethods prog members solutionsSoFar =
+ let __IsAlreadySolved c m solutionMap =
+ let existingKey = solutionMap |> Map.tryFindKey (fun (cc,mm) v -> CheckSameMethods (c,m) (cc,mm) && not (v = []))
+ match existingKey with
+ | Some(_) -> true
+ | None -> false
+
+ let rec __AnalyzeConstructorDeep prog mList solutionsSoFar =
+ let callGraph = GetCallGraph (solutionsSoFar |> Map.toList) Map.empty
+ match mList with
+ | (comp,mthd) :: rest ->
+ if not (__IsAlreadySolved comp mthd solutionsSoFar) then
+ let sol = AnalyzeConstructor 2 prog comp mthd callGraph
+ let unsolved = sol |> Map.filter (fun (c,m) lst -> lst = [] && not(__IsAlreadySolved c m solutionsSoFar)) |> Utils.MapKeys
+ let newSols = solutionsSoFar |> MergeSolutions sol
+ __AnalyzeConstructorDeep prog (rest@unsolved) newSols
+ else
+ __AnalyzeConstructorDeep prog rest solutionsSoFar
+ | [] -> solutionsSoFar
+
+ (* --- function body starts here --- *)
+ match members with
+ | (comp,m) :: rest ->
+ match m with
+ | Method(_,_,_,_,_) ->
+ let sol = __AnalyzeConstructorDeep prog [comp,m] solutionsSoFar
+ Logger.InfoLine ""
+ AnalyzeMethods prog rest sol
+ | _ -> AnalyzeMethods prog rest solutionsSoFar
+ | [] -> solutionsSoFar
+
+let Analyze prog filename =
+ let rec __AddMethodsFromProg methods solutions =
+ match methods with
+ | (c,m) :: rest ->
+ let exists = solutions |> Map.tryFindKey (fun (c1,m1) _ -> CheckSameMethods (c,m) (c1,m1))
+ match exists with
+ | Some(_) -> __AddMethodsFromProg rest solutions
+ | None -> __AddMethodsFromProg rest (solutions |> Map.add (c,m) [])
+ | [] -> solutions
+
+ /// Prints given solutions to a file
+ let __PrintSolution prog outFileName solutions =
+ use file = System.IO.File.CreateText(outFileName)
+ file.AutoFlush <- true
+ //let prog = Program(solutions |> Utils.MapKeys |> Map.ofList |> Utils.MapKeys)
+ // add all other methods (those for which we don't have synthesized solution) as well
+ let allMethods = FilterMembers prog FilterConstructorMembers
+ let extSolutions = solutions //__AddMethodsFromProg allMethods solutions
+ let synthCode = PrintImplCode prog extSolutions Options.CONFIG.genRepr false
+ fprintfn file "%s" synthCode
+
+ (* --- function body starts here --- *)
+ let solutions = AnalyzeMethods prog (GetMethodsToAnalyze prog) Map.empty
+ let progName = System.IO.Path.GetFileNameWithoutExtension(filename)
+ let outFlatSolFileName = dafnySynthFileNameTemplate.Replace("###", progName)
+ Logger.InfoLine "Printing synthesized code"
+ __PrintSolution prog outFlatSolFileName solutions
+ ()
+
+//let AnalyzeComponent_rustan c =
+// match c with
+// | Component(Class(name,typeParams,members), Model(_,_,cVars,frame,inv), code) ->
+// let aVars = Fields members
+// let aVars0 = Rename "0" aVars
+// let aVars1 = Rename "1" aVars
+// let allVars = List.concat [aVars; List.map (fun (a,b) -> b) aVars0; List.map (fun (a,b) -> b) aVars1; cVars]
+// let inv0 = Substitute (Map.ofList aVars0) inv
+// let inv1 = Substitute (Map.ofList aVars1) inv
+// // Now print it as a Dafny program
+// printf "class %s" name
+// match typeParams with
+// | [] -> ()
+// | _ -> printf "<%s>" (typeParams |> PrintSep ", " (fun tp -> tp))
+// printfn " {"
+// // the fields: original abstract fields plus two more copies thereof, plus and concrete fields
+// allVars |> List.iter (function Var(nm,None) -> printfn " var %s;" nm | Var(nm,Some(tp)) -> printfn " var %s: %s;" nm (PrintType tp))
+// // the method
+// printfn " method %s_checkInjective() {" name
+// printf " assume " ; (VarsAreDifferent aVars0 aVars1) ; printfn ";"
+// printfn " assume %s;" (PrintExpr 0 inv0)
+// printfn " assume %s;" (PrintExpr 0 inv1)
+// printfn " assert false;" // {:msg "Two abstract states map to the same concrete state"}
+// printfn " }"
+// // generate code
+// members |> List.iter (function
+// | Constructor(methodName,signature,pre,stmts) -> printf "%s" (GenerateCode methodName signature pre stmts inv false)
+// | Method(methodName,signature,pre,stmts) -> printf "%s" (GenerateCode methodName signature pre stmts inv true)
+// | _ -> ())
+// // the end of the class
+// printfn "}"
+// | _ -> assert false // unexpected case \ No newline at end of file
diff --git a/Source/Jennisys/Ast.fs b/Source/Jennisys/Ast.fs
new file mode 100644
index 00000000..d355023a
--- /dev/null
+++ b/Source/Jennisys/Ast.fs
@@ -0,0 +1,95 @@
+// ####################################################################
+/// The AST of a Jennisy program
+///
+/// author: Rustan Leino (leino@microsoft.com)
+/// author: Aleksandar Milicevic (t-alekm@microsoft.com)
+// ####################################################################
+
+namespace Ast
+
+open System
+open System.Numerics
+
+type Type =
+ | IntType
+ | BoolType
+ | SetType of Type (* type parameter *)
+ | SeqType of Type (* type parameter *)
+ | NamedType of string * string list (* type parameters *)
+ | InstantiatedType of string * Type list (* type parameters *)
+
+type VarDecl =
+ | Var of string * Type option * (* isOld *) bool
+
+(*
+ the difference between IdLiteral and VarLiteral is that the VarLiteral is more specific,
+ it always referes to a local variable (either method parameter or quantification variable)
+
+ ObjLiteral is a concrete object, so if two ObjLiterals have different names,
+ they are different objects (as opposed to IdLiterals and VarLiterals, which can alias).
+ *)
+type Expr =
+ | IntLiteral of int
+ | BoolLiteral of bool
+ | BoxLiteral of string
+ | VarLiteral of string
+ | IdLiteral of string
+ | ObjLiteral of string
+ | Star
+ | Dot of Expr * string
+ | UnaryExpr of string * Expr
+ | OldExpr of Expr
+ | LCIntervalExpr of Expr
+ | BinaryExpr of int * string * Expr * Expr
+ | IteExpr of (* cond *) Expr * (* thenExpr *) Expr * (* elseExpr *) Expr
+ | SelectExpr of Expr * Expr
+ | UpdateExpr of Expr * Expr * Expr
+ | SequenceExpr of Expr list
+ | SeqLength of Expr
+ | SetExpr of Expr list //TODO: maybe this should really be a set instead of a list
+ | ForallExpr of VarDecl list * Expr
+ | MethodCall of (* receiver *) Expr * (* component name *) string * (* method name *) string * (* actual parameters *) Expr list
+ | MethodOutSelect of (* method *) Expr * (* out param name *) string
+ | VarDeclExpr of (* var list *) VarDecl list * (* declareAlso *) bool
+ | AssertExpr of Expr
+ | AssumeExpr of Expr
+
+type Const =
+ | IntConst of int
+ | BoolConst of bool
+ | BoxConst of string
+ | SetConst of Set<Const>
+ | SeqConst of Const list
+ | NullConst
+ | NoneConst
+ | ThisConst of (* loc id *) string * Type option
+ | VarConst of string
+ | NewObj of (* loc id *) string * Type option
+ | Unresolved of (* loc id *) string
+
+type Stmt =
+ | Block of Stmt list
+ | ExprStmt of Expr
+ | Assign of Expr * Expr
+
+type Signature =
+ | Sig of (* ins *) VarDecl list * (* outs *) VarDecl list
+
+type Member =
+ | Field of VarDecl
+ | Method of (* name *) string * Signature * (* pre *) Expr * (* post *) Expr * (* isConstructor *) bool
+ | Invariant of Expr list
+
+type TopLevelDecl =
+ | Interface of string * string list * Member list
+ | DataModel of string * string list * VarDecl list * (* frame *) Expr list * (* invariant *) Expr
+ | Code of string * string list
+
+type SyntacticProgram =
+ | SProgram of TopLevelDecl list
+
+type Component =
+ | Component of (*interface*)TopLevelDecl * (*datamodel*)TopLevelDecl * (*code*)TopLevelDecl
+
+type Program =
+ | Program of Component list
diff --git a/Source/Jennisys/AstUtils.fs b/Source/Jennisys/AstUtils.fs
new file mode 100644
index 00000000..6aac59e2
--- /dev/null
+++ b/Source/Jennisys/AstUtils.fs
@@ -0,0 +1,1033 @@
+// ####################################################################
+/// Utility functions for manipulating AST elements
+///
+/// author: Aleksandar Milicevic (t-alekm@microsoft.com)
+// ####################################################################
+
+module AstUtils
+
+open Ast
+open Getters
+open Logger
+open Utils
+
+let ThisLiteral = ObjLiteral("this")
+let NullLiteral = ObjLiteral("null")
+
+let IsLogicalOp op = [ "&&"; "||"; "==>"; "<==>" ] |> Utils.ListContains op
+let IsRelationalOp op = [ "="; "!="; "<"; "<="; ">"; ">="; "in"; "!in" ] |> Utils.ListContains op
+
+let AreInverseOps op1 op2 = match op1, op2 with "<" , ">" | ">" , "<" | "<=", ">=" | ">=", "<=" -> true | _ -> false
+
+let DoesImplyOp op1 op2 =
+ match op1, op2 with
+ | "<" , "!=" | ">" , "!=" -> true
+ | "=" , ">=" | "=" , "<=" -> true
+ | ">" , ">=" | "<" , "<=" -> true
+ | _ -> false
+let IsCommutativeOp op = match op with "=" | "!=" -> true | _ -> false
+
+exception ExprConvFailed of string
+
+let Expr2Int e =
+ match e with
+ | IntLiteral(n) -> n
+ | _ -> raise (ExprConvFailed(sprintf "not an int but: %O" e))
+
+let Expr2Bool e =
+ match e with
+ | BoolLiteral(b) -> b
+ | _ -> raise (ExprConvFailed(sprintf "not a bool but: %O" e))
+
+let Expr2List e =
+ match e with
+ | SequenceExpr(elist) -> elist
+ | _ -> raise (ExprConvFailed(sprintf "not a Seq but: %O" e))
+
+let rec MyRewrite rewriterFunc rewriteRecurseFunc expr =
+ match expr with
+ | IntLiteral(_)
+ | BoolLiteral(_)
+ | BoxLiteral(_)
+ | Star
+ | VarLiteral(_)
+ | ObjLiteral(_)
+ | VarDeclExpr(_)
+ | IdLiteral(_) -> match rewriterFunc expr with
+ | Some(e) -> e
+ | None -> expr
+ | Dot(e, id) -> Dot(rewriteRecurseFunc e, id)
+ | ForallExpr(vars,e) -> ForallExpr(vars, rewriteRecurseFunc e)
+ | UnaryExpr(op,e) -> UnaryExpr(op, rewriteRecurseFunc e)
+ | OldExpr(e) -> OldExpr(rewriteRecurseFunc e)
+ | LCIntervalExpr(e) -> LCIntervalExpr(rewriteRecurseFunc e)
+ | SeqLength(e) -> SeqLength(rewriteRecurseFunc e)
+ | SelectExpr(e1, e2) -> SelectExpr(rewriteRecurseFunc e1, rewriteRecurseFunc e2)
+ | BinaryExpr(p,op,e1,e2) -> BinaryExpr(p, op, rewriteRecurseFunc e1, rewriteRecurseFunc e2)
+ | IteExpr(e1,e2,e3) -> IteExpr(rewriteRecurseFunc e1, rewriteRecurseFunc e2, rewriteRecurseFunc e3)
+ | UpdateExpr(e1,e2,e3) -> UpdateExpr(rewriteRecurseFunc e1, rewriteRecurseFunc e2, rewriteRecurseFunc e3)
+ | SequenceExpr(exs) -> SequenceExpr(exs |> List.map rewriteRecurseFunc)
+ | SetExpr(exs) -> SetExpr(exs |> List.map rewriteRecurseFunc)
+ | MethodCall(rcv,cname,mname,ins) -> MethodCall(rewriteRecurseFunc rcv, cname, mname, ins |> List.map rewriteRecurseFunc)
+ | MethodOutSelect(mth,name) -> MethodOutSelect(rewriteRecurseFunc mth, name)
+ | AssertExpr(e) -> AssertExpr(rewriteRecurseFunc e)
+ | AssumeExpr(e) -> AssumeExpr(rewriteRecurseFunc e)
+
+let rec Rewrite rewriterFunc expr =
+ let __RewriteOrRecurse e =
+ match rewriterFunc e with
+ | Some(ee) -> ee
+ | None -> Rewrite rewriterFunc e
+ MyRewrite rewriterFunc __RewriteOrRecurse expr
+
+// TODO: double check this!
+let rec RewriteBU rewriterFunc expr =
+ let rewriteRecurseFunc e =
+ RewriteBU rewriterFunc e
+// let e' = Rewrite rewriterFunc e
+// match rewriterFunc e' with
+// | Some(ee) -> ee
+// | None -> e'
+ let rewriteFunc e =
+ match rewriterFunc e with
+ | Some(ee) -> ee
+ | None -> e
+ let expr' =
+ match expr with
+ | IntLiteral(_)
+ | BoolLiteral(_)
+ | BoxLiteral(_)
+ | Star
+ | VarLiteral(_)
+ | ObjLiteral(_)
+ | VarDeclExpr(_)
+ | IdLiteral(_) -> expr
+ | Dot(e, id) -> Dot(rewriteRecurseFunc e, id)
+ | ForallExpr(vars,e) -> ForallExpr(vars, rewriteRecurseFunc e)
+ | UnaryExpr(op,e) -> UnaryExpr(op, rewriteRecurseFunc e)
+ | OldExpr(e) -> OldExpr(rewriteRecurseFunc e)
+ | LCIntervalExpr(e) -> LCIntervalExpr(rewriteRecurseFunc e)
+ | SeqLength(e) -> SeqLength(rewriteRecurseFunc e)
+ | SelectExpr(e1, e2) -> SelectExpr(rewriteRecurseFunc e1, rewriteRecurseFunc e2)
+ | BinaryExpr(p,op,e1,e2) -> BinaryExpr(p, op, rewriteRecurseFunc e1, rewriteRecurseFunc e2)
+ | IteExpr(e1,e2,e3) -> IteExpr(rewriteRecurseFunc e1, rewriteRecurseFunc e2, rewriteRecurseFunc e3)
+ | UpdateExpr(e1,e2,e3) -> UpdateExpr(rewriteRecurseFunc e1, rewriteRecurseFunc e2, rewriteRecurseFunc e3)
+ | SequenceExpr(exs) -> SequenceExpr(exs |> List.map rewriteRecurseFunc)
+ | SetExpr(exs) -> SetExpr(exs |> List.map rewriteRecurseFunc)
+ | MethodCall(rcv,cname,mname,ins) -> MethodCall(rewriteRecurseFunc rcv, cname, mname, ins |> List.map rewriteRecurseFunc)
+ | MethodOutSelect(mth,name) -> MethodOutSelect(rewriteRecurseFunc mth, name)
+ | AssertExpr(e) -> AssertExpr(rewriteRecurseFunc e)
+ | AssumeExpr(e) -> AssumeExpr(rewriteRecurseFunc e)
+ expr' |> rewriteFunc
+
+let rec RewriteWithCtx rewriterFunc ctx expr =
+ let __RewriteOrRecurse ctx e =
+ match rewriterFunc ctx e with
+ | Some(ee) -> ee
+ | None -> RewriteWithCtx rewriterFunc ctx e
+ match expr with
+ | IntLiteral(_)
+ | BoolLiteral(_)
+ | BoxLiteral(_)
+ | Star
+ | VarLiteral(_)
+ | ObjLiteral(_)
+ | VarDeclExpr(_)
+ | IdLiteral(_) -> match rewriterFunc ctx expr with
+ | Some(e) -> e
+ | None -> expr
+ | Dot(e, id) -> Dot(__RewriteOrRecurse ctx e, id)
+ | ForallExpr(vars,e) -> ForallExpr(vars, __RewriteOrRecurse (ctx @ vars) e)
+ | UnaryExpr(op,e) -> UnaryExpr(op, __RewriteOrRecurse ctx e)
+ | OldExpr(e) -> OldExpr(__RewriteOrRecurse ctx e)
+ | LCIntervalExpr(e) -> LCIntervalExpr(__RewriteOrRecurse ctx e)
+ | SeqLength(e) -> SeqLength(__RewriteOrRecurse ctx e)
+ | SelectExpr(e1, e2) -> SelectExpr(__RewriteOrRecurse ctx e1, __RewriteOrRecurse ctx e2)
+ | BinaryExpr(p,op,e1,e2) -> BinaryExpr(p, op, __RewriteOrRecurse ctx e1, __RewriteOrRecurse ctx e2)
+ | IteExpr(e1,e2,e3) -> IteExpr(__RewriteOrRecurse ctx e1, __RewriteOrRecurse ctx e2, __RewriteOrRecurse ctx e3)
+ | UpdateExpr(e1,e2,e3) -> UpdateExpr(__RewriteOrRecurse ctx e1, __RewriteOrRecurse ctx e2, __RewriteOrRecurse ctx e3)
+ | SequenceExpr(exs) -> SequenceExpr(exs |> List.map (__RewriteOrRecurse ctx))
+ | SetExpr(exs) -> SetExpr(exs |> List.map (__RewriteOrRecurse ctx))
+ | MethodCall(rcv,cname,mname,ins) -> MethodCall(__RewriteOrRecurse ctx rcv, cname, mname, ins |> List.map (__RewriteOrRecurse ctx))
+ | MethodOutSelect(mth,name) -> MethodOutSelect(__RewriteOrRecurse ctx mth, name)
+ | AssertExpr(e) -> AssertExpr(__RewriteOrRecurse ctx e)
+ | AssumeExpr(e) -> AssumeExpr(__RewriteOrRecurse ctx e)
+
+// ====================================================
+/// Substitutes all occurences of all IdLiterals having
+/// the same name as one of the variables in "vars" with
+/// VarLiterals, in "expr".
+// ====================================================
+let RewriteVars vars expr =
+ let __IdIsArg id = vars |> List.exists (fun var -> GetVarName var = id)
+ Rewrite (fun e ->
+ match e with
+ | IdLiteral(id) when __IdIsArg id -> Some(VarLiteral(id))
+ | _ -> None) expr
+
+// ================================================
+/// Substitutes all occurences of e1 with e2 in expr
+// ================================================
+let Substitute e1 e2 expr =
+ Rewrite (fun e ->
+ if e = e1 then
+ Some(e2)
+ else
+ None) expr
+
+// ================================================
+/// Distributes the negation operator over
+/// arithmetic relations
+// ================================================
+let rec DistributeNegation expr =
+ let __Neg op =
+ match op with
+ | "=" -> Some("!=")
+ | "!=" -> Some("=")
+ | "<" -> Some(">=")
+ | ">" -> Some("<=")
+ | ">=" -> Some("<")
+ | "<=" -> Some(">")
+ | _ -> None
+ Rewrite (fun e ->
+ match e with
+ | UnaryExpr("!", sub) ->
+ match sub with
+ | BinaryExpr(p,op,lhs,rhs) ->
+ match __Neg op with
+ | Some(op') -> Some(BinaryExpr(p, op', DistributeNegation lhs, DistributeNegation rhs))
+ | None -> None
+ | _ -> None
+ | _ -> None) expr
+
+let rec DescendExpr visitorFunc composeFunc leafVal expr =
+ let __Compose elist =
+ match elist with
+ | [] -> leafVal
+ | fs :: rest -> rest |> List.fold (fun acc e -> composeFunc (composeFunc acc (visitorFunc e)) (DescendExpr visitorFunc composeFunc leafVal e)) (visitorFunc fs)
+ match expr with
+ | IntLiteral(_)
+ | BoolLiteral(_)
+ | BoxLiteral(_)
+ | Star
+ | VarLiteral(_)
+ | ObjLiteral(_)
+ | VarDeclExpr(_)
+ | IdLiteral(_) -> leafVal
+ | AssertExpr(e)
+ | AssumeExpr(e)
+ | Dot(e, _)
+ | ForallExpr(_,e)
+ | LCIntervalExpr(e)
+ | OldExpr(e)
+ | UnaryExpr(_,e)
+ | MethodOutSelect(e,_)
+ | SeqLength(e) -> __Compose (e :: [])
+ | SelectExpr(e1, e2)
+ | BinaryExpr(_,_,e1,e2) -> __Compose (e1 :: e2 :: [])
+ | IteExpr(e1,e2,e3)
+ | UpdateExpr(e1,e2,e3) -> __Compose (e1 :: e2 :: e3 :: [])
+ | MethodCall(rcv,_,_,aparams) -> __Compose (rcv :: aparams)
+ | SequenceExpr(exs)
+ | SetExpr(exs) -> __Compose exs
+
+let rec DescendExpr2 visitorFunc expr acc =
+ let newAcc = acc |> visitorFunc expr
+ let __Pipe elist = elist |> List.fold (fun a e -> a |> DescendExpr2 visitorFunc e) newAcc
+ match expr with
+ | IntLiteral(_)
+ | BoolLiteral(_)
+ | BoxLiteral(_)
+ | Star
+ | VarLiteral(_)
+ | ObjLiteral(_)
+ | VarDeclExpr(_)
+ | IdLiteral(_) -> newAcc
+ | AssertExpr(e)
+ | AssumeExpr(e)
+ | Dot(e, _)
+ | ForallExpr(_,e)
+ | LCIntervalExpr(e)
+ | OldExpr(e)
+ | UnaryExpr(_,e)
+ | MethodOutSelect(e,_)
+ | SeqLength(e) -> __Pipe (e :: [])
+ | SelectExpr(e1, e2)
+ | BinaryExpr(_,_,e1,e2) -> __Pipe (e1 :: e2 :: [])
+ | IteExpr(e1,e2,e3)
+ | UpdateExpr(e1,e2,e3) -> __Pipe (e1 :: e2 :: e3 :: [])
+ | MethodCall(rcv,_,_,aparams) -> __Pipe (rcv :: aparams)
+ | SequenceExpr(exs)
+ | SetExpr(exs) -> __Pipe exs
+
+let rec DescendExpr2BU visitorFunc expr acc =
+ let __Pipe elist =
+ let newAcc = elist |> List.fold (fun a e -> a |> DescendExpr2 visitorFunc e) acc
+ newAcc |> visitorFunc expr
+ match expr with
+ | IntLiteral(_)
+ | BoolLiteral(_)
+ | BoxLiteral(_)
+ | Star
+ | VarLiteral(_)
+ | ObjLiteral(_)
+ | VarDeclExpr(_)
+ | IdLiteral(_) -> __Pipe []
+ | AssertExpr(e)
+ | AssumeExpr(e)
+ | Dot(e, _)
+ | ForallExpr(_,e)
+ | LCIntervalExpr(e)
+ | OldExpr(e)
+ | UnaryExpr(_,e)
+ | MethodOutSelect(e,_)
+ | SeqLength(e) -> __Pipe (e :: [])
+ | SelectExpr(e1, e2)
+ | BinaryExpr(_,_,e1,e2) -> __Pipe (e1 :: e2 :: [])
+ | IteExpr(e1,e2,e3)
+ | UpdateExpr(e1,e2,e3) -> __Pipe (e1 :: e2 :: e3 :: [])
+ | MethodCall(rcv,_,_,aparams) -> __Pipe (rcv :: aparams)
+ | SequenceExpr(exs)
+ | SetExpr(exs) -> __Pipe exs
+
+//TODO: if names in dafny models contain funky characters,
+// these gensym variables might not be valid identifiers
+let PrintGenSym (name: string) =
+ if name.StartsWith("gensym") then
+ name
+ else
+ let idx = name.LastIndexOf("!")
+ if idx <> -1 then
+ sprintf "gensym%s" (name.Substring(idx+1))
+ else
+ sprintf "gensym%s" name
+
+// =====================
+/// Returns TRUE literal
+// =====================
+let TrueLiteral = BoolLiteral(true)
+
+// =====================
+/// Returns FALSE literal
+// =====================
+let FalseLiteral = BoolLiteral(false)
+
+let UnaryNeg sub =
+ match sub with
+ | UnaryExpr("-", s) -> s
+ | _ -> UnaryExpr("-", sub)
+
+let UnaryNot sub =
+ match sub with
+ | UnaryExpr("!", s) -> s
+ | BoolLiteral(b) -> BoolLiteral(not b)
+ | BinaryExpr(p,"=",l,r) -> BinaryExpr(p,"!=",l,r)
+ | BinaryExpr(p,"!=",l,r) -> BinaryExpr(p,"=",l,r)
+ | BinaryExpr(p,"in",l,r) -> BinaryExpr(p,"!in",l,r)
+ | BinaryExpr(p,"!in=",l,r) -> BinaryExpr(p,"in",l,r)
+ | BinaryExpr(p,"<",l,r) -> BinaryExpr(p,">=",l,r)
+ | BinaryExpr(p,"<=",l,r) -> BinaryExpr(p,">",l,r)
+ | BinaryExpr(p,">",l,r) -> BinaryExpr(p,"<=",l,r)
+ | BinaryExpr(p,">=",l,r) -> BinaryExpr(p,"<",l,r)
+ | _ -> UnaryExpr("!", sub)
+
+// =======================================================================
+/// Returns a binary AND of the two given expressions with short-circuiting
+// =======================================================================
+let BinaryAnd (lhs: Expr) (rhs: Expr) =
+ match lhs, rhs with
+ | BoolLiteral(true), _ -> rhs
+ | BoolLiteral(false), _ -> FalseLiteral
+ | _, BoolLiteral(true) -> lhs
+ | _, BoolLiteral(false) -> FalseLiteral
+ | _, _ -> BinaryExpr(30, "&&", lhs, rhs)
+
+// =======================================================================
+/// Returns a binary OR of the two given expressions with short-circuiting
+// =======================================================================
+let BinaryOr (lhs: Expr) (rhs: Expr) =
+ match lhs, rhs with
+ | BoolLiteral(true), _ -> TrueLiteral
+ | BoolLiteral(false), _ -> rhs
+ | _, BoolLiteral(true) -> TrueLiteral
+ | _, BoolLiteral(false) -> lhs
+ | _, _ -> BinaryExpr(30, "||", lhs, rhs)
+
+// ===================================================================================
+/// Returns a binary IMPLIES of the two given expressions
+// ===================================================================================
+let BinaryImplies lhs rhs =
+ match lhs, rhs with
+ | BoolLiteral(false), _ -> TrueLiteral
+ | BoolLiteral(true), _ -> rhs
+ | _, BoolLiteral(true) -> lhs
+ | _, BoolLiteral(false) -> UnaryNot(lhs)
+ | _ -> BinaryExpr(20, "==>", lhs, rhs)
+
+// =======================================================
+/// Constructors for binary EQ/NEQ of two given expressions
+// =======================================================
+let BinaryNeq lhs rhs =
+ match lhs, rhs with
+ | BoolLiteral(true), x | x, BoolLiteral(true) -> UnaryNot x
+ | BoolLiteral(false), x | x, BoolLiteral(false) -> x
+ | _ -> BinaryExpr(40, "!=", lhs, rhs)
+
+let BinaryEq lhs rhs =
+ match lhs, rhs with
+ | BoolLiteral(true), x | x, BoolLiteral(true) -> x
+ | BoolLiteral(false), x | x, BoolLiteral(false) -> UnaryNot x
+ | _ when lhs = rhs -> TrueLiteral
+ | _ -> BinaryExpr(40, "=", lhs, rhs)
+
+// =======================================================
+/// Constructor for binary GETS
+// =======================================================
+let BinaryGets lhs rhs = Assign(lhs, rhs)
+
+let BinaryAdd lhs rhs = BinaryExpr(55, "+", lhs, rhs)
+let BinarySub lhs rhs = BinaryExpr(55, "-", lhs, rhs)
+
+// =======================================================
+/// Constructors for binary IN/!IN of two given expressions
+// =======================================================
+let BinaryIn lhs rhs =
+ match lhs, rhs with
+ | _, SequenceExpr(elist) | _, SetExpr(elist) when elist |> List.length = 0 -> FalseLiteral
+ | _, SequenceExpr(elist) | _, SetExpr(elist) when elist |> List.length = 1 -> BinaryEq lhs (elist.[0])
+ | _ -> BinaryExpr(40, "in", lhs, rhs)
+
+let BinaryNotIn lhs rhs =
+ match lhs, rhs with
+ | _, SequenceExpr(elist) | _, SetExpr(elist) when elist |> List.length = 0 -> TrueLiteral
+ | _, SequenceExpr(elist) | _, SetExpr(elist) when elist |> List.length = 1 -> BinaryNeq lhs (elist.[0])
+ | _ -> BinaryExpr(40, "!in", lhs, rhs)
+
+// ==========================================
+/// Splits "expr" into a list of its conjuncts
+// ==========================================
+let rec SplitIntoConjunts expr =
+ match expr with
+ | BoolLiteral(true) -> []
+ | BinaryExpr(_,"&&",e0,e1) -> List.concat [SplitIntoConjunts e0 ; SplitIntoConjunts e1]
+ | _ -> [expr]
+
+// ======================================
+/// Applies "f" to each conjunct of "expr"
+// ======================================
+let rec ForeachConjunct f expr =
+ SplitIntoConjunts expr |> List.fold (fun acc e -> acc + (f e)) ""
+
+// =======================================
+/// Converts a given constant to expression
+// =======================================
+let rec Const2Expr c =
+ match c with
+ | IntConst(n) -> IntLiteral(n)
+ | BoolConst(b) -> BoolLiteral(b)
+ | BoxConst(id) -> BoxLiteral(id)
+ | SeqConst(clist) ->
+ let expList = clist |> List.fold (fun acc c -> Const2Expr c :: acc) [] |> List.rev
+ SequenceExpr(expList)
+ | SetConst(cset) ->
+ let expSet = cset |> Set.fold (fun acc c -> Set.add (Const2Expr c) acc) Set.empty
+ SetExpr(Set.toList expSet)
+ | VarConst(id) -> VarLiteral(id)
+ | ThisConst(_,_) -> ObjLiteral("this")
+ | NewObj(name,_) -> ObjLiteral(PrintGenSym name)
+ | NullConst -> ObjLiteral("null")
+ | Unresolved(id) -> BoxLiteral(id) // failwithf "don't want to convert Unresolved(%s) to expr" name //
+ | _ -> failwithf "not implemented or not supported: %O" c
+
+let rec Expr2Const e =
+ match e with
+ | IntLiteral(n) -> IntConst(n)
+ | BoolLiteral(b) -> BoolConst(b)
+ | BoxLiteral(id) -> BoxConst(id)
+ | ObjLiteral("this") -> ThisConst("this",None)
+ | ObjLiteral("null") -> NullConst
+ | ObjLiteral(name) -> NewObj(name, None)
+ | IdLiteral(id) -> Unresolved(id)
+ | VarLiteral(id) -> VarConst(id)
+ | SequenceExpr(elist) -> SeqConst(elist |> List.map Expr2Const)
+ | SetExpr(elist) -> SetConst(elist |> List.map Expr2Const |> Set.ofList)
+ | _ -> failwithf "Not a constant: %O" e
+
+let rec Expr2ConstStrict e =
+ match e with
+ | IntLiteral(n) -> IntConst(n)
+ | BoolLiteral(b) -> BoolConst(b)
+ | BoxLiteral(id) -> BoxConst(id)
+ | ObjLiteral("this") -> ThisConst("this",None)
+ | ObjLiteral("null") -> NullConst
+ | ObjLiteral(name) -> NewObj(name, None)
+ | SequenceExpr(elist) -> SeqConst(elist |> List.map Expr2ConstStrict)
+ | SetExpr(elist) -> SetConst(elist |> List.map Expr2ConstStrict |> Set.ofList)
+ | _ -> failwithf "Not a constant: %O" e
+
+let TryExpr2Const e =
+ try
+ Some(Expr2Const e)
+ with
+ | ex -> None
+
+let IsConstExpr e =
+ try
+ Expr2Const e |> ignore
+ true
+ with
+ | _ -> false
+
+///////
+
+let GetMethodPre mthd =
+ match mthd with
+ | Method(_,_,pre,_,_) -> pre
+ | _ -> failwith ("not a method" + mthd.ToString())
+
+let GetMethodPrePost mthd =
+ let __FilterOutAssumes e = e |> SplitIntoConjunts |> List.filter (function AssumeExpr(_) -> false | _ -> true) |> List.fold BinaryAnd TrueLiteral
+ match mthd with
+ | Method(_,_,pre,post,_) -> __FilterOutAssumes pre,post
+ | _ -> failwith ("not a method: " + mthd.ToString())
+
+let GetMethodGhostPrecondition mthd =
+ match mthd with
+ | Method(_,_,pre,_,_) ->
+ pre |> SplitIntoConjunts |> List.choose (function AssumeExpr(e) -> Some(e) | _ -> None) |> List.fold BinaryAnd TrueLiteral
+ | _ -> failwith ("not a method: " + mthd.ToString())
+
+// ==============================================================
+/// Returns all invariants of a component as a list of expressions
+// ==============================================================
+let GetInvariantsAsList comp =
+ match comp with
+ | Component(Interface(_,_,members), DataModel(_,_,_,_,inv), _) ->
+ let clsInvs = members |> List.choose (function Invariant(exprList) -> Some(exprList) | _ -> None) |> List.concat
+ List.append (SplitIntoConjunts inv) clsInvs
+ | _ -> failwithf "unexpected kind of component: %O" comp
+
+/// Replaces all Old nodes with IdLiteral with name = "old_" + <name>
+let RewriteOldExpr expr =
+ expr |> RewriteBU (fun e -> match e with
+ | OldExpr(IdLiteral(name)) -> Some(IdLiteral(RenameToOld name))
+ | _ -> None)
+
+let MakeOldVar var =
+ match var with
+ | Var(name, ty, _) -> Var(name, ty, true)
+
+let MakeOldVars varLst =
+ varLst |> List.map MakeOldVar
+
+/// renames ALL variables to "old_"+<varname>
+let MakeOld expr =
+ expr |> RewriteBU (fun e -> match e with
+ | IdLiteral(name) when not (name="this") -> Some(IdLiteral(RenameToOld name))
+ | Dot(e, name) -> Some(Dot(e, RenameToOld name))
+ | _ -> None)
+
+let BringToPost expr =
+ expr |> RewriteBU (fun e -> match e with
+ | IdLiteral(name) -> Some(IdLiteral(RenameFromOld name))
+ | Dot(e, name) -> Some(Dot(e, RenameFromOld name))
+ | _ -> None)
+
+////////////////////////
+
+let AddReplaceMethod prog comp newMthd oldMethod =
+ match prog, comp with
+ | Program(clist), Component(Interface(cname, ctypeParams, members), model, code) ->
+ let newMembers =
+ match oldMethod with
+ | None -> members @ [newMthd]
+ | Some(m) -> Utils.ListReplace m newMthd members
+ let newCls = Interface(cname, ctypeParams, newMembers)
+ let newComp = Component(newCls, model, code)
+ let newProg = Program(Utils.ListReplace comp newComp clist)
+ newProg, newComp
+ | _ -> failwithf "Invalid component: %O" comp
+
+let UnwrapAssumes e = e |> SplitIntoConjunts |> List.map (function AssumeExpr(e) -> e | x -> x) |> List.fold BinaryAnd TrueLiteral
+
+let AddPrecondition m e =
+ match m with
+ | Method(mn, sgn, pre, post, cstr) -> Method(mn, sgn, BinaryAnd pre (AssumeExpr(e |> UnwrapAssumes)), post, cstr)
+ | _ -> failwithf "Not a method: %O" m
+
+let SetPrecondition m e =
+ match m with
+ | Method(mn, sgn, pre, post, cstr) -> Method(mn, sgn, AssumeExpr(e |> UnwrapAssumes), post, cstr)
+ | _ -> failwithf "Not a method: %O" m
+
+////////////////////
+
+exception EvalFailed of string
+exception DomainNotInferred
+
+let DefaultResolver e fldOpt =
+ match fldOpt with
+ | None -> e
+ | Some(fldName) -> Dot(e, fldName)
+
+let DefaultFallbackResolver resolverFunc e =
+ match resolverFunc e with
+ | Some(e') -> e'
+ | None -> e
+
+let __CheckEqual e1 e2 =
+ match e1, e2 with
+ | BoolLiteral(b1), BoolLiteral(b2) -> Some(b1 = b2)
+ | IntLiteral(n1), IntLiteral(n2) -> Some(n1 = n2)
+ | ObjLiteral(o1), ObjLiteral(o2) -> Some(o1 = o2)
+ | SetExpr(elist1), SetExpr(elist2) -> Some(Set.ofList elist1 = Set.ofList elist2)
+ | SequenceExpr(elist1), SequenceExpr(elist2) -> Some(elist1 = elist2)
+ | UnaryExpr("-", sub1), sub2
+ | sub1, UnaryExpr("-", sub2) when sub1 = sub2 -> Some(false)
+ | UnaryExpr("-", sub1), UnaryExpr("-", sub2) when sub1 = sub2 -> Some(true)
+ | UnaryExpr("!", sub1), sub2
+ | sub1, UnaryExpr("!", sub2) when sub1 = sub2 -> Some(false)
+ | UnaryExpr("!", sub1), UnaryExpr("-", sub2) when sub1 = sub2 -> Some(true)
+ | _ when e1 = e2 -> Some(true)
+ | _ -> None
+
+let EvalSym2 fullResolverFunc otherResolverFunc returnFunc ctx expr =
+ let rec __EvalSym resolverFunc returnFunc ctx expr =
+ let expr' =
+ match expr with
+ | IntLiteral(_) -> expr
+ | BoolLiteral(_) -> expr
+ | BoxLiteral(_) -> expr
+ | ObjLiteral(_) -> expr
+ | Star -> expr //TODO: can we do better?
+ | VarDeclExpr(_) -> expr
+ | AssertExpr(e) -> AssertExpr(__EvalSym resolverFunc returnFunc ctx e)
+ | AssumeExpr(e) -> AssumeExpr(__EvalSym resolverFunc returnFunc ctx e)
+ | VarLiteral(id) ->
+ try
+ let _,e = ctx |> List.find (fun (v,e) -> GetVarName v = id)
+ e
+ with
+ | ex -> resolverFunc expr None
+ | IdLiteral(_) -> resolverFunc expr None
+ | Dot(e, str) ->
+ let discr = __EvalSym resolverFunc returnFunc ctx e
+ resolverFunc discr (Some(str))
+ | SeqLength(e) ->
+ let e' = __EvalSym resolverFunc returnFunc ctx e
+ match e' with
+ | SequenceExpr(elist) -> IntLiteral(List.length elist)
+ | _ -> SeqLength(e')
+ | SequenceExpr(elist) ->
+ let elist' = elist |> List.map (__EvalSym resolverFunc returnFunc ctx) // List.fold (fun acc e -> (__EvalSym resolverFunc returnFunc ctx e) :: acc) [] |> List.rev
+ SequenceExpr(elist')
+ | SetExpr(elist) ->
+ let elist' = elist |> List.map (__EvalSym resolverFunc returnFunc ctx) //List.fold (fun acc e -> Set.add (__EvalSym resolverFunc returnFunc ctx e) acc) Set.empty
+ SetExpr(elist' |> Set.ofList |> Set.toList)
+ | MethodOutSelect(e,name) ->
+ MethodOutSelect(__EvalSym resolverFunc returnFunc ctx e, name)
+ | MethodCall(rcv,cname, mname,aparams) ->
+ let rcv' = __EvalSym resolverFunc returnFunc ctx rcv
+ let aparams' = aparams |> List.fold (fun acc e -> __EvalSym resolverFunc returnFunc ctx e :: acc) [] |> List.rev
+ MethodCall(rcv', cname, mname, aparams')
+ | LCIntervalExpr(_) -> expr
+ | SelectExpr(lst, idx) ->
+ let lst' = __EvalSym resolverFunc returnFunc ctx lst
+ let idx' = __EvalSym resolverFunc returnFunc ctx idx
+ match lst', idx' with
+ | SequenceExpr(elist), IntLiteral(n) -> elist.[n]
+ | SequenceExpr(elist), LCIntervalExpr(startIdx) ->
+ let startIdx' = __EvalSym resolverFunc returnFunc ctx startIdx
+ match startIdx' with
+ | IntLiteral(startIdxInt) ->
+ let rec __Skip n l = if n = 0 then l else __Skip (n-1) (List.tail l)
+ SequenceExpr(__Skip startIdxInt elist)
+ | _ -> SelectExpr(lst', idx')
+ | _ -> SelectExpr(lst', idx')
+ | UpdateExpr(lst,idx,v) ->
+ let lst', idx', v' = __EvalSym resolverFunc returnFunc ctx lst, __EvalSym resolverFunc returnFunc ctx idx, __EvalSym resolverFunc returnFunc ctx v
+ match lst', idx', v' with
+ | SequenceExpr(elist), IntLiteral(n), _ -> SequenceExpr(Utils.ListSet n v' elist)
+ | _ -> UpdateExpr(lst', idx', v')
+ | IteExpr(c, e1, e2) ->
+ let c' = __EvalSym fullResolverFunc returnFunc ctx c
+ match c' with
+ | BoolLiteral(b) -> if b then __EvalSym resolverFunc returnFunc ctx e1 else __EvalSym resolverFunc returnFunc ctx e2
+ | _ -> IteExpr(c', __EvalSym resolverFunc returnFunc ctx e1, __EvalSym resolverFunc returnFunc ctx e2)
+ | BinaryExpr(p,op,e1,e2) ->
+ let e1' = lazy (__EvalSym resolverFunc returnFunc ctx e1)
+ let e2' = lazy (__EvalSym resolverFunc returnFunc ctx e2)
+ let recomposed = lazy (BinaryExpr(p, op, e1'.Force(), e2'.Force()))
+ match op with
+ | "=" ->
+ let e1'' = e1'.Force()
+ let e2'' = e2'.Force()
+ let eq = __CheckEqual e1'' e2''
+ match eq with
+ | Some(b) -> BoolLiteral(b)
+ | None -> recomposed.Force()
+ | "!=" ->
+ let e1'' = e1'.Force()
+ let e2'' = e2'.Force()
+ let eq = __CheckEqual e1'' e2''
+ match eq with
+ | Some(b) -> BoolLiteral(not b)
+ | None -> recomposed.Force()
+ | "<" ->
+ match e1'.Force(), e2'.Force() with
+ | IntLiteral(n1), IntLiteral(n2) -> BoolLiteral(n1 < n2)
+ | _ -> recomposed.Force()
+ | "<=" ->
+ let e1'' = e1'.Force()
+ let e2'' = e2'.Force()
+ let eq = __CheckEqual e1'' e2''
+ match eq with
+ | Some(true) -> TrueLiteral
+ | _ -> match e1'', e2'' with
+ | IntLiteral(n1), IntLiteral(n2) -> BoolLiteral(n1 <= n2)
+ | _ -> recomposed.Force()
+ | ">" ->
+ match e1'.Force(), e2'.Force() with
+ | IntLiteral(n1), IntLiteral(n2) -> BoolLiteral(n1 > n2)
+ | _ -> recomposed.Force()
+ | ">=" ->
+ let e1'' = e1'.Force()
+ let e2'' = e2'.Force()
+ let eq = __CheckEqual e1'' e2''
+ match eq with
+ | Some(true) -> TrueLiteral
+ | _ -> match e1'', e2'' with
+ | IntLiteral(n1), IntLiteral(n2) -> BoolLiteral(n1 >= n2)
+ | _ -> recomposed.Force()
+ | ".." ->
+ let e1'' = e1'.Force()
+ let e2'' = e2'.Force()
+ match e1'', e2'' with
+ | IntLiteral(lo), IntLiteral(hi) -> SequenceExpr([lo .. hi] |> List.map (fun n -> IntLiteral(n)))
+ | _ -> recomposed.Force();
+ | "in" ->
+ match e1'.Force(), e2'.Force() with
+ | _, SetExpr(s)
+ | _, SequenceExpr(s) -> //BoolLiteral(Utils.ListContains (e1'.Force()) s)
+ if Utils.ListContains (e1'.Force()) s then
+ TrueLiteral
+ else
+ try
+ let contains = s |> List.map Expr2ConstStrict |> Utils.ListContains (e1'.Force() |> Expr2ConstStrict)
+ BoolLiteral(contains)
+ with
+ | _ -> recomposed.Force()
+ | _ -> recomposed.Force()
+ | "!in" ->
+ match e1'.Force(), e2'.Force() with
+ | _, SetExpr(s)
+ | _, SequenceExpr(s) -> //BoolLiteral(not (Utils.ListContains (e1'.Force()) s))
+ if Utils.ListContains (e1'.Force()) s then
+ FalseLiteral
+ else
+ try
+ let contains = s |> List.map Expr2ConstStrict |> Utils.ListContains (e1'.Force() |> Expr2ConstStrict)
+ BoolLiteral(not contains)
+ with
+ | _ -> recomposed.Force()
+ | _ -> recomposed.Force()
+ | "+" ->
+ let e1'' = e1'.Force();
+ let e2'' = e2'.Force();
+ match e1'', e2'' with
+ | IntLiteral(n1), IntLiteral(n2) -> IntLiteral(n1 + n2)
+ | SequenceExpr(l1), SequenceExpr(l2) -> SequenceExpr(List.append l1 l2)
+ | SetExpr(s1), SetExpr(s2) -> SetExpr(Set.union (Set.ofList s1) (Set.ofList s2) |> Set.toList)
+ | _ -> recomposed.Force()
+ | "-" ->
+ match e1'.Force(), e2'.Force() with
+ | IntLiteral(n1), IntLiteral(n2) -> IntLiteral(n1 - n2)
+ | SetExpr(s1), SetExpr(s2) -> SetExpr(Set.difference (Set.ofList s1) (Set.ofList s2) |> Set.toList)
+ | _ -> recomposed.Force()
+ | "*" ->
+ match e1'.Force(), e2'.Force() with
+ | IntLiteral(n1), IntLiteral(n2) -> IntLiteral(n1 * n2)
+ | _ -> recomposed.Force()
+ | "div" ->
+ match e1'.Force(), e2'.Force() with
+ | IntLiteral(n1), IntLiteral(n2) -> IntLiteral(n1 / n2)
+ | _ -> recomposed.Force()
+ | "mod" ->
+ match e1'.Force(), e2'.Force() with
+ | IntLiteral(n1), IntLiteral(n2) -> IntLiteral(n1 % n2)
+ | _ -> recomposed.Force()
+ | "&&" ->
+ // shortcircuit
+ match e1'.Force() with
+ | BoolLiteral(false) -> BoolLiteral(false)
+ | _ ->
+ match e1'.Force(), e2'.Force() with
+ | _, BoolLiteral(false) -> BoolLiteral(false)
+ | BoolLiteral(b1), BoolLiteral(b2) -> BoolLiteral(b1 && b2)
+ | _ -> BinaryAnd (e1'.Force()) (e2'.Force())
+ | "||" ->
+ // shortcircuit
+ match e1'.Force() with
+ | BoolLiteral(true) -> BoolLiteral(true)
+ | _ ->
+ match e1'.Force(), e2'.Force() with
+ | _, BoolLiteral(true) -> BoolLiteral(true)
+ | BoolLiteral(b1), BoolLiteral(b2) -> BoolLiteral(b1 || b2)
+ | _ -> BinaryOr (e1'.Force()) (e2'.Force())
+ | "==>" ->
+ // shortcircuit
+ match e1'.Force() with
+ | BoolLiteral(false) -> BoolLiteral(true)
+ | _ ->
+ let e1'' = e1'.Force()
+ let e2'' = e2'.Force()
+ BinaryImplies e1'' e2''
+ | "<==>" ->
+ match e1'.Force(), e2'.Force() with
+ | BoolLiteral(b1), BoolLiteral(b2) -> BoolLiteral(b1 = b2)
+ | x, BoolLiteral(b)
+ | BoolLiteral(b), x -> if b then x else UnaryNot(x)
+ | _ -> recomposed.Force()
+ | _ -> recomposed.Force()
+ | OldExpr(e) ->
+ let e' = __EvalSym resolverFunc returnFunc ctx e
+ let recomposed = OldExpr(e')
+ match e with
+ | IdLiteral(name) -> resolverFunc (IdLiteral(RenameToOld name)) None
+ | _ -> recomposed
+ | UnaryExpr(op, e) ->
+ let e' = __EvalSym resolverFunc returnFunc ctx e
+ let recomposed = UnaryExpr(op, e')
+ match op with
+ | "!" ->
+ match e' with
+ | BoolLiteral(b) -> BoolLiteral(not b)
+ | _ -> recomposed
+ | "-" ->
+ match e' with
+ | IntLiteral(n) -> IntLiteral(-n)
+ | _ -> recomposed
+ | _ -> recomposed
+ | ForallExpr(vars, e) ->
+ let rec __ExhaustVar v restV vDomain =
+ match vDomain with
+ | vv :: restD ->
+ let ctx' = (v,vv) :: ctx
+ let e' = __EvalSym resolverFunc returnFunc ctx' (ForallExpr(restV, e))
+ let erest = __ExhaustVar v restV restD
+ BinaryAnd e' erest
+ | [] -> BoolLiteral(true)
+ let rec __TraverseVars vars =
+ match vars with
+ | v :: restV ->
+ try
+ let vDom = GetVarDomain resolverFunc returnFunc ctx v e
+ __ExhaustVar v restV vDom
+ with
+ | ex -> ForallExpr([v], __TraverseVars restV)
+ | [] -> __EvalSym resolverFunc returnFunc ctx e
+ (* --- function body starts here --- *)
+ __TraverseVars vars
+ if expr' = FalseLiteral then
+ Logger.Debug ""
+ expr' |> returnFunc
+ and GetVarDomain resolverFunc returnFunc ctx var expr =
+ match expr with
+ | BinaryExpr(_, "==>", lhs, rhs) ->
+ let conjs = SplitIntoConjunts lhs
+ conjs |> List.fold (fun acc e ->
+ match e with
+ | BinaryExpr(_, "in", VarLiteral(vn), rhs) when GetVarName var = vn ->
+ match __EvalSym resolverFunc returnFunc ctx rhs with
+ | SetExpr(elist)
+ | SequenceExpr(elist) -> elist |> List.append acc
+ | x -> raise DomainNotInferred
+ | BinaryExpr(_, op, VarLiteral(vn),oth)
+ | BinaryExpr(_, op, oth, VarLiteral(vn)) when GetVarName var = vn && Set.ofList ["<"; "<="; ">"; ">="] |> Set.contains op ->
+ failwith "Not implemented yet"
+ | _ -> raise DomainNotInferred) []
+ | _ ->
+ Logger.WarnLine ("unknown pattern for a quantified expression; cannot infer domain of quantified variable \"" + (GetVarName var) + "\"")
+ raise DomainNotInferred
+ (* --- function body starts here --- *)
+ __EvalSym otherResolverFunc returnFunc ctx expr
+
+let EvalSym resolverFunc expr =
+ EvalSym2 resolverFunc resolverFunc (fun e -> e) [] expr
+
+let EvalSymRet fullResolverFunc resolverFunc returnFunc expr =
+ EvalSym2 fullResolverFunc resolverFunc returnFunc [] expr
+
+// ==========================================================
+/// Desugars a given expression so that all list constructors
+/// are expanded into explicit assignments to indexed elements
+// ==========================================================
+let MyDesugar expr removeOriginal =
+ let rec __Desugar expr =
+ match expr with
+ | IntLiteral(_)
+ | BoolLiteral(_)
+ | BoxLiteral(_)
+ | VarDeclExpr(_)
+ | IdLiteral(_)
+ | VarLiteral(_)
+ | ObjLiteral(_)
+ | Star
+ | Dot(_)
+ | SelectExpr(_)
+ | SeqLength(_)
+ | UpdateExpr(_)
+ | SetExpr(_)
+ | MethodCall(_)
+ | MethodOutSelect(_)
+ | SequenceExpr(_) -> expr
+ // forall v :: v in {a1 a2 ... an} ==> e ~~~> e[v/a1] && e[v/a2] && ... && e[v/an]
+ // forall v :: v in [a1 a2 ... an] ==> e ~~~> e[v/a1] && e[v/a2] && ... && e[v/an]
+ | ForallExpr([Var(vn1,ty1,old1)] as v, (BinaryExpr(_, "==>", BinaryExpr(_, "in", VarLiteral(vn2), rhsCol), sub) as ee)) when vn1 = vn2 ->
+ match rhsCol with
+ | SetExpr(elist)
+ | SequenceExpr(elist) -> elist |> List.fold (fun acc e -> BinaryAnd acc (__Desugar (Substitute (VarLiteral(vn2)) e sub))) TrueLiteral
+ | _ -> ForallExpr(v, __Desugar ee)
+ | ForallExpr(v,e) -> ForallExpr(v, __Desugar e)
+ | LCIntervalExpr(e) -> LCIntervalExpr(__Desugar e)
+ | OldExpr(e) -> OldExpr(__Desugar e)
+ | UnaryExpr(op,e) -> UnaryExpr(op, __Desugar e)
+ | AssertExpr(e) -> AssertExpr(__Desugar e)
+ | AssumeExpr(e) -> AssumeExpr(__Desugar e)
+ | IteExpr(c,e1,e2) -> IteExpr(c, __Desugar e1, __Desugar e2)
+ // lst = [a1 a2 ... an] ~~~> lst = [a1 a2 ... an] && lst[0] = a1 && lst[1] = a2 && ... && lst[n-1] = an && |lst| = n
+ | BinaryExpr(p,op,e1,e2) ->
+ let be = BinaryExpr(p, op, __Desugar e1, __Desugar e2)
+ let fs = if removeOriginal then TrueLiteral else be
+ try
+ match op with
+ | "=" ->
+ match EvalSym DefaultResolver e1, EvalSym DefaultResolver e2 with
+ | SequenceExpr(l1), SequenceExpr(l2) ->
+ let rec __fff lst1 lst2 cnt =
+ match lst1, lst2 with
+ | fs1 :: rest1, fs2 :: rest2 -> BinaryEq l1.[cnt] l2.[cnt] :: __fff rest1 rest2 (cnt+1)
+ | [], [] -> []
+ | _ -> failwith "Lists are of different sizes"
+ __fff l1 l2 0 |> List.fold (fun acc e -> BinaryAnd acc e) fs
+ | e, SequenceExpr(elist)
+ | SequenceExpr(elist), e ->
+ let rec __fff lst cnt =
+ match lst with
+ | fs :: rest -> BinaryEq (SelectExpr(e, IntLiteral(cnt))) elist.[cnt] :: __fff rest (cnt+1)
+ | [] -> [BinaryEq (SeqLength(e)) (IntLiteral(cnt))]
+ __fff elist 0 |> List.fold (fun acc e -> BinaryAnd acc e) fs
+ | _ -> be
+ | _ -> be
+ with
+ | EvalFailed(_) as ex -> (* printfn "%O" (ex.StackTrace); *) be
+ __Desugar expr
+
+let Desugar expr = MyDesugar expr false
+let DesugarAndRemove expr = MyDesugar expr true
+
+let rec DesugarLst exprLst =
+ match exprLst with
+ | expr :: rest -> Desugar expr :: DesugarLst rest
+ | [] -> []
+
+let ChangeThisReceiver receiver expr =
+ let rec __ChangeThis locals expr =
+ match expr with
+ | IntLiteral(_)
+ | BoolLiteral(_)
+ | BoxLiteral(_)
+ | Star
+ | VarDeclExpr(_)
+ | VarLiteral(_) -> expr
+ | ObjLiteral("this") -> receiver
+ | ObjLiteral(_) -> expr
+ | IdLiteral("null") -> failwith "should never happen anymore" //TODO
+ | IdLiteral("this") -> failwith "should never happen anymore"
+ | IdLiteral(id) -> if Set.contains id locals then VarLiteral(id) else __ChangeThis locals (Dot(ObjLiteral("this"), id))
+ | Dot(e, id) -> Dot(__ChangeThis locals e, id)
+ | AssertExpr(e) -> AssertExpr(__ChangeThis locals e)
+ | AssumeExpr(e) -> AssumeExpr(__ChangeThis locals e)
+ | ForallExpr(vars,e) -> let newLocals = vars |> List.map GetVarName |> Set.ofList |> Set.union locals
+ ForallExpr(vars, __ChangeThis newLocals e)
+ | LCIntervalExpr(e) -> LCIntervalExpr(__ChangeThis locals e)
+ | OldExpr(e) -> OldExpr(__ChangeThis locals e)
+ | UnaryExpr(op,e) -> UnaryExpr(op, __ChangeThis locals e)
+ | SeqLength(e) -> SeqLength(__ChangeThis locals e)
+ | SelectExpr(e1, e2) -> SelectExpr(__ChangeThis locals e1, __ChangeThis locals e2)
+ | BinaryExpr(p,op,e1,e2) -> BinaryExpr(p, op, __ChangeThis locals e1, __ChangeThis locals e2)
+ | IteExpr(e1,e2,e3) -> IteExpr(__ChangeThis locals e1, __ChangeThis locals e2, __ChangeThis locals e3)
+ | UpdateExpr(e1,e2,e3) -> UpdateExpr(__ChangeThis locals e1, __ChangeThis locals e2, __ChangeThis locals e3)
+ | SequenceExpr(exs) -> SequenceExpr(exs |> List.map (__ChangeThis locals))
+ | SetExpr(exs) -> SetExpr(exs |> List.map (__ChangeThis locals))
+ | MethodOutSelect(e, name) -> MethodOutSelect(__ChangeThis locals e, name)
+ | MethodCall(rcv,cname, mname,aparams) -> MethodCall(__ChangeThis locals rcv, cname, mname, aparams |> List.map (__ChangeThis locals))
+ (* --- function body starts here --- *)
+ __ChangeThis Set.empty expr
+
+let rec SimplifyExpr expr =
+ let __Simplify expr =
+ match expr with
+ | UnaryExpr("!", sub) -> Some(UnaryNot sub)
+ | BinaryExpr(_, "&&", l, r) -> Some(BinaryAnd l r)
+ | BinaryExpr(_, "||", l, r) -> Some(BinaryOr l r)
+ | BinaryExpr(_, "in", l, r) -> Some(BinaryIn l r)
+ | BinaryExpr(_, "!in", l, r) -> Some(BinaryNotIn l r)
+ | BinaryExpr(_, "==>", l, r) -> Some(BinaryImplies l r)
+ | BinaryExpr(_, "=", l, r) -> Some(BinaryEq l r)
+ | BinaryExpr(_, "!=", l, r) -> Some(BinaryNeq l r)
+ | _ -> None
+ RewriteBU __Simplify expr
+
+let rec ExtractTopLevelExpressions stmt =
+ match stmt with
+ | ExprStmt(e) -> [e]
+ | Assign(e1, e2) -> [e1; e2]
+ | Block(slist) -> slist |> List.fold (fun acc s -> acc @ ExtractTopLevelExpressions s) []
+
+let rec PullUpMethodCalls stmt =
+ let stmtList = new System.Collections.Generic.LinkedList<_>()
+ let rec __PullUpMethodCalls expr =
+ let newExpr = RewriteBU (fun expr ->
+ match expr with
+ | MethodOutSelect(_) ->
+ let vname = SymGen.NewSymFake expr
+ let e' = VarLiteral(vname)
+ let var = VarDeclExpr([Var(vname,None,false)], true)
+ let asgn = BinaryGets var expr
+ stmtList.AddLast asgn |> ignore
+ Some(e')
+ | _ -> None
+ ) expr
+ newExpr, (stmtList |> List.ofSeq)
+ stmtList.Clear()
+ match stmt with
+ | ExprStmt(e) ->
+ let e', slist = __PullUpMethodCalls e
+ slist @ [ExprStmt(e')]
+ | Assign(e1, e2) ->
+ let e2', slist = __PullUpMethodCalls e2
+ slist @ [Assign(e1, e2')]
+ | Block(slist) -> slist |> List.fold (fun acc s -> acc @ PullUpMethodCalls s) []
+
+// ==========================================================
+/// Very simple for now:
+/// - if "m" is a constructor, everything is modifiable
+/// - if the method's post condition contains assignments to fields, everything is modifiable
+/// - otherwise, all objects are immutable
+///
+/// (TODO: instead it should read the "modifies" clause of a method and figure out what's modifiable from there)
+// ==========================================================
+let IsModifiableObj obj (c,m) =
+ let __IsFld name = FindVar c name |> Utils.OptionToBool
+ match m with
+ | Method(name,_,_,_,_) when name.EndsWith("__mod__") -> true
+ | Method(_,_,_,_,true) -> true
+ | Method(_,_,_,post,false) ->
+ DescendExpr2 (fun e acc ->
+ match e with
+ | BinaryExpr(_,"=",IdLiteral(name),r) when __IsFld name -> true
+ | Dot(_,name) when __IsFld name -> true
+ | _ -> acc
+ ) post false
+ | _ -> failwithf "expected a Method but got %O" m \ No newline at end of file
diff --git a/Source/Jennisys/CodeGen.fs b/Source/Jennisys/CodeGen.fs
new file mode 100644
index 00000000..8df4ca60
--- /dev/null
+++ b/Source/Jennisys/CodeGen.fs
@@ -0,0 +1,429 @@
+module CodeGen
+
+open Ast
+open Getters
+open AstUtils
+open Utils
+open Resolver
+open TypeChecker
+open PrintUtils
+open DafnyPrinter
+open DafnyModelUtils
+open Options
+
+let validFuncName = "Valid()"
+let validSelfFuncName = "Valid_self()"
+let validReprFuncName = "Valid_repr()"
+
+/// requires: numUnrols >= 0
+/// requires: |fldExprs| = |fldNames|
+let rec GetUnrolledFieldValidExpr fldExprs fldNames validFuncToUse numUnrolls =
+ let rec __Combine exprLst strLst =
+ match exprLst with
+ | e :: rest ->
+ let resLst1 = strLst |> List.map (fun s -> Dot(e, s))
+ List.concat [resLst1; __Combine rest strLst]
+ | [] -> []
+ let rec __NotNull e =
+ match e with
+ | IdLiteral(_)
+ | ObjLiteral(_) -> BinaryNeq e (ObjLiteral("null"))
+ | Dot(sub, str) -> BinaryAnd (__NotNull sub) (BinaryNeq e (ObjLiteral("null")))
+ | _ -> failwith "not supposed to happen"
+ (* --- function body starts here --- *)
+ assert (numUnrolls >= 0)
+ if numUnrolls = 0 then
+ [TrueLiteral]
+ else
+ let exprList = fldExprs |> List.map (fun e -> BinaryImplies (__NotNull e) (Dot(e, validFuncToUse)))
+ if numUnrolls = 1 then
+ exprList
+ else
+ let fldExprs = __Combine fldExprs fldNames
+ List.append exprList (GetUnrolledFieldValidExpr fldExprs fldNames validFuncToUse (numUnrolls - 1))
+
+let GetFieldValidExpr flds validFunName numUnrolls =
+ let fldExprs = flds |> List.map (fun var -> IdLiteral(GetExtVarName var))
+ let fldNames = flds |> List.map GetExtVarName
+ let unrolledExprs = GetUnrolledFieldValidExpr fldExprs fldNames validFunName numUnrolls
+ // add the recursive definition as well
+ let recExprs =
+ if not (validFunName = validFuncName) && Options.CONFIG.recursiveValid then
+ flds //|> List.filter (fun var -> not ((GetExtVarName var).StartsWith("_back_"))) //don't use back pointers
+ |> List.map (fun var ->
+ let name = GetExtVarName var
+ BinaryImplies (BinaryNeq (IdLiteral(name)) NullLiteral) (Dot(IdLiteral(name), validFuncName)))
+ else
+ []
+ recExprs @ unrolledExprs
+
+let GetFieldsForValidExpr allFields prog comp : VarDecl list =
+ let frameVars = GetFrameFields comp
+ allFields |> List.filter (fun var -> IsUserType prog (GetVarType var))
+ |> List.filter (fun var -> Utils.ListContains var frameVars)
+
+let GetFieldsValidExprList clsName allFields prog : Expr list =
+ let fields = GetFieldsForValidExpr allFields prog (FindComponent prog clsName |> ExtractOption)
+ let fieldsByType = GroupFieldsByType fields
+ fieldsByType |> Map.fold (fun acc t varSet ->
+ let validFunName, numUnrolls =
+ match t with
+ | Some(ty) when clsName = (GetTypeShortName ty) -> validSelfFuncName, Options.CONFIG.numLoopUnrolls
+ | _ -> validFuncName, 1
+ acc |> List.append (GetFieldValidExpr (Set.toList varSet) validFunName numUnrolls)
+ ) []
+
+let PrintValidFunctionCode comp prog vars allInvs genRepr nameSuffix: string =
+ let validFuncName = "Valid" + nameSuffix + "()"
+ let validReprFuncName = "Valid_repr" + nameSuffix + "()"
+ let validSelfFuncName = "Valid_self" + nameSuffix + "()"
+ let idt = " "
+ let __PrintInvs invs =
+ invs |> List.fold (fun acc e -> List.concat [acc ; SplitIntoConjunts e]) []
+ |> PrintSep (" &&" + newline) (fun e -> sprintf "%s(%s)" idt (PrintExpr 0 e))
+ |> fun s -> if s = "" then (idt + "true") else s
+ let clsName = GetClassName comp
+ let compTypeName = GetClassType comp |> PrintType
+ let hasLoop = vars |> List.exists (fun var -> match GetVarType var with Some(ty) when compTypeName = PrintType ty -> true | _ -> false)
+ let fieldsValid = GetFieldsValidExprList clsName vars prog
+
+ let frameFldNames = GetFrameFields comp |> List.map GetExtVarName
+ let validReprBody =
+ " this in Repr &&" + newline +
+ " null !in Repr" +
+ (PrintSep "" (fun x -> " &&" + newline + " ($x != null ==> $x in Repr && $x.Repr <= Repr && this !in $x.Repr)".Replace("$x", x)) frameFldNames)
+
+ let vr =
+ if genRepr then
+ " function " + validReprFuncName + ": bool" + newline +
+ " reads *;" + newline +
+ " {" + newline +
+ validReprBody + newline +
+ " }" + newline + newline
+ else
+ ""
+
+ let decreasesStr =
+ if Options.CONFIG.recursiveValid then
+ if hasLoop then
+ if genRepr then
+ " decreases Repr;" + newline
+ else
+ // TODO: Dafny currently doesn't accept "decreases *" on methods
+ " decreases *;" + newline
+ else
+ ""
+ else ""
+ vr +
+ " function " + validSelfFuncName + ": bool" + newline +
+ " reads *;" + newline +
+ " {" + newline +
+ (if genRepr then " " + validReprFuncName + " &&" + newline else "") +
+ (__PrintInvs allInvs) + newline +
+ " }" + newline +
+ newline +
+ " function " + validFuncName + ": bool" + newline +
+ " reads *;" + newline +
+ decreasesStr +
+ " {" + newline +
+ " this." + validSelfFuncName + " &&" + newline +
+ (__PrintInvs fieldsValid) + newline +
+ " }" + newline
+
+let PrintDafnyCodeSkeleton prog methodPrinterFunc genRepr genOld =
+ match prog with
+ | Program(components) -> components |> List.fold (fun acc comp ->
+ match comp with
+ | Component(Interface(name,typeParams,members), DataModel(_,_,cVars,frame,inv), code) as comp ->
+ let aVars = FilterFieldMembers members
+ let aOldVars = MakeOldVars aVars
+ let cOldVars = MakeOldVars cVars
+ let allInvs = GetInvariantsAsList comp |> DesugarLst
+ let allOldInvs = MakeOld (allInvs |> List.fold BinaryAnd TrueLiteral) |> SplitIntoConjunts
+ let aVarsAndRepr = aVars |> List.append (Utils.Ite genRepr [Var("Repr", Some(SetType(NamedType("object", []))), false)] [])
+ let compMethods = FilterConstructorMembers members
+ // Now print it as a Dafny program
+ acc +
+ (sprintf "class %s%s {" name (PrintTypeParams typeParams)) + newline +
+ // the fields: original abstract fields plus concrete fields
+ (sprintf "%s" (PrintFields aVarsAndRepr 2 true)) + newline +
+ (sprintf "%s" (PrintFields cVars 2 false)) + newline +
+ (if genOld then
+ (sprintf "%s" (PrintFields aOldVars 2 true)) + newline +
+ (sprintf "%s" (PrintFields cOldVars 2 false)) + newline
+ else
+ "") +
+ // generate the Valid function
+ (sprintf "%s" (PrintValidFunctionCode comp prog (aVars @ cVars) allInvs genRepr "")) + newline +
+ (if genOld then
+ (sprintf "%s" (PrintValidFunctionCode comp prog (aOldVars @ cOldVars) allOldInvs genRepr "_old")) + newline
+ else
+ "") +
+ // call the method printer function on all methods of this component
+ (methodPrinterFunc comp) +
+ // the end of the class
+ "}" + newline + newline
+ | _ -> assert false; "") ""
+
+let PrintPrePost pfix expr =
+ SplitIntoConjunts expr |> PrintSep "" (fun e -> pfix + (PrintExpr 0 e) + ";")
+
+let GetPreconditionForMethod m =
+ let validExpr = IdLiteral(validFuncName);
+ if IsConstructor m then
+ GetMethodPrePost m |> fst
+ else
+ BinaryAnd validExpr (GetMethodPrePost m |> fst)
+
+let GetPostconditionForMethod prog m genRepr =
+ let validExpr = IdLiteral(validFuncName);
+ match m with
+ | Method(_,_,_,post,isConstr) ->
+ // this.Valid() and user-defined post-condition
+ let postExpr = BinaryAnd validExpr post
+ // method out args are valid
+ let postExpr = (GetMethodOutArgs m) |> List.fold (fun acc var ->
+ if IsUserType prog (GetVarType var) then
+ let varExpr = VarLiteral(GetExtVarName var)
+ let argValidExpr = BinaryImplies (BinaryNeq varExpr NullLiteral) (Dot(varExpr, validFuncName))
+ BinaryAnd acc argValidExpr
+ else
+ acc
+ ) postExpr
+ // fresh Repr
+ if genRepr then
+ let freshExpr = if isConstr then "fresh(Repr - {this})" else "fresh(Repr - old(Repr))";
+ BinaryAnd (IdLiteral(freshExpr)) postExpr
+ else
+ postExpr
+ | _ -> failwithf "expected a method, got %O" m
+
+let PrintAssumePostcondition prog m genRepr prefix =
+ PrintPrePost prefix (GetPostconditionForMethod prog m genRepr |> Desugar) + newline
+
+let GetAllocObjects heapInst =
+ heapInst.assignments |> List.fold (fun acc a ->
+ match a with
+ | FieldAssignment((obj,fld),_) when not (obj.name = "this") ->
+ acc |> Set.add obj
+ | FieldAssignment(_, ObjLiteral(name)) when not (name = "this" || name = "null") ->
+ acc |> Set.add (heapInst.objs |> Map.find name)
+ | _ -> acc
+ ) Set.empty
+
+let PrintAllocNewObjects heapInst indent =
+ let idt = Indent indent
+ GetAllocObjects heapInst |> Set.fold (fun acc obj -> acc + (sprintf "%svar %s := new %s;%s" idt obj.name (PrintType obj.objType) newline)) ""
+
+let PrintVarAssignments heapInst indent =
+ let idt = Indent indent
+ let stmts = ConvertToStatements heapInst true
+ let str = stmts |> PrintSep (newline) (fun s -> (PrintStmt s indent false))
+ str + newline
+
+///
+let PrintReprAssignments prog heapInst indent =
+ let __FollowsFunc o1 o2 =
+ heapInst.assignments |> List.fold (fun acc assgn ->
+ match assgn with
+ | FieldAssignment ((srcObj,fld),value) -> acc || (srcObj = o1 && value = ObjLiteral(o2.name))
+ | _ -> false
+ ) false
+ let idt = Indent indent
+ let objs = heapInst.assignments |> List.fold (fun acc assgn ->
+ match assgn with
+ | FieldAssignment((obj,var),_) -> if GetVarName var = "" then acc else acc |> Set.add obj
+ | _ -> acc
+ ) Set.empty
+ |> Set.toList
+ |> Utils.TopSort __FollowsFunc
+ |> List.rev
+ let rec __GetReprConcrete obj =
+ let expr = SetExpr([ObjLiteral(obj.name)])
+ let builder = CascadingBuilder<_>(expr)
+ builder {
+ let typeName = GetTypeShortName obj.objType
+ let! comp = FindComponent prog typeName
+ let vars = GetFrameFields comp
+ let nonNullVars = vars |> List.choose (fun v ->
+ let lst = heapInst.assignments |> List.choose (function FieldAssignment(x,y) -> Some(x,y) | _ -> None)
+ match Utils.ListMapTryFind (obj,v) lst with
+ | Some(ObjLiteral(n)) when not (n = "null" || n = obj.name) -> Some(v,n)
+ | _ -> None)
+ return nonNullVars |> List.map (fun (var,objName) -> var,(Map.find objName heapInst.objs))
+ |> List.fold (fun acc (var,varValObj) ->
+ if Options.CONFIG.genMod then
+ BinaryAdd acc (Dot(Dot(ObjLiteral(obj.name), (GetVarName var)), "Repr"))
+ else
+ BinaryAdd acc (__GetReprConcrete varValObj)
+ ) expr
+ }
+
+ let reprGetsList = objs |> List.fold (fun acc obj ->
+ let objStmt = BinaryGets (Dot(ObjLiteral(obj.name), "Repr")) (__GetReprConcrete obj)
+ objStmt :: acc
+// let expr = SetExpr([ObjLiteral(obj.name)])
+// let builder = CascadingBuilder<_>(expr)
+// let fullRhs = builder {
+// let typeName = GetTypeShortName obj.objType
+// let! comp = FindComponent prog typeName
+// let vars = GetFrameFields comp
+// let nonNullVars = vars |> List.filter (fun v ->
+// let lst = heapInst.assignments |> List.choose (function FieldAssignment(x,y) -> Some(x,y) | _ -> None)
+// match Utils.ListMapTryFind (obj,v) lst with
+// | Some(ObjLiteral(n)) when not (n = "null") -> true
+// | _ -> false)
+// return nonNullVars |> List.fold (fun a v ->
+// BinaryAdd a (Dot(Dot(ObjLiteral(obj.name), (GetVarName v)), "Repr"))
+// ) expr
+// }
+// let fullReprExpr = BinaryGets (Dot(ObjLiteral(obj.name), "Repr")) fullRhs
+// fullReprExpr :: acc
+ ) []
+
+ let reprStr = if not (reprGetsList = []) then
+ idt + "// repr stuff" + newline +
+ (PrintStmtList reprGetsList indent true)
+ else
+ ""
+
+ let reprValidExpr = GetAllocObjects heapInst |> Set.fold (fun acc obj -> BinaryAnd acc (Dot(ObjLiteral(obj.name), validFuncName))) TrueLiteral
+
+ let assertValidStr = if not (reprValidExpr = TrueLiteral) then
+ idt + "// assert repr objects are valid (helps verification)" + newline +
+ (PrintStmt (ExprStmt(AssertExpr(reprValidExpr))) indent true)
+ else
+ ""
+ let outStr = reprStr + assertValidStr
+ if outStr = "" then
+ outStr
+ else
+ newline + outStr
+
+let rec PrintHeapCreationCodeOld prog (comp,meth) sol indent genRepr =
+ let rec __RewriteOldStmt stmt =
+ match stmt with
+ | Assign(l, r) -> Assign(l, BringToPost r)
+ | ExprStmt(e) -> ExprStmt(BringToPost e)
+ | Block(slist) -> Block(slist |> List.map __RewriteOldStmt)
+
+ let __RewriteOldAsgn a =
+ match a with
+ | FieldAssignment((o,f),e) -> FieldAssignment((o,f), BringToPost e)
+ | ArbitraryStatement(stmt) -> ArbitraryStatement(__RewriteOldStmt stmt)
+
+ /// inserts an assignments into a list of assignments such that the list remains
+ /// topologically sorted wrt field dependencies between different assignments
+ let rec __InsertSorted asgsLst asg =
+ let ___DependsOn dependentAsg asg =
+ match asg, dependentAsg with
+ | FieldAssignment((o,f),_), FieldAssignment(_,e) ->
+ let mf = fun e acc ->
+ match e with
+ | IdLiteral(name) when name = GetVarName f && o.name = "this" -> true
+ | Dot(discr, name) ->
+ let t1 = InferType prog comp (fun s -> None) discr
+ let t2 = FindComponentForType prog o.objType
+ acc || (name = GetVarName f && t1 = t2)
+ | _ -> acc
+ DescendExpr2 (mf
+ ) e false
+ | _ -> false
+ match asgsLst with
+ | [] -> [asg]
+ | a :: rest -> if ___DependsOn a asg then asg :: a :: rest else a :: __InsertSorted rest asg
+
+ /// - removes all FieldAssignments to unmodifiable objects and old variables
+ /// - rewrites expressions not to use old fields
+ let __RemoveUnmodifiableStuff heapInst =
+ let newAsgs = heapInst.assignments |> List.fold (fun acc a ->
+ match a with
+ | FieldAssignment((obj,_),_) when not (Set.contains obj heapInst.modifiableObjs) -> acc
+ | FieldAssignment((_,var),_) when IsOldVar var -> acc
+ | _ -> __InsertSorted acc (__RewriteOldAsgn a)
+ ) []
+ { heapInst with assignments = newAsgs }
+
+ let idt = Indent indent
+ match sol with
+ | (c, hi) :: rest ->
+ let heapInstMod = __RemoveUnmodifiableStuff hi
+ let __ReprAssignments ind =
+ if genRepr then
+ (PrintReprAssignments prog heapInstMod ind)
+ else
+ ""
+ if c = TrueLiteral then
+ (PrintAllocNewObjects heapInstMod indent) +
+ (PrintVarAssignments heapInstMod indent) +
+ (__ReprAssignments indent) +
+ (PrintHeapCreationCodeOld prog (comp,meth) rest indent genRepr)
+ else
+ if List.length rest > 0 then
+ idt + "if (" + (PrintExpr 0 c) + ") {" + newline +
+ (PrintAllocNewObjects heapInstMod (indent+2)) +
+ (PrintVarAssignments heapInstMod (indent+2)) +
+ (__ReprAssignments (indent+2)) +
+ idt + "} else {" + newline +
+ (PrintHeapCreationCodeOld prog (comp,meth) rest (indent+2) genRepr) +
+ idt + "}" + newline
+ else
+ (PrintAllocNewObjects heapInstMod indent) +
+ (PrintVarAssignments heapInstMod indent) +
+ (__ReprAssignments indent)
+ | [] -> ""
+
+let PrintHeapCreationCode prog (comp,meth) sol indent genRepr =
+ let idt = Indent indent
+ let ghostPre = GetMethodGhostPrecondition meth
+ if ghostPre = TrueLiteral then
+ PrintHeapCreationCodeOld prog (comp,meth) sol indent genRepr
+ else
+ (ghostPre |> SplitIntoConjunts |> PrintSep newline (fun e -> idt + "assume " + (PrintExpr 0 e) + ";")) + newline +
+ (PrintHeapCreationCodeOld prog (comp,meth) sol indent genRepr)
+
+let GenConstructorCode prog comp mthd decreasesClause body genRepr =
+ let validExpr = IdLiteral(validFuncName);
+ match mthd with
+ | Method(methodName,sign,_,_,isConstr) ->
+ let preExpr = GetPreconditionForMethod mthd |> Desugar
+ let postExpr = GetPostconditionForMethod prog mthd genRepr |> Desugar
+ let thisObj = ThisObj comp
+ " method " + methodName + (PrintSig sign) +
+ (if IsModifiableObj thisObj (comp,mthd) then newline + " modifies this;" else "") +
+ (PrintPrePost (newline + " requires ") preExpr) +
+ (PrintPrePost (newline + " ensures ") postExpr) +
+ newline +
+ decreasesClause +
+ " {" + newline +
+ body +
+ " }" + newline
+ | _ -> ""
+
+let GetDecreasesClause (c,m) sol =
+ if IsRecursiveSol (c,m) sol then
+ " decreases Repr;" + newline
+ else
+ ""
+
+// solutions: (comp, constructor) |--> condition * heapInst
+let PrintImplCode prog solutions genRepr =
+ PrintDafnyCodeSkeleton prog (fun comp ->
+ let cname = GetComponentName comp
+ solutions |> Map.fold (fun acc (c,m) sol ->
+ if (GetComponentName c) = cname then
+ let mthdBody,decr =
+ match sol with
+ | [] ->
+ let body = " //unable to synthesize" +
+ (PrintAssumePostcondition prog m genRepr (newline + " assume "))
+ let decr = ""
+ body,decr
+ | _ ->
+ let body = PrintHeapCreationCode prog (c,m) sol 4 genRepr
+ let decr = GetDecreasesClause (c,m) sol
+ body,decr
+ acc + newline + (GenConstructorCode prog comp m decr mthdBody genRepr) + newline
+
+ else
+ acc) "") genRepr \ No newline at end of file
diff --git a/Source/Jennisys/DafnyModelUtils.fs b/Source/Jennisys/DafnyModelUtils.fs
new file mode 100644
index 00000000..e734f3bb
--- /dev/null
+++ b/Source/Jennisys/DafnyModelUtils.fs
@@ -0,0 +1,455 @@
+// #########################################################################
+/// Utilities for reading/building models from Boogie Visual Debugger files
+///
+/// author: Aleksandar Milicevic (t-alekm@microsoft.com)
+// #########################################################################
+
+module DafnyModelUtils
+
+(*
+ The heap maps objects and fields to locations.
+ heap: Const * VarDecl option |--> Const
+
+ The environment maps locations to values (except that it can also
+ be locations to locations, because not all values are explicitly
+ present in the model.
+ envMap: Const |--> Const
+
+ The context is just a list of equality constraints collected on the way
+ ctx: Set<Set<Const>>, where the inner set contains exactly two constants
+*)
+
+open Ast
+open Getters
+open AstUtils
+open Utils
+
+open Microsoft.Boogie
+
+let HEAP_SELECT_FNAME = "MapType1Select"
+let SEQ_BUILD_FNAME = "Seq#Build"
+let SEQ_APPEND_FNAME = "Seq#Append"
+let SEQ_LENGTH_FNAME = "Seq#Length"
+let SEQ_INDEX_FNAME = "Seq#Index"
+let SET_EMPTY_FNAME = "Set#Empty"
+let SET_SELECT_FNAME = "MapType0Select"
+let UNBOX_FNAME = "$Unbox"
+let BOOL_2_U_FNAME = "bool_2_U"
+let U_2_BOOL_FNAME = "U_2_bool"
+let INT_2_U_FNAME = "int_2_U"
+let U_2_INT_FNAME = "U_2_int"
+let DTYPE_FNAME = "dtype"
+let NULL_FNAME = "null"
+
+type HeapModel = {
+ heap : Map<Const * VarDecl, Const>;
+ env : Map<Const, Const>;
+ ctx : Set<Set<Const>>;
+}
+
+let MkHeapModel heap env ctx =
+ { heap = heap; env = env; ctx = ctx }
+
+let GetElemFullName (elem: Model.Element) =
+ elem.Names |> Seq.filter (fun ft -> ft.Func.Arity = 0)
+ |> Seq.choose (fun ft -> Some(ft.Func.Name))
+ |> Utils.SeqToOption
+
+let GetRefName (ref: Model.Element) =
+ match ref with
+ | :? Model.Uninterpreted as uref -> uref.Name
+ | _ -> failwith ("not a ref (Uninterpreted) but: " + ref.GetType().Name)
+
+let GetInt (elem: Model.Element) =
+ let __NotIntFail e = failwith ("not an int element: " + elem.ToString())
+ let rec __GetIntStrict (e: Model.Element) cont =
+ match e with
+ | :? Model.Number as ival -> ival.AsInt()
+ | _ -> cont e
+ __GetIntStrict elem (fun e ->
+ let f = e.Model.MkFunc(U_2_INT_FNAME, 1)
+ let matches = f.Apps |> Seq.filter (fun ft -> ft.Args.[0] = e) |> Seq.map (fun ft -> ft.Result)
+ if matches |> Seq.isEmpty then
+ __NotIntFail e
+ else
+ __GetIntStrict (matches |> Seq.nth 0) __NotIntFail)
+
+let GetBool (elem: Model.Element) =
+ let __NotBoolFail e = failwith ("not a bool element: " + elem.ToString())
+ let rec __GetBoolStrict (e: Model.Element) cont =
+ match e with
+ | :? Model.Boolean as bval -> bval.Value
+ | _ -> cont e
+ __GetBoolStrict elem (fun e ->
+ let f = e.Model.MkFunc(U_2_BOOL_FNAME, 1)
+ let matches = f.Apps |> Seq.filter (fun ft -> ft.Args.[0] = e) |> Seq.map (fun ft -> ft.Result)
+ if matches |> Seq.isEmpty then
+ __NotBoolFail e
+ else
+ __GetBoolStrict (matches |> Seq.nth 0) __NotBoolFail)
+
+let ConvertValue (refVal: Model.Element) =
+ match refVal with
+ | :? Model.Number as ival -> IntConst(ival.AsInt())
+ | :? Model.Boolean as bval -> BoolConst(bval.Value)
+ | :? Model.Array as aval -> failwith "reading array values from model not implemented"
+ | :? Model.Uninterpreted as uval ->
+ try BoolConst(GetBool refVal)
+ with _ -> try IntConst(GetInt refVal)
+ with _ -> Unresolved(uval.Name) (* just a symbolic name for now, which we'll hopefully resolve later*)
+ | _ -> failwith ("unexpected model element kind: " + refVal.ToString())
+
+let LastDotSplit (str: string) =
+ let dotIdx = str.LastIndexOf(".")
+ let s1 = if dotIdx = -1 then "" else str.Substring(0, dotIdx)
+ let s2 = str.Substring(dotIdx + 1)
+ s1,s2
+
+let GetType (e: Model.Element) prog =
+ let fNameOpt = GetElemFullName e
+ match fNameOpt with
+ | Some(fname) -> match fname with
+ | "intType" -> Some(IntType)
+ | Prefix "class." clsName ->
+ let _,shortClsName = LastDotSplit clsName
+ match FindComponent prog shortClsName with
+ | Some(comp) -> Some(GetClassType comp)
+ | None -> None
+ | _ -> None
+ | None -> None
+
+let GetLoc (e: Model.Element) =
+ Unresolved(GetRefName e)
+
+let FindOrCreateSeq env key len =
+ match Map.tryFind key env with
+ | Some(SeqConst(lst)) -> lst,env
+ | None ->
+ let emptyList = Utils.GenList len NoneConst
+ let newSeq = SeqConst(emptyList)
+ let newMap = env |> Map.add key newSeq
+ emptyList,newMap
+ | Some(_) as x-> failwith ("not a SeqConst but: " + x.ToString())
+
+let FindSeqInEnv env key =
+ match Map.find key env with
+ | SeqConst(lst) -> lst
+ | _ as x-> failwith ("not a SeqConst but: " + x.ToString())
+
+let TryFindSetInEnv env key =
+ match Map.tryFind key env with
+ | Some(SetConst(s)) -> Some(s)
+ | Some(x) -> failwith ("not a SetConst but: " + x.ToString())
+ | None -> None
+
+let AddConstr c1 c2 ctx =
+ if c1 = c2 then
+ ctx
+ else
+ match c1, c2 with
+ | Unresolved(_), _ | _, Unresolved(_) ->
+ // find partitions
+ let s1Opt = ctx |> Set.filter (fun ss -> Set.contains c1 ss) |> Utils.SetToOption
+ let s2Opt = ctx |> Set.filter (fun ss -> Set.contains c2 ss) |> Utils.SetToOption
+ match s1Opt, s2Opt with
+ // both already exist --> so just merge them
+ | Some(s1), Some(s2) -> ctx |> Set.remove s1 |> Set.remove s2 |> Set.add (Set.union s1 s2)
+ // exactly one already exists --> add to existing
+ | Some(s1), None -> ctx |> Set.remove s1 |> Set.add (Set.add c2 s1)
+ | None, Some(s2) -> ctx |> Set.remove s2 |> Set.add (Set.add c1 s2)
+ // neither exists --> create a new one
+ | None, None -> ctx |> Set.add (Set.ofList [c1; c2])
+ | _ -> failwith ("trying to add an equality constraint between two constants: " + c1.ToString() + ", and " + c2.ToString())
+
+let rec UpdateContext lst1 lst2 ctx =
+ match lst1, lst2 with
+ | fs1 :: rest1, fs2 :: rest2 ->
+ match fs1, fs2 with
+ | NoneConst,_ | _,NoneConst -> UpdateContext rest1 rest2 ctx
+ | _ -> UpdateContext rest1 rest2 (AddConstr fs1 fs2 ctx)
+ | [], [] -> ctx
+ | _ -> failwith "lists are not of the same length"
+
+let UnboxIfNeeded (model: Microsoft.Boogie.Model) (e: Model.Element) =
+ let f_unbox = model.MkFunc(UNBOX_FNAME, 2)
+ let unboxed = f_unbox.Apps |> Seq.filter (fun ft -> if (GetLoc ft.Args.[1]) = (GetLoc e) then true else false)
+ |> Seq.choose (fun ft -> Some(ft.Result))
+ |> Utils.SeqToOption
+ match unboxed with
+ | Some(e) -> ConvertValue e
+ | None -> GetLoc e
+
+let ReadHeap (model: Microsoft.Boogie.Model) prog =
+ let f_heap_select = model.MkFunc(HEAP_SELECT_FNAME, 3)
+ let values = f_heap_select.Apps
+ values |> Seq.fold (fun acc ft ->
+ assert (ft.Args.Length = 3)
+ let ref = ft.Args.[1]
+ let fld = ft.Args.[2]
+ assert (Seq.length fld.Names = 1)
+ let fldFullName = (Seq.nth 0 fld.Names).Func.Name
+ let pfix,fldName = LastDotSplit fldFullName
+ let _,clsName = LastDotSplit pfix
+ let refVal = ft.Result
+ let refObj = Unresolved(GetRefName ref)
+ let nonebuilder = CascadingBuilder<_>(None)
+ let fldVarOpt = nonebuilder {
+ let! comp = FindComponent prog clsName
+ if fldName.StartsWith("old_") then
+ let fn = RenameFromOld fldName
+ let! var = FindVar comp fn
+ return Some(MakeOldVar var)
+ else
+ return FindVar comp fldName
+ }
+ match fldVarOpt with
+ | Some(fldVar) ->
+ let fldType = GetVarType fldVar
+ let fldVal = ConvertValue refVal
+ acc |> Map.add (refObj, fldVar) fldVal
+ | None -> acc
+ ) Map.empty
+
+// ====================================================================
+/// Reads values that were assigned to given arguments. Those values
+/// can be in functions with the same name as the argument name appended
+/// with an "#" and some number after it.
+// ====================================================================
+let rec ReadArgValues (model: Microsoft.Boogie.Model) args =
+ match args with
+ | v :: rest ->
+ let name = GetVarName v
+ let farg = model.Functions |> Seq.filter (fun f -> f.Arity = 0 && f.Name.StartsWith(name + "#")) |> Utils.SeqToOption
+ match farg with
+ | Some(func) ->
+ let fldVar = v
+ assert (Seq.length func.Apps = 1)
+ let ft = Seq.head func.Apps
+ let fldVal = ConvertValue (ft.Result)
+ ReadArgValues model rest |> Map.add (VarConst(name)) fldVal
+ | None -> failwith ("cannot find corresponding function for parameter " + name)
+ | [] -> Map.empty
+
+// ==============================================================
+/// Reads stuff about sequences from a given model and ads it to
+/// the given "envMap" map and a "ctx" set. The relevant stuff is
+/// fetched from the following functions:
+/// Seq#Length, Seq#Index, Seq#Build, Seq#Append
+// ==============================================================
+let ReadSeq (model: Microsoft.Boogie.Model) (envMap,ctx) =
+ // reads stuff from Seq#Length
+ let rec __ReadSeqLen (model: Microsoft.Boogie.Model) (len_tuples: Model.FuncTuple list) (envMap,ctx) =
+ match len_tuples with
+ | ft :: rest ->
+ let len = GetInt ft.Result
+ let emptyList = Utils.GenList len NoneConst
+ let newMap = envMap |> Map.add (GetLoc ft.Args.[0]) (SeqConst(emptyList))
+ __ReadSeqLen model rest (newMap,ctx)
+ | _ -> (envMap,ctx)
+
+ // reads stuff from Seq#Index
+ let rec __ReadSeqIndex (model: Microsoft.Boogie.Model) (idx_tuples: Model.FuncTuple list) (envMap,ctx) =
+ match idx_tuples with
+ | ft :: rest ->
+ let srcLstKey = GetLoc ft.Args.[0]
+ let idx = GetInt ft.Args.[1]
+ let oldLst,envMap = FindOrCreateSeq envMap srcLstKey (idx+1)
+ let lstElem = UnboxIfNeeded model ft.Result
+ let newLst = Utils.ListSet idx lstElem oldLst
+ let newCtx = UpdateContext oldLst newLst ctx
+ let newEnv = envMap |> Map.add srcLstKey (SeqConst(newLst))
+ __ReadSeqIndex model rest (newEnv,newCtx)
+ | _ -> (envMap,ctx)
+
+ // reads stuff from Seq#Build
+ let rec __ReadSeqBuild (model: Microsoft.Boogie.Model) (bld_tuples: Model.FuncTuple list) (envMap,ctx) =
+ match bld_tuples with
+ | ft :: rest ->
+ let srcLstLoc = GetLoc ft.Args.[0]
+ let lstElemVal = UnboxIfNeeded model ft.Args.[1]
+ let dstLstLoc = GetLoc ft.Result
+ let oldLst = FindSeqInEnv envMap srcLstLoc
+ let dstLst = FindSeqInEnv envMap dstLstLoc
+ let newLst = oldLst @ [lstElemVal]
+ let newCtx = UpdateContext dstLst newLst ctx
+ let newEnv = envMap |> Map.add dstLstLoc (SeqConst(newLst))
+ __ReadSeqBuild model rest (newEnv,newCtx)
+ | _ -> (envMap,ctx)
+
+ // reads stuff from Seq#Append
+ let rec __ReadSeqAppend (model: Microsoft.Boogie.Model) (app_tuples: Model.FuncTuple list) (envMap,ctx) =
+ match app_tuples with
+ | ft :: rest ->
+ let srcLst1Loc = GetLoc ft.Args.[0]
+ let srcLst2Loc = GetLoc ft.Args.[1]
+ let dstLstLoc = GetLoc ft.Result
+ let oldLst1 = FindSeqInEnv envMap srcLst1Loc
+ let oldLst2 = FindSeqInEnv envMap srcLst2Loc
+ let dstLst = FindSeqInEnv envMap dstLstLoc
+ let newLst = oldLst1 @ oldLst2
+ let newCtx = UpdateContext dstLst newLst ctx
+ let newEnv = envMap |> Map.add dstLstLoc (SeqConst(newLst))
+ __ReadSeqAppend model rest (newEnv,newCtx)
+ | _ -> (envMap,ctx)
+
+ // keeps reading from Seq#Build and Seq#Append until fixpoint
+ let rec __ReadUntilFixpoint hmodel =
+ let f_seq_bld = model.MkFunc(SEQ_BUILD_FNAME, 2)
+ let f_seq_app = model.MkFunc(SEQ_APPEND_FNAME, 2)
+ let hmodel' = hmodel |> __ReadSeqBuild model (List.ofSeq f_seq_bld.Apps)
+ |> __ReadSeqAppend model (List.ofSeq f_seq_app.Apps)
+ if hmodel' = hmodel then
+ hmodel'
+ else
+ __ReadUntilFixpoint hmodel'
+
+ let f_seq_len = model.MkFunc(SEQ_LENGTH_FNAME, 1)
+ let f_seq_idx = model.MkFunc(SEQ_INDEX_FNAME, 2)
+ let hmodel = (envMap,ctx)
+ let hmodel' = hmodel |> __ReadSeqLen model (List.ofSeq f_seq_len.Apps)
+ |> __ReadSeqIndex model (List.ofSeq f_seq_idx.Apps)
+ __ReadUntilFixpoint hmodel'
+
+
+
+// =====================================================
+/// Reads stuff about sets from a given model and adds it
+/// to the given "envMap" map and "ctx" set.
+// =====================================================
+let ReadSet (model: Microsoft.Boogie.Model) (envMap,ctx) =
+ // reads stuff from Set#Empty
+ let rec __ReadSetEmpty (empty_tuples: Model.FuncTuple list) (envMap,ctx) =
+ match empty_tuples with
+ | ft :: rest ->
+ let newMap = envMap |> Map.add (GetLoc ft.Result) (SetConst(Set.empty))
+ __ReadSetEmpty rest (newMap,ctx)
+ | [] -> (envMap,ctx)
+
+ // reads stuff from [2]
+ let rec __ReadSetMembership (set_tuples: Model.FuncTuple list) (env,ctx) =
+ match set_tuples with
+ | ft :: rest ->
+ if GetBool ft.Result then
+ let srcSetKey = GetLoc ft.Args.[0]
+ let srcSet = match TryFindSetInEnv env srcSetKey with
+ | Some(s) -> s
+ | None -> Set.empty
+ let elem = UnboxIfNeeded model ft.Args.[1]
+ let newEnv = env |> Map.add srcSetKey (SetConst(Set.add elem srcSet))
+ __ReadSetMembership rest (newEnv,ctx)
+ else
+ __ReadSetMembership rest (env,ctx)
+ | [] -> (env,ctx)
+
+ let t_set_empty = Seq.toList (model.MkFunc(SET_EMPTY_FNAME, 1).Apps)
+ let t_set = Seq.toList (model.MkFunc(SET_SELECT_FNAME, 2).Apps)
+ (envMap,ctx) |> __ReadSetEmpty t_set_empty
+ |> __ReadSetMembership t_set
+
+(* More complicated way which now doesn't seem to be necessary *)
+//let ReadSet (model: Microsoft.Boogie.Model) (envMap,ctx) =
+// // reads stuff from Set#Empty
+// let rec __ReadSetEmpty (empty_tuples: Model.FuncTuple list) (envMap,ctx) =
+// match empty_tuples with
+// | ft :: rest ->
+// let newMap = envMap |> Map.add (GetLoc ft.Result) (SetConst(Set.empty))
+// __ReadSetEmpty rest (newMap,ctx)
+// | [] -> (envMap,ctx)
+//
+// // reads stuff from Set#UnionOne and Set#Union
+// let rec __ReadSetUnions (envMap,ctx) =
+// // this one goes through a given list of "UnionOne" tuples, updates
+// // the env for those set that it was able to resolve, and returns a
+// // list of tuples for which it wasn't able to resolve sets
+// let rec ___RSU1 (tuples: Model.FuncTuple list) env unprocessed =
+// match tuples with
+// | ft :: rest ->
+// let srcSetKey = GetLoc ft.Args.[0]
+// match TryFindSetInEnv env srcSetKey with
+// | Some(oldSet) ->
+// let elem = UnboxIfNeeded model ft.Args.[1]
+// let newSet = Set.add elem oldSet
+// // update contex?
+// let newEnv = env |> Map.add (GetLoc ft.Result) (SetConst(newSet))
+// ___RSU1 rest newEnv unprocessed
+// | None -> ___RSU1 rest env (ft :: unprocessed)
+// | [] -> (env,unprocessed)
+// // this one goes through a given list of "Union" tuples, updates
+// // the env for those set that it was able to resolve, and returns a
+// // list of tuples for which it wasn't able to resolve sets
+// let rec ___RSU (tuples: Model.FuncTuple list) env unprocessed =
+// match tuples with
+// | ft :: rest ->
+// let set1Key = GetLoc ft.Args.[0]
+// let set2Key = GetLoc ft.Args.[1]
+// match TryFindSetInEnv env set1Key, TryFindSetInEnv env set2Key with
+// | Some(oldSet1), Some(oldSet2) ->
+// let newSet = Set.union oldSet1 oldSet2
+// // update contex?
+// let newEnv = env |> Map.add (GetLoc ft.Result) (SetConst(newSet))
+// ___RSU rest newEnv unprocessed
+// | _ -> ___RSU rest env (ft :: unprocessed)
+// | [] -> (env,unprocessed)
+// // this one keeps looping as loong as the list of unprocessed tuples
+// // is decreasing, it ends when if falls down to 0, or fails if
+// // the list stops decreasing
+// let rec ___RSU_until_fixpoint u1tuples utuples env =
+// let newEnv1,unprocessed1 = ___RSU1 u1tuples env []
+// let newEnv2,unprocessed2 = ___RSU utuples newEnv1 []
+// let oldLen = (List.length u1tuples) + (List.length utuples)
+// let totalUnprocLen = (List.length unprocessed1) + (List.length unprocessed2)
+// if totalUnprocLen = 0 then
+// newEnv2
+// elif totalUnprocLen < oldLen then
+// ___RSU_until_fixpoint unprocessed1 unprocessed2 newEnv2
+// else
+// failwith "cannot resolve all sets in Set#UnionOne/Set#Union"
+// // finally, just invoke the fixpoint function for UnionOne and Union tuples
+// let t_union_one = Seq.toList (model.MkFunc("Set#UnionOne", 2).Apps)
+// let t_union = Seq.toList (model.MkFunc("Set#Union", 2).Apps)
+// let newEnv = ___RSU_until_fixpoint t_union_one t_union envMap
+// (newEnv,ctx)
+//
+// let f_set_empty = model.MkFunc("Set#Empty", 1)
+// (envMap,ctx) |> __ReadSetEmpty (List.ofSeq f_set_empty.Apps)
+// |> __ReadSetUnions
+
+// ======================================================
+/// Reads staff about the null constant from a given model
+/// and adds it to the given "envMap" map and "ctx" set.
+// ======================================================
+let ReadNull (model: Microsoft.Boogie.Model) (envMap,ctx) =
+ let f_null = model.MkFunc(NULL_FNAME, 0)
+ assert (f_null.AppCount = 1)
+ let e = (f_null.Apps |> Seq.nth 0).Result
+ let newEnv = envMap |> Map.add (GetLoc e) NullConst
+ (newEnv,ctx)
+
+// ============================================================================================
+/// Reads the evinronment map and the context set.
+///
+/// It starts by reading the model for the "dtype" function to discover all objects on the heap,
+/// and then proceeds by reading stuff about the null constant, about sequences, and about sets.
+// ============================================================================================
+let ReadEnv (model: Microsoft.Boogie.Model) prog =
+ let f_dtype = model.MkFunc(DTYPE_FNAME, 1)
+ let refs = f_dtype.Apps |> Seq.choose (fun ft -> Some(ft.Args.[0]))
+ let envMap = f_dtype.Apps |> Seq.fold (fun acc ft ->
+ let locName = GetRefName ft.Args.[0]
+ let elemName = GetElemFullName ft.Args.[0]
+ let loc = Unresolved(locName)
+ let locType = GetType ft.Result prog
+ let value = match elemName with
+ | Some(n) when n.StartsWith("this") -> ThisConst(locName.Replace("*", ""), locType)
+ | _ -> NewObj(locName.Replace("*", ""), locType)
+ acc |> Map.add loc value
+ ) Map.empty
+ (envMap, Set.ofList([])) |> ReadNull model
+ |> ReadSeq model
+ |> ReadSet model
+
+let ReadFieldValuesFromModel (model: Microsoft.Boogie.Model) prog comp meth =
+ let heap = ReadHeap model prog
+ let env0,ctx = ReadEnv model prog
+ let env = env0 |> Utils.MapAddAll (ReadArgValues model (GetMethodArgs meth))
+ MkHeapModel heap env ctx \ No newline at end of file
diff --git a/Source/Jennisys/DafnyPrinter.fs b/Source/Jennisys/DafnyPrinter.fs
new file mode 100644
index 00000000..f2e71e8b
--- /dev/null
+++ b/Source/Jennisys/DafnyPrinter.fs
@@ -0,0 +1,135 @@
+module DafnyPrinter
+
+open Ast
+open Getters
+open AstUtils
+open PrintUtils
+
+let rec PrintType ty =
+ match ty with
+ | IntType -> "int"
+ | BoolType -> "bool"
+ | SeqType(t) -> sprintf "seq<%s>" (PrintType t)
+ | SetType(t) -> sprintf "set<%s>" (PrintType t)
+ | NamedType(id,args) -> if List.isEmpty args then id else sprintf "%s<%s>" id (PrintSep ", " (fun s -> s) args)
+ | InstantiatedType(id,args) -> if List.isEmpty args then id else sprintf "%s<%s>" id (PrintSep ", " (fun t -> PrintType t) args)
+
+let PrintVarDecl vd =
+ let name = GetExtVarName vd
+ match GetVarType vd with
+ | None -> name
+ | Some(ty) -> sprintf "%s: %s" name (PrintType ty)
+
+let rec PrintExpr ctx expr =
+ match expr with
+ | IntLiteral(d) -> sprintf "%d" d
+ | BoolLiteral(b) -> sprintf "%b" b
+ | BoxLiteral(id) -> sprintf "box_%s" id
+ | ObjLiteral(id)
+ | VarLiteral(id)
+ | IdLiteral(id) -> id
+ | VarDeclExpr(vlist, declare) ->
+ let decl = if declare then "var " else ""
+ let vars = PrintSep ", " PrintVarDecl vlist
+ sprintf "%s%s" decl vars
+ | Star -> "*"
+ | Dot(e,id) -> sprintf "%s.%s" (PrintExpr 100 e) id
+ | LCIntervalExpr(e) -> sprintf "%s.." (PrintExpr 90 e)
+ | OldExpr(e) -> sprintf "old(%s)" (PrintExpr 90 e)
+ | UnaryExpr(op,UnaryExpr(op2, e2)) -> sprintf "%s(%s)" op (PrintExpr 90 (UnaryExpr(op2, e2)))
+ | UnaryExpr(op,e) -> sprintf "%s%s" op (PrintExpr 90 e)
+ | BinaryExpr(strength,"in",lhs,BinaryExpr(_,"...",lo,hi)) ->
+ let needParens = strength <= ctx
+ let openParen = if needParens then "(" else ""
+ let closeParen = if needParens then ")" else ""
+ let loStr = PrintExpr strength lo
+ let hiStr = PrintExpr strength hi
+ let lhsStr = PrintExpr strength lhs
+ sprintf "%s%s <= %s && %s <= %s%s" openParen loStr lhsStr lhsStr hiStr closeParen
+ | BinaryExpr(strength,op,e0,e1) ->
+ let op =
+ match op with
+ | "=" -> "=="
+ | "div" -> "/"
+ | "mod" -> "%"
+ | _ -> op
+ let needParens = strength <= ctx
+ let openParen = if needParens then "(" else ""
+ let closeParen = if needParens then ")" else ""
+ sprintf "%s%s %s %s%s" openParen (PrintExpr strength e0) op (PrintExpr strength e1) closeParen
+ | IteExpr(c,e1,e2) -> sprintf "(if %s then %s else %s)" (PrintExpr 25 c) (PrintExpr 25 e1) (PrintExpr 25 e2)
+ | SelectExpr(e,i) -> sprintf "%s[%s]" (PrintExpr 100 e) (PrintExpr 0 i)
+ | UpdateExpr(e,i,v) -> sprintf "%s[%s := %s]" (PrintExpr 100 e) (PrintExpr 0 i) (PrintExpr 0 v)
+ | SequenceExpr(ee) -> sprintf "[%s]" (ee |> PrintSep ", " (PrintExpr 0))
+ | SeqLength(e) -> sprintf "|%s|" (PrintExpr 0 e)
+ | SetExpr(ee) -> sprintf "{%s}" (ee |> PrintSep ", " (PrintExpr 0))
+ | AssertExpr(e) -> sprintf "assert %s" (PrintExpr 0 e)
+ | AssumeExpr(e) -> sprintf "assume %s" (PrintExpr 0 e)
+ | ForallExpr(vv,e) ->
+ let needParens = true
+ let openParen = if needParens then "(" else ""
+ let closeParen = if needParens then ")" else ""
+ sprintf "%sforall %s :: %s%s" openParen (vv |> PrintSep ", " PrintVarDecl) (PrintExpr 0 e) closeParen
+ | MethodCall(rcv,_,name,aparams) ->
+ sprintf "%s.%s(%s)" (PrintExpr 0 rcv) name (aparams |> PrintSep ", " (PrintExpr 0))
+ | MethodOutSelect(mth,name) ->
+ // TODO: this can only work if there is only 1 out parameter
+ sprintf "%s" (PrintExpr 0 mth)
+
+let rec PrintConst cst =
+ match cst with
+ | IntConst(v) -> sprintf "%d" v
+ | BoolConst(b) -> sprintf "%b" b
+ | BoxConst(id) -> sprintf "box_%s" id
+ | VarConst(v) -> sprintf "%s" v
+ | SetConst(cset) -> sprintf "{%s}" (PrintSep ", " (fun c -> PrintConst c) (Set.toList cset))
+ | SeqConst(cseq) -> sprintf "[%s]" (PrintSep ", " (fun c -> PrintConst c) cseq)
+ | NullConst -> "null"
+ | NoneConst -> "<none>"
+ | ThisConst(_,_) -> "this"
+ | NewObj(name,_) -> PrintGenSym name
+ | Unresolved(name) -> sprintf "Unresolved(%s)" name
+
+let PrintSig signature =
+ match signature with
+ | Sig(ins, outs) ->
+ let returnClause =
+ if outs <> [] then sprintf " returns (%s)" (outs |> PrintSep ", " PrintVarDecl)
+ else ""
+ sprintf "(%s)%s" (ins |> PrintSep ", " PrintVarDecl) returnClause
+
+let PrintTypeParams typeParams =
+ match typeParams with
+ | [] -> ""
+ | _ -> sprintf "<%s>" (typeParams |> PrintSep ", " (fun tp -> tp))
+
+let PrintFields vars indent ghost =
+ let ghostStr = if ghost then "ghost " else ""
+ vars |> List.fold (fun acc v -> match GetVarType v with
+ | None -> acc + (sprintf "%s%svar %s;%s" (Indent indent) ghostStr (GetExtVarName v) newline)
+ | Some(tp) -> acc + (sprintf "%s%svar %s: %s;%s" (Indent indent) ghostStr (GetExtVarName v) (PrintType tp) newline)) ""
+
+let rec _PrintStmt stmt indent printNewline =
+ let idt = Indent indent
+ let nl = if printNewline then newline else ""
+ match stmt with
+ | Block(stmts) ->
+ idt + "{" + nl +
+ (_PrintStmtList stmts (indent + 2) true) +
+ idt + "}" + nl
+ | Assign(lhs,rhs) -> sprintf "%s%s := %s;%s" idt (PrintExpr 0 lhs) (PrintExpr 0 rhs) nl
+ | ExprStmt(expr) -> sprintf "%s%s;%s" idt (PrintExpr 0 expr) nl
+and _PrintStmtList stmts indent printNewLine =
+ let idt = Indent indent
+ let str = stmts |> PrintSep newline (fun s -> _PrintStmt s indent false)
+ if printNewLine then
+ str + newline
+ else
+ str
+
+let PrintStmt stmt indent printNewline =
+ let stmts = PullUpMethodCalls stmt
+ _PrintStmtList stmts indent printNewline
+
+let PrintStmtList stmts indent printNewLine =
+ stmts |> List.fold (fun acc s -> acc + (PrintStmt s indent printNewLine)) "" \ No newline at end of file
diff --git a/Source/Jennisys/EnvUtils.fs b/Source/Jennisys/EnvUtils.fs
new file mode 100644
index 00000000..0b840311
--- /dev/null
+++ b/Source/Jennisys/EnvUtils.fs
@@ -0,0 +1,9 @@
+module EnvUtils
+
+open Ast
+
+let GetThisLoc env =
+ Map.findKey (fun k v ->
+ match v with
+ | ThisConst(_) -> true
+ | _ -> false) env \ No newline at end of file
diff --git a/Source/Jennisys/FixpointSolver.fs b/Source/Jennisys/FixpointSolver.fs
new file mode 100644
index 00000000..1ca3b057
--- /dev/null
+++ b/Source/Jennisys/FixpointSolver.fs
@@ -0,0 +1,374 @@
+module FixpointSolver
+
+open Ast
+open AstUtils
+open Printer
+open Resolver
+open Utils
+
+/////////////
+
+type UnifDirection = LTR | RTL
+
+exception CannotUnify
+
+let rec SelectiveUnifyImplies okToUnifyFunc lhs rhs dir unifs =
+ ///
+ let __AddOrNone unifs name e =
+ if okToUnifyFunc name then
+ Some(unifs |> Utils.MapAddNew name e)
+ else
+ None
+
+ ///
+ let __UnifLists lstL lstR =
+ if List.length lstL = List.length lstR then
+ try
+ let unifs2 = List.fold2 (fun acc elL elR -> match SelectiveUnifyImplies okToUnifyFunc elL elR dir acc with
+ | Some(u) -> u
+ | None -> raise CannotUnify) unifs lstL lstR
+ Some(unifs2)
+ with
+ | CannotUnify -> None
+ else
+ None
+
+ ///
+ let __ApplyUnifs unifs exprList =
+ exprList |> List.fold (fun acc e ->
+ let e' = e |> Rewrite (fun e ->
+ match e with
+ | VarLiteral(id) when Map.containsKey id unifs -> Some(unifs |> Map.find id)
+ | _ -> None)
+ acc |> Set.add e'
+ ) Set.empty
+
+ if lhs = FalseLiteral || rhs = TrueLiteral then
+ Some(unifs)
+ else
+ try
+ let l,r = match dir with
+ | LTR -> lhs,rhs
+ | RTL -> rhs,lhs
+ match l, r with
+ | VarLiteral(vname), rhs -> __AddOrNone unifs vname rhs
+ | IntLiteral(nL), IntLiteral(nR) when nL = nR ->
+ Some(unifs)
+ | BoolLiteral(bL), BoolLiteral(bR) when bL = bR ->
+ Some(unifs)
+ | SetExpr(elistL), SetExpr(elistR) ->
+ let s1 = elistL |> __ApplyUnifs unifs
+ let s2 = elistR |> Set.ofList
+ if (s1 = s2) then
+ Some(unifs)
+ else
+ __UnifLists elistL elistR
+ | SequenceExpr(elistL), SequenceExpr(elistR) when List.length elistL = List.length elistR ->
+ __UnifLists elistL elistR
+ | _ when l = r ->
+ Some(unifs)
+ | _ ->
+ let __TryUnifyPair x1 a1 x2 a2 unifs =
+ let builder = new Utils.CascadingBuilder<_>(None)
+ builder {
+ let! unifsLhs = SelectiveUnifyImplies okToUnifyFunc x1 a1 dir unifs
+ let! unifsRhs = SelectiveUnifyImplies okToUnifyFunc x2 a2 dir unifsLhs
+ return Some(unifsRhs)
+ }
+
+ // target implies candidate!
+ let rec ___f2 consequence premise unifs =
+ match consequence, premise with
+ // same operators + commutative -> try both
+ | BinaryExpr(_, opT, lhsT, rhsT), BinaryExpr(_, opC, lhsC, rhsC) when opT = opC && IsCommutativeOp opT ->
+ match __TryUnifyPair lhsC lhsT rhsC rhsT unifs with
+ | Some(x) -> Some(x)
+ | None -> __TryUnifyPair lhsC rhsT rhsC lhsT unifs
+ // operators are the same
+ | BinaryExpr(_, opT, lhsT, rhsT), BinaryExpr(_, opC, lhsC, rhsC) when opC = opT ->
+ __TryUnifyPair lhsC lhsT rhsC rhsT unifs
+ // operators are exactly the invers of one another
+ | BinaryExpr(_, opT, lhsT, rhsT), BinaryExpr(_, opC, lhsC, rhsC) when AreInverseOps opC opT ->
+ __TryUnifyPair lhsC rhsT rhsC lhsT unifs
+ //
+ | BinaryExpr(_, opT, lhsT, rhsT), BinaryExpr(_, opC, lhsC, rhsC) when DoesImplyOp opC opT ->
+ __TryUnifyPair lhsC lhsT rhsC rhsT unifs
+ | UnaryExpr(opC, subC), UnaryExpr(opP, subP) when opC = opP ->
+ SelectiveUnifyImplies okToUnifyFunc subP subC dir unifs
+ | SelectExpr(lstC, idxC), SelectExpr(lstP, idxP) ->
+ __TryUnifyPair lstP lstC idxP idxC unifs
+ | SeqLength(lstC), SeqLength(lstP) ->
+ SelectiveUnifyImplies okToUnifyFunc lstP lstC dir unifs
+ | Dot(exprC, fldNameC), Dot(exprP, fldNameP) when fldNameC = fldNameP ->
+ SelectiveUnifyImplies okToUnifyFunc exprP exprC dir unifs
+ | _ -> None
+
+ let rec ___f1 targetLst candidateLst unifs =
+ match targetLst, candidateLst with
+ | targetExpr :: targetRest, candExpr :: candRest ->
+ // trying to find a unification for "targetExpr"
+ let uOpt = match ___f2 targetExpr candExpr unifs with
+ // found -> just return
+ | Some(unifs2) -> Some(unifs2)
+ // not found -> keep looking in the rest of the candidate expressions
+ | None -> ___f1 [targetExpr] candRest unifs
+ match uOpt with
+ // found -> try find for the rest of the target expressions
+ | Some(unifs2) -> ___f1 targetRest candidateLst unifs2
+ // not found -> fail
+ | None -> None
+ | targetExpr :: _, [] ->
+ // no more candidates for unification for this targetExpr -> fail
+ None
+ | [], _ ->
+ // we've found unifications for all target expressions -> return the current unifications map
+ Some(unifs)
+
+ let __HasSetExpr e = DescendExpr2 (fun ex acc -> if acc then true else match ex with SetExpr(_) -> true | _ -> false) e false
+ let __PreprocSplitSort e = e |> DesugarAndRemove |> DistributeNegation |> SplitIntoConjunts |> List.sortBy (fun e -> if __HasSetExpr e then 1 else 0)
+ let lhsConjs = lhs |> __PreprocSplitSort
+ let rhsConjs = rhs |> __PreprocSplitSort
+ ___f1 rhsConjs lhsConjs unifs
+ with
+ | CannotUnify
+ | KeyAlreadyExists -> None
+
+let UnifyImplies lhs rhs dir unifs = SelectiveUnifyImplies (fun e -> true) lhs rhs dir unifs
+
+////////////////////////////////////////////
+
+let rec ComputeClosure heapInst expandExprFunc premises =
+ let bogusExpr = VarLiteral("!@#$%^&*()")
+
+ let ApplyUnifs unifs expr =
+ Rewrite (function
+ | VarLiteral(id) when unifs |> Map.containsKey id ->
+ Some(unifs |> Map.find id)
+ | _ -> None
+ ) expr
+
+ let FindMatches expr except premises =
+ //Logger.TraceLine ("finding matches for: " + (PrintExpr 0 expr) + "; #premises = " + (Set.count premises |> sprintf "%i"))
+ let okToUnifyFunc = fun (varName: string) -> varName.StartsWith("$")
+ if expr = TrueLiteral then
+ []
+ else
+ let matches =
+ premises |> Set.toList
+ |> List.choose (function BinaryExpr(_,"=",lhs,rhs) ->
+ if lhs = expr && not (rhs = except) then
+ Some(rhs)
+ elif rhs = expr && not (lhs = except) then
+ Some(lhs)
+ else
+ match SelectiveUnifyImplies okToUnifyFunc lhs expr LTR Map.empty with
+ | Some(unifs) -> Some(ApplyUnifs unifs rhs)
+ | None ->
+ match SelectiveUnifyImplies okToUnifyFunc rhs expr LTR Map.empty with
+ | Some(unifs) -> Some(ApplyUnifs unifs lhs)
+ | None -> None
+ | _ -> None)
+ //Logger.TraceLine (sprintf "Number of matches for %s: %i" (PrintExpr 0 expr) (List.length matches))
+ matches
+
+ let MySetAdd expr set =
+ let x = Printer.PrintExpr 0 expr
+ if x.Contains("$") || not (expandExprFunc expr) then
+ set
+ else
+ match expr with
+ | BinaryExpr(p,op,lhs,rhs) when IsCommutativeOp op && Set.contains (BinaryExpr(p,op,rhs,lhs)) set -> set
+ | BinaryExpr(p,op,lhs,rhs) when IsCommutativeOp op && rhs = lhs -> set
+ | _ -> Set.add expr set
+
+ let SelectExprCombinerFunc lst idx =
+ // distribute the indexing operation if possible
+ let rec __fff lst idx =
+ //Logger.TraceLine ("SelectExpr fff for " + (PrintExpr 0 lst))
+ let selExpr = SelectExpr(lst, idx)
+ match lst with
+ | BinaryExpr(_,"+",lhs,rhs) ->
+ let idxVal = EvalFull heapInst idx |> Expr2Int
+ let lhsVal = EvalFull heapInst lhs |> Expr2List
+ let rhsVal = EvalFull heapInst rhs |> Expr2List
+ if idxVal < List.length lhsVal then
+ __fff lhs idx
+ else
+ __fff rhs (BinarySub idx (IntLiteral(List.length lhsVal)))
+ | SequenceExpr(elist) ->
+ let idxVal = EvalFull heapInst idx |> Expr2Int
+ [elist.[idxVal]]
+ | _ -> [selExpr]
+ __fff lst idx
+
+ let SeqLenCombinerFunc lst =
+ // distribute the SeqLength operation if possible
+ let rec __fff lst =
+ //Logger.TraceLine ("SeqLen fff for " + (PrintExpr 0 lst))
+ let lenExpr = SeqLength(lst)
+ match lst with
+ | BinaryExpr(_,"+",lhs,rhs) ->
+ BinaryAdd (__fff lhs) (__fff rhs) //TODO: this ought to be incorrect!
+ | SequenceExpr(elist) ->
+ IntLiteral(List.length elist)
+ | _ -> lenExpr
+ [__fff lst]
+
+ let BinaryInCombiner lhs rhs =
+ // distribute the "in" operation if possible
+ let rec __fff lhs rhs =
+ //Logger.TraceLine ("In fff for " + (PrintExpr 0 lhs) + " and " + (PrintExpr 0 rhs))
+ let binInExpr = BinaryIn lhs rhs
+// match rhs with
+// | BinaryExpr(_,"+",BinaryExpr(_,"+",SetExpr(_), Dot(_)), Dot(_)) -> Logger.Trace ""
+// | _ -> ()//TODO: remove
+
+ match rhs with
+ | BinaryExpr(_,"+",l,r) ->
+// let lhsVal = EvalFull heapInst lhs
+// let lVal = EvalFull heapInst l
+// let rVal = EvalFull heapInst r
+// match lVal,rVal with
+// | SequenceExpr(elist), _ | _, SequenceExpr(elist)
+// | SetExpr(elist), _ | _, SetExpr(elist) ->
+// if elist |> Utils.ListContains lhsVal then
+// __fff lhs l
+// else
+// __fff lhs r
+// | _ -> [binInExpr]
+///////////////////////////////
+// [BinaryOr (BinaryIn lhs l) (BinaryIn lhs r)]
+ let opt1 = BinaryIn lhs l
+ let opt2 = BinaryIn lhs r
+ match EvalFull heapInst opt1 with
+ | BoolLiteral(true) -> [opt1]
+ | _ -> match EvalFull heapInst opt2 with
+ | BoolLiteral(true) -> [opt2]
+ | _ -> Utils.ListCombine BinaryOr (__fff lhs l) (__fff lhs r)
+ //[BinaryOr (BinaryIn lhs l)(BinaryIn lhs r)]
+ | SequenceExpr(elist) ->
+ let len = elist |> List.length
+ if len = 0 then
+ [FalseLiteral]
+ elif len = 1 then
+ [BinaryEq lhs elist.[0]]
+ else
+ let lhsVal = EvalFull heapInst lhs
+ let lst0Val = EvalFull heapInst elist.[0]
+ if lhsVal = lst0Val then
+ [BinaryEq lhs elist.[0]]
+ else
+ __fff lhs (SequenceExpr(elist |> List.tail))
+ //[BinaryIn lhs (SequenceExpr(elist |> List.tail))]
+ | SetExpr(elist) ->
+ let evalElist = elist |> List.map (EvalFull heapInst)
+ let evalLhs = EvalFull heapInst lhs
+ try
+ let idx = evalElist |> List.findIndex (fun e -> e = evalLhs)
+ [BinaryEq lhs elist.[idx]]
+ with
+ | _ -> [binInExpr]
+ | _ -> [binInExpr]
+ __fff lhs rhs
+
+ let BinaryNotInCombiner lhs rhs =
+ // distribute the "!in" operation if possible
+ let rec __fff lhs rhs =
+ //Logger.TraceLine ("NotIn fff for " + (PrintExpr 0 lhs) + " and " + (PrintExpr 0 rhs))
+ let binNotInExpr = BinaryNotIn lhs rhs
+ match rhs with
+ | BinaryExpr(_,"+",l,r) ->
+// let lhsVal = EvalFull heapInst lhs
+// let lVal = EvalFull heapInst l
+// let rVal = EvalFull heapInst r
+// match lVal,rVal with
+// | SequenceExpr(elistL), SequenceExpr(elistR)
+// | SetExpr(elistL), SetExpr(elistR) ->
+// (__fff lhs l) @
+// (__fff lhs r)
+// | _ -> [binNotInExpr]
+ __fff lhs l @ __fff lhs r
+ | SequenceExpr(elist) ->
+ let len = elist |> List.length
+ if len = 0 then
+ [TrueLiteral]
+ elif len = 1 then
+ [BinaryNeq lhs elist.[0]]
+ else
+ let lhsVal = EvalFull heapInst lhs
+ let lst0Val = EvalFull heapInst elist.[0]
+ [BinaryNeq lhs elist.[0]] @
+ __fff lhs (SequenceExpr(elist |> List.tail))
+ //[BinaryNotIn lhs (SequenceExpr(elist |> List.tail))]
+ | _ -> [binNotInExpr]
+ __fff lhs rhs
+
+ let rec __CombineAllMatches expr premises =
+ //Logger.TraceLine ("Combining all matches for: " + (PrintExpr 0 expr))
+ let lst0 = FindMatches expr bogusExpr premises
+ let lstCombined =
+ match expr with
+ | BinaryExpr(p,op,lhs,rhs) ->
+ let lhsMatches = __CombineAllMatches lhs premises
+ let rhsMatches = __CombineAllMatches rhs premises
+ let lst1 = Utils.ListCombine (fun e1 e2 -> BinaryExpr(p,op,e1,e2)) lhsMatches rhsMatches
+ let lst2 =
+ if op = "in" then
+ Utils.ListCombineMult BinaryInCombiner lhsMatches rhsMatches
+ elif op = "!in" then
+ Utils.ListCombineMult BinaryNotInCombiner lhsMatches rhsMatches
+ else
+ []
+ lst1 @ lst2
+ | UnaryExpr(op,sub) ->
+ __CombineAllMatches sub premises |> List.map (fun e -> UnaryExpr(op,e))
+ | SelectExpr(lst,idx) ->
+ let lstMatches = __CombineAllMatches lst premises
+ let idxMatches = __CombineAllMatches idx premises
+ Utils.ListCombineMult SelectExprCombinerFunc lstMatches idxMatches
+ | SeqLength(lst) ->
+ __CombineAllMatches lst premises |> List.map SeqLenCombinerFunc |> List.concat
+ // TODO: other cases
+ | _ -> []
+ expr :: (lst0 @ lstCombined)
+
+ let rec __ExpandPremise expr premises =
+ let __AddToPremisses exprLst premises = exprLst |> List.fold (fun acc e -> MySetAdd e acc) premises
+ let allMatches = lazy(__CombineAllMatches expr premises)
+ match expr with
+ | BinaryExpr(p,op,lhs,rhs) when IsRelationalOp op ->
+ let x = allMatches.Force()
+ __AddToPremisses x premises
+ | SelectExpr(lst, idx) ->
+ let x = allMatches.Force()
+ __AddToPremisses x premises
+ | _ -> premises
+
+ let rec __Iter exprLst premises =
+ match exprLst with
+ | expr :: rest ->
+ let newPremises =
+ if expandExprFunc expr then
+ //Logger.TraceLine ("expanding " + (PrintExpr 0 expr))
+ __ExpandPremise expr premises
+ else
+ premises
+ __Iter rest newPremises
+ | [] -> premises
+
+ (* --- function body starts here --- *)
+ let iterOnceFunc p = __Iter (p |> Set.toList) p
+ // TODO: iterate only 3 times, instead of to full closure
+ let p1 = iterOnceFunc premises
+ let p2 = p1 |> iterOnceFunc
+ //let p3 = p2 |> iterOnceFunc
+ p2
+// let premises' = iterOnceFunc premises
+// if premises' = premises then
+// premises'
+// else
+// Logger.TraceLine "-------closure----------------"
+// //premises' |> Set.iter (fun e -> Logger.TraceLine (Printer.PrintExpr 0 e))
+// ComputeClosure heapInst expandExprFunc premises'
+
+
diff --git a/Source/Jennisys/Getters.fs b/Source/Jennisys/Getters.fs
new file mode 100644
index 00000000..2e2732af
--- /dev/null
+++ b/Source/Jennisys/Getters.fs
@@ -0,0 +1,284 @@
+module Getters
+
+open Ast
+
+let RenameToOld name =
+ "old_" + name
+
+let RenameFromOld (name: string) =
+ if name.StartsWith("old_") then
+ name.Substring(4)
+ else
+ name
+
+// --- search functions ---
+
+// ==================================
+/// Returns variable name
+// ==================================
+let GetVarName var =
+ match var with
+ | Var(name,_,_) -> name
+
+let GetExtVarName var =
+ match var with
+ | Var(id, _, false) -> id
+ | Var(id, _, true) -> RenameToOld id
+
+let IsOldVar var =
+ match var with
+ | Var(_,_,isOld) -> isOld
+
+// ==================================
+/// Returns variable type
+// ==================================
+let GetVarType var =
+ match var with
+ | Var(_,t,_) -> t
+
+// ===============================================
+/// Returns whether there exists a variable
+/// in a given VarDecl list with a given name (id)
+// ===============================================
+let IsInVarList varLst id =
+ varLst |> List.exists (fun var -> GetVarName var = id)
+
+
+// =========================================================
+/// Out of all "members" returns only those that are "Field"s
+// =========================================================
+let FilterFieldMembers members =
+ members |> List.choose (function Field(vd) -> Some(vd) | _ -> None)
+
+// =============================================================
+/// Out of all "members" returns only those that are constructors
+// =============================================================
+let FilterConstructorMembers members =
+ members |> List.choose (function Method(_,_,_,_, true) as m -> Some(m) | _ -> None)
+
+// =============================================================
+/// Out of all "members" returns only those that are
+/// constructors and have at least one input parameter
+// =============================================================
+let FilterConstructorMembersWithParams members =
+ members |> List.choose (function Method(_,Sig(ins,outs),_,_, true) as m when not (List.isEmpty ins) -> Some(m) | _ -> None)
+
+// ==========================================================
+/// Out of all "members" returns only those that are "Method"s
+// ==========================================================
+let FilterMethodMembers members =
+ members |> List.choose (function Method(_,_,_,_,_) as m -> Some(m) | _ -> None)
+
+// =======================================================================
+/// Returns all members of the program "prog" that pass the filter "filter"
+// =======================================================================
+let FilterMembers prog filter =
+ match prog with
+ | Program(components) ->
+ components |> List.fold (fun acc comp ->
+ match comp with
+ | Component(Interface(_,_,members),_,_) -> List.concat [acc ; members |> filter |> List.choose (fun m -> Some(comp, m))]
+ | _ -> acc) []
+
+let GetAbstractFields comp =
+ match comp with
+ | Component(Interface(_,_,members), _, _) -> FilterFieldMembers members
+ | _ -> failwithf "internal error: invalid component: %O" comp
+
+let GetConcreteFields comp =
+ match comp with
+ | Component(_, DataModel(_,_,cVars,_,_), _) -> cVars
+ | _ -> failwithf "internal error: invalid component: %O" comp
+
+// =================================
+/// Returns all fields of a component
+// =================================
+let GetAllFields comp =
+ List.concat [GetAbstractFields comp; GetConcreteFields comp]
+
+// ===========================================================
+/// Returns a map (Type |--> Set<Var>) where all
+/// the given fields are grouped by their type
+///
+/// ensures: forall v :: v in ret.values.elems ==> v in fields
+/// ensures: forall k :: k in ret.keys ==>
+/// forall v1, v2 :: v1, v2 in ret[k].elems ==>
+/// v1.type = v2.type
+// ===========================================================
+let rec GroupFieldsByType fields =
+ match fields with
+ | Var(name, ty, old) :: rest ->
+ let map = GroupFieldsByType rest
+ let fldSet = Map.tryFind ty map |> Utils.ExtractOptionOr Set.empty
+ map |> Map.add ty (fldSet |> Set.add (Var(name, ty, old)))
+ | [] -> Map.empty
+
+let IsConcreteField comp fldName = GetConcreteFields comp |> List.exists (fun var -> GetVarName var = fldName)
+let IsAbstractField comp fldName = GetAbstractFields comp |> List.exists (fun var -> GetVarName var = fldName)
+
+// =================================
+/// Returns class name of a component
+// =================================
+let GetClassName comp =
+ match comp with
+ | Component(Interface(name,_,_),_,_) -> name
+ | _ -> failwith ("unrecognized component: " + comp.ToString())
+
+let GetClassType comp =
+ match comp with
+ | Component(Interface(name,typeParams,_),_,_) -> NamedType(name, typeParams)
+ | _ -> failwith ("unrecognized component: " + comp.ToString())
+
+// ========================
+/// Returns name of a method
+// ========================
+let GetMethodName mthd =
+ match mthd with
+ | Method(name,_,_,_,_) -> name
+ | _ -> failwith ("not a method: " + mthd.ToString())
+
+// ===========================================================
+/// Returns full name of a method (= <class_name>.<method_name>
+// ===========================================================
+let GetMethodFullName comp mthd =
+ (GetClassName comp) + "." + (GetMethodName mthd)
+
+// =============================
+/// Returns signature of a method
+// =============================
+let GetMethodSig mthd =
+ match mthd with
+ | Method(_,sgn,_,_,_) -> sgn
+ | _ -> failwith ("not a method: " + mthd.ToString())
+
+// =========================================================
+/// Returns all arguments of a method (both input and output)
+// =========================================================
+let GetSigVars sign =
+ match sign with
+ | Sig(ins, outs) -> List.concat [ins; outs]
+
+let GetMethodInArgs mthd =
+ match mthd with
+ | Method(_,Sig(ins, _),_,_,_) -> ins
+ | _ -> failwith ("not a method: " + mthd.ToString())
+
+let GetMethodOutArgs mthd =
+ match mthd with
+ | Method(_,Sig(_, outs),_,_,_) -> outs
+ | _ -> failwith ("not a method: " + mthd.ToString())
+
+let GetMethodArgs mthd =
+ let ins = GetMethodInArgs mthd
+ let outs = GetMethodOutArgs mthd
+ List.concat [ins; outs]
+
+let IsConstructor mthd =
+ match mthd with
+ | Method(_,_,_,_,isConstr) -> isConstr
+ | _ -> failwithf "expected a method but got %O" mthd
+
+let rec GetTypeShortName ty =
+ match ty with
+ | IntType -> "int"
+ | BoolType -> "bool"
+ | SetType(_) -> "set"
+ | SeqType(_) -> "seq"
+ | NamedType(n,_) | InstantiatedType(n,_) -> n
+
+// ==================================
+/// Returns component name
+// ==================================
+let GetComponentName comp =
+ match comp with
+ | Component(Interface(name,_,_),_,_) -> name
+ | _ -> failwithf "invalid component %O" comp
+
+let GetComponentTypeParameters comp =
+ match comp with
+ | Component(Interface(_,tp,_),_,_) -> tp
+ | _ -> failwithf "invalid component %O" comp
+
+
+// ==================================
+/// Returns all members of a component
+// ==================================
+let GetMembers comp =
+ match comp with
+ | Component(Interface(_,_,members),_,_) -> members
+ | _ -> failwith ("unrecognized component: " + comp.ToString())
+
+// ====================================================
+/// Finds a component of a program that has a given name
+// ====================================================
+let FindComponent (prog: Program) clsName =
+ match prog with
+ | Program(comps) -> comps |> List.filter (function Component(Interface(name,_,_),_,_) when name = clsName -> true | _ -> false)
+ |> Utils.ListToOption
+
+let FindComponentForType prog ty =
+ FindComponent prog (GetTypeShortName ty)
+
+let FindComponentForTypeOpt prog tyOpt =
+ match tyOpt with
+ | Some(ty) -> FindComponentForType prog ty
+ | None -> None
+
+let CheckSameCompType comp ty =
+ GetComponentName comp = GetTypeShortName ty
+
+let GetComponentType comp =
+ NamedType(GetComponentName comp, GetComponentTypeParameters comp)
+
+// ===================================================
+/// Finds a method of a component that has a given name
+// ===================================================
+let FindMethod comp methodName =
+ let x = GetMembers comp
+ let y = x |> FilterMethodMembers
+ let z = y |> List.filter (function Method(name,_,_,_,_) when name = methodName -> true | _ -> false)
+ GetMembers comp |> FilterMethodMembers |> List.filter (function Method(name,_,_,_,_) when name = methodName -> true | _ -> false)
+ |> Utils.ListToOption
+
+// ==============================================
+/// Finds a field of a class that has a given name
+// ==============================================
+//let FindCompVar prog clsName fldName =
+// let copt = FindComponent prog clsName
+// match copt with
+// | Some(comp) ->
+// GetAllFields comp |> List.filter (function Var(name,_) when name = fldName -> true | _ -> false)
+// |> Utils.ListToOption
+// | None -> None
+
+let FindVar comp fldName =
+ GetAllFields comp |> List.filter (fun var -> GetVarName var = fldName)
+ |> Utils.ListToOption
+
+// ======================================
+/// Returns the frame of a given component
+// ======================================
+let GetFrame comp =
+ match comp with
+ | Component(_, DataModel(_,_,_,frame,_), _) -> frame
+ | _ -> failwithf "not a valid component %O" comp
+
+let GetFrameFields comp =
+ let frame = GetFrame comp
+ frame |> List.choose (function IdLiteral(name) -> Some(name) | _ -> None) // TODO: is it really enough to handle only IdLiteral's
+ |> List.choose (fun varName ->
+ let v = FindVar comp varName
+ Utils.ExtractOptionMsg ("field not found: " + varName) v |> ignore
+ v
+ )
+
+// ==============================================
+/// Checks whether two given methods are the same.
+///
+/// Methods are the same if their names are the
+/// same and their components have the same name.
+// ==============================================
+let CheckSameMethods (c1,m1) (c2,m2) =
+ GetComponentName c1 = GetComponentName c2 && GetMethodName m1 = GetMethodName m2
+
+//////////////////////// \ No newline at end of file
diff --git a/Source/Jennisys/Jennisys.fs b/Source/Jennisys/Jennisys.fs
new file mode 100644
index 00000000..b10c9cfc
--- /dev/null
+++ b/Source/Jennisys/Jennisys.fs
@@ -0,0 +1,72 @@
+// This project type requires the F# PowerPack at http://fsharppowerpack.codeplex.com/releases
+// Learn more about F# at http://fsharp.net
+// Original project template by Jomo Fisher based on work of Brian McNamara, Don Syme and Matt Valerio
+// This posting is provided "AS IS" with no warranties, and confers no rights.
+module Main
+
+open System
+open System.IO
+open Microsoft.FSharp.Text.Lexing
+
+open Ast
+open AstUtils
+open Lexer
+open Options
+open Parser
+open Printer
+open TypeChecker
+open Analyzer
+
+let readAndProcess (filename: string) =
+ printfn "// Jennisys, Copyright (c) 2011, Microsoft."
+ // lex
+ let f = if filename = null then Console.In else new StreamReader(filename) :> TextReader
+ let lexbuf = LexBuffer<char>.FromTextReader(f)
+ lexbuf.EndPos <- { pos_bol = 0;
+ pos_fname=if filename = null then "stdin" else filename;
+ pos_cnum=0;
+ pos_lnum=1 }
+
+ let sprog =
+ try
+ // parse
+ Parser.start Lexer.tokenize lexbuf
+ with
+ | ex ->
+ let pos = lexbuf.EndPos
+ printfn " [PARSE ERROR]: %s(%d,%d): %s" pos.FileName pos.Line pos.Column ex.Message
+ Environment.Exit(1)
+ failwith ""
+ match TypeCheck sprog with
+ | None -> () // errors have already been reported
+ | Some(prog) ->
+ Analyze prog filename
+
+
+try
+ let args = Environment.GetCommandLineArgs()
+ ParseCmdLineArgs (List.ofArray args |> List.tail)
+ if CONFIG.breakIntoDebugger then ignore (System.Diagnostics.Debugger.Launch()) else ()
+ if CONFIG.help then
+ printfn "%s" PrintHelpMsg
+ else
+ if CONFIG.inputFilename = "" then
+ printfn "*** Error: No input file was specified."
+ else
+ readAndProcess CONFIG.inputFilename
+with
+ | InvalidCmdLineOption(msg)
+ | InvalidCmdLineArg(msg) as ex ->
+ printfn " [ERROR] %s" msg;
+ printfn "%s" PrintHelpMsg
+ | EvalFailed(msg) as ex ->
+ printfn " [EVALUATION ERROR] %s" msg
+ printfn "%O" ex.StackTrace
+
+//let mc = MethodOutSelect (MethodCall(IdLiteral("left"),"SetNode","Find",[VarLiteral("n")]), "ret")
+//let expr = BinaryOr (BinaryOr (BinaryEq (VarLiteral("a")) (VarLiteral("b"))) mc) (mc)
+//printfn "%s" (PrintExpr 0 expr)
+//printfn ""
+//
+//let stmt = ExprStmt(expr)
+//printfn "%s" (DafnyPrinter.PrintStmt stmt 0 false) \ No newline at end of file
diff --git a/Source/Jennisys/Jennisys.fsproj b/Source/Jennisys/Jennisys.fsproj
new file mode 100644
index 00000000..d3493749
--- /dev/null
+++ b/Source/Jennisys/Jennisys.fsproj
@@ -0,0 +1,118 @@
+<?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>
+ <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+ <ProductVersion>8.0.30703</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{f2ff4b3a-2fe8-474a-88df-6950f7d78908}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <RootNamespace>Language</RootNamespace>
+ <AssemblyName>Jennisys</AssemblyName>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <TargetFrameworkProfile>Client</TargetFrameworkProfile>
+ <Name>Language</Name>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <Tailcalls>false</Tailcalls>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <WarningLevel>3</WarningLevel>
+ <PlatformTarget>x86</PlatformTarget>
+ <DocumentationFile>bin\Debug\Language.XML</DocumentationFile>
+ <StartArguments>examples/oopsla12/IntSet.jen /method:IntSet.Singleton</StartArguments>
+ <StartWorkingDirectory>C:\boogie\Jennisys\Jennisys\</StartWorkingDirectory>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <Tailcalls>true</Tailcalls>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <WarningLevel>3</WarningLevel>
+ <PlatformTarget>x86</PlatformTarget>
+ <DocumentationFile>bin\Release\Language.XML</DocumentationFile>
+ </PropertyGroup>
+ <Import Project="$(MSBuildExtensionsPath32)\..\Microsoft F#\v4.0\Microsoft.FSharp.Targets" />
+ <Import Project="$(MSBuildExtensionsPath32)\..\FSharpPowerPack-2.0.0.0\bin\FSharp.PowerPack.targets" />
+ <PropertyGroup>
+ <FsLexOutputFolder>$(IntermediateOutputPath)</FsLexOutputFolder>
+ <FsYaccOutputFolder>$(IntermediateOutputPath)</FsYaccOutputFolder>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="SymGen.fs" />
+ <Compile Include="Logger.fs" />
+ <Compile Include="Utils.fs" />
+ <Compile Include="Options.fs" />
+ <Compile Include="PipelineUtils.fs" />
+ <Compile Include="Ast.fs" />
+ <Compile Include="Getters.fs" />
+ <Compile Include="AstUtils.fs" />
+ <Compile Include="$(IntermediateOutputPath)\Parser.fs">
+ <Visible>false</Visible>
+ <Link>Parser.fs</Link>
+ </Compile>
+ <Compile Include="$(IntermediateOutputPath)\Lexer.fs">
+ <Visible>false</Visible>
+ <Link>Lexer.fs</Link>
+ </Compile>
+ <Compile Include="DafnyModelUtils.fs" />
+ <Compile Include="EnvUtils.fs" />
+ <FsYacc Include="Parser.fsy">
+ <OtherFlags>--module Parser</OtherFlags>
+ </FsYacc>
+ <FsLex Include="Lexer.fsl">
+ <OtherFlags>--unicode</OtherFlags>
+ </FsLex>
+ <Compile Include="PrintUtils.fs" />
+ <Compile Include="Printer.fs" />
+ <Compile Include="DafnyPrinter.fs" />
+ <Compile Include="TypeChecker.fs" />
+ <Compile Include="Resolver.fs" />
+ <Compile Include="FixpointSolver.fs" />
+ <Compile Include="MethodUnifier.fs" />
+ <Compile Include="Modularizer.fs" />
+ <Compile Include="CodeGen.fs" />
+ <Compile Include="Analyzer.fs" />
+ <Compile Include="Jennisys.fs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Reference Include="Boogie">
+ <HintPath>..\..\Binaries\Boogie.exe</HintPath>
+ </Reference>
+ <Reference Include="FSharp.PowerPack">
+ <HintPath>C:\Program Files\FSharpPowerPack-1.9.9.9\bin\FSharp.PowerPack.dll</HintPath>
+ </Reference>
+ <Reference Include="mscorlib" />
+ <Reference Include="FSharp.Core" />
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Numerics" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\Source\Model\Model.csproj">
+ <Name>Model</Name>
+ <Project>{acef88d5-dadd-46da-bae1-2144d63f4c83}</Project>
+ <Private>True</Private>
+ </ProjectReference>
+ </ItemGroup>
+ <!--
+ <ItemGroup>
+ <ProjectReference Include="..\..\Source\Model\Model.csproj">
+ <Name>Model</Name>
+ <Project>{acef88d5-dadd-46da-bae1-2144d63f4c83}</Project>
+ <Private>True</Private>
+ </ProjectReference>
+ </ItemGroup>
+ -->
+ <!-- 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/Jennisys/Lexer.fsl b/Source/Jennisys/Lexer.fsl
new file mode 100644
index 00000000..e1d4795b
--- /dev/null
+++ b/Source/Jennisys/Lexer.fsl
@@ -0,0 +1,83 @@
+{
+module Lexer
+open System
+open Parser
+open Microsoft.FSharp.Text.Lexing
+
+let lexeme lexbuf =
+ LexBuffer<char>.LexemeString lexbuf
+}
+
+// These are some regular expression definitions
+let digit = ['0'-'9']
+let nondigit = [ 'a'-'z' 'A'-'Z' '_' ]
+let idchar = (nondigit | digit)
+let whitespace = [' ' '\t' ]
+let newline = ('\n' | '\r' '\n')
+
+rule tokenize = parse
+| whitespace { tokenize lexbuf }
+| newline { lexbuf.EndPos <- lexbuf.EndPos.NextLine; tokenize lexbuf }
+// TODO: | "//"[-newline]* { tokenize lexbuf }
+// keywords
+| "interface" { INTERFACE }
+| "datamodel" { DATAMODEL }
+| "code" { CODE }
+| "var" { VAR }
+| "constructor" { CONSTRUCTOR }
+| "method" { METHOD }
+| "frame" { FRAME }
+| "invariant" { INVARIANT }
+| "returns" { RETURNS }
+| "requires" { REQUIRES }
+| "ensures" { ENSURES }
+| "forall" { FORALL }
+// Types
+| "int" { INTTYPE }
+| "bool" { BOOLTYPE }
+| "seq" { SEQTYPE }
+| "set" { SETTYPE }
+// Operators
+| "..." { DOTDOTDOT }
+| ".." { DOTDOT }
+| "." { DOT }
+| "old" { OLD }
+| "+" { PLUS }
+| "-" { MINUS }
+| "*" { STAR }
+| "div" { DIV }
+| "mod" { MOD }
+| "&&" { AND }
+| "||" { OR }
+| "!" { NOT }
+| "==>" { IMPLIES }
+| "<==>" { IFF }
+| "<" { LESS }
+| "<=" { ATMOST }
+| "=" { EQ }
+| "!=" { NEQ }
+| ">=" { ATLEAST }
+| ">" { GREATER }
+| "in" { IN }
+| "!in" { NOTIN }
+// Misc
+| ":=" { GETS }
+| "(" { LPAREN }
+| ")" { RPAREN }
+| "[" { LBRACKET }
+| "]" { RBRACKET }
+| "{" { LCURLY }
+| "}" { RCURLY }
+| "|" { VERTBAR }
+| ":" { COLON }
+| "::" { COLONCOLON }
+| "," { COMMA }
+| "?" { QMARK }
+// Numberic constants
+| digit+ { INTEGER (System.Convert.ToInt32(lexeme lexbuf)) }
+// identifiers
+| idchar+ { ID (LexBuffer<char>.LexemeString lexbuf) }
+// EOF
+| eof { EOF }
+| _ { printfn "Unrecognized input character: %s" (lexeme lexbuf) ; EOF }
+
diff --git a/Source/Jennisys/Logger.fs b/Source/Jennisys/Logger.fs
new file mode 100644
index 00000000..dbf762cd
--- /dev/null
+++ b/Source/Jennisys/Logger.fs
@@ -0,0 +1,41 @@
+// #######################################################
+/// Simple logging facility
+///
+/// author: Aleksandar Milicevic (t-alekm@microsoft.com)
+// #######################################################
+
+module Logger
+
+let newline = System.Environment.NewLine
+
+let _ALL = 100
+let _TRACE = 90
+let _DEBUG = 70
+let _INFO = 50
+let _WARN = 40
+let _ERROR = 20
+let _NONE = 0
+
+let logLevel = _ALL
+
+let Log level msg =
+ if logLevel >= level then
+ printf "%s" msg
+
+let LogLine level msg =
+ Log level (msg + newline)
+
+let Trace msg = Log _TRACE msg
+let TraceLine msg = LogLine _TRACE msg
+
+let Debug msg = Log _DEBUG msg
+let DebugLine msg = LogLine _DEBUG msg
+
+let Info msg = Log _INFO msg
+let InfoLine msg = LogLine _INFO msg
+
+let Warn msg = Log _WARN msg
+let WarnLine msg = LogLine _WARN msg
+
+let Error msg = Log _ERROR msg
+let ErrorLine msg = LogLine _ERROR msg \ No newline at end of file
diff --git a/Source/Jennisys/MethodUnifier.fs b/Source/Jennisys/MethodUnifier.fs
new file mode 100644
index 00000000..d2b1db68
--- /dev/null
+++ b/Source/Jennisys/MethodUnifier.fs
@@ -0,0 +1,107 @@
+module MethodUnifier
+
+open Ast
+open Getters
+open AstUtils
+open FixpointSolver
+open PrintUtils
+open Resolver
+open Utils
+
+let TryUnify targetMthd candMethod =
+ let targetPre,targetPost = GetMethodPrePost targetMthd
+ let targetPre = BinaryAnd targetPre (GetMethodGhostPrecondition targetMthd)
+ let candPre,candPost = GetMethodPrePost candMethod
+ let candPre = BinaryAnd candPre (GetMethodGhostPrecondition candMethod)
+ let builder = new CascadingBuilder<_>(None)
+ builder {
+ let! unifs1 = UnifyImplies targetPre candPre RTL Map.empty
+ let! unifs2 = UnifyImplies candPost targetPost LTR unifs1
+ return Some(unifs2)
+ }
+
+let rec TryFindAMatch targetMthd candidateMethods =
+ let targetMthdName = GetMethodName targetMthd
+ match candidateMethods with
+ | candMthd :: rest ->
+ if GetMethodName candMthd = targetMthdName then
+ // skip if it is the same method
+ TryFindAMatch targetMthd rest
+ else
+ match TryUnify targetMthd candMthd with
+ | Some(unifs) -> Some(candMthd,unifs)
+ | None -> TryFindAMatch targetMthd rest
+ | [] -> None
+
+let TryFindExistingOpt comp targetMthd =
+ TryFindAMatch targetMthd (GetMembers comp |> FilterMethodMembers)
+
+let TryFindExisting comp targetMthd =
+ match TryFindAMatch targetMthd (GetMembers comp |> FilterMethodMembers) with
+ | Some(m,unifs) -> m,unifs
+ | None -> targetMthd, Map.empty
+
+let ApplyMethodUnifs receiver (c,m) unifs =
+ let __Apply args = args |> List.map (fun var ->
+ let name = GetExtVarName var
+ match Map.tryFind name unifs with
+ | Some(e) -> e
+ | None -> VarLiteral(name))
+ let ins = GetMethodInArgs m |> __Apply
+ let outs = GetMethodOutArgs m |> __Apply
+
+ let retVars, asgs = outs |> List.fold (fun (acc1,acc2) e ->
+ let vname = SymGen.NewSymFake e
+ let v = Var(vname, None, false)
+ let acc1' = acc1 @ [v]
+ let acc2' = acc2 @ [ArbitraryStatement(Assign(VarLiteral(vname), e))]
+ acc1', acc2'
+ ) ([],[])
+ let mcallExpr = MethodCall(receiver, GetComponentName c, GetMethodName m, ins)
+ match retVars, outs with
+ | [], [] -> [ArbitraryStatement(ExprStmt(mcallExpr))]
+ | [_], [VarLiteral(vn2)] -> [ArbitraryStatement(Assign(VarDeclExpr([Var(vn2, None, false)], false), mcallExpr))]
+ | _ ->
+ let mcall = ArbitraryStatement(Assign(VarDeclExpr(retVars, true), mcallExpr))
+ mcall :: asgs
+
+// ====================================================
+///
+// ====================================================
+let TryFindExistingAndConvertToSolution indent comp m cond callGraph =
+ let __Calls caller callee =
+ let keyOpt = callGraph |> Map.tryFindKey (fun (cc,mm) mset -> CheckSameMethods (comp,caller) (cc,mm))
+ match keyOpt with
+ | Some(k) -> callGraph |> Map.find k |> Set.contains ((GetComponentName comp),(GetMethodName callee))
+ | None -> false
+ (* --- function body starts here --- *)
+ if not Options.CONFIG.genMod then
+ None
+ else
+ let idt = Indent indent
+ let candidateMethods = GetMembers comp |> List.filter (fun cm ->
+ match cm with
+ | Method(mname,_,_,_,_) when not (__Calls cm m) -> true
+ | _ -> false)
+ match TryFindAMatch m candidateMethods with
+ | Some(m',unifs) ->
+ Logger.InfoLine (idt + " - substitution method found:")
+ Logger.InfoLine (Printer.PrintMethodSignFull (indent+6) comp m')
+ Logger.DebugLine (idt + " Unifications: ")
+ let idtt = idt + " "
+ unifs |> Map.fold (fun acc k v -> acc + (sprintf "%s%s -> %s%s" idtt k (Printer.PrintExpr 0 v) newline)) "" |> Logger.Debug
+ let obj = { name = "this"; objType = GetClassType comp }
+ let modObjs = if IsModifiableObj obj (comp,m) then Set.singleton obj else Set.empty
+ let body = ApplyMethodUnifs ThisLiteral (comp,m') unifs
+ let hInst = { objs = Utils.MapSingleton obj.name obj;
+ modifiableObjs = modObjs;
+ assignments = body;
+ concreteValues = body;
+ methodArgs = Map.empty;
+ methodRetVals = Map.empty;
+ concreteMethodRetVals = Map.empty;
+ globals = Map.empty }
+ Some(Map.empty |> Map.add (comp,m) [cond, hInst]
+ |> Map.add (comp,m') [])
+ | None -> None
+
diff --git a/Source/Jennisys/Modularizer.fs b/Source/Jennisys/Modularizer.fs
new file mode 100644
index 00000000..f5d7e7b7
--- /dev/null
+++ b/Source/Jennisys/Modularizer.fs
@@ -0,0 +1,206 @@
+module Modularizer
+
+open Ast
+open Getters
+open AstUtils
+open MethodUnifier
+open PrintUtils
+open Resolver
+open Utils
+
+// =======================================================================
+/// Merges two solution maps so that if there are multiple entries for a
+/// single (comp,method) pair it concatenates them (corresponds to multiple
+/// branches).
+// =======================================================================
+let MergeSolutions sol1 sol2 =
+ let rec __Merge sol1map sol2lst res =
+ match sol2lst with
+ | ((c2,m2), lst2) :: rest ->
+ match sol1map |> Map.tryFindKey (fun (c1,m1) lst1 -> CheckSameMethods (c1,m1) (c2,m2)) with
+ | Some(c1,m1) ->
+ let lst1 = sol1map |> Map.find(c1,m1)
+ let newRes = res |> Map.add (c1,m1) (lst1@lst2)
+ __Merge sol1map rest newRes
+ | None ->
+ let newRes = res |> Map.add (c2,m2) lst2
+ __Merge sol1map rest newRes
+ | [] -> res
+ (* --- function body starts here --- *)
+ __Merge sol1 (sol2 |> Map.toList) sol1
+
+// ===========================================
+///
+// ===========================================
+let rec MakeModular indent prog comp meth cond hInst callGraph =
+ let directChildren = lazy (GetDirectModifiableChildren hInst)
+
+ let __IsAbstractField ty var =
+ let builder = CascadingBuilder<_>(false)
+ let varName = GetVarName var
+ builder {
+ let! comp = FindComponent prog (GetTypeShortName ty)
+ let! fld = GetAbstractFields comp |> List.fold (fun acc v -> if GetVarName v = varName then Some(varName) else acc) None
+ return true
+ }
+
+ let __FindObj objName =
+ try
+ //hInst.assignments |> List.find (fun ((obj,_),_) -> obj.name = objName) |> fst |> fst
+ hInst.assignments |> List.choose (function FieldAssignment((obj,_),_) ->
+ if (obj.name = objName) then Some(obj) else None
+ | _ -> None)
+ |> List.head
+ with
+ | ex -> failwithf "obj %s not found for method %s" objName (GetMethodFullName comp meth)
+
+ let __GetObjLitType objLitName =
+ (__FindObj objLitName).objType
+
+ // ===============================================================================
+ /// Goes through the assignments of the heapInstance and returns only those
+ /// assignments that correspond to abstract fields of the given "objLitName" object
+ // ===============================================================================
+ let __GetAbsFldAssignments objLitName =
+ hInst.assignments |> List.choose (function
+ FieldAssignment ((obj,var),e) ->
+ if obj.name = objLitName && __IsAbstractField obj.objType var then
+ Some(var,e)
+ else
+ None
+ | _ -> None)
+
+ // ===============================================================================
+ /// The given assignment is:
+ /// x := e
+ ///
+ /// If e is an object (e.g. gensym32) with e.g. two abstract fields "a" and "b",
+ /// with values 3 and 8 respectively, then the "x := e" spec is fixed as following:
+ /// x.a := 3 && x.b := 8
+ ///
+ /// List values are handled similarly, e.g.:
+ /// x := [gensym32]
+ /// is translated into
+ /// |x| = 1 && x[0].a = 3 && x[0].b = 8
+ // ===============================================================================
+ let rec __ExamineAndFix x e =
+ match e with
+ | ObjLiteral(id) when not (Utils.ListContains e (directChildren.Force())) -> //TODO: is it really only non-direct children?
+ let absFlds = __GetAbsFldAssignments id
+ absFlds |> List.fold (fun acc (var,vval) -> BinaryAnd acc (BinaryEq (Dot(x, GetVarName var)) vval)) TrueLiteral
+ | SequenceExpr(elist) ->
+ let rec __fff lst acc cnt =
+ match lst with
+ | fsExpr :: rest ->
+ let acc = BinaryAnd acc (__ExamineAndFix (SelectExpr(x, IntLiteral(cnt))) fsExpr)
+ __fff rest acc (cnt+1)
+ | [] ->
+ let lenExpr = BinaryEq (SeqLength(x)) (IntLiteral(cnt))
+ BinaryAnd lenExpr acc
+ __fff elist TrueLiteral 0
+ | _ -> BinaryEq x e
+
+ // ================================================================================
+ /// The spec for an object consists of assignments to its abstract fields with one
+ /// caveat: if some assignments include non-direct children objects of "this", then
+ /// those objects cannot be used directly in the spec; instead, their properties must
+ /// be expanded and embeded (that's what the "ExamineAndFix" function does)
+ // ================================================================================
+ let __GetSpecFor objLitName =
+ let absFieldAssignments = __GetAbsFldAssignments objLitName
+ let absFldAssgnExpr = absFieldAssignments |> List.fold (fun acc (var,e) -> BinaryAnd acc (__ExamineAndFix (IdLiteral(GetVarName var)) e)) TrueLiteral
+ let retValExpr = hInst.methodRetVals |> Map.fold (fun acc varName varValueExpr -> BinaryAnd acc (BinaryEq (VarLiteral(varName)) varValueExpr)) TrueLiteral
+ BinaryAnd absFldAssgnExpr retValExpr
+
+ // ================================================================================================
+ /// Simply traverses a given expression and returns all arguments of the "meth" method that are used
+ // ================================================================================================
+ let __GetArgsUsed expr =
+ let args = GetMethodArgs meth
+ let argSet = DescendExpr2 (fun e acc ->
+ match e with
+ | VarLiteral(vname) ->
+ match args |> List.tryFind (fun var -> GetVarName var = vname) with
+ | Some(var) -> acc |> Set.add var
+ | None -> acc
+ | _ -> acc
+ ) expr Set.empty
+ argSet |> Set.toList
+
+ let rec __GetDelegateMethods objs acc =
+ match objs with
+ | ObjLiteral(name) as obj :: rest ->
+ let mName = sprintf "_synth_%s_%s" (GetMethodFullName comp meth |> String.map (fun c -> if c = '.' then '_' else c)) name
+ let pre,_ = GetMethodPrePost meth //TrueLiteral
+ let post = __GetSpecFor name
+ let ins = __GetArgsUsed (BinaryAnd pre post)
+ let sgn = Sig(ins, [])
+ let m = Method(mName, sgn, pre, post, true)
+ let c = FindComponent prog (name |> __GetObjLitType |> GetTypeShortName) |> Utils.ExtractOption
+ let m',unifs = TryFindExisting c m
+ let args = ApplyMethodUnifs obj (c,m') unifs
+ __GetDelegateMethods rest (acc |> Map.add obj (c,m',args))
+ | _ :: rest -> failwith "internal error: expected to see only ObjLiterals"
+ | [] -> acc
+
+ // =======================================================================
+ /// Tries to make a given solution for a given method into more modular,
+ /// by delegating some statements (initialization of inner objects) to
+ /// method calls.
+ // =======================================================================
+ let __GetModularBranch =
+ let delegateMethods = __GetDelegateMethods (directChildren.Force()) Map.empty
+ let initChildrenExprList = delegateMethods |> Map.toList
+ |> List.map (fun (_, (_,_,asgs)) -> asgs)
+ |> List.concat
+ let newAssgns = hInst.assignments |> List.filter (function FieldAssignment((obj,_),_) -> obj.name = "this" | _ -> false)
+ let newMethodsLst = delegateMethods |> Map.fold (fun acc receiver (c,newMthd,_) ->
+ (c,newMthd) :: acc
+ ) []
+ newMethodsLst, { hInst with assignments = initChildrenExprList @ newAssgns }
+
+ (* --- function body starts here --- *)
+ let idt = Indent indent
+ if Options.CONFIG.genMod then
+ Logger.InfoLine (idt + " - delegating to method calls ...")
+ // first try to find a match for the entire method (based on the given solution)
+ let postSpec = __GetSpecFor "this"
+ let meth' = match meth with
+ | Method (mname, msig, mpre, _, isConstr) -> Method(mname, msig, mpre, postSpec, isConstr)
+ | _ -> failwithf "internal error: expected a Method but got %O" meth
+ match TryFindExistingAndConvertToSolution indent comp meth' cond callGraph with
+ | Some(sol) -> sol |> FixSolution comp meth
+ | None ->
+ // if not found, try to split into parts
+ let newMthdLst, newHeapInst = __GetModularBranch
+ let msol = Utils.MapSingleton (comp,meth) [cond, newHeapInst]
+ newMthdLst |> List.fold (fun acc (c,m) ->
+ acc |> MergeSolutions (Utils.MapSingleton (c,m) [])
+ ) msol
+ else
+ Utils.MapSingleton (comp,meth) [cond, hInst]
+
+//let GetModularSol prog sol =
+// let comp = fst (fst sol)
+// let meth = snd (fst sol)
+// let rec __xxx prog lst =
+// match lst with
+// | (cond, hInst) :: rest ->
+// let newProg, newComp, newMthdLst, newhInst = GetModularBranch prog comp meth hInst
+// let newProg, newRest = __xxx newProg rest
+// newProg, ((cond, newhInst) :: newRest)
+// | [] -> prog, []
+// let newProg, newSolutions = __xxx prog (snd sol)
+// let newComp = FindComponent newProg (GetComponentName comp) |> Utils.ExtractOption
+// newProg, ((newComp, meth), newSolutions)
+//
+//let Modularize prog solutions =
+// let rec __Modularize prog sols acc =
+// match sols with
+// | sol :: rest ->
+// let (newProg, newSol) = GetModularSol prog sol
+// let newAcc = acc |> Map.add (fst newSol) (snd newSol)
+// __Modularize newProg rest newAcc
+// | [] -> (prog, acc)
+// (* --- function body starts here --- *)
+// __Modularize prog (Map.toList solutions) Map.empty
diff --git a/Source/Jennisys/Options.fs b/Source/Jennisys/Options.fs
new file mode 100644
index 00000000..fe640f48
--- /dev/null
+++ b/Source/Jennisys/Options.fs
@@ -0,0 +1,162 @@
+// ####################################################################
+/// This module is intended to store and handle configuration options
+///
+/// author: Aleksandar Milicevic (t-alekm@microsoft.com)
+// ####################################################################
+
+module Options
+
+open Utils
+
+type Config = {
+ help : bool;
+ inputFilename : string;
+ methodToSynth : string;
+ constructorsOnly : bool;
+ inferConditionals : bool;
+ verifyPartialSolutions : bool;
+ verifySolutions : bool;
+ checkUnifications : bool;
+ genRepr : bool;
+ genMod : bool;
+ timeout : int;
+ numLoopUnrolls : int;
+ recursiveValid : bool;
+ breakIntoDebugger : bool;
+ minimizeGuards : bool;
+}
+
+type CfgOption<'a> = {
+ optionName: string;
+ optionType: string;
+ optionSetter: 'a -> Config -> Config;
+ descr: string;
+}
+
+exception InvalidCmdLineArg of string
+exception InvalidCmdLineOption of string
+
+let CheckNonEmpty value optName =
+ if value = "" then raise (InvalidCmdLineArg("A value for option " + optName + " must not be empty")) else value
+
+let CheckInt value optName =
+ try
+ System.Int32.Parse value
+ with
+ | ex -> raise (InvalidCmdLineArg("A value for option " + optName + " must be a boolean"))
+
+let CheckBool value optName =
+ if value = "" then
+ true
+ else
+ try
+ System.Boolean.Parse value
+ with
+ | ex -> raise (InvalidCmdLineArg("A value for option " + optName + " must be an integer"))
+
+let cfgOptions = [
+ { optionName = "help"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with help = CheckBool v "help"}); descr = "prints out the available switches"; }
+ { optionName = "method"; optionType = "string"; optionSetter = (fun v (cfg: Config) -> {cfg with methodToSynth = CheckNonEmpty v "method"}); descr = "select methods to synthesize; method names are in the form <ClassName>.<MethodName>; multiple methods can be given as a list of comma separated values"; }
+ { optionName = "constrOnly"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with constructorsOnly = CheckBool v "constrOnly"}); descr = "synthesize constructors only"; }
+ { optionName = "inferConds"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with inferConditionals = CheckBool v "inferConds"}); descr = "try to infer conditions"; }
+ { optionName = "noInferConds"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with inferConditionals = not (CheckBool v "inferConds")}); descr = "don't try to infer conditions"; }
+ { optionName = "verifyParSol"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with verifyPartialSolutions = CheckBool v "verifyParSol"}); descr = "verify partial solutions"; }
+ { optionName = "noVerifyParSol"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with verifyPartialSolutions = not (CheckBool v "verifyParSol")}); descr = "don't verify partial solutions"; }
+ { optionName = "verifySol"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with verifySolutions = CheckBool v "verifySol"}); descr = "verify final solution"; }
+ { optionName = "noVerifySol"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with verifySolutions = not (CheckBool v "verifySol")}); descr = "don't verify final solution"; }
+ { optionName = "checkUnifs"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with checkUnifications = CheckBool v "checkUnifs"}); descr = "verify unifications"; }
+ { optionName = "noCheckUnifs"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with checkUnifications = not (CheckBool v "noCheckUnifs")}); descr = "don't verify unifications"; }
+ { optionName = "genRepr"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with genRepr = CheckBool v "genRepr"}); descr = "generate Repr field"; }
+ { optionName = "noGenRepr"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with genRepr = not (CheckBool v "noGenRepr")}); descr = "don't generate Repr field"; }
+ { optionName = "genMod"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with genMod = CheckBool v "genMod"}); descr = "generate modular code (delegate to methods)"; }
+ { optionName = "noGenMod"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with genMod = not (CheckBool v "noGenMod")}); descr = "dont generate modular code (delegate to methods)"; }
+ { optionName = "timeout"; optionType = "int"; optionSetter = (fun v (cfg: Config) -> {cfg with timeout = CheckInt v "timeout"}); descr = "timeout"; }
+ { optionName = "unrolls"; optionType = "int"; optionSetter = (fun v (cfg: Config) -> {cfg with numLoopUnrolls = CheckInt v "unrolls"}); descr = "number of unrolls of the Valid() function"; }
+ { optionName = "recValid"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with recursiveValid = CheckBool v "recValid"}); descr = "generate recursive Valid() function"; }
+ { optionName = "noRecValid"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with recursiveValid = not (CheckBool v "noRecValid")}); descr = "unroll Valid() function"; }
+ { optionName = "break"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with breakIntoDebugger = CheckBool v "break"}); descr = "launches debugger upon start-up"; }
+ { optionName = "minGuards"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with minimizeGuards = CheckBool v "minGuards"}); descr = "tries to remove unnecessary clauses from the inferred guards"; }
+ { optionName = "noMinGuards"; optionType = "bool"; optionSetter = (fun v (cfg: Config) -> {cfg with minimizeGuards = not (CheckBool v "noMinGuards")}); descr = "don't minimize guards"; }
+]
+
+let cfgOptMap = cfgOptions |> List.fold (fun acc o -> acc |> Map.add o.optionName o) Map.empty
+
+let newline = System.Environment.NewLine
+
+let PrintHelpMsg =
+ let maxw = cfgOptions |> List.fold (fun acc o -> if String.length o.optionName > acc then String.length o.optionName else acc) 0
+ let maxwStr = sprintf "%d" (maxw + 2)
+ let strf = new Printf.StringFormat<_>(" %-" + maxwStr + "s: %-6s | %s")
+ let rec __PrintHelp optLst =
+ match optLst with
+ | fs :: [] -> (sprintf strf fs.optionName fs.optionType fs.descr)
+ | fs :: rest -> (sprintf strf fs.optionName fs.optionType fs.descr) + newline + (__PrintHelp rest)
+ | [] -> ""
+ (* --- function body starts here --- *)
+ newline +
+ "Jennisys usage: Jennisys [ option ... ] filename" + newline +
+ " where <option> is one of " + newline + newline +
+ " ----- General options -----------------------------------------------------" + newline +
+ " (available switches are: /, -, --)" + newline + newline +
+ (__PrintHelp cfgOptions)
+
+let defaultConfig: Config = {
+ help = false;
+ inputFilename = "";
+ inferConditionals = true;
+ methodToSynth = "*";
+ constructorsOnly = false;
+ verifyPartialSolutions = true;
+ verifySolutions = true;
+ checkUnifications = false;
+ genRepr = true;
+ genMod = false;
+ timeout = 0;
+ numLoopUnrolls = 2;
+ recursiveValid = true;
+ breakIntoDebugger = false;
+ minimizeGuards = true;
+}
+
+/// Should not be mutated outside the ParseCmdLineArgs method, which is
+/// typically called only once at the beginning of the program execution
+let mutable CONFIG = defaultConfig
+
+let ParseCmdLineArgs args =
+ let __StripSwitches str =
+ match str with
+ | Prefix "--" x
+ | Prefix "-" x
+ | Prefix "/" x -> x
+ | _ -> str
+
+ let __Split (str: string) =
+ let stripped = __StripSwitches str
+ if stripped = str then
+ ("",str)
+ else
+ let splits = stripped.Split([| ':' |])
+ if splits.Length > 2 then raise (InvalidCmdLineOption("more than 2 colons in " + str))
+ if splits.Length = 2 then
+ let opt = splits.[0]
+ let value = splits.[1]
+ (opt,value)
+ else
+ let x = __StripSwitches splits.[0]
+ (x, "")
+
+ let rec __Parse args cfg =
+ match args with
+ | fs :: rest ->
+ let opt,value = __Split fs
+ if opt = "" then
+ __Parse rest { cfg with inputFilename = CheckNonEmpty value opt }
+ else
+ match Map.tryFind opt cfgOptMap with
+ | Some(opt) -> __Parse rest (opt.optionSetter value cfg)
+ | None -> raise (InvalidCmdLineOption("Unknown option: " + opt))
+ | [] -> cfg
+
+ (* --- function body starts here --- *)
+ CONFIG <- __Parse args defaultConfig
+
diff --git a/Source/Jennisys/Parser.fsy b/Source/Jennisys/Parser.fsy
new file mode 100644
index 00000000..de8e1fb8
--- /dev/null
+++ b/Source/Jennisys/Parser.fsy
@@ -0,0 +1,214 @@
+%{
+
+open Ast
+open Getters
+open AstUtils
+
+let rec MyFold ee acc =
+ match ee with
+ | [] -> acc
+ | x::rest -> BinaryAnd x (MyFold rest acc)
+
+%}
+
+// The start token becomes a parser function in the compiled code:
+%start start
+
+// These are the terminal tokens of the grammar along with the types of
+// the data carried by each token:
+%token <string> ID
+%token <int> INTEGER
+%token DOT
+%token NOT
+%token STAR DIV MOD
+%token PLUS MINUS
+%token OLD
+%token DOTDOT DOTDOTDOT
+%token EQ NEQ LESS ATMOST ATLEAST GREATER IN NOTIN
+%token AND OR
+%token IMPLIES
+%token IFF
+%token LPAREN RPAREN LBRACKET RBRACKET LCURLY RCURLY VERTBAR
+%token GETS COLON COLONCOLON COMMA QMARK
+%token INTERFACE DATAMODEL CODE
+%token VAR CONSTRUCTOR METHOD FRAME INVARIANT RETURNS REQUIRES ENSURES FORALL
+%token INTTYPE BOOLTYPE SEQTYPE SETTYPE
+%token EOF
+
+// This is the type of the data produced by a successful reduction of the 'start'
+// symbol:
+%type < Ast.SyntacticProgram > start
+
+%%
+
+// These are the rules of the grammar along with the F# code of the
+// actions executed as rules are reduced. In this case the actions
+// produce data using F# data construction terms.
+start: TopLevelDecls EOF { SProgram($1) }
+
+TopLevelDecls:
+ | { [] }
+ | TopLevelDecl TopLevelDecls { $1 :: $2 }
+
+TopLevelDecl:
+ | INTERFACE ID TypeParams LCURLY Members RCURLY { Interface($2, $3, $5) }
+ | DATAMODEL ID TypeParams LCURLY FrameMembers RCURLY { match $5 with (vv,fr,inv) -> DataModel($2, $3, vv, fr, inv) }
+ | CODE ID TypeParams LCURLY RCURLY { Code($2, $3) }
+
+TypeParams:
+ | { [] }
+ | LBRACKET IdList RBRACKET { $2 }
+
+IdList:
+ | ID { [$1] }
+ | ID IdList { $1 :: $2 }
+
+Members:
+ | { [] }
+ | Member Members { $1 :: $2 }
+
+Signature:
+ | LPAREN VarDeclList RPAREN { Sig($2, []) }
+ | LPAREN VarDeclList RPAREN RETURNS LPAREN VarDeclList RPAREN { Sig($2, $6) }
+
+Pre:
+ | { TrueLiteral }
+ | REQUIRES Expr Pre { BinaryAnd $2 $3 }
+
+Post:
+ | { TrueLiteral }
+ | ENSURES Expr Post { BinaryAnd $2 $3 }
+ | ID GETS Expr Post { BinaryAnd (BinaryExpr(40,"=",IdLiteral($1),$3)) $4 }
+
+StmtList:
+ | { [] }
+ | Stmt StmtList { $1 :: $2 }
+
+Stmt:
+ | BlockStmt { $1 }
+ | Expr GETS Expr { Assign($1, $3) }
+
+BlockStmt:
+ | LCURLY StmtList RCURLY { Block $2 }
+
+Member:
+ | VAR VarDecl { Field($2) }
+ | CONSTRUCTOR ID Signature Pre Post { Method($2, $3, RewriteVars (GetSigVars $3) $4, RewriteVars (GetSigVars $3) $5, true) }
+ | METHOD ID Signature Pre Post { Method($2, $3, RewriteVars (GetSigVars $3) $4, RewriteVars (GetSigVars $3) $5, false) }
+ | INVARIANT ExprList { Invariant($2) }
+
+FrameMembers:
+ | { [], [], TrueLiteral }
+ | VAR VarDecl FrameMembers { match $3 with (vv,fr,inv) -> $2 :: vv, fr, inv }
+ | FRAME FrameMembers { $2 }
+ | FRAME FramePartitionList FrameMembers { match $3 with (vv,fr,inv) -> vv, List.append $2 fr, inv }
+ | INVARIANT ExprList FrameMembers { match $3 with (vv,fr,inv) -> vv, fr, MyFold $2 inv }
+
+FramePartitionList:
+ | FramePartition { $1 }
+ | FramePartition FramePartitionList { List.append $1 $2 }
+
+VarDeclList:
+ | { [] }
+ | VarDecl { [$1] }
+ | VarDecl COMMA VarDeclList { $1 :: $3 }
+
+VarDecl:
+ | ID { Var($1,None, false) }
+ | ID COLON Type { Var($1,Some($3), false) }
+
+Type:
+ | INTTYPE { IntType }
+ | BOOLTYPE { BoolType }
+ | ID { NamedType($1, []) }
+ | SEQTYPE LBRACKET Type RBRACKET { SeqType($3) }
+ | SETTYPE LBRACKET Type RBRACKET { SetType($3) }
+ | ID LBRACKET Type RBRACKET { InstantiatedType($1, [$3]) }
+
+ExprList:
+ | { [] }
+ | Expr { [$1] }
+ | Expr ExprList { $1 :: $2 }
+
+Expr:
+ | Expr10 { $1 }
+
+Expr10:
+ | Expr20 { $1 }
+ | Expr10 IFF Expr20 { BinaryExpr(10,"<==>",$1,$3) }
+
+Expr20:
+ | Expr25 { $1 }
+ | Expr25 IMPLIES Expr20 { BinaryExpr(20,"==>",$1,$3) }
+
+Expr25:
+ | Expr30 { $1 }
+ | Expr30 QMARK Expr25 COLON Expr25 { IteExpr($1,$3,$5) }
+Expr30:
+ | Expr40 { $1 }
+ | Expr40 AND Expr30and { BinaryAnd $1 $3 }
+ | Expr40 OR Expr30or { BinaryOr $1 $3 }
+Expr30and:
+ | Expr40 { $1 }
+ | Expr40 AND Expr30and { BinaryAnd $1 $3 }
+Expr30or:
+ | Expr40 { $1 }
+ | Expr40 AND Expr30or { BinaryOr $1 $3 }
+
+Expr40:
+ | Expr50 { $1 }
+ | Expr50 EQ Expr50 { BinaryExpr(40,"=",$1,$3) }
+ | Expr50 NEQ Expr50 { BinaryExpr(40,"!=",$1,$3) }
+ | Expr50 LESS Expr50 { BinaryExpr(40,"<",$1,$3) }
+ | Expr50 ATMOST Expr50 { BinaryExpr(40,"<=",$1,$3) }
+ | Expr50 ATLEAST Expr50 { BinaryExpr(40,">=",$1,$3) }
+ | Expr50 GREATER Expr50 { BinaryExpr(40,">",$1,$3) }
+ | Expr50 IN Expr50 { BinaryExpr(40,"in",$1,$3) }
+ | Expr50 NOTIN Expr50 { BinaryExpr(40,"!in",$1,$3) }
+
+Expr50:
+ | Expr55 { $1 }
+ | Expr55 DOTDOTDOT Expr55 { BinaryExpr(50,"...",$1,$3) }
+
+Expr55:
+ | Expr60 { $1 }
+ | Expr55 PLUS Expr60 { BinaryExpr(55,"+",$1,$3) }
+ | Expr55 MINUS Expr60 { BinaryExpr(55,"-",$1,$3) }
+
+Expr60:
+ | Expr90 { $1 }
+ | Expr60 STAR Expr90 { BinaryExpr(60,"*",$1,$3) }
+ | Expr60 DIV Expr90 { BinaryExpr(60,"div",$1,$3) }
+ | Expr60 MOD Expr90 { BinaryExpr(60,"mod",$1,$3) }
+
+Expr90:
+ | Expr100 { $1 }
+ | OLD LPAREN Expr90 RPAREN { OldExpr($3) }
+ | NOT Expr90 { UnaryExpr("!", $2) }
+ | MINUS Expr90 { UnaryExpr("-", $2) }
+ | Expr90 DOTDOT { LCIntervalExpr($1) }
+
+Expr100:
+ | INTEGER { IntLiteral($1) }
+ | ID { if $1 = "this" then
+ ObjLiteral("this")
+ elif $1 = "null" then
+ ObjLiteral("null")
+ else
+ IdLiteral($1) }
+ | Expr100 DOT ID { Dot($1, $3) }
+ | Expr100 LBRACKET StarExpr RBRACKET { SelectExpr($1, $3) }
+ | Expr100 LBRACKET Expr GETS Expr RBRACKET { UpdateExpr($1, $3, $5) }
+ | LPAREN Expr RPAREN { $2 }
+ | LBRACKET ExprList RBRACKET { SequenceExpr($2) }
+ | LCURLY ExprList RCURLY { SetExpr($2) }
+ | VERTBAR Expr VERTBAR { SeqLength($2) }
+ | FORALL VarDeclList COLONCOLON Expr { ForallExpr($2, RewriteVars $2 $4) }
+
+StarExpr:
+ | STAR { Star }
+ | Expr { $1 }
+
+FramePartition:
+ | Expr100 { [$1] }
+ | Expr100 STAR FramePartition { $1 :: $3 }
diff --git a/Source/Jennisys/PipelineUtils.fs b/Source/Jennisys/PipelineUtils.fs
new file mode 100644
index 00000000..a87d442f
--- /dev/null
+++ b/Source/Jennisys/PipelineUtils.fs
@@ -0,0 +1,63 @@
+// ####################################################################
+/// Utility functions for executing shell commands and
+/// running Dafny in particular
+///
+/// author: Aleksandar Milicevic (t-alekm@microsoft.com)
+// ####################################################################
+
+module PipelineUtils
+
+open Logger
+
+let dafnyScratchSuffix = "scratch"
+let dafnyVerifySuffix = "verify"
+let dafnyUnifSuffix = "unif"
+let dafnySynthFileNameTemplate = @"c:\tmp\jennisys-synth_###.dfy"
+let dafnyModularSynthFileNameTemplate = @"c:\tmp\jennisys-synth_###_mod.dfy"
+
+let mutable lastDafnyExitCode = 0 //TODO: how to avoid this muttable state?
+
+let CreateEmptyModelFile modelFile =
+ use mfile = System.IO.File.CreateText(modelFile)
+ fprintf mfile ""
+
+// =======================================================
+/// Runs Dafny on the given "inputFile" and prints
+/// the resulting model to the given "modelFile"
+// =======================================================
+let RunDafny inputFile modelFile =
+ //TraceLine "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Running Dafny @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
+ CreateEmptyModelFile modelFile
+ async {
+ use proc = new System.Diagnostics.Process()
+ proc.StartInfo.FileName <- @"c:\tmp\StartDafny-jen.bat"
+ proc.StartInfo.Arguments <- (sprintf "/mv:%s /timeLimit:%d %s" modelFile Options.CONFIG.timeout inputFile)
+ proc.StartInfo.WindowStyle <- System.Diagnostics.ProcessWindowStyle.Hidden
+ assert proc.Start()
+ proc.WaitForExit()
+ lastDafnyExitCode <- proc.ExitCode
+ } |> Async.RunSynchronously
+
+// =======================================================
+/// Runs Dafny on the given "dafnyCode" and returns models
+// =======================================================
+let RunDafnyProgram dafnyProgram suffix =
+ let inFileName = @"c:\tmp\jennisys-" + suffix + ".dfy"
+ let modelFileName = @"c:\tmp\jennisys-" + suffix + ".bvd"
+ use file = System.IO.File.CreateText(inFileName)
+ file.AutoFlush <- true
+ fprintfn file "%s" dafnyProgram
+ file.Close()
+ // run Dafny
+ RunDafny inFileName modelFileName
+ // read models from the model file
+ use modelFile = System.IO.File.OpenText(modelFileName)
+ Microsoft.Boogie.Model.ParseModels modelFile
+
+// =======================================================
+/// Checks whether the given dafny program verifies
+// =======================================================
+let CheckDafnyProgram dafnyProgram suffix =
+ let models = RunDafnyProgram dafnyProgram suffix
+ // if there are no models, verification was successful
+ lastDafnyExitCode = 0 && models.Count = 0
diff --git a/Source/Jennisys/PrintUtils.fs b/Source/Jennisys/PrintUtils.fs
new file mode 100644
index 00000000..138b5e77
--- /dev/null
+++ b/Source/Jennisys/PrintUtils.fs
@@ -0,0 +1,12 @@
+module PrintUtils
+
+let newline = System.Environment.NewLine // "\r\n"
+
+let rec Indent i =
+ if i = 0 then "" else " " + (Indent (i-1))
+
+let rec PrintSep sep f list =
+ match list with
+ | [] -> ""
+ | [a] -> f a
+ | a :: more -> (f a) + sep + (PrintSep sep f more) \ No newline at end of file
diff --git a/Source/Jennisys/Printer.fs b/Source/Jennisys/Printer.fs
new file mode 100644
index 00000000..32ae21ac
--- /dev/null
+++ b/Source/Jennisys/Printer.fs
@@ -0,0 +1,156 @@
+module Printer
+
+open Ast
+open Getters
+open AstUtils
+open PrintUtils
+
+let rec PrintType ty =
+ match ty with
+ | IntType -> "int"
+ | BoolType -> "bool"
+ | NamedType(id, args) -> if List.isEmpty args then id else (PrintSep ", " (fun s -> s) args)
+ | SeqType(t) -> sprintf "seq[%s]" (PrintType t)
+ | SetType(t) -> sprintf "set[%s]" (PrintType t)
+ | InstantiatedType(id,args) -> sprintf "%s[%s]" id (PrintSep ", " (fun a -> PrintType a) args)
+
+let PrintVarDecl vd =
+ let name = GetExtVarName vd
+ match GetVarType vd with
+ | None -> name
+ | Some(ty) -> sprintf "%s: %s" name (PrintType ty)
+
+let rec PrintExpr ctx expr =
+ match expr with
+ | IntLiteral(d) -> sprintf "%d" d
+ | BoolLiteral(b) -> sprintf "%b" b
+ | BoxLiteral(id) -> sprintf "box_%s" id
+ | ObjLiteral(id)
+ | VarLiteral(id)
+ | IdLiteral(id) -> id
+ | VarDeclExpr(vlist, declare) ->
+ let decl = if declare then "var " else ""
+ let vars = PrintSep ", " PrintVarDecl vlist
+ sprintf "%s%s" decl vars
+ | Star -> "*"
+ | Dot(e,id) -> sprintf "%s.%s" (PrintExpr 100 e) id
+ | LCIntervalExpr(e) -> sprintf "%s.." (PrintExpr 90 e)
+ | OldExpr(e) -> sprintf "old(%s)" (PrintExpr 90 e)
+ | UnaryExpr(op,UnaryExpr(op2, e2)) -> sprintf "%s(%s)" op (PrintExpr 90 (UnaryExpr(op2, e2)))
+ | UnaryExpr(op,e) -> sprintf "%s%s" op (PrintExpr 90 e)
+ | BinaryExpr(strength,op,e0,e1) ->
+ let needParens = strength <= ctx
+ let openParen = if needParens then "(" else ""
+ let closeParen = if needParens then ")" else ""
+ sprintf "%s%s %s %s%s" openParen (PrintExpr strength e0) op (PrintExpr strength e1) closeParen
+ | IteExpr(c,e1,e2) -> sprintf "%s ? %s : %s" (PrintExpr 25 c) (PrintExpr 25 e1) (PrintExpr 25 e2)
+ | SelectExpr(e,i) -> sprintf "%s[%s]" (PrintExpr 100 e) (PrintExpr 0 i)
+ | UpdateExpr(e,i,v) -> sprintf "%s[%s := %s]" (PrintExpr 100 e) (PrintExpr 0 i) (PrintExpr 0 v)
+ | SequenceExpr(ee) -> sprintf "[%s]" (ee |> PrintSep " " (PrintExpr 0))
+ | SeqLength(e) -> sprintf "|%s|" (PrintExpr 0 e)
+ | SetExpr(ee) -> sprintf "{%s}" (ee |> PrintSep " " (PrintExpr 0))
+ | AssertExpr(e) -> sprintf "assert %s" (PrintExpr 0 e)
+ | AssumeExpr(e) -> sprintf "assume %s" (PrintExpr 0 e)
+ | ForallExpr(vv,e) ->
+ let needParens = ctx <> 0
+ let openParen = if needParens then "(" else ""
+ let closeParen = if needParens then ")" else ""
+ sprintf "%sforall %s :: %s%s" openParen (vv |> PrintSep ", " PrintVarDecl) (PrintExpr 0 e) closeParen
+ | MethodCall(rcv,_,name,aparams) ->
+ sprintf "%s.%s(%s)" (PrintExpr 0 rcv) name (aparams |> PrintSep ", " (PrintExpr 0))
+ | MethodOutSelect(mth,name) ->
+ sprintf "%s[\"%s\"]" (PrintExpr 0 mth) name
+
+let rec PrintConst cst =
+ match cst with
+ | IntConst(v) -> sprintf "%d" v
+ | BoolConst(b) -> sprintf "%b" b
+ | BoxConst(id) -> sprintf "box_%s" id
+ | VarConst(v) -> sprintf "%s" v
+ | SetConst(cset) -> sprintf "{%s}" (PrintSep " " (fun c -> PrintConst c) (Set.toList cset))
+ | SeqConst(cseq) -> sprintf "[%s]" (PrintSep " " (fun c -> PrintConst c) cseq)
+ | NullConst -> "null"
+ | NoneConst -> "<none>"
+ | ThisConst(_,_) -> "this"
+ | NewObj(name,_) -> PrintGenSym name
+ | Unresolved(name) -> sprintf "Unresolved(%s)" name
+
+let PrintSig signature =
+ match signature with
+ | Sig(ins, outs) ->
+ let returnClause =
+ if outs <> [] then sprintf " returns (%s)" (outs |> PrintSep ", " PrintVarDecl)
+ else ""
+ sprintf "(%s)%s" (ins |> PrintSep ", " PrintVarDecl) returnClause
+
+let rec PrintStmt stmt indent printNewline =
+ let idt = (Indent indent)
+ let nl = if printNewline then newline else ""
+ match stmt with
+ | Block(stmts) ->
+ idt + "{" + nl +
+ (PrintStmtList stmts (indent + 2) true) +
+ idt + "}" + nl
+ | Assign(lhs,rhs) -> sprintf "%s%s := %s%s" idt (PrintExpr 0 lhs) (PrintExpr 0 rhs) nl
+ | ExprStmt(expr) -> sprintf "%s%s%s" idt (PrintExpr 0 expr) nl
+and PrintStmtList stmts indent printNewline =
+ stmts |> List.fold (fun acc s -> acc + (PrintStmt s indent printNewline)) ""
+
+let PrintRoutine signature pre body =
+ let preStr = pre |> ForeachConjunct (fun e -> sprintf " requires %s%s" (PrintExpr 0 e) newline)
+ sprintf "%s%s%s%s" (PrintSig signature) newline preStr (PrintExpr 0 body)
+
+let PrintMember m =
+ match m with
+ | Field(vd) -> sprintf " var %s%s" (PrintVarDecl vd) newline
+ | Method(id,signature,pre,body,true) -> sprintf " constructor %s%s" id (PrintRoutine signature pre body)
+ | Method(id,signature,pre,body,false) -> sprintf " method %s%s" id (PrintRoutine signature pre body)
+ | Invariant(_) -> "" // invariants are handled separately
+
+let PrintTopLevelDeclHeader kind id typeParams =
+ let typeParamStr =
+ match typeParams with
+ | [] -> ""
+ | _ -> sprintf "[%s]" (typeParams |> PrintSep ", " (fun tp -> tp))
+ sprintf "%s %s%s {%s" kind id typeParamStr newline
+
+let PrintDecl d =
+ match d with
+ | Interface(id,typeParams,members) ->
+ sprintf "%s%s}%s" (PrintTopLevelDeclHeader "interface" id typeParams)
+ (List.fold (fun acc m -> acc + (PrintMember m)) "" members)
+ newline
+ | DataModel(id,typeParams,vars,frame,inv) ->
+ (PrintTopLevelDeclHeader "model" id typeParams) +
+ (vars |> List.fold (fun acc vd -> acc + " var " + (PrintVarDecl vd) + newline) "") +
+ " frame" + newline +
+ (frame |> List.fold (fun acc fr -> acc + " " + (PrintExpr 0 fr) + newline) "") +
+ " invariant" + newline +
+ (inv |> ForeachConjunct (fun e -> " " + (PrintExpr 0 e) + newline)) +
+ "}" + newline
+ | Code(id,typeParams) ->
+ (PrintTopLevelDeclHeader "code" id typeParams) + "}" + newline
+
+let PrintMethodSignFull indent comp m =
+ let idt = Indent indent
+ let __PrintPrePost pfix expr = SplitIntoConjunts expr |> PrintSep newline (fun e -> pfix + (PrintExpr 0 e) + ";")
+ let compName = GetComponentName comp
+ match m with
+ | Method(methodName, sgn, pre, post, isConstr) ->
+ let mc = if isConstr then "constructor" else "method"
+ let preStr = (__PrintPrePost (idt + " requires ") pre)
+ let postStr = (__PrintPrePost (idt + " ensures ") post)
+ idt + mc + " " + compName + "." + methodName + (PrintSig sgn) + newline +
+ preStr + (if preStr = "" then "" else newline) +
+ postStr
+ | _ -> failwithf "not a method: %O" m
+
+let Print prog =
+ match prog with
+ | SProgram(decls) -> List.fold (fun acc d -> acc + (PrintDecl d)) "" decls
+
+let PrintObjRefName o =
+ match o with
+ | ThisConst(_,_) -> "this";
+ | NewObj(name, _) -> PrintGenSym name
+ | _ -> failwith ("unresolved object ref: " + o.ToString()) \ No newline at end of file
diff --git a/Source/Jennisys/README.txt b/Source/Jennisys/README.txt
new file mode 100644
index 00000000..30f91b2e
--- /dev/null
+++ b/Source/Jennisys/README.txt
@@ -0,0 +1,28 @@
+1. Installation instructions
+----------------------------
+
+ - download the entire boogie source distribution and place it in c:\boogie
+ - create c:\tmp folder
+ - copy the Jennisys\scripts\StartDafny-jen.bat script into c:\tmp
+
+2. Running the examples
+----------------------------
+
+ $ cd Jennisys
+ $ bin/Debug/Jennisys.exe examples/<name>.jen
+
+ The most current and complete set of examples is in the
+ "examples/oopsla12" folder. No additional Jennisys switches need be
+ passed for either of them.
+
+ Synthesized programs will be generated in "c:\tmp", and their file
+ names will follow the following pattern:
+
+ "jennisys-synth_<example-name>.dfy"
+
+ To verify the correctness of the synthesized programs, run
+
+ $ Dafny /compile:0 jennisys-synth_<example-name>.dfy
+
+ Expected outputs (i.e., synthesized Dafny programs) for the examples
+ in "examples/oopsla12" can be found in the same folder.
diff --git a/Source/Jennisys/Resolver.fs b/Source/Jennisys/Resolver.fs
new file mode 100644
index 00000000..bc330520
--- /dev/null
+++ b/Source/Jennisys/Resolver.fs
@@ -0,0 +1,380 @@
+// ####################################################################
+/// Utilities for resolving the "Unresolved" constants with respect to
+/// a given context (heap/env/ctx)
+///
+/// author: Aleksandar Milicevic (t-alekm@microsoft.com)
+// ####################################################################
+
+module Resolver
+
+open Ast
+open Getters
+open AstUtils
+open Printer
+open EnvUtils
+open DafnyModelUtils
+
+type Obj = { name: string; objType: Type }
+
+type AssignmentType =
+ | FieldAssignment of (Obj * VarDecl) * Expr // the first string is the symbolic name of an object literal
+ | ArbitraryStatement of Stmt
+
+type HeapInstance = {
+ objs : Map<string, Obj>;
+ modifiableObjs : Set<Obj>;
+ assignments : AssignmentType list;
+ concreteValues : AssignmentType list;
+ methodArgs : Map<string, Const>;
+ methodRetVals : Map<string, Expr>;
+ concreteMethodRetVals : Map<string, Expr>;
+ globals : Map<string, Expr>;
+}
+
+let NoObj = { name = ""; objType = NamedType("", []) }
+let ThisObj comp = {name = "this"; objType = GetComponentType comp}
+
+let ExtractAllExpressions asg =
+ match asg with
+ | FieldAssignment(_,e) -> [e]
+ | ArbitraryStatement(s) -> ExtractTopLevelExpressions s
+
+// use the orginal method, not the one with an extra precondition
+let FixSolution origComp origMeth sol =
+ sol |> Map.fold (fun acc (cc,mm) v ->
+ if CheckSameMethods (cc,mm) (origComp,origMeth) then
+ acc |> Map.add (origComp,origMeth) v
+ else
+ acc |> Map.add (cc,mm) v) Map.empty
+
+let ConvertToStatements heapInst onModifiableObjsOnly =
+ let stmtLst1 = heapInst.assignments |> List.choose (fun asgn ->
+ match asgn with
+ | FieldAssignment((o,f),e) when (not onModifiableObjsOnly || Set.contains o heapInst.modifiableObjs) ->
+ if IsOldVar f then
+ None
+ else
+ let fldName = GetVarName f
+ if fldName = "" then
+ Some(ExprStmt(e))
+ else
+ Some(Assign(Dot(ObjLiteral(o.name), fldName), e))
+ | ArbitraryStatement(stmt) -> Some(stmt)
+ | _ -> None)
+ let stmtLst2 = heapInst.methodRetVals |> Map.toList
+ |> List.map (fun (retVarName, retVarVal) -> Assign(VarLiteral(retVarName), retVarVal))
+ stmtLst1 @ stmtLst2
+
+// resolving values
+exception ConstResolveFailed of string
+
+// ================================================================
+/// Resolves a given Const (cst) with respect to a given env/ctx.
+///
+/// If unable to resolve, it just delegates the task to the
+/// failResolver function
+// ================================================================
+let rec ResolveCont hModel failResolver cst =
+ match cst with
+ | Unresolved(_) as u ->
+ // see if it is in the env map first
+ let envVal = Map.tryFind cst hModel.env
+ match envVal with
+ | Some(c) -> ResolveCont hModel failResolver c
+ | None ->
+ // not found in the env map --> check the equality sets
+ let eq = hModel.ctx |> Set.filter (fun eqSet -> Set.contains u eqSet)
+ |> Utils.SetToOption
+ match eq with
+ | Some(eqSet) ->
+ let cOpt = eqSet |> Set.filter (function Unresolved(_) -> false | _ -> true)
+ |> Utils.SetToOption
+ match cOpt with
+ | Some(c) -> c
+ | _ -> failResolver cst hModel
+ | None ->
+ failResolver cst hModel
+// // finally, see if it's an *input* (have no way of telling input from output params here) method argument
+// let m = hModel.env |> Map.filter (fun k v -> v = u && match k with VarConst(name) -> true | _ -> false) |> Map.toList
+// match m with
+// | (vc,_) :: [] -> vc
+// | _ -> failResolver cst hModel
+ | SeqConst(cseq) ->
+ let resolvedLst = cseq |> List.rev |> List.fold (fun acc c -> ResolveCont hModel failResolver c :: acc) []
+ SeqConst(resolvedLst)
+ | SetConst(cset) ->
+ let resolvedSet = cset |> Set.fold (fun acc c -> acc |> Set.add (ResolveCont hModel failResolver c)) Set.empty
+ SetConst(resolvedSet)
+ | _ -> cst
+
+// =====================================================================
+/// Tries to resolve a given Const (cst) with respect to a given env/ctx.
+///
+/// If unable to resolve, just returns the original Unresolved const.
+// =====================================================================
+let TryResolve hModel cst =
+ ResolveCont hModel (fun c _ -> c) cst
+
+// ==============================================================
+/// Resolves a given Const (cst) with respect to a given env/ctx.
+///
+/// If unable to resolve, raises a ConstResolveFailed exception
+// ==============================================================
+let Resolve hModel cst =
+ ResolveCont hModel (fun c _ ->
+ match c with
+ | Unresolved(id) -> BoxConst(id)
+ | _ -> failwithf "internal error: expected Unresolved but got %O" c
+ ) cst //fun c _ -> raise (ConstResolveFailed("failed to resolve " + c.ToString()))
+
+// ==================================================================
+/// Evaluates a given expression with respect to a given heap instance
+// ==================================================================
+
+let rec _EvalResolver heapInst useConcrete resolveExprFunc expr fldNameOpt =
+ let rec __FurtherResolve expr =
+ match expr with
+ | SetExpr(elist) -> SetExpr(elist |> List.map __FurtherResolve)
+ | SequenceExpr(elist) -> SequenceExpr(elist |> List.map __FurtherResolve)
+ | VarLiteral(_) ->
+ try
+ _EvalResolver heapInst useConcrete resolveExprFunc expr None
+ with
+ | _ -> expr
+ | IdLiteral(id) when not (id = "this" || id = "null") ->
+ try
+ _EvalResolver heapInst useConcrete resolveExprFunc expr None
+ with
+ | _ -> expr
+ | _ -> expr
+
+ (* --- function body starts here --- *)
+ let ex = match fldNameOpt with
+ | None -> expr
+ | Some(n) -> Dot(expr, n)
+ if not (resolveExprFunc ex) then
+ ex
+ else
+ match fldNameOpt with
+ | None ->
+ match expr with
+ | ObjLiteral("this") | ObjLiteral("null") -> expr
+ | IdLiteral("this") | IdLiteral("null") -> failwith "should never happen anymore" //TODO
+ | VarLiteral(id) ->
+ match heapInst.methodArgs |> Map.tryFind id with
+ | Some(argValue) -> argValue |> Const2Expr
+ | None ->
+ let retVals = if useConcrete then heapInst.concreteMethodRetVals else heapInst.methodRetVals
+ match retVals |> Map.tryFind id with
+ | Some(e) -> e |> __FurtherResolve
+ | None -> raise (EvalFailed("cannot find value for method parameter " + id))
+ | IdLiteral(id) ->
+ let globalVal = heapInst.globals |> Map.tryFind id
+ match globalVal with
+ | Some(e) -> e
+ | None -> _EvalResolver heapInst useConcrete resolveExprFunc ThisLiteral (Some(id))
+ | _ -> raise (EvalFailed(sprintf "I'm not supposed to resolve %O" expr))
+ | Some(fldName) ->
+ match expr with
+ | ObjLiteral(objName) ->
+ let asgs = if useConcrete then heapInst.concreteValues else heapInst.assignments
+ let h2 = asgs |> List.filter (function FieldAssignment((o, var), v) -> o.name = objName && GetExtVarName var = fldName | _ -> false)
+ match h2 with
+ | FieldAssignment((_,_),x) :: [] -> __FurtherResolve x
+ | _ :: _ -> raise (EvalFailed(sprintf "can't evaluate expression deterministically: %s.%s resolves to multiple locations" objName fldName))
+ | [] -> raise (EvalFailed(sprintf "can't find value for %s.%s" objName fldName)) // TODO: what if that value doesn't matter for the solution, and that's why it's not present in the model???
+ | _ -> Dot(expr, fldName)
+
+let _Eval heapInst resolveExprFunc returnFunc expr =
+ (* --- function body starts here --- *)
+ //EvalSym (__EvalResolver resolveExprFunc) expr
+ EvalSymRet (_EvalResolver heapInst false resolveExprFunc) returnFunc expr
+
+/// Resolves nothing
+let EvalNone heapInst expr =
+ EvalSym (_EvalResolver heapInst false (fun e -> false)) expr
+
+let fullResolver heapInst = _EvalResolver heapInst true (fun e -> true)
+
+/// Resolves everything
+let EvalFull heapInst expr =
+ EvalSym (fullResolver heapInst) expr
+ //_Eval heapInst (fun _ -> true) (fun e -> e) expr
+
+let Eval heapInst resolveExprFunc expr =
+ let returnFunc = fun expr -> match expr with IdLiteral(id) -> Dot(ThisLiteral, id) | _ -> expr
+ EvalSymRet (fullResolver heapInst) (_EvalResolver heapInst false resolveExprFunc) returnFunc expr
+
+let EvalAndCheckTrue heapInst resolveExprFunc expr =
+ let returnFunc = fun expr ->
+ let expr =
+ match expr with
+ //| IteExpr(c,t,e) ->
+ // let cond = c |> EvalFull heapInst |> Expr2Bool
+ // if cond then t else e
+ //| ForallExpr(vars, sub) -> expr
+ // TODO: this is just to ensure that all field accesses to this object are prefixed with "this."
+ // this is not the best place to do it, though
+ | IdLiteral(id) -> Dot(ThisLiteral, id)
+ | _ -> expr
+
+ // TODO: infer type of expr and then re-execute only if its type is Bool
+ let e1 = EvalFull heapInst expr //EvalSym (_EvalResolver heapInst true (fun _ -> true)) expr
+ match e1 with
+ | BoolLiteral(b) ->
+ if b then
+ expr
+ else
+ FalseLiteral
+ //UnaryNot expr
+ | _ -> expr
+ EvalSymRet (fullResolver heapInst) (_EvalResolver heapInst false resolveExprFunc) returnFunc expr
+ //_Eval heapInst resolveExprFunc returnFunc expr
+
+// =====================================================================
+/// Takes an unresolved model of the heap (HeapModel), resolves all
+/// references in the model and returns an instance of the heap
+/// (HeapInstance), where all fields for all objects have explicit
+/// assignments.
+// =====================================================================
+let ResolveModel hModel (comp,meth) =
+ let outArgs = GetMethodOutArgs meth
+ let hmap = hModel.heap |> Map.fold (fun acc (o,f) l ->
+ let objName, objTypeOpt = match Resolve hModel o with
+ | ThisConst(_,t) -> "this", t;
+ | NewObj(name, t) -> PrintGenSym name, t
+ | _ -> failwith ("unresolved object ref: " + o.ToString())
+ let objType = objTypeOpt |> Utils.ExtractOptionMsg "unknown object type"
+ let obj = {name = objName; objType = objType}
+ let value = TryResolve hModel l |> Const2Expr
+ Utils.ListMapAdd (obj, f) value acc
+ ) []
+ |> List.map (fun el -> FieldAssignment(el))
+ let objs, modObjs = hmap |> List.fold (fun (acc1,acc2) asgn ->
+ match asgn with
+ | FieldAssignment((obj,_),_) ->
+ let acc1' = acc1 |> Map.add obj.name obj
+ let acc2' =
+ if IsModifiableObj obj (comp,meth) then
+ acc2 |> Set.add obj
+ else
+ acc2
+ acc1',acc2'
+ | _ -> acc1,acc2
+ ) (Map.empty, Set.empty)
+ let argmap, retvals = hModel.env |> Map.fold (fun (acc1,acc2) k v ->
+ match k with
+ | VarConst(name) ->
+ let resolvedValExpr = Resolve hModel v
+ if outArgs |> List.exists (fun var -> GetVarName var = name) then
+ acc1, acc2 |> Map.add name (resolvedValExpr |> Const2Expr)
+ else
+ acc1 |> Map.add name resolvedValExpr, acc2
+ | _ -> acc1, acc2
+ ) (Map.empty, Map.empty)
+ { objs = objs;
+ modifiableObjs = modObjs;
+ assignments = hmap;
+ concreteValues = hmap;
+ methodArgs = argmap;
+ methodRetVals = retvals;
+ concreteMethodRetVals = retvals;
+ globals = Map.empty }
+
+let rec GetCallGraph solutions graph =
+ let rec __SearchExprsForMethodCalls elist acc =
+ match elist with
+ | e :: rest ->
+ match e with
+ // no need to descend for, just check if the top-level one is MEthodCall
+ | MethodCall(_,cname,mname,_) -> __SearchExprsForMethodCalls rest (acc |> Set.add (cname,mname))
+ | _ -> __SearchExprsForMethodCalls rest acc
+ | [] -> acc
+ match solutions with
+ | ((comp,m), sol) :: rest ->
+ let callees = sol |> List.fold (fun acc (cond, hInst) ->
+ hInst.assignments |> List.fold (fun acc asgn ->
+ match asgn with
+ | FieldAssignment(_,e) ->
+ __SearchExprsForMethodCalls [e] acc
+ | ArbitraryStatement(stmt) ->
+ let exprs = ExtractTopLevelExpressions stmt
+ __SearchExprsForMethodCalls exprs acc
+ ) acc
+ ) Set.empty
+ let graph' = graph |> Map.add (comp,m) callees
+ GetCallGraph rest graph'
+ | [] -> graph
+
+//////////////////////////////
+
+//TODO: below here should really go to a different module
+
+let __Is1stLevelExpr methodsOk heapInst expr =
+ DescendExpr2 (fun expr acc ->
+ if not acc then
+ false
+ else
+ match expr with
+ | Dot(discr, fldName) ->
+ try
+ let obj = EvalFull heapInst discr
+ match obj with
+ | ObjLiteral(id) -> id = "this"
+ | _ -> failwithf "Didn't expect the discriminator of a Dot to not be ObjLiteral"
+ with
+ | _ -> false
+ | MethodCall(_) -> methodsOk
+ | _ -> true
+ ) expr true
+
+let Is1stLevelExpr = __Is1stLevelExpr true
+
+let IsSolution1stLevelOnly heapInst =
+ let rec __IsSol1stLevel stmts =
+ match stmts with
+ | stmt :: rest ->
+ match stmt with
+ | Assign(_, e)
+ | ExprStmt(e) ->
+ let ok = Is1stLevelExpr heapInst e
+ ok && __IsSol1stLevel rest
+ | Block(stmts) -> __IsSol1stLevel (stmts @ rest)
+ | [] -> true
+ (* --- function body starts here --- *)
+ __IsSol1stLevel (ConvertToStatements heapInst true)
+
+let IsRecursiveSol (c,m) sol =
+ let compName = GetComponentName c
+ let methName = GetMethodName m
+ let allAssignments = sol |> List.map (fun (_,hInst) -> hInst.assignments) |> List.concat
+ let allExprs = (allAssignments |> List.map ExtractAllExpressions |> List.concat) @
+ (sol |> List.map (fun (_,hInst) -> hInst.methodRetVals |> Map.toList |> List.map snd) |> List.concat)
+ let singleExpr = allExprs |> List.fold BinaryAnd TrueLiteral
+ DescendExpr2 (fun expr acc ->
+ if acc then
+ true
+ else
+ match expr with
+ | MethodCall(_, cn, mn, elst) when cn = compName && mn = methName ->
+ true
+ | _ -> false
+ ) singleExpr false
+
+/// Returns a list of direct modifiable children objects with respect to "this" object
+///
+/// All returned expressions are of type ObjLiteral
+///
+/// ensures: forall e :: e in ret ==> e is ObjInstance
+let GetDirectModifiableChildren hInst =
+ let rec __AddDirectChildren e acc =
+ match e with
+ | ObjLiteral(_) when not (e = ThisLiteral || e = NullLiteral) -> acc |> Set.add e
+ | SequenceExpr(elist)
+ | SetExpr(elist) -> elist |> List.fold (fun acc2 e2 -> __AddDirectChildren e2 acc2) acc
+ | _ -> acc
+
+ (* --- function body starts here --- *)
+ let thisRhsExprs = hInst.assignments |> List.choose (function FieldAssignment((obj,_),e) when obj.name = "this" && Set.contains obj hInst.modifiableObjs -> Some(e) | _ -> None)
+ thisRhsExprs |> List.fold (fun acc e -> __AddDirectChildren e acc) Set.empty
+ |> Set.toList
diff --git a/Source/Jennisys/SymGen.fs b/Source/Jennisys/SymGen.fs
new file mode 100644
index 00000000..b736ef31
--- /dev/null
+++ b/Source/Jennisys/SymGen.fs
@@ -0,0 +1,9 @@
+module SymGen
+
+let incr =
+ let counter = ref 0
+ fun () ->
+ counter := !counter + 1
+ !counter
+
+let NewSymFake expr = sprintf "x_%d" (incr()) \ No newline at end of file
diff --git a/Source/Jennisys/TypeChecker.fs b/Source/Jennisys/TypeChecker.fs
new file mode 100644
index 00000000..cef88072
--- /dev/null
+++ b/Source/Jennisys/TypeChecker.fs
@@ -0,0 +1,67 @@
+module TypeChecker
+
+open Ast
+open Getters
+open AstUtils
+open Printer
+open System.Collections.Generic
+
+let GetClass name decls =
+ match decls |> List.tryFind (function Interface(n,_,_) when n = name -> true | _ -> false) with
+ | Some(cl) -> cl
+ | None -> Interface(name,[],[])
+
+let GetModel name decls =
+ match decls |> List.tryFind (function DataModel(n,_,_,_,_) when n = name -> true | _ -> false) with
+ | Some(m) -> m
+ | None -> DataModel(name,[],[],[],BoolLiteral(true))
+
+let GetCode name decls =
+ match decls |> List.tryFind (function Code(n,_) when n = name -> true | _ -> false) with
+ | Some(c) -> c
+ | None -> Code(name,[])
+
+let IsUserType prog tpo =
+ match tpo with
+ | Some(tp) ->
+ let tpname = match tp with
+ | NamedType(tname,_) -> tname
+ | InstantiatedType(tname, _) -> tname
+ | _ -> ""
+ match prog with
+ | Program(components) -> components |> List.filter (function Component(Interface(name,_,_),_,_) when name = tpname -> true
+ | _ -> false) |> List.isEmpty |> not
+ | None -> false
+
+let TypeCheck prog =
+ match prog with
+ | SProgram(decls) ->
+ let componentNames = decls |> List.choose (function Interface(name,_,_) -> Some(name) | _ -> None)
+ let clist = componentNames |> List.map (fun name -> Component(GetClass name decls, GetModel name decls, GetCode name decls))
+ Some(Program(clist))
+
+let MethodArgChecker prog meth varName =
+ let ins = GetMethodInArgs meth
+ let outs = GetMethodOutArgs meth
+ ins @ outs |> List.choose (fun var -> if GetVarName var = varName then GetVarType var |> FindComponentForTypeOpt prog else None) |> Utils.ListToOption
+
+// TODO: implement this
+let rec InferType prog thisComp checkLocalFunc expr =
+ let __FindVar comp fldName =
+ let var = FindVar comp fldName |> Utils.ExtractOption
+ let c = FindComponentForType prog (Utils.ExtractOption (GetVarType var)) |> Utils.ExtractOption
+ Some(c)
+
+ try
+ match expr with
+ | ObjLiteral("this") -> Some(thisComp)
+ | ObjLiteral("null") -> None
+ | IdLiteral(id) -> __FindVar thisComp id
+ | VarLiteral(id) -> checkLocalFunc id
+ | Dot(discr, fldName) ->
+ match InferType prog thisComp checkLocalFunc discr with
+ | Some(comp) -> __FindVar comp fldName
+ | None -> None
+ | _ -> None
+ with
+ | ex -> None \ No newline at end of file
diff --git a/Source/Jennisys/Utils.fs b/Source/Jennisys/Utils.fs
new file mode 100644
index 00000000..56b6b779
--- /dev/null
+++ b/Source/Jennisys/Utils.fs
@@ -0,0 +1,368 @@
+// ####################################################################
+/// Various utility functions
+///
+/// author: Aleksandar Milicevic (t-alekm@microsoft.com)
+// ####################################################################
+
+module Utils
+
+// -------------------------------------------
+// ----------- collection util funcs ---------
+// -------------------------------------------
+
+// =====================================
+/// ensures: ret = b ? Some(b) : None
+// =====================================
+let BoolToOption b =
+ if b then
+ Some(b)
+ else
+ None
+
+// =====================================
+/// ensures: ret = (opt == Some(_))
+// =====================================
+let OptionToBool opt =
+ match opt with
+ | Some(_) -> true
+ | None -> false
+
+// =====================================
+/// ensures: ret = (opt == Some(_))
+// =====================================
+let IsSomeOption opt =
+ match opt with
+ | Some(_) -> true
+ | None -> false
+
+// =====================================
+/// ensures: ret = (opt == None)
+// =====================================
+let IsNoneOption opt = IsSomeOption opt |> not
+
+// =====================================
+/// requres: x = Some(a) or failswith msg
+/// ensures: ret = a
+// =====================================
+let ExtractOptionMsg msg x =
+ match x with
+ | Some(a) -> a
+ | None -> failwith msg
+
+// ====================
+/// requres: x = Some(a)
+/// ensures: ret = a
+// ====================
+let ExtractOption x =
+ ExtractOptionMsg "can't extract anything from a None" x
+
+// ====================================
+/// ensures: res = Some(a) ==> ret = a
+/// ensures: res = None ==> ret = defVal
+// ====================================
+let ExtractOptionOr defVal opt =
+ match opt with
+ | Some(a) -> a
+ | None -> defVal
+
+// ==========================================================
+/// requres: List.length lst <= 1, otherwise fails with errMsg
+/// ensures: if |lst| = 0 then
+/// ret = None
+/// else
+/// ret = Some(lst[0])
+// ==========================================================
+let ListToOptionMsg lst errMsg =
+ if List.length lst > 1 then
+ failwith errMsg
+ if List.isEmpty lst then
+ None
+ else
+ Some(lst.[0])
+
+let ListToOption lst = ListToOptionMsg lst "given list contains more than one element"
+
+let ListDeduplicate lst =
+ let rec __Dedup lst (visitedSet: System.Collections.Generic.HashSet<_>) acc =
+ match lst with
+ | fs :: rest ->
+ let newAcc =
+ if visitedSet.Add(fs) then
+ acc @ [fs]
+ else
+ acc
+ __Dedup rest visitedSet newAcc
+ | _ -> acc
+ __Dedup lst (new System.Collections.Generic.HashSet<_>()) []
+
+let rec ListCombine combinerFunc lst1 lst2 =
+ match lst1 with
+ | e1 :: rest ->
+ let resLst1 = lst2 |> List.fold (fun acc e2 -> acc @ [combinerFunc e1 e2]) []
+ List.concat [resLst1; ListCombine combinerFunc rest lst2]
+ | [] -> []
+
+let rec ListCombineMult combinerFunc lst1 lst2 =
+ match lst1 with
+ | e1 :: rest ->
+ let resLst1 = lst2 |> List.fold (fun acc e2 -> acc @ combinerFunc e1 e2) []
+ List.concat [resLst1; ListCombineMult combinerFunc rest lst2]
+ | [] -> []
+
+// =============================================================
+/// ensures: forall i :: 0 <= i < |lst| ==> ret[i] = Some(lst[i])
+// =============================================================
+let rec ConvertToOptionList lst =
+ match lst with
+ | fs :: rest -> Some(fs) :: ConvertToOptionList rest
+ | [] -> []
+
+// =========================================================
+/// requres: Seq.length seq <= 1, otherwise fails with errMsg
+/// ensures: if |seq| = 0 then
+/// ret = None
+/// else
+/// ret = Some(seq[0])
+// =========================================================
+let SeqToOptionMsg seq errMsg =
+ if Seq.length seq > 1 then
+ failwith errMsg
+ if Seq.isEmpty seq then
+ None
+ else
+ Some(Seq.nth 0 seq)
+
+let SeqToOption seq = SeqToOptionMsg seq "given seq contains more than one element"
+
+// =========================================================
+/// requires: Set.count set <= 1, otherwise fails with errMsg
+/// ensures: if |set| = 0 then
+/// ret = None
+/// else
+/// ret = Some(set[0])
+// =========================================================
+let SetToOptionMsg set errMsg =
+ if Set.count set > 1 then
+ failwith errMsg
+ if (Set.isEmpty set) then
+ None
+ else
+ Some(set |> Set.toList |> List.head)
+
+let SetToOption set = SetToOptionMsg set "give set contains more than one value"
+
+// ============================================================
+/// requires: n >= 0
+/// ensures: |ret| = n && forall i :: 0 <= i < n ==> ret[i] = e
+// ============================================================
+let rec GenList n e =
+ if n < 0 then
+ failwith "n must be positive"
+ if n = 0 then
+ []
+ else
+ e :: (GenList (n-1) e)
+
+// =======================================
+/// ensures: forall i :: 0 <= i < |lst| ==>
+/// if lst[i] = oldElem then
+/// ret[i] = newElem
+/// else
+/// ret[i] = lst[i]
+// =======================================
+let ListReplace oldElem newElem lst =
+ lst |> List.map (fun e -> if e = oldElem then newElem else e)
+
+// =================================================
+/// if (exists (k,v) :: (k,v) in lst && k = key) then
+/// ret = Some(v)
+/// else
+/// ret = None
+// =================================================
+let ListMapTryFind key lst =
+ let filtered = lst |> List.filter (fun (k,v) -> k = key)
+ match filtered with
+ | fs :: rest -> Some(snd fs)
+ | [] -> None
+
+// ==================================================
+/// Replaces the first occurence of the given key in
+/// the given list with the given value, or appends
+/// (key,value) if key does not exist in the list
+// ==================================================
+let rec ListMapAdd key value lst =
+ match lst with
+ | (k,v) :: rest -> if k = key then (k, value) :: rest else (k,v) :: (ListMapAdd key value rest)
+ | [] -> [(key,value)]
+
+// ==========================
+/// ensures: ret = elem in lst
+// ==========================
+let ListContains elem lst =
+ lst |> List.exists (fun e -> e = elem)
+
+// ====================================================
+/// Removes all elements in lst that are equal to "elem"
+// ====================================================
+let ListRemove elem lst =
+ lst |> List.choose (fun e -> if e = elem then None else Some(e))
+
+let rec ListRemoveIdx idx lst =
+ if idx = 0 then
+ List.tail lst
+ else
+ List.head lst :: ListRemoveIdx (idx - 1) (List.tail lst)
+
+// ===============================================================
+/// ensures: |ret| = max(|lst| - cnt, 0)
+/// ensures: forall i :: cnt <= i < |lst| ==> ret[i] = lst[i-cnt]
+// ===============================================================
+let rec ListSkip cnt lst =
+ if cnt = 0 then
+ lst
+ else
+ match lst with
+ | fs :: rest -> ListSkip (cnt-1) rest
+ | [] -> []
+
+// ===============================================================
+/// ensures: forall i :: 0 <= i < max(|srcList|, |dstList|) ==>
+/// if i = idx then
+/// ret[i] = v
+/// elif i < |srcList| then
+/// ret[i] = srcList[i]
+/// else
+/// ret[i] = dstList[i]
+// ===============================================================
+let rec ListBuild srcList idx v dstList =
+ match srcList, dstList with
+ | fs1 :: rest1, fs2 :: rest2 -> if idx = 0 then
+ v :: List.concat [rest1 ; ListSkip (List.length rest1) rest2]
+ else
+ fs1 :: ListBuild rest1 (idx-1) v rest2
+ | [], fs2 :: rest2 -> if idx = 0 then
+ v :: rest2
+ else
+ fs2 :: ListBuild [] (idx-1) v rest2
+ | _, [] -> failwith "index out of range"
+
+// =======================================
+/// ensures: forall i :: 0 <= i < |lst| ==>
+/// if i = idx then
+/// ret[i] = v
+/// else
+/// ret[i] = lst[i]
+// =======================================
+let rec ListSet idx v lst =
+ match lst with
+ | fs :: rest -> if idx = 0 then
+ v :: rest
+ else
+ fs :: ListSet (idx-1) v rest
+ | [] -> failwith "index out of range"
+
+exception KeyAlreadyExists
+
+// =======================================
+/// requires (key |--> value) !in map
+///
+/// ensures ret = map ++ (key |--> value)
+// =======================================
+let MapAddNew key value map =
+ match Map.tryFind key map with
+ | Some(existingValue) ->
+ if existingValue = value then
+ map
+ else
+ raise KeyAlreadyExists
+ | None ->
+ map |> Map.add key value
+
+// =======================================
+/// ensures: forall k,v ::
+/// if k,v in map2 then
+// k,v in ret
+/// elif k,v in map1 then
+/// k,v in ret
+/// else
+/// k,v !in ret
+// =======================================
+let rec MapAddAll map1 map2 =
+ map2 |> Map.fold (fun acc k v -> acc |> Map.add k v) map1
+
+// =======================================
+/// ensures: |ret| = 1
+/// ensures: (key -> value) in ret
+// =======================================
+let MapSingleton key value =
+ Map.empty |> Map.add key value
+
+let MapKeys map =
+ map |> Map.toList |> List.map (fun (k,v) -> k)
+
+let MapReplaceKey oldKey newKey newVal map =
+ map |> Map.toList |> List.fold (fun acc (k,v) -> if k = oldKey then acc |> Map.add newKey newVal else acc |> Map.add k v) Map.empty
+
+// -------------------------------------------
+// ------------ algorithms -------------------
+// -------------------------------------------
+
+// =======================================================================
+/// Topologically sorts a given list
+///
+/// ensures: |ret| = |lst|
+/// ensures: forall e in lst :: e in ret
+/// ensures: forall i,j :: 0 <= i < j < ==> not (followsFunc ret[j] ret[i])
+// =======================================================================
+let rec TopSort followsFunc lst =
+ match lst with
+ | [] -> []
+ | fs :: [] -> [fs]
+ | fs :: rest ->
+ let min = rest |> List.fold (fun acc elem -> if followsFunc acc elem then elem else acc) fs
+ min :: TopSort followsFunc (ListRemove min lst)
+
+// -------------------------------------------
+// ------ string active patterns -------------
+// -------------------------------------------
+
+let (|Prefix|_|) (p:string) (s:string) =
+ if s.StartsWith(p) then
+ Some(s.Substring(p.Length))
+ else
+ None
+
+// -------------------------------------------
+// --------------- workflow ------------------
+// -------------------------------------------
+
+let IfDo1 cond func1 a =
+ if cond then
+ func1 a
+ else
+ a
+
+let IfDo2 cond func2 (a1,a2) =
+ if cond then
+ func2 a1 a2
+ else
+ a1,a2
+
+let Ite cond f1 f2 =
+ if cond then
+ f1
+ else
+ f2
+
+type CascadingBuilder<'a>(failVal: 'a) =
+ member this.Bind(v, f) =
+ match v with
+ | Some(x) -> f x
+ | None -> failVal
+ member this.Return(v) = v
+
+// -------------------------------------------
+// --------------- random --------------------
+// -------------------------------------------
+
+let Iden x = x \ No newline at end of file
diff --git a/Source/Jennisys/examples/BHeap.jen b/Source/Jennisys/examples/BHeap.jen
new file mode 100644
index 00000000..55258bde
--- /dev/null
+++ b/Source/Jennisys/examples/BHeap.jen
@@ -0,0 +1,33 @@
+interface BHeap {
+ var elems: set[int]
+
+ constructor Singleton(x: int)
+ ensures elems = {x}
+
+ constructor Dupleton(a: int, b: int)
+ requires a != b
+ ensures elems = {a b}
+
+ constructor Tripleton(x: int, y: int, z: int)
+ requires x != y && y != z && z != x
+ ensures elems = {x y z}
+
+ method Find(n: int) returns (ret: bool)
+ ensures ret = (n in elems)
+}
+
+datamodel BHeap {
+ var data: int
+ var left: BHeap
+ var right: BHeap
+
+ frame
+ left * right
+
+ invariant
+ elems = {data} + (left != null ? left.elems : {}) + (right != null ? right.elems : {})
+ left != null ==> forall e :: e in left.elems ==> e < data
+ right != null ==> forall e :: e in right.elems ==> e < data
+ left = null ==> right = null
+ (left != null && right = null) ==> left.elems = {left.data}
+}
diff --git a/Source/Jennisys/examples/DList.jen b/Source/Jennisys/examples/DList.jen
new file mode 100644
index 00000000..43337ca8
--- /dev/null
+++ b/Source/Jennisys/examples/DList.jen
@@ -0,0 +1,39 @@
+interface DNode[T] {
+ var list: seq[T]
+
+ invariant
+ |list| > 0
+
+ constructor Init(t: T)
+ ensures list = [t]
+
+ constructor Double(p: T, q: T)
+ ensures list = [p q]
+
+ method List() returns (ret: seq[T])
+ ensures ret = list
+
+ method Size() returns (ret: int)
+ ensures ret = |list|
+
+ method Get(idx: int) returns (ret: T)
+ requires idx >= 0 && idx < |list|
+ ensures ret = list[idx]
+
+ method Find(n: T) returns (ret: bool)
+ ensures ret = (n in list)
+}
+
+datamodel DNode[T] {
+ var data: T
+ var next: DNode[T]
+ var prev: DNode[T]
+
+ frame
+ next
+
+ invariant
+ next = null ==> list = [data]
+ next != null ==> (list = [data] + next.list && next.prev = this)
+ prev != null ==> prev.next = this
+}
diff --git a/Source/Jennisys/examples/List.jen b/Source/Jennisys/examples/List.jen
new file mode 100644
index 00000000..85a3b692
--- /dev/null
+++ b/Source/Jennisys/examples/List.jen
@@ -0,0 +1,77 @@
+interface List[T] {
+ var list: seq[T]
+
+ constructor Empty()
+ ensures list = []
+
+ constructor Singleton(t: T)
+ ensures list = [t]
+
+ constructor Double(p: T, q: T)
+ ensures list = [p q]
+}
+
+datamodel List[T] {
+ var root: Node[T]
+
+ frame
+ root
+
+ invariant
+ root = null ==> |list| = 0
+ root != null ==> list = root.list
+}
+
+interface Node[T] {
+ var list: seq[T]
+
+ invariant
+ |list| > 0
+
+ constructor Init(t: T)
+ ensures list = [t]
+
+ constructor Double(p: T, q: T)
+ ensures list = [p q]
+
+ method List() returns (ret: seq[T])
+ ensures ret = list
+
+ method Tail() returns (tail: Node[T])
+ ensures |list| = 1 ==> tail = null
+ ensures |list| > 1 ==> tail != null && tail.list = list[1..]
+
+ method Size() returns (ret: int)
+ ensures ret = |list|
+
+ method SkipFew(num: int) returns (ret: Node[T])
+ requires num >= 0
+ ensures num >= |list| ==> ret = null
+ ensures num < |list| ==> ret != null && ret.list = list[num..]
+
+ method Get(idx: int) returns (ret: T)
+ requires idx >= 0 && idx < |list|
+ ensures ret = list[idx]
+
+ method Index(n: T) returns (ret: int)
+ ensures n !in list ==> ret = -1
+ ensures n in list ==> ret >= 0 && ret < |list| && list[ret] = n
+
+ method Find(n: T) returns (ret: bool)
+ ensures ret = (n in list)
+
+ method Insert(n: T)
+ ensures list = old(list) + [n]
+}
+
+datamodel Node[T] {
+ var data: T
+ var next: Node[T]
+
+ frame
+ next
+
+ invariant
+ next = null ==> list = [data]
+ next != null ==> list = [data] + next.list
+}
diff --git a/Source/Jennisys/examples/List2.jen b/Source/Jennisys/examples/List2.jen
new file mode 100644
index 00000000..3bd527fb
--- /dev/null
+++ b/Source/Jennisys/examples/List2.jen
@@ -0,0 +1,68 @@
+interface IntList {
+ var list: seq[int]
+
+ constructor Empty()
+ ensures list = []
+
+ constructor SingletonTwo()
+ ensures list = [2]
+
+ constructor OneTwo()
+ ensures list = [1 2]
+
+ constructor Singleton(p: int)
+ ensures list = [p]
+
+ constructor TwoConsecutive(p: int)
+ ensures list = [p] + [p+1]
+
+ constructor Double(p: int, q: int)
+ ensures list = [p] + [q]
+
+ constructor Sum(p: int, q: int)
+ ensures list = [p + q]
+}
+
+datamodel IntList {
+ var root: IntNode
+
+ frame
+ root
+
+ invariant
+ root = null <==> |list| = 0
+ root != null ==> list = root.list
+}
+
+interface IntNode {
+ var list: seq[int]
+
+ invariant
+ |list| > 0
+
+ constructor SingletonZero()
+ ensures list = [0]
+
+ constructor Init(x: int)
+ ensures list = [x]
+
+ constructor Double(x: int, y: int)
+ ensures list = [x y]
+
+ method Max() returns (ret: int)
+ ensures ret in list
+ ensures forall t :: t in list ==> ret >= t
+
+}
+
+datamodel IntNode {
+ var data: int
+ var next: IntNode
+
+ frame
+ next
+
+ invariant
+ next = null ==> list = [data]
+ next != null ==> list = [data] + next.list
+}
diff --git a/Source/Jennisys/examples/List3.jen b/Source/Jennisys/examples/List3.jen
new file mode 100644
index 00000000..9130f82a
--- /dev/null
+++ b/Source/Jennisys/examples/List3.jen
@@ -0,0 +1,71 @@
+interface IntList {
+ var list: seq[int]
+
+ constructor Empty()
+ ensures list = []
+
+ constructor SingletonTwo()
+ ensures list = [2]
+
+ constructor OneTwo()
+ ensures list = [1 2]
+
+ constructor Singleton(p: int)
+ ensures list = [p]
+
+ constructor TwoConsecutive(p: int)
+ ensures list = [p] + [p+1]
+
+ constructor Double(p: int, q: int)
+ ensures list = [p] + [q]
+
+ constructor Sum(p: int, q: int)
+ ensures list = [p + q]
+}
+
+datamodel IntList {
+ var root: IntNode
+
+ frame
+ root
+
+ invariant
+ root = null ==> |list| = 0
+ root != null ==> (|list| = |root.succ| + 1 &&
+ list[0] = root.data &&
+ (forall i :: i in 1 ... |root.succ| ==> (root.succ[i-1] != null && list[i] = root.succ[i-1].data)))
+}
+
+interface IntNode {
+ var succ: seq[IntNode]
+ var data: int
+
+ constructor Zero()
+ ensures data = 0
+ ensures succ = []
+
+ constructor OneTwo()
+ ensures data = 1
+ ensures |succ| = 1 && succ[0] != null && succ[0].data = 2
+
+ constructor Init(p: int)
+ ensures data = p
+
+ constructor InitInc(p: int)
+ ensures data = p + 1
+
+
+ invariant
+ !(null in succ)
+}
+
+datamodel IntNode {
+ var next: IntNode
+
+ frame
+ next
+
+ invariant
+ next = null ==> |succ| = 0
+ next != null ==> (succ = [next] + next.succ)
+}
diff --git a/Source/Jennisys/examples/Number.jen b/Source/Jennisys/examples/Number.jen
new file mode 100644
index 00000000..e31613bd
--- /dev/null
+++ b/Source/Jennisys/examples/Number.jen
@@ -0,0 +1,44 @@
+interface Number {
+ var num: int
+
+ constructor Init(p: int)
+ ensures num = p
+
+ constructor Double(p: int)
+ ensures num = 2*p
+
+ constructor Sum(a: int, b: int)
+ ensures num = a + b
+
+ constructor Min2(a: int, b: int)
+ ensures a < b ==> num = a
+ ensures a >= b ==> num = b
+
+ constructor Min22(a: int, b: int)
+ ensures num in {a b}
+ ensures num <= a && num <= b
+
+ constructor Min3(a: int, b: int, c: int)
+ ensures num in {a b c}
+ ensures num <= a && num <= b && num <= c
+
+ constructor Min32(a: int, b: int, c: int)
+ ensures num in {a b c}
+ ensures forall x :: x in {a b c} ==> num <= x
+
+ constructor MinSum(a: int, b: int, c: int)
+ ensures num in {a+b a+c b+c}
+ ensures num <= a+b && num <= b+c && num <= a+c
+
+ constructor Min4(a: int, b: int, c: int, d: int)
+ ensures num in {a b c d}
+ ensures forall x :: x in {a b c d} ==> num <= x
+
+ constructor Abs(a: int)
+ ensures num in {a (-a)} && num >= 0
+
+}
+
+datamodel Number {
+
+} \ No newline at end of file
diff --git a/Source/Jennisys/examples/NumberMethods.jen b/Source/Jennisys/examples/NumberMethods.jen
new file mode 100644
index 00000000..f9b17f74
--- /dev/null
+++ b/Source/Jennisys/examples/NumberMethods.jen
@@ -0,0 +1,40 @@
+interface NumberMethods {
+
+ method Double(p: int) returns (ret: int)
+ ensures ret = 2*p
+
+ method Sum(a: int, b: int) returns (ret: int)
+ ensures ret = a + b
+
+ method Min2(a: int, b: int) returns (ret: int)
+ ensures a < b ==> ret = a
+ ensures a >= b ==> ret = b
+
+ method Min22(a: int, b: int) returns (ret: int)
+ ensures ret in {a b}
+ ensures ret <= a && ret <= b
+
+ method Min3(a: int, b: int, c: int) returns (ret: int)
+ ensures ret in {a b c}
+ ensures ret <= a && ret <= b && ret <= c
+
+ method Min32(a: int, b: int, c: int) returns (ret: int)
+ ensures ret in {a b c}
+ ensures forall x :: x in {a b c} ==> ret <= x
+
+ method MinSum(a: int, b: int, c: int) returns (ret: int)
+ ensures ret in {a+b a+c b+c}
+ ensures ret <= a+b && ret <= b+c && ret <= a+c
+
+ method Min4(a: int, b: int, c: int, d: int) returns (ret: int)
+ ensures ret in {a b c d}
+ ensures forall x :: x in {a b c d} ==> ret <= x
+
+ method Abs(a: int) returns (ret: int)
+ ensures ret in {a (-a)} && ret >= 0
+
+}
+
+datamodel NumberMethods {
+
+} \ No newline at end of file
diff --git a/Source/Jennisys/examples/Set.jen b/Source/Jennisys/examples/Set.jen
new file mode 100644
index 00000000..01532f96
--- /dev/null
+++ b/Source/Jennisys/examples/Set.jen
@@ -0,0 +1,72 @@
+interface Set {
+ var elems: set[int]
+
+ constructor Empty()
+ ensures elems = {}
+
+ constructor SingletonZero()
+ ensures elems = {0}
+
+ constructor Singleton(t: int)
+ ensures elems = {t}
+
+ constructor Sum(p: int, q: int)
+ ensures elems = {p + q}
+
+ constructor Double(p: int, q: int)
+ requires p != q
+ ensures elems = {p q}
+
+}
+
+datamodel Set {
+ var root: SetNode
+
+ frame
+ root
+
+ invariant
+ root = null ==> elems = {}
+ root != null ==> elems = root.elems
+}
+
+interface SetNode {
+ var elems: set[int]
+
+ constructor Init(x: int)
+ ensures elems = {x}
+
+ constructor Double(a: int, b: int)
+ requires a != b
+ ensures elems = {a b}
+
+ constructor DoubleBase(x: int, y: int)
+ requires x > y
+ ensures elems = {x y}
+
+ constructor Triple(x: int, y: int, z: int)
+ requires x != y && y != z && z != x
+ ensures elems = {x y z}
+
+ constructor TripleBase(x: int, y: int, z: int)
+ requires x < y && y < z
+ ensures elems = {x y z}
+
+ method Find(n: int) returns (ret: bool)
+ ensures ret = (n in elems)
+
+}
+
+datamodel SetNode {
+ var data: int
+ var left: SetNode
+ var right: SetNode
+
+ frame
+ left * right
+
+ invariant
+ elems = {data} + (left != null ? left.elems : {}) + (right != null ? right.elems : {})
+ left != null ==> forall e :: e in left.elems ==> e < data
+ right != null ==> forall e :: e in right.elems ==> e > data
+}
diff --git a/Source/Jennisys/examples/Set2.jen b/Source/Jennisys/examples/Set2.jen
new file mode 100644
index 00000000..cfbcbce7
--- /dev/null
+++ b/Source/Jennisys/examples/Set2.jen
@@ -0,0 +1,60 @@
+class Set { var elems: seq[int]
+
+ constructor Empty()
+ ensures elems = []
+
+ constructor Singleton(t: int)
+ ensures elems = [t]
+
+ constructor Sum(p: int, q: int)
+ ensures elems = [p + q]
+
+}
+
+model Set {
+ var root: SetNode
+
+ frame
+ root
+
+ invariant
+ root = null ==> elems = []
+ root != null ==> elems = root.elems
+}
+
+class SetNode {
+ var elems: seq[int]
+
+ constructor Init(x: int)
+ ensures elems = [x]
+
+ constructor Double(a: int, b: int)
+ requires a != b
+ ensures |elems| = 2 && a in elems && b in elems
+
+ constructor DoubleBase(x: int, y: int)
+ requires x < y
+ ensures elems = [x y]
+
+ constructor Triple(x: int, y: int, z: int)
+ requires x != y && y != z && z != x
+ ensures |elems| = 3 && x in elems && y in elems && z in elems
+
+ constructor TripleBase(x: int, y: int, z: int)
+ requires x < y && y < z
+ ensures elems = [x y z]
+}
+
+model SetNode {
+ var data: int
+ var left: SetNode
+ var right: SetNode
+
+ frame
+ left * right
+
+ invariant
+ elems = (left != null ? left.elems : []) + [data] + (right != null ? right.elems : [])
+ left != null ==> forall e :: e in left.elems ==> e < data
+ right != null ==> forall e :: e in right.elems ==> e > data
+}
diff --git a/Source/Jennisys/examples/Simple.jen b/Source/Jennisys/examples/Simple.jen
new file mode 100644
index 00000000..2f5e7feb
--- /dev/null
+++ b/Source/Jennisys/examples/Simple.jen
@@ -0,0 +1,31 @@
+interface Simple {
+ var a: int
+
+ method Inc(p: int)
+ a := old(a) + p
+
+ method Init(n: int)
+ a := n
+
+ method Max(x: int, y: int)
+ ensures x < y ==> a = y
+ ensures x >= y ==> a = x
+
+
+ method Max2(x: int, y: int)
+ ensures a = x || a = y
+ ensures forall t :: t in {x y} ==> a >= t
+
+ method Max3__mod__(x: int, y: int)
+ ensures a in {x y}
+ ensures forall t :: t in {x y} ==> a >= t
+
+ method MaxAll__mod__(x: seq[int])
+ ensures a in x
+ ensures forall t :: t in x ==> a >= t
+}
+
+datamodel Simple {
+ var c: int
+ invariant a = c
+} \ No newline at end of file
diff --git a/Source/Jennisys/examples/jennisys-synth_List.dfy b/Source/Jennisys/examples/jennisys-synth_List.dfy
new file mode 100644
index 00000000..0611c78b
--- /dev/null
+++ b/Source/Jennisys/examples/jennisys-synth_List.dfy
@@ -0,0 +1,147 @@
+class List<T> {
+ ghost var Repr: set<object>;
+ ghost var list: seq<T>;
+
+ var root: Node<T>;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (root != null ==> root in Repr && root.Repr <= Repr && this !in root.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (root == null ==> |list| == 0) &&
+ (root != null ==> list == root.list)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (root != null ==> root.Valid())
+ }
+
+ method Empty()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [];
+ {
+ this.list := [];
+ this.root := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+ method Singleton(t: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [t];
+ {
+ var gensym65 := new Node<T>;
+ gensym65.data := t;
+ gensym65.list := [t];
+ gensym65.next := null;
+ this.list := [t];
+ this.root := gensym65;
+ // repr stuff
+ gensym65.Repr := {gensym65};
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method Double(p: T, q: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p, q];
+ {
+ var gensym66 := new Node<T>;
+ var gensym67 := new Node<T>;
+ gensym66.data := p;
+ gensym66.list := [p, q];
+ gensym66.next := gensym67;
+ gensym67.data := q;
+ gensym67.list := [q];
+ gensym67.next := null;
+ this.list := [p, q];
+ this.root := gensym66;
+ // repr stuff
+ gensym67.Repr := {gensym67};
+ gensym66.Repr := {gensym66} + gensym66.next.Repr;
+ this.Repr := {this} + this.root.Repr;
+ }
+
+}
+
+class Node<T> {
+ ghost var Repr: set<object>;
+ ghost var list: seq<T>;
+
+ var data: T;
+ var next: Node<T>;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (next != null ==> next in Repr && next.Repr <= Repr && this !in next.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (next == null <==> list == [data] && list[0] == data) &&
+ (next != null ==> list == [data] + next.list) &&
+ (|list| > 0)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (next != null ==> next.Valid_self() && (next.next != null ==> next.next.Valid_self()))
+ }
+
+ method Init(t: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [t];
+ {
+ this.data := t;
+ this.list := [t];
+ this.next := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+ method Double(p: T, q: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p, q];
+ {
+ var gensym71 := new Node<T>;
+ gensym71.data := q;
+ gensym71.list := [q];
+ gensym71.next := null;
+ this.data := p;
+ this.list := [p, q];
+ this.next := gensym71;
+ // repr stuff
+ gensym71.Repr := {gensym71};
+ this.Repr := {this} + this.next.Repr;
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/jennisys-synth_List2.dfy b/Source/Jennisys/examples/jennisys-synth_List2.dfy
new file mode 100644
index 00000000..13e521a8
--- /dev/null
+++ b/Source/Jennisys/examples/jennisys-synth_List2.dfy
@@ -0,0 +1,207 @@
+class IntList {
+ ghost var Repr: set<object>;
+ ghost var list: seq<int>;
+
+ var root: IntNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (root != null ==> root in Repr && root.Repr <= Repr && this !in root.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (root == null <==> |list| == 0) &&
+ (root != null ==> list == root.list)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (root != null ==> root.Valid())
+ }
+
+ method Empty()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [];
+ {
+ this.list := [];
+ this.root := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+ method SingletonTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [2];
+ {
+ var gensym65 := new IntNode;
+ gensym65.data := 2;
+ gensym65.list := [2];
+ gensym65.next := null;
+ this.list := [2];
+ this.root := gensym65;
+ // repr stuff
+ gensym65.Repr := {gensym65};
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method OneTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [1] + [2];
+ {
+ var gensym62 := new IntNode;
+ var gensym69 := new IntNode;
+ gensym62.data := 1;
+ gensym62.list := [1, 2];
+ gensym62.next := gensym69;
+ gensym69.data := 2;
+ gensym69.list := [2];
+ gensym69.next := null;
+ this.list := [1, 2];
+ this.root := gensym62;
+ // repr stuff
+ gensym69.Repr := {gensym69};
+ gensym62.Repr := {gensym62} + gensym62.next.Repr;
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method Singleton(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p];
+ {
+ var gensym66 := new IntNode;
+ gensym66.data := p;
+ gensym66.list := [p];
+ gensym66.next := null;
+ this.list := [p];
+ this.root := gensym66;
+ // repr stuff
+ gensym66.Repr := {gensym66};
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method TwoConsecutive(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p] + [p + 1];
+ {
+ var gensym63 := new IntNode;
+ var gensym71 := new IntNode;
+ gensym63.data := p;
+ gensym63.list := [p] + [p + 1];
+ gensym63.next := gensym71;
+ gensym71.data := p + 1;
+ gensym71.list := [p + 1];
+ gensym71.next := null;
+ this.list := [p] + [p + 1];
+ this.root := gensym63;
+ // repr stuff
+ gensym71.Repr := {gensym71};
+ gensym63.Repr := {gensym63} + gensym63.next.Repr;
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method Double(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p] + [q];
+ {
+ var gensym64 := new IntNode;
+ var gensym71 := new IntNode;
+ gensym64.data := p;
+ gensym64.list := [p] + [q];
+ gensym64.next := gensym71;
+ gensym71.data := q;
+ gensym71.list := [q];
+ gensym71.next := null;
+ this.list := [p] + [q];
+ this.root := gensym64;
+ // repr stuff
+ gensym71.Repr := {gensym71};
+ gensym64.Repr := {gensym64} + gensym64.next.Repr;
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method Sum(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p + q];
+ {
+ var gensym67 := new IntNode;
+ gensym67.data := p + q;
+ gensym67.list := [p + q];
+ gensym67.next := null;
+ this.list := [p + q];
+ this.root := gensym67;
+ // repr stuff
+ gensym67.Repr := {gensym67};
+ this.Repr := {this} + this.root.Repr;
+ }
+
+}
+
+class IntNode {
+ ghost var Repr: set<object>;
+ ghost var list: seq<int>;
+
+ var data: int;
+ var next: IntNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (next != null ==> next in Repr && next.Repr <= Repr && this !in next.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (next == null ==> list == [data] && list[0] == data) &&
+ (next != null ==> list == [data] + next.list) &&
+ (|list| > 0)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (next != null ==> next.Valid_self() && (next.next != null ==> next.next.Valid_self()))
+ }
+
+ method SingletonZero()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [0];
+ {
+ this.data := 0;
+ this.list := [0];
+ this.next := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/jennisys-synth_List3.dfy b/Source/Jennisys/examples/jennisys-synth_List3.dfy
new file mode 100644
index 00000000..e202412f
--- /dev/null
+++ b/Source/Jennisys/examples/jennisys-synth_List3.dfy
@@ -0,0 +1,255 @@
+class IntList {
+ ghost var Repr: set<object>;
+ ghost var list: seq<int>;
+
+ var root: IntNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (root != null ==> root in Repr && root.Repr <= Repr && this !in root.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (root == null ==> |list| == 0) &&
+ (root != null ==> |list| == |root.succ| + 1 && (list[0] == root.data && (forall i: int :: 0 < i && i <= |root.succ| ==> root.succ[i - 1] != null && list[i] == root.succ[i - 1].data)))
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (root != null ==> root.Valid())
+ }
+
+ method Empty()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [];
+ {
+ this.list := [];
+ this.root := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+ method SingletonTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [2];
+ {
+ var gensym65 := new IntNode;
+ gensym65.data := 2;
+ gensym65.next := null;
+ gensym65.succ := [];
+ this.list := [2];
+ this.root := gensym65;
+ // repr stuff
+ gensym65.Repr := {gensym65};
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method OneTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [1] + [2];
+ {
+ var gensym63 := new IntNode;
+ var gensym75 := new IntNode;
+ gensym63.data := 1;
+ gensym63.next := gensym75;
+ gensym63.succ := [gensym75];
+ gensym75.data := 2;
+ gensym75.next := null;
+ gensym75.succ := [];
+ this.list := [1, 2];
+ this.root := gensym63;
+ // repr stuff
+ gensym75.Repr := {gensym75};
+ gensym63.Repr := {gensym63} + gensym63.next.Repr;
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method Singleton(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p];
+ {
+ var gensym66 := new IntNode;
+ gensym66.data := p;
+ gensym66.next := null;
+ gensym66.succ := [];
+ this.list := [p];
+ this.root := gensym66;
+ // repr stuff
+ gensym66.Repr := {gensym66};
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method TwoConsecutive(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p] + [p + 1];
+ {
+ var gensym64 := new IntNode;
+ var gensym75 := new IntNode;
+ gensym64.data := p;
+ gensym64.next := gensym75;
+ gensym64.succ := [gensym75];
+ gensym75.data := p + 1;
+ gensym75.next := null;
+ gensym75.succ := [];
+ this.list := [p] + [p + 1];
+ this.root := gensym64;
+ // repr stuff
+ gensym75.Repr := {gensym75};
+ gensym64.Repr := {gensym64} + gensym64.next.Repr;
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method Double(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p] + [q];
+ {
+ var gensym65 := new IntNode;
+ var gensym77 := new IntNode;
+ gensym65.data := p;
+ gensym65.next := gensym77;
+ gensym65.succ := [gensym77];
+ gensym77.data := q;
+ gensym77.next := null;
+ gensym77.succ := [];
+ this.list := [p] + [q];
+ this.root := gensym65;
+ // repr stuff
+ gensym77.Repr := {gensym77};
+ gensym65.Repr := {gensym65} + gensym65.next.Repr;
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method Sum(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p + q];
+ {
+ var gensym67 := new IntNode;
+ gensym67.data := p + q;
+ gensym67.next := null;
+ gensym67.succ := [];
+ this.list := [p + q];
+ this.root := gensym67;
+ // repr stuff
+ gensym67.Repr := {gensym67};
+ this.Repr := {this} + this.root.Repr;
+ }
+
+}
+
+class IntNode {
+ ghost var Repr: set<object>;
+ ghost var succ: seq<IntNode>;
+ ghost var data: int;
+
+ var next: IntNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (next != null ==> next in Repr && next.Repr <= Repr && this !in next.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (next == null ==> |succ| == 0) &&
+ (next != null ==> succ == [next] + next.succ) &&
+ (!(null in succ))
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (next != null ==> next.Valid_self() && (next.next != null ==> next.next.Valid_self()))
+ }
+
+ method Zero()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == 0;
+ ensures succ == [];
+ {
+ this.data := 0;
+ this.next := null;
+ this.succ := [];
+ // repr stuff
+ this.Repr := {this};
+ }
+
+ method OneTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == 1;
+ ensures |succ| == 1;
+ ensures succ[0] != null;
+ ensures succ[0].data == 2;
+ {
+ var gensym71 := new IntNode;
+ gensym71.data := 2;
+ gensym71.next := null;
+ gensym71.succ := [];
+ this.data := 1;
+ this.next := gensym71;
+ this.succ := [gensym71];
+ // repr stuff
+ gensym71.Repr := {gensym71};
+ this.Repr := {this} + this.next.Repr;
+ }
+
+ method Init(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == p;
+ {
+ this.data := p;
+ this.next := null;
+ this.succ := [];
+ // repr stuff
+ this.Repr := {this};
+ }
+
+ method InitInc(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == p + 1;
+ {
+ this.data := p + 1;
+ this.next := null;
+ this.succ := [];
+ // repr stuff
+ this.Repr := {this};
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/jennisys-synth_Number.dfy b/Source/Jennisys/examples/jennisys-synth_Number.dfy
new file mode 100644
index 00000000..5ede7f5c
--- /dev/null
+++ b/Source/Jennisys/examples/jennisys-synth_Number.dfy
@@ -0,0 +1,202 @@
+class Number {
+ ghost var Repr: set<object>;
+ ghost var num: int;
+
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ true
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ true
+ }
+
+ method Init(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num == p;
+ {
+ this.num := p;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+ method Double(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num == 2 * p;
+ {
+ this.num := 2 * p;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+ method Sum(a: int, b: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num == a + b;
+ {
+ this.num := a + b;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+ method Min2(a: int, b: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures a < b ==> num == a;
+ ensures a >= b ==> num == b;
+ {
+ if (a >= b ==> a == b) {
+ this.num := a;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ this.num := b;
+ // repr stuff
+ this.Repr := {this};
+ }
+ }
+
+ method Min22(a: int, b: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a, b};
+ ensures num <= a;
+ ensures num <= b;
+ {
+ if (a <= b) {
+ this.num := a;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ this.num := b;
+ // repr stuff
+ this.Repr := {this};
+ }
+ }
+
+ method Min3(a: int, b: int, c: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a, b, c};
+ ensures num <= a;
+ ensures num <= b;
+ ensures num <= c;
+ {
+ if (a <= b && a <= c) {
+ this.num := a;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ if (c <= a && c <= b) {
+ this.num := c;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ this.num := b;
+ // repr stuff
+ this.Repr := {this};
+ }
+ }
+ }
+
+ method MinSum(a: int, b: int, c: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a + b, a + c, b + c};
+ ensures num <= a + b;
+ ensures num <= b + c;
+ ensures num <= a + c;
+ {
+ if (a + b <= b + c && a + b <= a + c) {
+ this.num := a + b;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ if (a + c <= a + b && a + c <= b + c) {
+ this.num := a + c;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ this.num := b + c;
+ // repr stuff
+ this.Repr := {this};
+ }
+ }
+ }
+
+ method Min4(a: int, b: int, c: int, d: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a, b, c, d};
+ ensures num <= a;
+ ensures num <= b;
+ ensures num <= c;
+ ensures num <= d;
+ {
+ if (a <= b && (a <= c && a <= d)) {
+ this.num := a;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ if (d <= a && (d <= b && d <= c)) {
+ this.num := d;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ if (c <= a && (c <= b && c <= d)) {
+ this.num := c;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ this.num := b;
+ // repr stuff
+ this.Repr := {this};
+ }
+ }
+ }
+ }
+
+ method Abs(a: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures a < 0 ==> num == -a;
+ ensures a >= 0 ==> num == a;
+ {
+ if (!(a >= 0)) {
+ this.num := -a;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ this.num := a;
+ // repr stuff
+ this.Repr := {this};
+ }
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/jennisys-synth_Set.dfy b/Source/Jennisys/examples/jennisys-synth_Set.dfy
new file mode 100644
index 00000000..efc9aa07
--- /dev/null
+++ b/Source/Jennisys/examples/jennisys-synth_Set.dfy
@@ -0,0 +1,344 @@
+class Set {
+ ghost var Repr: set<object>;
+ ghost var elems: set<int>;
+
+ var root: SetNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (root != null ==> root in Repr && root.Repr <= Repr && this !in root.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (root == null ==> elems == {}) &&
+ (root != null ==> elems == root.elems)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (root != null ==> root.Valid())
+ }
+
+ method Empty()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {};
+ {
+ this.elems := {};
+ this.root := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+ method Singleton(t: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {t};
+ {
+ var gensym66 := new SetNode;
+ gensym66.data := t;
+ gensym66.elems := {t};
+ gensym66.left := null;
+ gensym66.right := null;
+ this.elems := {t};
+ this.root := gensym66;
+ // repr stuff
+ gensym66.Repr := {gensym66};
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method Sum(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p + q};
+ {
+ var gensym68 := new SetNode;
+ gensym68.data := p + q;
+ gensym68.elems := {p + q};
+ gensym68.left := null;
+ gensym68.right := null;
+ this.elems := {p + q};
+ this.root := gensym68;
+ // repr stuff
+ gensym68.Repr := {gensym68};
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method Double(p: int, q: int)
+ modifies this;
+ requires p != q;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p, q};
+ {
+ if (q < p) {
+ var gensym71 := new SetNode;
+ var gensym75 := new SetNode;
+ gensym71.data := p;
+ gensym71.elems := {p, q};
+ gensym71.left := gensym75;
+ gensym71.right := null;
+ gensym75.data := q;
+ gensym75.elems := {q};
+ gensym75.left := null;
+ gensym75.right := null;
+ this.elems := {p, q};
+ this.root := gensym71;
+ // repr stuff
+ gensym75.Repr := {gensym75};
+ gensym71.Repr := {gensym71} + gensym71.left.Repr;
+ this.Repr := {this} + this.root.Repr;
+ } else {
+ var gensym71 := new SetNode;
+ var gensym75 := new SetNode;
+ gensym71.data := q;
+ gensym71.elems := {p, q};
+ gensym71.left := gensym75;
+ gensym71.right := null;
+ gensym75.data := p;
+ gensym75.elems := {p};
+ gensym75.left := null;
+ gensym75.right := null;
+ this.elems := {p, q};
+ this.root := gensym71;
+ // repr stuff
+ gensym75.Repr := {gensym75};
+ gensym71.Repr := {gensym71} + gensym71.left.Repr;
+ this.Repr := {this} + this.root.Repr;
+ }
+ }
+
+}
+
+class SetNode {
+ ghost var Repr: set<object>;
+ ghost var elems: set<int>;
+
+ var data: int;
+ var left: SetNode;
+ var right: SetNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (left != null ==> left in Repr && left.Repr <= Repr && this !in left.Repr) &&
+ (right != null ==> right in Repr && right.Repr <= Repr && this !in right.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (elems == ({data} + (if left != null then left.elems else {})) + (if right != null then right.elems else {})) &&
+ (left != null ==> (forall e :: e in left.elems ==> e < data)) &&
+ (right != null ==> (forall e :: e in right.elems ==> e > data))
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (left != null ==> left.Valid_self() && (left.left != null ==> left.left.Valid_self())) &&
+ (right != null ==> right.Valid_self() && (right.right != null ==> right.right.Valid_self()))
+ }
+
+ method Init(t: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {t};
+ {
+ this.data := t;
+ this.elems := {t};
+ this.left := null;
+ this.right := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+ method Double(p: int, q: int)
+ modifies this;
+ requires p != q;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p, q};
+ {
+ if (q > p) {
+ var gensym79 := new SetNode;
+ gensym79.data := q;
+ gensym79.elems := {q};
+ gensym79.left := null;
+ gensym79.right := null;
+ this.data := p;
+ this.elems := {p, q};
+ this.left := null;
+ this.right := gensym79;
+ // repr stuff
+ gensym79.Repr := {gensym79};
+ this.Repr := {this} + this.right.Repr;
+ } else {
+ var gensym79 := new SetNode;
+ gensym79.data := p;
+ gensym79.elems := {p};
+ gensym79.left := null;
+ gensym79.right := null;
+ this.data := q;
+ this.elems := {p, q};
+ this.left := null;
+ this.right := gensym79;
+ // repr stuff
+ gensym79.Repr := {gensym79};
+ this.Repr := {this} + this.right.Repr;
+ }
+ }
+
+ method Triple(p: int, q: int, r: int)
+ modifies this;
+ requires p != q;
+ requires q != r;
+ requires r != p;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p, q, r};
+ {
+ if (p < q && r > q) {
+ var gensym83 := new SetNode;
+ var gensym84 := new SetNode;
+ gensym83.data := r;
+ gensym83.elems := {r};
+ gensym83.left := null;
+ gensym83.right := null;
+ gensym84.data := p;
+ gensym84.elems := {p};
+ gensym84.left := null;
+ gensym84.right := null;
+ this.data := q;
+ this.elems := {p, q, r};
+ this.left := gensym84;
+ this.right := gensym83;
+ // repr stuff
+ gensym83.Repr := {gensym83};
+ gensym84.Repr := {gensym84};
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ if (p < r && q > r) {
+ var gensym85 := new SetNode;
+ var gensym86 := new SetNode;
+ gensym85.data := q;
+ gensym85.elems := {q};
+ gensym85.left := null;
+ gensym85.right := null;
+ gensym86.data := p;
+ gensym86.elems := {p};
+ gensym86.left := null;
+ gensym86.right := null;
+ this.data := r;
+ this.elems := {p, q, r};
+ this.left := gensym86;
+ this.right := gensym85;
+ // repr stuff
+ gensym85.Repr := {gensym85};
+ gensym86.Repr := {gensym86};
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ if (r < p && q > p) {
+ var gensym84 := new SetNode;
+ var gensym85 := new SetNode;
+ gensym84.data := q;
+ gensym84.elems := {q};
+ gensym84.left := null;
+ gensym84.right := null;
+ gensym85.data := r;
+ gensym85.elems := {r};
+ gensym85.left := null;
+ gensym85.right := null;
+ this.data := p;
+ this.elems := {p, q, r};
+ this.left := gensym85;
+ this.right := gensym84;
+ // repr stuff
+ gensym84.Repr := {gensym84};
+ gensym85.Repr := {gensym85};
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ if (q < p && r > p) {
+ var gensym82 := new SetNode;
+ var gensym83 := new SetNode;
+ gensym82.data := r;
+ gensym82.elems := {r};
+ gensym82.left := null;
+ gensym82.right := null;
+ gensym83.data := q;
+ gensym83.elems := {q};
+ gensym83.left := null;
+ gensym83.right := null;
+ this.data := p;
+ this.elems := {p, q, r};
+ this.left := gensym83;
+ this.right := gensym82;
+ // repr stuff
+ gensym82.Repr := {gensym82};
+ gensym83.Repr := {gensym83};
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ if (q < r && p > r) {
+ var gensym85 := new SetNode;
+ var gensym86 := new SetNode;
+ gensym85.data := p;
+ gensym85.elems := {p};
+ gensym85.left := null;
+ gensym85.right := null;
+ gensym86.data := q;
+ gensym86.elems := {q};
+ gensym86.left := null;
+ gensym86.right := null;
+ this.data := r;
+ this.elems := {p, q, r};
+ this.left := gensym86;
+ this.right := gensym85;
+ // repr stuff
+ gensym85.Repr := {gensym85};
+ gensym86.Repr := {gensym86};
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ var gensym82 := new SetNode;
+ var gensym83 := new SetNode;
+ gensym82.data := p;
+ gensym82.elems := {p};
+ gensym82.left := null;
+ gensym82.right := null;
+ gensym83.data := r;
+ gensym83.elems := {r};
+ gensym83.left := null;
+ gensym83.right := null;
+ this.data := q;
+ this.elems := {p, q, r};
+ this.left := gensym83;
+ this.right := gensym82;
+ // repr stuff
+ gensym82.Repr := {gensym82};
+ gensym83.Repr := {gensym83};
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/mod/jennisys-synth_List.dfy b/Source/Jennisys/examples/mod/jennisys-synth_List.dfy
new file mode 100644
index 00000000..474eb9f1
--- /dev/null
+++ b/Source/Jennisys/examples/mod/jennisys-synth_List.dfy
@@ -0,0 +1,202 @@
+class List<T> {
+ ghost var Repr: set<object>;
+ ghost var list: seq<T>;
+
+ var root: Node<T>;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (root != null ==> root in Repr && root.Repr <= Repr && this !in root.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (root == null ==> |list| == 0) &&
+ (root != null ==> list == root.list)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (root != null ==> root.Valid())
+ }
+
+
+ method Double(p: T, q: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p, q];
+ {
+ var gensym68 := new Node<T>;
+ gensym68._synth_List_Double_gensym68(p, q);
+ this.list := [p, q];
+ this.root := gensym68;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+
+ method Empty()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [];
+ {
+ this.list := [];
+ this.root := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Singleton(t: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [t];
+ {
+ var gensym69 := new Node<T>;
+ gensym69._synth_List_Singleton_gensym69(t);
+ this.list := [t];
+ this.root := gensym69;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+}
+
+class Node<T> {
+ ghost var Repr: set<object>;
+ ghost var list: seq<T>;
+
+ var data: T;
+ var next: Node<T>;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (next != null ==> next in Repr && next.Repr <= Repr && this !in next.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (next == null <==> list == [data] && list[0] == data) &&
+ (next != null ==> list == [data] + next.list) &&
+ (|list| > 0)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (next != null ==> next.Valid_self()) &&
+ (next != null && next.next != null ==> next.next.Valid_self())
+ }
+
+
+ method Init(t: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [t];
+ {
+ this.data := t;
+ this.list := [t];
+ this.next := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_List_Double_gensym68(p: T, q: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures |list| == 2;
+ ensures list[0] == p;
+ ensures list[1] == q;
+ {
+ var gensym75 := new Node<T>;
+ gensym75._synth_Node__synth_List_Double_gensym68_gensym75(q);
+ this.data := p;
+ this.list := [p, q];
+ this.next := gensym75;
+ // repr stuff
+ this.Repr := {this} + this.next.Repr;
+ }
+
+
+ method _synth_List_Singleton_gensym69(t: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures |list| == 1;
+ ensures list[0] == t;
+ {
+ this.data := t;
+ this.list := [t];
+ this.next := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Double(p: T, q: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p, q];
+ {
+ var gensym73 := new Node<T>;
+ gensym73._synth_Node_Double_gensym73(q);
+ this.data := p;
+ this.list := [p, q];
+ this.next := gensym73;
+ // repr stuff
+ this.Repr := {this} + this.next.Repr;
+ }
+
+
+ method _synth_Node_Double_gensym73(q: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures |list| == 1;
+ ensures list[0] == q;
+ {
+ this.data := q;
+ this.list := [q];
+ this.next := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_Node__synth_List_Double_gensym68_gensym75(q: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures |list| == 1;
+ ensures list[0] == q;
+ {
+ this.data := q;
+ this.list := [q];
+ this.next := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/mod/jennisys-synth_List2.dfy b/Source/Jennisys/examples/mod/jennisys-synth_List2.dfy
new file mode 100644
index 00000000..46213b46
--- /dev/null
+++ b/Source/Jennisys/examples/mod/jennisys-synth_List2.dfy
@@ -0,0 +1,323 @@
+class IntList {
+ ghost var Repr: set<object>;
+ ghost var list: seq<int>;
+
+ var root: IntNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (root != null ==> root in Repr && root.Repr <= Repr && this !in root.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (root == null <==> |list| == 0) &&
+ (root != null ==> list == root.list)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (root != null ==> root.Valid())
+ }
+
+
+ method Double(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p] + [q];
+ {
+ var gensym67 := new IntNode;
+ gensym67._synth_IntList_Double_gensym67(p, q);
+ this.list := [p] + [q];
+ this.root := gensym67;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+
+ method Empty()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [];
+ {
+ this.list := [];
+ this.root := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method OneTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [1, 2];
+ {
+ var gensym65 := new IntNode;
+ gensym65._synth_IntList_OneTwo_gensym65();
+ this.list := [1, 2];
+ this.root := gensym65;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+
+ method Singleton(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p];
+ {
+ var gensym70 := new IntNode;
+ gensym70._synth_IntList_Singleton_gensym70(p);
+ this.list := [p];
+ this.root := gensym70;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+
+ method SingletonTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [2];
+ {
+ var gensym69 := new IntNode;
+ gensym69._synth_IntList_SingletonTwo_gensym69();
+ this.list := [2];
+ this.root := gensym69;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+
+ method Sum(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p + q];
+ {
+ var gensym71 := new IntNode;
+ gensym71._synth_IntList_Sum_gensym71(p, q);
+ this.list := [p + q];
+ this.root := gensym71;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+
+ method TwoConsecutive(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p] + [p + 1];
+ {
+ var gensym66 := new IntNode;
+ gensym66._synth_IntList_TwoConsecutive_gensym66(p);
+ this.list := [p] + [p + 1];
+ this.root := gensym66;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+}
+
+class IntNode {
+ ghost var Repr: set<object>;
+ ghost var list: seq<int>;
+
+ var data: int;
+ var next: IntNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (next != null ==> next in Repr && next.Repr <= Repr && this !in next.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (next == null ==> list == [data] && list[0] == data) &&
+ (next != null ==> list == [data] + next.list) &&
+ (|list| > 0)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (next != null ==> next.Valid_self()) &&
+ (next != null && next.next != null ==> next.next.Valid_self())
+ }
+
+
+ method SingletonZero()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [0];
+ {
+ this.data := 0;
+ this.list := [0];
+ this.next := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntList_Double_gensym67(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p] + [q];
+ {
+ var gensym78 := new IntNode;
+ gensym78._synth_IntNode__synth_IntList_Double_gensym67_gensym78(q);
+ this.data := p;
+ this.list := [p] + [q];
+ this.next := gensym78;
+ // repr stuff
+ this.Repr := {this} + this.next.Repr;
+ }
+
+
+ method _synth_IntList_OneTwo_gensym65()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures |list| == 2;
+ ensures list[0] == 1;
+ ensures list[1] == 2;
+ {
+ var gensym75 := new IntNode;
+ gensym75._synth_IntNode__synth_IntList_OneTwo_gensym65_gensym75();
+ this.data := 1;
+ this.list := [1, 2];
+ this.next := gensym75;
+ // repr stuff
+ this.Repr := {this} + this.next.Repr;
+ }
+
+
+ method _synth_IntList_SingletonTwo_gensym69()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures |list| == 1;
+ ensures list[0] == 2;
+ {
+ this.data := 2;
+ this.list := [2];
+ this.next := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntList_Singleton_gensym70(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures |list| == 1;
+ ensures list[0] == p;
+ {
+ this.data := p;
+ this.list := [p];
+ this.next := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntList_Sum_gensym71(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures |list| == 1;
+ ensures list[0] == p + q;
+ {
+ this.data := p + q;
+ this.list := [p + q];
+ this.next := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntList_TwoConsecutive_gensym66(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p] + [p + 1];
+ {
+ var gensym78 := new IntNode;
+ gensym78._synth_IntNode__synth_IntList_TwoConsecutive_gensym66_gensym78(p);
+ this.data := p;
+ this.list := [p] + [p + 1];
+ this.next := gensym78;
+ // repr stuff
+ this.Repr := {this} + this.next.Repr;
+ }
+
+
+ method _synth_IntNode__synth_IntList_Double_gensym67_gensym78(q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures |list| == 1;
+ ensures list[0] == q;
+ {
+ this.data := q;
+ this.list := [q];
+ this.next := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntNode__synth_IntList_OneTwo_gensym65_gensym75()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures |list| == 1;
+ ensures list[0] == 2;
+ {
+ this.data := 2;
+ this.list := [2];
+ this.next := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntNode__synth_IntList_TwoConsecutive_gensym66_gensym78(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures |list| == 1;
+ ensures list[0] == p + 1;
+ {
+ this.data := p + 1;
+ this.list := [p + 1];
+ this.next := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/mod/jennisys-synth_List3.dfy b/Source/Jennisys/examples/mod/jennisys-synth_List3.dfy
new file mode 100644
index 00000000..e079b608
--- /dev/null
+++ b/Source/Jennisys/examples/mod/jennisys-synth_List3.dfy
@@ -0,0 +1,393 @@
+class IntList {
+ ghost var Repr: set<object>;
+ ghost var list: seq<int>;
+
+ var root: IntNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (root != null ==> root in Repr && root.Repr <= Repr && this !in root.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (root == null ==> |list| == 0) &&
+ (root != null ==> |list| == |root.succ| + 1 && (list[0] == root.data && (forall i :: 1 <= i && i <= |root.succ| ==> root.succ[i - 1] != null && list[i] == root.succ[i - 1].data)))
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (root != null ==> root.Valid())
+ }
+
+
+ method Double(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p] + [q];
+ {
+ var gensym68 := new IntNode;
+ gensym68._synth_IntList_Double_gensym68(p, q);
+ this.list := [p] + [q];
+ this.root := gensym68;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+
+ method Empty()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [];
+ {
+ this.list := [];
+ this.root := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method OneTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [1, 2];
+ {
+ var gensym65 := new IntNode;
+ gensym65._synth_IntList_OneTwo_gensym65();
+ this.list := [1, 2];
+ this.root := gensym65;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+
+ method Singleton(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p];
+ {
+ var gensym70 := new IntNode;
+ gensym70._synth_IntList_Singleton_gensym70(p);
+ this.list := [p];
+ this.root := gensym70;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+
+ method SingletonTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [2];
+ {
+ var gensym69 := new IntNode;
+ gensym69._synth_IntList_SingletonTwo_gensym69();
+ this.list := [2];
+ this.root := gensym69;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+
+ method Sum(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p + q];
+ {
+ var gensym71 := new IntNode;
+ gensym71._synth_IntList_Sum_gensym71(p, q);
+ this.list := [p + q];
+ this.root := gensym71;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+
+ method TwoConsecutive(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p] + [p + 1];
+ {
+ var gensym67 := new IntNode;
+ gensym67._synth_IntList_TwoConsecutive_gensym67(p);
+ this.list := [p] + [p + 1];
+ this.root := gensym67;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+}
+
+class IntNode {
+ ghost var Repr: set<object>;
+ ghost var succ: seq<IntNode>;
+ ghost var data: int;
+
+ var next: IntNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (next != null ==> next in Repr && next.Repr <= Repr && this !in next.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (next == null ==> |succ| == 0) &&
+ (next != null ==> succ == [next] + next.succ) &&
+ (!(null in succ))
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (next != null ==> next.Valid_self()) &&
+ (next != null && next.next != null ==> next.next.Valid_self())
+ }
+
+
+ method Init(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == p;
+ {
+ this.data := p;
+ this.next := null;
+ this.succ := [];
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method InitInc(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == p + 1;
+ {
+ this.data := p + 1;
+ this.next := null;
+ this.succ := [];
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Zero()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == 0;
+ ensures succ == [];
+ {
+ this.data := 0;
+ this.next := null;
+ this.succ := [];
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntList_Double_gensym68(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == p;
+ ensures |succ| == 1;
+ ensures succ[0].data == q;
+ ensures succ[0].succ == [];
+ {
+ var gensym80 := new IntNode;
+ gensym80._synth_IntNode__synth_IntList_Double_gensym68_gensym80(q);
+ this.data := p;
+ this.next := gensym80;
+ this.succ := [gensym80];
+ // repr stuff
+ this.Repr := {this} + this.next.Repr;
+ }
+
+
+ method _synth_IntList_OneTwo_gensym65()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == 1;
+ ensures |succ| == 1;
+ ensures succ[0].data == 2;
+ ensures succ[0].succ == [];
+ {
+ var gensym78 := new IntNode;
+ gensym78._synth_IntNode__synth_IntList_OneTwo_gensym65_gensym78();
+ this.data := 1;
+ this.next := gensym78;
+ this.succ := [gensym78];
+ // repr stuff
+ this.Repr := {this} + this.next.Repr;
+ }
+
+
+ method _synth_IntList_SingletonTwo_gensym69()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == 2;
+ ensures |succ| == 0;
+ {
+ this.data := 2;
+ this.next := null;
+ this.succ := [];
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntList_Singleton_gensym70(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == p;
+ ensures |succ| == 0;
+ {
+ this.data := p;
+ this.next := null;
+ this.succ := [];
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntList_Sum_gensym71(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == p + q;
+ ensures |succ| == 0;
+ {
+ this.data := p + q;
+ this.next := null;
+ this.succ := [];
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntList_TwoConsecutive_gensym67(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == p;
+ ensures |succ| == 1;
+ ensures succ[0].data == p + 1;
+ ensures succ[0].succ == [];
+ {
+ var gensym79 := new IntNode;
+ gensym79._synth_IntNode__synth_IntList_TwoConsecutive_gensym67_gensym79(p);
+ this.data := p;
+ this.next := gensym79;
+ this.succ := [gensym79];
+ // repr stuff
+ this.Repr := {this} + this.next.Repr;
+ }
+
+
+ method OneTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == 1;
+ ensures |succ| == 1;
+ ensures succ[0] != null;
+ ensures succ[0].data == 2;
+ {
+ var gensym73 := new IntNode;
+ gensym73._synth_IntNode_OneTwo_gensym73();
+ this.data := 1;
+ this.next := gensym73;
+ this.succ := [gensym73];
+ // repr stuff
+ this.Repr := {this} + this.next.Repr;
+ }
+
+
+ method _synth_IntNode_OneTwo_gensym73()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == 2;
+ ensures |succ| == 0;
+ {
+ this.data := 2;
+ this.next := null;
+ this.succ := [];
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntNode__synth_IntList_Double_gensym68_gensym80(q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == q;
+ ensures |succ| == 0;
+ {
+ this.data := q;
+ this.next := null;
+ this.succ := [];
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntNode__synth_IntList_OneTwo_gensym65_gensym78()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == 2;
+ ensures |succ| == 0;
+ {
+ this.data := 2;
+ this.next := null;
+ this.succ := [];
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntNode__synth_IntList_TwoConsecutive_gensym67_gensym79(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == p + 1;
+ ensures |succ| == 0;
+ {
+ this.data := p + 1;
+ this.next := null;
+ this.succ := [];
+ // repr stuff
+ this.Repr := {this};
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/mod/jennisys-synth_Number.dfy b/Source/Jennisys/examples/mod/jennisys-synth_Number.dfy
new file mode 100644
index 00000000..3f1e6b4b
--- /dev/null
+++ b/Source/Jennisys/examples/mod/jennisys-synth_Number.dfy
@@ -0,0 +1,233 @@
+class Number {
+ ghost var Repr: set<object>;
+ ghost var num: int;
+
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ true
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ true
+ }
+
+
+ method Double(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num == 2 * p;
+ {
+ this.num := 2 * p;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Init(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num == p;
+ {
+ this.num := p;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Sum(a: int, b: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num == a + b;
+ {
+ this.num := a + b;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Abs(a: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a, -a};
+ ensures num >= 0;
+ {
+ if (a >= 0) {
+ this.num := a;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ this.num := -a;
+ // repr stuff
+ this.Repr := {this};
+ }
+ }
+
+
+ method Min4(a: int, b: int, c: int, d: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a, b, c, d};
+ ensures (forall x :: x in {a, b, c, d} ==> num <= x);
+ {
+ if (a <= b && (a <= c && a <= d)) {
+ this.num := a;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ if (d <= a && (d <= b && d <= c)) {
+ this.num := d;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ if (c <= a && (c <= b && c <= d)) {
+ this.num := c;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ this.num := b;
+ // repr stuff
+ this.Repr := {this};
+ }
+ }
+ }
+ }
+
+
+ method MinSum(a: int, b: int, c: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a + b, a + c, b + c};
+ ensures num <= a + b;
+ ensures num <= b + c;
+ ensures num <= a + c;
+ {
+ if (a + b <= b + c && a + b <= a + c) {
+ this.num := a + b;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ if (a + c <= a + b && a + c <= b + c) {
+ this.num := a + c;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ this.num := b + c;
+ // repr stuff
+ this.Repr := {this};
+ }
+ }
+ }
+
+
+ method Min32(a: int, b: int, c: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a, b, c};
+ ensures (forall x :: x in {a, b, c} ==> num <= x);
+ {
+ if (a <= b && a <= c) {
+ this.num := a;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ if (c <= a && c <= b) {
+ this.num := c;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ this.num := b;
+ // repr stuff
+ this.Repr := {this};
+ }
+ }
+ }
+
+
+ method Min3(a: int, b: int, c: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a, b, c};
+ ensures num <= a;
+ ensures num <= b;
+ ensures num <= c;
+ {
+ if (a <= b && a <= c) {
+ this.num := a;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ if (c <= a && c <= b) {
+ this.num := c;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ this.num := b;
+ // repr stuff
+ this.Repr := {this};
+ }
+ }
+ }
+
+
+ method Min22(a: int, b: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a, b};
+ ensures num <= a;
+ ensures num <= b;
+ {
+ if (a <= b) {
+ this.num := a;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ this.num := b;
+ // repr stuff
+ this.Repr := {this};
+ }
+ }
+
+
+ method Min2(a: int, b: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures a < b ==> num == a;
+ ensures a >= b ==> num == b;
+ {
+ if (a >= b ==> a == b) {
+ this.num := a;
+ // repr stuff
+ this.Repr := {this};
+ } else {
+ this.num := b;
+ // repr stuff
+ this.Repr := {this};
+ }
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/mod/jennisys-synth_Set.dfy b/Source/Jennisys/examples/mod/jennisys-synth_Set.dfy
new file mode 100644
index 00000000..40404bfb
--- /dev/null
+++ b/Source/Jennisys/examples/mod/jennisys-synth_Set.dfy
@@ -0,0 +1,388 @@
+class Set {
+ ghost var Repr: set<object>;
+ ghost var elems: set<int>;
+
+ var root: SetNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (root != null ==> root in Repr && root.Repr <= Repr && this !in root.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (root == null ==> elems == {}) &&
+ (root != null ==> elems == root.elems)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (root != null ==> root.Valid())
+ }
+
+
+ method Double(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p, q};
+ {
+ var gensym67 := new SetNode;
+ gensym67._synth_Set_Double_gensym67(p, q);
+ this.elems := {p, q};
+ this.root := gensym67;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+
+ method Empty()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {};
+ {
+ this.elems := {};
+ this.root := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Singleton(t: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {t};
+ {
+ var gensym67 := new SetNode;
+ gensym67._synth_Set_Singleton_gensym67(t);
+ this.elems := {t};
+ this.root := gensym67;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+
+ method Sum(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p + q};
+ {
+ var gensym69 := new SetNode;
+ gensym69._synth_Set_Sum_gensym69(p, q);
+ this.elems := {p + q};
+ this.root := gensym69;
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ }
+
+}
+
+class SetNode {
+ ghost var Repr: set<object>;
+ ghost var elems: set<int>;
+
+ var data: int;
+ var left: SetNode;
+ var right: SetNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (left != null ==> left in Repr && left.Repr <= Repr && this !in left.Repr) &&
+ (right != null ==> right in Repr && right.Repr <= Repr && this !in right.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (elems == ({data} + (if left != null then left.elems else {})) + (if right != null then right.elems else {})) &&
+ (left != null ==> (forall e :: e in left.elems ==> e < data)) &&
+ (right != null ==> (forall e :: e in right.elems ==> e > data))
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (left != null ==> left.Valid_self()) &&
+ (right != null ==> right.Valid_self()) &&
+ (left != null && left.left != null ==> left.left.Valid_self()) &&
+ (left != null && left.right != null ==> left.right.Valid_self()) &&
+ (right != null && right.left != null ==> right.left.Valid_self()) &&
+ (right != null && right.right != null ==> right.right.Valid_self())
+ }
+
+
+ method Double(p: int, q: int)
+ modifies this;
+ requires p != q;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p, q};
+ {
+ if (q > p) {
+ var gensym77 := new SetNode;
+ gensym77._synth_SetNode_Double_gensym77(q);
+ this.data := p;
+ this.elems := {p, q};
+ this.left := null;
+ this.right := gensym77;
+ // repr stuff
+ this.Repr := {this} + this.right.Repr;
+ } else {
+ var gensym77 := new SetNode;
+ gensym77._synth_SetNode_Double_gensym77(p);
+ this.data := q;
+ this.elems := {p, q};
+ this.left := null;
+ this.right := gensym77;
+ // repr stuff
+ this.Repr := {this} + this.right.Repr;
+ }
+ }
+
+
+ method _synth_SetNode_Double_gensym77(q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {q};
+ {
+ this.data := q;
+ this.elems := {q};
+ this.left := null;
+ this.right := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_SetNode_Triple_gensym80(r: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {r};
+ {
+ this.data := r;
+ this.elems := {r};
+ this.left := null;
+ this.right := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Triple(p: int, q: int, r: int)
+ modifies this;
+ requires p != q;
+ requires q != r;
+ requires r != p;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p, q, r};
+ {
+ if (p < q && r > q) {
+ var gensym80 := new SetNode;
+ var gensym81 := new SetNode;
+ gensym80._synth_SetNode_Triple_gensym80(r);
+ gensym81._synth_SetNode_Triple_gensym81(p);
+ this.data := q;
+ this.elems := {p, q, r};
+ this.left := gensym81;
+ this.right := gensym80;
+ // repr stuff
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ if (r < p && q > p) {
+ var gensym80 := new SetNode;
+ var gensym81 := new SetNode;
+ gensym80._synth_SetNode_Triple_gensym80(q);
+ gensym81._synth_SetNode_Triple_gensym81(r);
+ this.data := p;
+ this.elems := {p, q, r};
+ this.left := gensym81;
+ this.right := gensym80;
+ // repr stuff
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ if (p < r && q > r) {
+ var gensym80 := new SetNode;
+ var gensym81 := new SetNode;
+ gensym80._synth_SetNode_Triple_gensym80(q);
+ gensym81._synth_SetNode_Triple_gensym81(p);
+ this.data := r;
+ this.elems := {p, q, r};
+ this.left := gensym81;
+ this.right := gensym80;
+ // repr stuff
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ if (r < q && p > q) {
+ var gensym80 := new SetNode;
+ var gensym81 := new SetNode;
+ gensym80._synth_SetNode_Triple_gensym80(p);
+ gensym81._synth_SetNode_Triple_gensym81(r);
+ this.data := q;
+ this.elems := {p, q, r};
+ this.left := gensym81;
+ this.right := gensym80;
+ // repr stuff
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ if (q < r && p > r) {
+ var gensym80 := new SetNode;
+ var gensym81 := new SetNode;
+ gensym80._synth_SetNode_Triple_gensym80(p);
+ gensym81._synth_SetNode_Triple_gensym81(q);
+ this.data := r;
+ this.elems := {p, q, r};
+ this.left := gensym81;
+ this.right := gensym80;
+ // repr stuff
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ var gensym80 := new SetNode;
+ var gensym81 := new SetNode;
+ gensym80._synth_SetNode_Triple_gensym80(r);
+ gensym81._synth_SetNode_Triple_gensym81(q);
+ this.data := p;
+ this.elems := {p, q, r};
+ this.left := gensym81;
+ this.right := gensym80;
+ // repr stuff
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ method _synth_SetNode_Triple_gensym81(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p};
+ {
+ this.data := p;
+ this.elems := {p};
+ this.left := null;
+ this.right := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Init(t: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {t};
+ {
+ this.data := t;
+ this.elems := {t};
+ this.left := null;
+ this.right := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_SetNode__synth_Set_Double_gensym67_gensym77(q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {q};
+ {
+ this.data := q;
+ this.elems := {q};
+ this.left := null;
+ this.right := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_Set_Double_gensym67(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p, q};
+ {
+ if (q > p) {
+ var gensym77 := new SetNode;
+ gensym77._synth_SetNode__synth_Set_Double_gensym67_gensym77(q);
+ this.data := p;
+ this.elems := {p, q};
+ this.left := null;
+ this.right := gensym77;
+ // repr stuff
+ this.Repr := {this} + this.right.Repr;
+ } else {
+ if (p > q) {
+ var gensym77 := new SetNode;
+ gensym77._synth_SetNode__synth_Set_Double_gensym67_gensym77(p);
+ this.data := q;
+ this.elems := {p, q};
+ this.left := null;
+ this.right := gensym77;
+ // repr stuff
+ this.Repr := {this} + this.right.Repr;
+ } else {
+ this.data := q;
+ this.elems := {p, q};
+ this.left := null;
+ this.right := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+ }
+ }
+
+
+ method _synth_Set_Singleton_gensym67(t: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {t};
+ {
+ this.data := t;
+ this.elems := {t};
+ this.left := null;
+ this.right := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_Set_Sum_gensym69(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p + q};
+ {
+ this.data := p + q;
+ this.elems := {p + q};
+ this.left := null;
+ this.right := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/mod2/jennisys-synth_DList.dfy b/Source/Jennisys/examples/mod2/jennisys-synth_DList.dfy
new file mode 100644
index 00000000..3e1aa99f
--- /dev/null
+++ b/Source/Jennisys/examples/mod2/jennisys-synth_DList.dfy
@@ -0,0 +1,255 @@
+class DList<T> {
+ ghost var Repr: set<object>;
+ ghost var list: seq<T>;
+
+ var root: DNode<T>;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (root != null ==> root in Repr && root.Repr <= Repr && this !in root.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (root == null ==> |list| == 0) &&
+ (root != null ==> list == root.list)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (root != null ==> root.Valid())
+ }
+
+
+ method Double(p: T, q: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p, q];
+ ensures list[0] == p;
+ ensures list[1] == q;
+ ensures |list| == 2;
+ {
+ var gensym79 := new DNode<T>;
+ var gensym85 := new DNode<T>;
+ this.list := [p, q];
+ this.root := gensym79;
+ gensym79.data := p;
+ gensym79.list := [p, q];
+ gensym79.next := gensym85;
+ gensym79.prev := null;
+ gensym85.data := q;
+ gensym85.list := [q];
+ gensym85.next := null;
+ gensym85.prev := gensym79;
+
+ // repr stuff
+ gensym85.Repr := {gensym85};
+ gensym79.Repr := {gensym79} + {gensym85};
+ this.Repr := {this} + ({gensym79} + {gensym85});
+ // assert repr objects are valid (helps verification)
+ assert gensym79.Valid() && gensym85.Valid();
+ }
+
+
+ method Empty()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [];
+ ensures |list| == 0;
+ {
+ this.list := [];
+ this.root := null;
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Singleton(t: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [t];
+ ensures list[0] == t;
+ ensures |list| == 1;
+ {
+ var gensym78 := new DNode<T>;
+ this.list := [t];
+ this.root := gensym78;
+ gensym78.data := t;
+ gensym78.list := [t];
+ gensym78.next := null;
+ gensym78.prev := null;
+
+ // repr stuff
+ gensym78.Repr := {gensym78};
+ this.Repr := {this} + {gensym78};
+ // assert repr objects are valid (helps verification)
+ assert gensym78.Valid();
+ }
+
+}
+
+class DNode<T> {
+ ghost var Repr: set<object>;
+ ghost var list: seq<T>;
+
+ var data: T;
+ var next: DNode<T>;
+ var prev: DNode<T>;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (next != null ==> next in Repr && next.Repr <= Repr && this !in next.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (next == null ==> (list == [data] && list[0] == data) && |list| == 1) &&
+ (next != null ==> list == [data] + next.list && next.prev == this) &&
+ (prev != null ==> prev.next == this) &&
+ (|list| > 0)
+ }
+
+ function Valid(): bool
+ reads *;
+ decreases Repr;
+ {
+ this.Valid_self() &&
+ (next != null ==> next.Valid()) &&
+ (next != null ==> next.Valid_self()) &&
+ (next != null && next.next != null ==> next.next.Valid_self())
+ }
+
+
+ method Double(p: T, q: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p, q];
+ ensures list[0] == p;
+ ensures list[1] == q;
+ ensures |list| == 2;
+ {
+ var gensym95 := new DNode<T>;
+ this.data := p;
+ this.list := [p, q];
+ this.next := gensym95;
+ this.prev := null;
+ gensym95.data := q;
+ gensym95.list := [q];
+ gensym95.next := null;
+ gensym95.prev := this;
+
+ // repr stuff
+ this.Repr := {this} + {gensym95};
+ gensym95.Repr := {gensym95};
+ // assert repr objects are valid (helps verification)
+ assert gensym95.Valid();
+ }
+
+
+ method Find(n: T) returns (ret: bool)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == (n in list);
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := n == this.data;
+ } else {
+ var x_5 := this.next.Find(n);
+ ret := n == this.data || x_5;
+ }
+ }
+
+
+ method Get(idx: int) returns (ret: T)
+ requires Valid();
+ requires idx >= 0;
+ requires idx < |list|;
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == list[idx];
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := this.data;
+ } else {
+ if (idx == 0) {
+ ret := this.data;
+ } else {
+ var x_6 := this.next.Get(idx - 1);
+ ret := x_6;
+ }
+ }
+ }
+
+
+ method Init(t: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [t];
+ ensures list[0] == t;
+ ensures |list| == 1;
+ {
+ this.data := t;
+ this.list := [t];
+ this.next := null;
+ this.prev := null;
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method List() returns (ret: seq<T>)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == list;
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := [this.data];
+ } else {
+ var x_7 := this.next.List();
+ ret := [this.data] + x_7;
+ }
+ }
+
+
+ method Size() returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == |list|;
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := 1;
+ } else {
+ var x_8 := this.next.Size();
+ ret := 1 + x_8;
+ }
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/mod2/jennisys-synth_List.dfy b/Source/Jennisys/examples/mod2/jennisys-synth_List.dfy
new file mode 100644
index 00000000..9939dcc2
--- /dev/null
+++ b/Source/Jennisys/examples/mod2/jennisys-synth_List.dfy
@@ -0,0 +1,249 @@
+class List<T> {
+ ghost var Repr: set<object>;
+ ghost var list: seq<T>;
+
+ var root: Node<T>;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (root != null ==> root in Repr && root.Repr <= Repr && this !in root.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (root == null ==> |list| == 0) &&
+ (root != null ==> list == root.list)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (root != null ==> root.Valid())
+ }
+
+
+ method Double(p: T, q: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p, q];
+ ensures list[0] == p;
+ ensures list[1] == q;
+ ensures |list| == 2;
+ {
+ var gensym78 := new Node<T>;
+ gensym78.Double(p, q);
+ this.list := [p, q];
+ this.root := gensym78;
+
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym78.Valid();
+ }
+
+
+ method Empty()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [];
+ ensures |list| == 0;
+ {
+ this.list := [];
+ this.root := null;
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Singleton(t: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [t];
+ ensures list[0] == t;
+ ensures |list| == 1;
+ {
+ var gensym77 := new Node<T>;
+ gensym77.Init(t);
+ this.list := [t];
+ this.root := gensym77;
+
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym77.Valid();
+ }
+
+}
+
+class Node<T> {
+ ghost var Repr: set<object>;
+ ghost var list: seq<T>;
+
+ var data: T;
+ var next: Node<T>;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (next != null ==> next in Repr && next.Repr <= Repr && this !in next.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (next == null ==> (list == [data] && list[0] == data) && |list| == 1) &&
+ (next != null ==> list == [data] + next.list) &&
+ (|list| > 0)
+ }
+
+ function Valid(): bool
+ reads *;
+ decreases Repr;
+ {
+ this.Valid_self() &&
+ (next != null ==> next.Valid()) &&
+ (next != null ==> next.Valid_self()) &&
+ (next != null && next.next != null ==> next.next.Valid_self())
+ }
+
+
+ method Double(p: T, q: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p, q];
+ ensures list[0] == p;
+ ensures list[1] == q;
+ ensures |list| == 2;
+ {
+ var gensym87 := new Node<T>;
+ gensym87.Init(q);
+ this.data := p;
+ this.list := [p, q];
+ this.next := gensym87;
+
+ // repr stuff
+ this.Repr := {this} + this.next.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym87.Valid();
+ }
+
+
+ method Find(n: T) returns (ret: bool)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == (n in list);
+ decreases Repr;
+ {
+ if (n != this.data && this.next == null) {
+ ret := n == this.data;
+ } else {
+ if (this.next != null) {
+ var x_6 := this.next.Find(n);
+ ret := n == this.data || x_6;
+ } else {
+ ret := true;
+ }
+ }
+ }
+
+
+ method Get(idx: int) returns (ret: T)
+ requires Valid();
+ requires idx >= 0;
+ requires idx < |list|;
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == list[idx];
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := this.data;
+ } else {
+ if (idx == 0) {
+ ret := this.data;
+ } else {
+ var x_7 := this.next.Get(idx - 1);
+ ret := x_7;
+ }
+ }
+ }
+
+
+ method Init(t: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [t];
+ ensures list[0] == t;
+ ensures |list| == 1;
+ {
+ this.data := t;
+ this.list := [t];
+ this.next := null;
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method List() returns (ret: seq<T>)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == list;
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := [this.data];
+ } else {
+ var x_8 := this.next.List();
+ ret := [this.data] + x_8;
+ }
+ }
+
+
+ method Size() returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == |list|;
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := 1;
+ } else {
+ var x_9 := this.next.Size();
+ ret := 1 + x_9;
+ }
+ }
+
+
+ method Tail() returns (tail: Node<T>)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures |list| == 1 ==> tail == null;
+ ensures |list| > 1 ==> tail != null && tail.list == list[1..];
+ ensures tail != null ==> tail.Valid();
+ {
+ tail := this.next;
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/mod2/jennisys-synth_List2.dfy b/Source/Jennisys/examples/mod2/jennisys-synth_List2.dfy
new file mode 100644
index 00000000..f994186b
--- /dev/null
+++ b/Source/Jennisys/examples/mod2/jennisys-synth_List2.dfy
@@ -0,0 +1,225 @@
+class IntList {
+ ghost var Repr: set<object>;
+ ghost var list: seq<int>;
+
+ var root: IntNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (root != null ==> root in Repr && root.Repr <= Repr && this !in root.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (root == null <==> |list| == 0) &&
+ (root != null ==> list == root.list)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (root != null ==> root.Valid())
+ }
+
+
+ method Double(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p] + [q];
+ ensures list[0] == p;
+ ensures list[1] == q;
+ ensures |list| == 2;
+ {
+ var gensym74 := new IntNode;
+ gensym74.Double(p, q);
+ this.list := [p] + [q];
+ this.root := gensym74;
+
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym74.Valid();
+ }
+
+
+ method Empty()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [];
+ ensures |list| == 0;
+ {
+ this.list := [];
+ this.root := null;
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method OneTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [1, 2];
+ ensures list[0] == 1;
+ ensures list[1] == 2;
+ ensures |list| == 2;
+ {
+ this.Double(1, 2);
+ }
+
+
+ method Singleton(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p];
+ ensures list[0] == p;
+ ensures |list| == 1;
+ {
+ var gensym73 := new IntNode;
+ gensym73.Init(p);
+ this.list := [p];
+ this.root := gensym73;
+
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym73.Valid();
+ }
+
+
+ method SingletonTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [2];
+ ensures list[0] == 2;
+ ensures |list| == 1;
+ {
+ this.Singleton(2);
+ }
+
+
+ method Sum(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p + q];
+ ensures list[0] == p + q;
+ ensures |list| == 1;
+ {
+ this.Singleton(p + q);
+ }
+
+
+ method TwoConsecutive(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p] + [p + 1];
+ ensures list[0] == p;
+ ensures list[1] == p + 1;
+ ensures |list| == 2;
+ {
+ this.Double(p, p + 1);
+ }
+
+}
+
+class IntNode {
+ ghost var Repr: set<object>;
+ ghost var list: seq<int>;
+
+ var data: int;
+ var next: IntNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (next != null ==> next in Repr && next.Repr <= Repr && this !in next.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (next == null ==> (list == [data] && list[0] == data) && |list| == 1) &&
+ (next != null ==> list == [data] + next.list) &&
+ (|list| > 0)
+ }
+
+ function Valid(): bool
+ reads *;
+ decreases Repr;
+ {
+ this.Valid_self() &&
+ (next != null ==> next.Valid()) &&
+ (next != null ==> next.Valid_self()) &&
+ (next != null && next.next != null ==> next.next.Valid_self())
+ }
+
+
+ method Double(x: int, y: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [x, y];
+ ensures list[0] == x;
+ ensures list[1] == y;
+ ensures |list| == 2;
+ {
+ var gensym87 := new IntNode;
+ gensym87.Init(y);
+ this.data := x;
+ this.list := [x, y];
+ this.next := gensym87;
+
+ // repr stuff
+ this.Repr := {this} + this.next.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym87.Valid();
+ }
+
+
+ method Init(x: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [x];
+ ensures list[0] == x;
+ ensures |list| == 1;
+ {
+ this.data := x;
+ this.list := [x];
+ this.next := null;
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method SingletonZero()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [0];
+ ensures list[0] == 0;
+ ensures |list| == 1;
+ {
+ this.Init(0);
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/mod2/jennisys-synth_List3.dfy b/Source/Jennisys/examples/mod2/jennisys-synth_List3.dfy
new file mode 100644
index 00000000..65e308bd
--- /dev/null
+++ b/Source/Jennisys/examples/mod2/jennisys-synth_List3.dfy
@@ -0,0 +1,309 @@
+class IntList {
+ ghost var Repr: set<object>;
+ ghost var list: seq<int>;
+
+ var root: IntNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (root != null ==> root in Repr && root.Repr <= Repr && this !in root.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (root == null ==> |list| == 0) &&
+ (root != null ==> |list| == |root.succ| + 1 && (list[0] == root.data && (forall i :: 1 <= i && i <= |root.succ| ==> root.succ[i - 1] != null && list[i] == root.succ[i - 1].data)))
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (root != null ==> root.Valid())
+ }
+
+
+ method Double(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p] + [q];
+ ensures list[0] == p;
+ ensures list[1] == q;
+ ensures |list| == 2;
+ {
+ var gensym79 := new IntNode;
+ gensym79._synth_IntList_Double_gensym79(p, q);
+ this.list := [p] + [q];
+ this.root := gensym79;
+
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym79.Valid();
+ }
+
+
+ method Empty()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [];
+ ensures |list| == 0;
+ {
+ this.list := [];
+ this.root := null;
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method OneTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [1, 2];
+ ensures list[0] == 1;
+ ensures list[1] == 2;
+ ensures |list| == 2;
+ {
+ this.Double(1, 2);
+ }
+
+
+ method Singleton(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p];
+ ensures list[0] == p;
+ ensures |list| == 1;
+ {
+ var gensym77 := new IntNode;
+ gensym77._synth_IntList_Singleton_gensym77(p);
+ this.list := [p];
+ this.root := gensym77;
+
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym77.Valid();
+ }
+
+
+ method SingletonTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [2];
+ ensures list[0] == 2;
+ ensures |list| == 1;
+ {
+ this.Singleton(2);
+ }
+
+
+ method Sum(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p + q];
+ ensures list[0] == p + q;
+ ensures |list| == 1;
+ {
+ this.Singleton(p + q);
+ }
+
+
+ method TwoConsecutive(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p] + [p + 1];
+ ensures list[0] == p;
+ ensures list[1] == p + 1;
+ ensures |list| == 2;
+ {
+ this.Double(p, p + 1);
+ }
+
+}
+
+class IntNode {
+ ghost var Repr: set<object>;
+ ghost var succ: seq<IntNode>;
+ ghost var data: int;
+
+ var next: IntNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (next != null ==> next in Repr && next.Repr <= Repr && this !in next.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (next == null ==> |succ| == 0) &&
+ (next != null ==> succ == [next] + next.succ) &&
+ (!(null in succ))
+ }
+
+ function Valid(): bool
+ reads *;
+ decreases Repr;
+ {
+ this.Valid_self() &&
+ (next != null ==> next.Valid()) &&
+ (next != null ==> next.Valid_self()) &&
+ (next != null && next.next != null ==> next.next.Valid_self())
+ }
+
+
+ method Init(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == p;
+ {
+ this.data := p;
+ this.next := null;
+ this.succ := [];
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method InitInc(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == p + 1;
+ {
+ this.Init(p + 1);
+ }
+
+
+ method OneTwo()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == 1;
+ ensures |succ| == 1;
+ ensures succ[0] != null;
+ ensures succ[0].data == 2;
+ {
+ var gensym83 := new IntNode;
+ gensym83._synth_IntNode_OneTwo_gensym83();
+ this.data := 1;
+ this.next := gensym83;
+ this.succ := [gensym83];
+
+ // repr stuff
+ this.Repr := {this} + this.next.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym83.Valid();
+ }
+
+
+ method Zero()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == 0;
+ ensures succ == [];
+ ensures |succ| == 0;
+ {
+ this.data := 0;
+ this.next := null;
+ this.succ := [];
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntList_Double_gensym79(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == p;
+ ensures |succ| == 1;
+ ensures succ[0].data == q;
+ ensures succ[0].succ == [];
+ ensures |succ[0].succ| == 0;
+ {
+ var gensym93 := new IntNode;
+ gensym93._synth_IntNode__synth_IntList_Double_gensym79_gensym93(q);
+ this.data := p;
+ this.next := gensym93;
+ this.succ := [gensym93];
+
+ // repr stuff
+ this.Repr := {this} + this.next.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym93.Valid();
+ }
+
+
+ method _synth_IntList_Singleton_gensym77(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == p;
+ ensures |succ| == 0;
+ {
+ this.data := p;
+ this.next := null;
+ this.succ := [];
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntNode_OneTwo_gensym83()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == 2;
+ ensures |succ| == 0;
+ {
+ this.data := 2;
+ this.next := null;
+ this.succ := [];
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method _synth_IntNode__synth_IntList_Double_gensym79_gensym93(q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures data == q;
+ ensures |succ| == 0;
+ {
+ this.data := q;
+ this.next := null;
+ this.succ := [];
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/mod2/jennisys-synth_Number.dfy b/Source/Jennisys/examples/mod2/jennisys-synth_Number.dfy
new file mode 100644
index 00000000..9bb3c398
--- /dev/null
+++ b/Source/Jennisys/examples/mod2/jennisys-synth_Number.dfy
@@ -0,0 +1,181 @@
+class Number {
+ ghost var Repr: set<object>;
+ ghost var num: int;
+
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ true
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ true
+ }
+
+
+ method Abs(a: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a, -a};
+ ensures num >= 0;
+ {
+ if (a >= 0) {
+ this.Init(a);
+ } else {
+ this.Init(-a);
+ }
+ }
+
+
+ method Double(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num == 2 * p;
+ {
+ this.Init(2 * p);
+ }
+
+
+ method Init(p: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num == p;
+ {
+ this.num := p;
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Min2(a: int, b: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures a < b ==> num == a;
+ ensures a >= b ==> num == b;
+ {
+ if (a < b) {
+ this.Init(a);
+ } else {
+ this.Init(b);
+ }
+ }
+
+
+ method Min22(a: int, b: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a, b};
+ ensures num <= a;
+ ensures num <= b;
+ {
+ if (a <= b) {
+ this.Init(a);
+ } else {
+ this.Init(b);
+ }
+ }
+
+
+ method Min3(a: int, b: int, c: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a, b, c};
+ ensures num <= a;
+ ensures num <= b;
+ ensures num <= c;
+ {
+ this.Min32(a, b, c);
+ }
+
+
+ method Min32(a: int, b: int, c: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a, b, c};
+ ensures num <= a;
+ ensures num <= b;
+ ensures num <= c;
+ {
+ if (a <= b && a <= c) {
+ this.Init(a);
+ } else {
+ if (c <= a && c <= b) {
+ this.Init(c);
+ } else {
+ this.Init(b);
+ }
+ }
+ }
+
+
+ method Min4(a: int, b: int, c: int, d: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a, b, c, d};
+ ensures num <= a;
+ ensures num <= b;
+ ensures num <= c;
+ ensures num <= d;
+ {
+ if ((a <= b && a <= c) && a <= d) {
+ this.Init(a);
+ } else {
+ if ((d <= a && d <= b) && d <= c) {
+ this.Init(d);
+ } else {
+ if ((c <= a && c <= b) && c <= d) {
+ this.Init(c);
+ } else {
+ this.Init(b);
+ }
+ }
+ }
+ }
+
+
+ method MinSum(a: int, b: int, c: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num in {a + b, a + c, b + c};
+ ensures num <= a + b;
+ ensures num <= b + c;
+ ensures num <= a + c;
+ {
+ this.Min3(a + b, b + c, a + c);
+ }
+
+
+ method Sum(a: int, b: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures num == a + b;
+ {
+ this.Init(a + b);
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/mod2/jennisys-synth_NumberMethods.dfy b/Source/Jennisys/examples/mod2/jennisys-synth_NumberMethods.dfy
new file mode 100644
index 00000000..b20e2741
--- /dev/null
+++ b/Source/Jennisys/examples/mod2/jennisys-synth_NumberMethods.dfy
@@ -0,0 +1,167 @@
+class NumberMethods {
+ ghost var Repr: set<object>;
+
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ true
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ true
+ }
+
+
+ method Abs(a: int) returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret in {a, -a};
+ ensures ret >= 0;
+ {
+ if (-a >= 0) {
+ ret := -a;
+ } else {
+ ret := a;
+ }
+ }
+
+
+ method Double(p: int) returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == 2 * p;
+ {
+ ret := 2 * p;
+ }
+
+
+ method Min2(a: int, b: int) returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures a < b ==> ret == a;
+ ensures a >= b ==> ret == b;
+ {
+ if (a < b) {
+ ret := a;
+ } else {
+ ret := b;
+ }
+ }
+
+
+ method Min22(a: int, b: int) returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret in {a, b};
+ ensures ret <= a;
+ ensures ret <= b;
+ {
+ if (a <= b) {
+ ret := a;
+ } else {
+ ret := b;
+ }
+ }
+
+
+ method Min3(a: int, b: int, c: int) returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret in {a, b, c};
+ ensures ret <= a;
+ ensures ret <= b;
+ ensures ret <= c;
+ {
+ ret := this.Min32(a, b, c);
+ }
+
+
+ method Min32(a: int, b: int, c: int) returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret in {a, b, c};
+ ensures ret <= a;
+ ensures ret <= b;
+ ensures ret <= c;
+ {
+ if (a <= b && a <= c) {
+ ret := a;
+ } else {
+ if (c <= a && c <= b) {
+ ret := c;
+ } else {
+ ret := b;
+ }
+ }
+ }
+
+
+ method Min4(a: int, b: int, c: int, d: int) returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret in {a, b, c, d};
+ ensures ret <= a;
+ ensures ret <= b;
+ ensures ret <= c;
+ ensures ret <= d;
+ {
+ if ((a <= b && a <= c) && a <= d) {
+ ret := a;
+ } else {
+ if ((d <= a && d <= b) && d <= c) {
+ ret := d;
+ } else {
+ if ((c <= a && c <= b) && c <= d) {
+ ret := c;
+ } else {
+ ret := b;
+ }
+ }
+ }
+ }
+
+
+ method MinSum(a: int, b: int, c: int) returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret in {a + b, a + c, b + c};
+ ensures ret <= a + b;
+ ensures ret <= b + c;
+ ensures ret <= a + c;
+ {
+ ret := this.Min3(a + b, b + c, a + c);
+ }
+
+
+ method Sum(a: int, b: int) returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == a + b;
+ {
+ ret := a + b;
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/mod2/jennisys-synth_Set.dfy b/Source/Jennisys/examples/mod2/jennisys-synth_Set.dfy
new file mode 100644
index 00000000..fea364d6
--- /dev/null
+++ b/Source/Jennisys/examples/mod2/jennisys-synth_Set.dfy
@@ -0,0 +1,304 @@
+class Set {
+ ghost var Repr: set<object>;
+ ghost var elems: set<int>;
+
+ var root: SetNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (root != null ==> root in Repr && root.Repr <= Repr && this !in root.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (root == null ==> elems == {}) &&
+ (root != null ==> elems == root.elems)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (root != null ==> root.Valid())
+ }
+
+
+ method Double(p: int, q: int)
+ modifies this;
+ requires p != q;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p, q};
+ {
+ var gensym80 := new SetNode;
+ gensym80.Double(p, q);
+ this.elems := {q, p};
+ this.root := gensym80;
+
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym80.Valid();
+ }
+
+
+ method Empty()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {};
+ {
+ this.elems := {};
+ this.root := null;
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Singleton(t: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {t};
+ {
+ var gensym75 := new SetNode;
+ gensym75.Init(t);
+ this.elems := {t};
+ this.root := gensym75;
+
+ // repr stuff
+ this.Repr := {this} + this.root.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym75.Valid();
+ }
+
+
+ method SingletonZero()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {0};
+ {
+ this.Singleton(0);
+ }
+
+
+ method Sum(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p + q};
+ {
+ this.Singleton(p + q);
+ }
+
+}
+
+class SetNode {
+ ghost var Repr: set<object>;
+ ghost var elems: set<int>;
+
+ var data: int;
+ var left: SetNode;
+ var right: SetNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (left != null ==> left in Repr && left.Repr <= Repr && this !in left.Repr) &&
+ (right != null ==> right in Repr && right.Repr <= Repr && this !in right.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (elems == ({data} + (if left != null then left.elems else {})) + (if right != null then right.elems else {})) &&
+ (left != null ==> (forall e :: e in left.elems ==> e < data)) &&
+ (right != null ==> (forall e :: e in right.elems ==> e > data))
+ }
+
+ function Valid(): bool
+ reads *;
+ decreases Repr;
+ {
+ this.Valid_self() &&
+ (left != null ==> left.Valid()) &&
+ (right != null ==> right.Valid()) &&
+ (left != null ==> left.Valid_self()) &&
+ (right != null ==> right.Valid_self()) &&
+ (left != null && left.left != null ==> left.left.Valid_self()) &&
+ (left != null && left.right != null ==> left.right.Valid_self()) &&
+ (right != null && right.left != null ==> right.left.Valid_self()) &&
+ (right != null && right.right != null ==> right.right.Valid_self())
+ }
+
+
+ method Double(a: int, b: int)
+ modifies this;
+ requires a != b;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {a, b};
+ {
+ if (b > a) {
+ this.DoubleBase(b, a);
+ } else {
+ var gensym88 := new SetNode;
+ gensym88.Init(a);
+ this.data := b;
+ this.elems := {b, a};
+ this.left := null;
+ this.right := gensym88;
+
+ // repr stuff
+ this.Repr := {this} + this.right.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym88.Valid();
+ }
+ }
+
+
+ method DoubleBase(x: int, y: int)
+ modifies this;
+ requires x > y;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {x, y};
+ {
+ var gensym88 := new SetNode;
+ gensym88.Init(x);
+ this.data := y;
+ this.elems := {y, x};
+ this.left := null;
+ this.right := gensym88;
+
+ // repr stuff
+ this.Repr := {this} + this.right.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym88.Valid();
+ }
+
+
+ method Find(n: int) returns (ret: bool)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == (n in elems);
+ decreases Repr;
+ {
+ if (this.left != null && this.right != null) {
+ var x_9 := this.left.Find(n);
+ var x_10 := this.right.Find(n);
+ ret := (n == this.data || x_9) || x_10;
+ } else {
+ if (this.left != null && this.right == null) {
+ var x_11 := this.left.Find(n);
+ ret := n == this.data || x_11;
+ } else {
+ if (this.right != null && this.left == null) {
+ var x_12 := this.right.Find(n);
+ ret := n == this.data || x_12;
+ } else {
+ ret := n == this.data;
+ }
+ }
+ }
+ }
+
+
+ method Init(x: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {x};
+ {
+ this.data := x;
+ this.elems := {x};
+ this.left := null;
+ this.right := null;
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Triple(x: int, y: int, z: int)
+ modifies this;
+ requires x != y;
+ requires y != z;
+ requires z != x;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {x, y, z};
+ {
+ if (z < x && y > x) {
+ this.TripleBase(z, x, y);
+ } else {
+ if (x < y && z > y) {
+ this.TripleBase(x, y, z);
+ } else {
+ if (x < z && y > z) {
+ this.TripleBase(x, z, y);
+ } else {
+ if (y < z && x > z) {
+ this.TripleBase(y, z, x);
+ } else {
+ if (z < y && x > y) {
+ this.TripleBase(z, y, x);
+ } else {
+ var gensym82 := new SetNode;
+ var gensym83 := new SetNode;
+ gensym82.Init(y);
+ gensym83.Init(z);
+ this.data := x;
+ this.elems := {y, x, z};
+ this.left := gensym82;
+ this.right := gensym83;
+
+ // repr stuff
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym82.Valid() && gensym83.Valid();
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ method TripleBase(x: int, y: int, z: int)
+ modifies this;
+ requires x < y;
+ requires y < z;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {x, y, z};
+ {
+ var gensym89 := new SetNode;
+ var gensym90 := new SetNode;
+ gensym89.Init(z);
+ gensym90.Init(x);
+ this.data := y;
+ this.elems := {x, y, z};
+ this.left := gensym90;
+ this.right := gensym89;
+
+ // repr stuff
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym89.Valid() && gensym90.Valid();
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/oopsla12/BHeap.jen b/Source/Jennisys/examples/oopsla12/BHeap.jen
new file mode 100644
index 00000000..41ebec85
--- /dev/null
+++ b/Source/Jennisys/examples/oopsla12/BHeap.jen
@@ -0,0 +1,34 @@
+interface BHeap {
+ var elems: set[int]
+
+ constructor Singleton(x: int)
+ elems := {x}
+
+ constructor Dupleton(a: int, b: int)
+ requires a != b
+ elems := {a b}
+
+ constructor Tripleton(x: int, y: int, z: int)
+ requires x != y && y != z && z != x
+ elems := {x y z}
+
+ method Find(n: int) returns (ret: bool)
+ ret := n in elems
+}
+
+datamodel BHeap {
+ var data: int
+ var left: BHeap
+ var right: BHeap
+
+ frame
+ left * right
+
+ invariant
+ elems = {data} + (left != null ? left.elems : {})
+ + (right != null ? right.elems : {})
+ left != null ==> forall e :: e in left.elems ==> e < data
+ right != null ==> forall e :: e in right.elems ==> e < data
+ left = null ==> right = null
+ left != null && right = null ==> left.elems = {left.data}
+} \ No newline at end of file
diff --git a/Source/Jennisys/examples/oopsla12/BHeap_synth.dfy b/Source/Jennisys/examples/oopsla12/BHeap_synth.dfy
new file mode 100644
index 00000000..addba4ae
--- /dev/null
+++ b/Source/Jennisys/examples/oopsla12/BHeap_synth.dfy
@@ -0,0 +1,220 @@
+class BHeap {
+ ghost var Repr: set<object>;
+ ghost var elems: set<int>;
+
+ var data: int;
+ var left: BHeap;
+ var right: BHeap;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (left != null ==> left in Repr && left.Repr <= Repr && this !in left.Repr) &&
+ (right != null ==> right in Repr && right.Repr <= Repr && this !in right.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (elems == ({data} + (if left != null then left.elems else {})) + (if right != null then right.elems else {})) &&
+ (left != null ==> (forall e :: e in left.elems ==> e < data)) &&
+ (right != null ==> (forall e :: e in right.elems ==> e < data)) &&
+ (left == null ==> right == null) &&
+ (left != null && right == null ==> left.elems == {left.data})
+ }
+
+ function Valid(): bool
+ reads *;
+ decreases Repr;
+ {
+ this.Valid_self() &&
+ (left != null ==> left.Valid()) &&
+ (right != null ==> right.Valid()) &&
+ (left != null ==> left.Valid_self()) &&
+ (right != null ==> right.Valid_self()) &&
+ (left != null && left.left != null ==> left.left.Valid_self()) &&
+ (left != null && left.right != null ==> left.right.Valid_self()) &&
+ (right != null && right.left != null ==> right.left.Valid_self()) &&
+ (right != null && right.right != null ==> right.right.Valid_self())
+ }
+
+
+ method Dupleton(a: int, b: int)
+ modifies this;
+ requires a != b;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {a, b};
+ {
+ if (b < a) {
+ var gensym71 := new BHeap;
+ var gensym73 := new BHeap;
+ this.data := a;
+ this.elems := {b, a};
+ this.left := gensym73;
+ this.right := gensym71;
+ gensym71.data := b;
+ gensym71.elems := {b};
+ gensym71.left := null;
+ gensym71.right := null;
+ gensym73.data := b;
+ gensym73.elems := {b};
+ gensym73.left := null;
+ gensym73.right := null;
+
+ // repr stuff
+ gensym71.Repr := {gensym71};
+ gensym73.Repr := {gensym73};
+ this.Repr := ({this} + {gensym73}) + {gensym71};
+ // assert repr objects are valid (helps verification)
+ assert gensym71.Valid() && gensym73.Valid();
+ } else {
+ var gensym71 := new BHeap;
+ var gensym73 := new BHeap;
+ this.data := b;
+ this.elems := {a, b};
+ this.left := gensym73;
+ this.right := gensym71;
+ gensym71.data := a;
+ gensym71.elems := {a};
+ gensym71.left := null;
+ gensym71.right := null;
+ gensym73.data := a;
+ gensym73.elems := {a};
+ gensym73.left := null;
+ gensym73.right := null;
+
+ // repr stuff
+ gensym71.Repr := {gensym71};
+ gensym73.Repr := {gensym73};
+ this.Repr := ({this} + {gensym73}) + {gensym71};
+ // assert repr objects are valid (helps verification)
+ assert gensym71.Valid() && gensym73.Valid();
+ }
+ }
+
+
+ method Find(n: int) returns (ret: bool)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == (n in elems);
+ decreases Repr;
+ {
+ if (this.left == null) {
+ ret := n == this.data;
+ } else {
+ if (this.right != null) {
+ var x_10 := this.left.Find(n);
+ var x_11 := this.right.Find(n);
+ ret := (n == this.data || x_10) || x_11;
+ } else {
+ var x_12 := this.left.Find(n);
+ ret := n == this.data || x_12;
+ }
+ }
+ }
+
+
+ method Singleton(x: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {x};
+ {
+ this.data := x;
+ this.elems := {x};
+ this.left := null;
+ this.right := null;
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Tripleton(x: int, y: int, z: int)
+ modifies this;
+ requires x != y;
+ requires y != z;
+ requires z != x;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {x, y, z};
+ {
+ if (z < y && x < y) {
+ var gensym75 := new BHeap;
+ var gensym77 := new BHeap;
+ this.data := y;
+ this.elems := {z, x, y};
+ this.left := gensym77;
+ this.right := gensym75;
+ gensym75.data := x;
+ gensym75.elems := {x};
+ gensym75.left := null;
+ gensym75.right := null;
+ gensym77.data := z;
+ gensym77.elems := {z};
+ gensym77.left := null;
+ gensym77.right := null;
+
+ // repr stuff
+ gensym75.Repr := {gensym75};
+ gensym77.Repr := {gensym77};
+ this.Repr := ({this} + {gensym77}) + {gensym75};
+ // assert repr objects are valid (helps verification)
+ assert gensym75.Valid() && gensym77.Valid();
+ } else {
+ if (x < z) {
+ var gensym75 := new BHeap;
+ var gensym77 := new BHeap;
+ this.data := z;
+ this.elems := {x, y, z};
+ this.left := gensym77;
+ this.right := gensym75;
+ gensym75.data := x;
+ gensym75.elems := {x};
+ gensym75.left := null;
+ gensym75.right := null;
+ gensym77.data := y;
+ gensym77.elems := {y};
+ gensym77.left := null;
+ gensym77.right := null;
+
+ // repr stuff
+ gensym75.Repr := {gensym75};
+ gensym77.Repr := {gensym77};
+ this.Repr := ({this} + {gensym77}) + {gensym75};
+ // assert repr objects are valid (helps verification)
+ assert gensym75.Valid() && gensym77.Valid();
+ } else {
+ var gensym75 := new BHeap;
+ var gensym77 := new BHeap;
+ this.data := x;
+ this.elems := {z, y, x};
+ this.left := gensym77;
+ this.right := gensym75;
+ gensym75.data := y;
+ gensym75.elems := {y};
+ gensym75.left := null;
+ gensym75.right := null;
+ gensym77.data := z;
+ gensym77.elems := {z};
+ gensym77.left := null;
+ gensym77.right := null;
+
+ // repr stuff
+ gensym75.Repr := {gensym75};
+ gensym77.Repr := {gensym77};
+ this.Repr := ({this} + {gensym77}) + {gensym75};
+ // assert repr objects are valid (helps verification)
+ assert gensym75.Valid() && gensym77.Valid();
+ }
+ }
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/oopsla12/DList.jen b/Source/Jennisys/examples/oopsla12/DList.jen
new file mode 100644
index 00000000..7e087f95
--- /dev/null
+++ b/Source/Jennisys/examples/oopsla12/DList.jen
@@ -0,0 +1,40 @@
+interface DList[T] {
+ var list: seq[T]
+
+ invariant
+ |list| > 0
+
+ constructor Init(t: T)
+ list := [t]
+
+ constructor Double(p: T, q: T)
+ list := [p q]
+
+ method List() returns (ret: seq[T])
+ ret := list
+
+ method Size() returns (ret: int)
+ ret := |list|
+
+ method Get(idx: int) returns (ret: T)
+ requires 0 <= idx && idx < |list|
+ ret := list[idx]
+
+ method Find(n: T) returns (ret: bool)
+ ret := n in list
+}
+
+datamodel DList[T] {
+ var data: T
+ var next: DList[T]
+ var prev: DList[T]
+
+ frame
+ next
+
+ invariant
+ next = null ==> list = [data]
+ next != null ==> (list = [data] + next.list
+ && next.prev = this)
+ prev != null ==> prev.next = this
+} \ No newline at end of file
diff --git a/Source/Jennisys/examples/oopsla12/DList_synth.dfy b/Source/Jennisys/examples/oopsla12/DList_synth.dfy
new file mode 100644
index 00000000..897a6de0
--- /dev/null
+++ b/Source/Jennisys/examples/oopsla12/DList_synth.dfy
@@ -0,0 +1,154 @@
+class DList<T> {
+ ghost var Repr: set<object>;
+ ghost var list: seq<T>;
+
+ var data: T;
+ var next: DList<T>;
+ var prev: DList<T>;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (next != null ==> next in Repr && next.Repr <= Repr && this !in next.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (next == null ==> (list == [data] && list[0] == data) && |list| == 1) &&
+ (next != null ==> list == [data] + next.list && next.prev == this) &&
+ (prev != null ==> prev.next == this) &&
+ (|list| > 0)
+ }
+
+ function Valid(): bool
+ reads *;
+ decreases Repr;
+ {
+ this.Valid_self() &&
+ (next != null ==> next.Valid()) &&
+ (next != null ==> next.Valid_self()) &&
+ (next != null && next.next != null ==> next.next.Valid_self())
+ }
+
+
+ method Double(p: T, q: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p, q];
+ ensures list[0] == p;
+ ensures list[1] == q;
+ ensures |list| == 2;
+ {
+ var gensym71 := new DList<T>;
+ this.data := p;
+ this.list := [p, q];
+ this.next := gensym71;
+ this.prev := null;
+ gensym71.data := q;
+ gensym71.list := [q];
+ gensym71.next := null;
+ gensym71.prev := this;
+
+ // repr stuff
+ this.Repr := {this} + {gensym71};
+ gensym71.Repr := {gensym71};
+ // assert repr objects are valid (helps verification)
+ assert gensym71.Valid();
+ }
+
+
+ method Find(n: T) returns (ret: bool)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == (n in list);
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := n == this.data;
+ } else {
+ var x_5 := this.next.Find(n);
+ ret := n == this.data || x_5;
+ }
+ }
+
+
+ method Get(idx: int) returns (ret: T)
+ requires Valid();
+ requires 0 <= idx;
+ requires idx < |list|;
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == list[idx];
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := this.data;
+ } else {
+ if (idx == 0) {
+ ret := this.data;
+ } else {
+ var x_6 := this.next.Get(idx - 1);
+ ret := x_6;
+ }
+ }
+ }
+
+
+ method Init(t: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [t];
+ ensures list[0] == t;
+ ensures |list| == 1;
+ {
+ this.data := t;
+ this.list := [t];
+ this.next := null;
+ this.prev := null;
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method List() returns (ret: seq<T>)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == list;
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := [this.data];
+ } else {
+ var x_7 := this.next.List();
+ ret := [this.data] + x_7;
+ }
+ }
+
+
+ method Size() returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == |list|;
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := 1;
+ } else {
+ var x_8 := this.next.Size();
+ ret := 1 + x_8;
+ }
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/oopsla12/IntSet.jen b/Source/Jennisys/examples/oopsla12/IntSet.jen
new file mode 100644
index 00000000..4800371e
--- /dev/null
+++ b/Source/Jennisys/examples/oopsla12/IntSet.jen
@@ -0,0 +1,30 @@
+interface IntSet {
+ var elems: set[int]
+
+ constructor Singleton(x: int)
+ elems := {x}
+
+ constructor Dupleton(x: int, y: int)
+ requires x != y
+ elems := {x y}
+
+ method Find(x: int) returns (ret: bool)
+ ret := x in elems
+}
+
+datamodel IntSet {
+ var data: int
+ var left: IntSet
+ var right: IntSet
+
+ frame left * right
+
+ invariant
+ elems = {data} +
+ (left != null ? left.elems : {}) +
+ (right != null ? right.elems : {})
+ left != null ==>
+ (forall e :: e in left.elems ==> e < data)
+ right != null ==>
+ (forall e :: e in right.elems ==> data < e)
+} \ No newline at end of file
diff --git a/Source/Jennisys/examples/oopsla12/IntSet_synth.dfy b/Source/Jennisys/examples/oopsla12/IntSet_synth.dfy
new file mode 100644
index 00000000..55523e79
--- /dev/null
+++ b/Source/Jennisys/examples/oopsla12/IntSet_synth.dfy
@@ -0,0 +1,130 @@
+class IntSet {
+ ghost var Repr: set<object>;
+ ghost var elems: set<int>;
+
+ var data: int;
+ var left: IntSet;
+ var right: IntSet;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (left != null ==> left in Repr && left.Repr <= Repr && this !in left.Repr) &&
+ (right != null ==> right in Repr && right.Repr <= Repr && this !in right.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (elems == ({data} + (if left != null then left.elems else {})) + (if right != null then right.elems else {})) &&
+ (left != null ==> (forall e :: e in left.elems ==> e < data)) &&
+ (right != null ==> (forall e :: e in right.elems ==> data < e))
+ }
+
+ function Valid(): bool
+ reads *;
+ decreases Repr;
+ {
+ this.Valid_self() &&
+ (left != null ==> left.Valid()) &&
+ (right != null ==> right.Valid()) &&
+ (left != null ==> left.Valid_self()) &&
+ (right != null ==> right.Valid_self()) &&
+ (left != null && left.left != null ==> left.left.Valid_self()) &&
+ (left != null && left.right != null ==> left.right.Valid_self()) &&
+ (right != null && right.left != null ==> right.left.Valid_self()) &&
+ (right != null && right.right != null ==> right.right.Valid_self())
+ }
+
+
+ method Dupleton(x: int, y: int)
+ modifies this;
+ requires x != y;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {x, y};
+ {
+ if (x < y) {
+ var gensym73 := new IntSet;
+ this.data := x;
+ this.elems := {x, y};
+ this.left := null;
+ this.right := gensym73;
+ gensym73.data := y;
+ gensym73.elems := {y};
+ gensym73.left := null;
+ gensym73.right := null;
+
+ // repr stuff
+ gensym73.Repr := {gensym73};
+ this.Repr := {this} + {gensym73};
+ // assert repr objects are valid (helps verification)
+ assert gensym73.Valid();
+ } else {
+ var gensym73 := new IntSet;
+ this.data := y;
+ this.elems := {y, x};
+ this.left := null;
+ this.right := gensym73;
+ gensym73.data := x;
+ gensym73.elems := {x};
+ gensym73.left := null;
+ gensym73.right := null;
+
+ // repr stuff
+ gensym73.Repr := {gensym73};
+ this.Repr := {this} + {gensym73};
+ // assert repr objects are valid (helps verification)
+ assert gensym73.Valid();
+ }
+ }
+
+
+ method Find(x: int) returns (ret: bool)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == (x in elems);
+ decreases Repr;
+ {
+ if (this.left != null && this.right != null) {
+ var x_13 := this.left.Find(x);
+ var x_14 := this.right.Find(x);
+ ret := (x == this.data || x_13) || x_14;
+ } else {
+ if (this.left != null) {
+ var x_15 := this.left.Find(x);
+ ret := x == this.data || x_15;
+ } else {
+ if (this.right != null) {
+ var x_16 := this.right.Find(x);
+ ret := x == this.data || x_16;
+ } else {
+ ret := x == this.data;
+ }
+ }
+ }
+ }
+
+
+ method Singleton(x: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {x};
+ {
+ this.data := x;
+ this.elems := {x};
+ this.left := null;
+ this.right := null;
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/oopsla12/List.jen b/Source/Jennisys/examples/oopsla12/List.jen
new file mode 100644
index 00000000..10a70050
--- /dev/null
+++ b/Source/Jennisys/examples/oopsla12/List.jen
@@ -0,0 +1,29 @@
+interface List[T] {
+ var list: seq[T]
+ invariant |list| > 0
+
+ constructor Singleton(t: T)
+ list := [t]
+ constructor Dupleton(p: T, q: T)
+ list := [p q]
+ method Elems() returns (ret: seq[T])
+ ret := list
+ method Get(idx: int) returns (ret: T)
+ requires 0 <= idx && idx < |list|
+ ret := list[idx]
+ method Find(n: T) returns (ret: bool)
+ ret := n in list
+ method Size() returns (ret: int)
+ ret := |list|
+}
+
+datamodel List[T] {
+ var data: T
+ var next: List[T]
+
+ frame next
+
+ invariant
+ next = null ==> list = [data]
+ next != null ==> list = [data] + next.list
+} \ No newline at end of file
diff --git a/Source/Jennisys/examples/oopsla12/List_synth.dfy b/Source/Jennisys/examples/oopsla12/List_synth.dfy
new file mode 100644
index 00000000..5cbfa10e
--- /dev/null
+++ b/Source/Jennisys/examples/oopsla12/List_synth.dfy
@@ -0,0 +1,146 @@
+class List<T> {
+ ghost var Repr: set<object>;
+ ghost var list: seq<T>;
+
+ var data: T;
+ var next: List<T>;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (next != null ==> next in Repr && next.Repr <= Repr && this !in next.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (next == null ==> (list == [data] && list[0] == data) && |list| == 1) &&
+ (next != null ==> list == [data] + next.list) &&
+ (|list| > 0)
+ }
+
+ function Valid(): bool
+ reads *;
+ decreases Repr;
+ {
+ this.Valid_self() &&
+ (next != null ==> next.Valid()) &&
+ (next != null ==> next.Valid_self()) &&
+ (next != null && next.next != null ==> next.next.Valid_self())
+ }
+
+
+ method Dupleton(p: T, q: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [p, q];
+ ensures list[0] == p;
+ ensures list[1] == q;
+ ensures |list| == 2;
+ {
+ var gensym71 := new List<T>;
+ gensym71.Singleton(q);
+ this.data := p;
+ this.list := [p, q];
+ this.next := gensym71;
+
+ // repr stuff
+ this.Repr := {this} + this.next.Repr;
+ // assert repr objects are valid (helps verification)
+ assert gensym71.Valid();
+ }
+
+
+ method Elems() returns (ret: seq<T>)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == list;
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := [this.data];
+ } else {
+ var x_5 := this.next.Elems();
+ ret := [this.data] + x_5;
+ }
+ }
+
+
+ method Find(n: T) returns (ret: bool)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == (n in list);
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := n == this.data;
+ } else {
+ var x_6 := this.next.Find(n);
+ ret := n == this.data || x_6;
+ }
+ }
+
+
+ method Get(idx: int) returns (ret: T)
+ requires Valid();
+ requires 0 <= idx;
+ requires idx < |list|;
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == list[idx];
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := this.data;
+ } else {
+ if (idx == 0) {
+ ret := this.data;
+ } else {
+ var x_7 := this.next.Get(idx - 1);
+ ret := x_7;
+ }
+ }
+ }
+
+
+ method Singleton(t: T)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures list == [t];
+ ensures list[0] == t;
+ ensures |list| == 1;
+ {
+ this.data := t;
+ this.list := [t];
+ this.next := null;
+
+ // repr stuff
+ this.Repr := {this};
+ }
+
+
+ method Size() returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret == |list|;
+ decreases Repr;
+ {
+ if (this.next == null) {
+ ret := 1;
+ } else {
+ var x_8 := this.next.Size();
+ ret := 1 + x_8;
+ }
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/oopsla12/Math.jen b/Source/Jennisys/examples/oopsla12/Math.jen
new file mode 100644
index 00000000..0cc772b3
--- /dev/null
+++ b/Source/Jennisys/examples/oopsla12/Math.jen
@@ -0,0 +1,20 @@
+interface Math {
+ method Min2(a: int, b: int) returns (ret: int)
+ ensures a < b ==> ret = a
+ ensures a >= b ==> ret = b
+
+ method Min3Sum(a: int, b: int, c: int)
+ returns (ret: int)
+ ensures ret in {a+b a+c b+c}
+ ensures forall x :: x in {a+b a+c b+c} ==> ret <= x
+
+ method Min4(a: int, b: int, c: int, d: int)
+ returns (ret: int)
+ ensures ret in {a b c d}
+ ensures forall x :: x in {a b c d} ==> ret <= x
+
+ method Abs(a: int) returns (ret: int)
+ ensures ret in {a (-a)} && ret >= 0
+}
+
+datamodel Math {} \ No newline at end of file
diff --git a/Source/Jennisys/examples/oopsla12/Math_synth.dfy b/Source/Jennisys/examples/oopsla12/Math_synth.dfy
new file mode 100644
index 00000000..68893b3d
--- /dev/null
+++ b/Source/Jennisys/examples/oopsla12/Math_synth.dfy
@@ -0,0 +1,105 @@
+class Math {
+ ghost var Repr: set<object>;
+
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ true
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ true
+ }
+
+
+ method Abs(a: int) returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret in {a, -a};
+ ensures ret >= 0;
+ {
+ if (a >= 0) {
+ ret := a;
+ } else {
+ ret := -a;
+ }
+ }
+
+
+ method Min2(a: int, b: int) returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures a < b ==> ret == a;
+ ensures a >= b ==> ret == b;
+ {
+ if (a < b) {
+ ret := a;
+ } else {
+ ret := b;
+ }
+ }
+
+
+ method Min3Sum(a: int, b: int, c: int) returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret in {a + b, a + c, b + c};
+ ensures ret <= a + b;
+ ensures ret <= a + c;
+ ensures ret <= b + c;
+ {
+ if (a + b <= a + c && a + b <= b + c) {
+ ret := a + b;
+ } else {
+ if (b + c <= a + c) {
+ ret := b + c;
+ } else {
+ ret := a + c;
+ }
+ }
+ }
+
+
+ method Min4(a: int, b: int, c: int, d: int) returns (ret: int)
+ requires Valid();
+ ensures fresh(Repr - old(Repr));
+ ensures Valid();
+ ensures ret in {a, b, c, d};
+ ensures ret <= a;
+ ensures ret <= b;
+ ensures ret <= c;
+ ensures ret <= d;
+ {
+ if ((a <= b && a <= c) && a <= d) {
+ ret := a;
+ } else {
+ if (d <= b && d <= c) {
+ ret := d;
+ } else {
+ if (c <= b) {
+ ret := c;
+ } else {
+ ret := b;
+ }
+ }
+ }
+ }
+
+}
+
+
diff --git a/Source/Jennisys/examples/set.dfy b/Source/Jennisys/examples/set.dfy
new file mode 100644
index 00000000..627f1ecb
--- /dev/null
+++ b/Source/Jennisys/examples/set.dfy
@@ -0,0 +1,246 @@
+class Set {
+ ghost var Repr: set<object>;
+ ghost var elems: set<int>;
+
+ var root: SetNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (root != null ==> root in Repr && root.Repr <= Repr && this !in root.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (root == null ==> elems == {}) &&
+ (root != null ==> elems == root.elems)
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (root != null ==> root.Valid())
+ }
+
+ method Empty()
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {};
+ {
+ this.elems := {};
+ this.root := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+ method Singleton(t: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {t};
+ {
+ var gensym66 := new SetNode;
+ gensym66.Init(t);
+ this.elems := {t};
+ this.root := gensym66;
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method Sum(p: int, q: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p + q};
+ {
+ var gensym68 := new SetNode;
+ gensym68.Init(p+q);
+ this.elems := {p + q};
+ this.root := gensym68;
+ this.Repr := {this} + this.root.Repr;
+ }
+
+ method Double(p: int, q: int)
+ modifies this;
+ requires p != q;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p, q};
+ {
+ var gensym71 := new SetNode;
+ gensym71.Double(p, q);
+ this.elems := {p, q};
+ this.root := gensym71;
+ this.Repr := {this} + this.root.Repr;
+ }
+
+}
+
+class SetNode {
+ ghost var Repr: set<object>;
+ ghost var elems: set<int>;
+
+ var data: int;
+ var left: SetNode;
+ var right: SetNode;
+
+ function Valid_repr(): bool
+ reads *;
+ {
+ this in Repr &&
+ null !in Repr &&
+ (left != null ==> left in Repr && left.Repr <= Repr && this !in left.Repr) &&
+ (right != null ==> right in Repr && right.Repr <= Repr && this !in right.Repr)
+ }
+
+ function Valid_self(): bool
+ reads *;
+ {
+ Valid_repr() &&
+ (elems == ({data} + (if left != null then left.elems else {})) + (if right != null then right.elems else {})) &&
+ (left != null ==> (forall e :: e in left.elems ==> e < data)) &&
+ (right != null ==> (forall e :: e in right.elems ==> e > data))
+ }
+
+ function Valid(): bool
+ reads *;
+ {
+ this.Valid_self() &&
+ (left != null ==> left.Valid_self() && (left.left != null ==> left.left.Valid_self())) &&
+ (right != null ==> right.Valid_self() && (right.right != null ==> right.right.Valid_self()))
+ }
+
+ method Init(t: int)
+ modifies this;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {t};
+ {
+ this.data := t;
+ this.elems := {t};
+ this.left := null;
+ this.right := null;
+ // repr stuff
+ this.Repr := {this};
+ }
+
+ method Double(p: int, q: int)
+ modifies this;
+// requires p != q;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p, q};
+ {
+ if (q > p) {
+ var gensym79 := new SetNode;
+ gensym79.Init(q);
+ this.data := p;
+ this.elems := {p, q};
+ this.left := null;
+ this.right := gensym79;
+ this.Repr := {this} + this.right.Repr;
+ } else if (q < p) {
+ var gensym79 := new SetNode;
+ gensym79.Init(p);
+ this.data := q;
+ this.elems := {p, q};
+ this.left := null;
+ this.right := gensym79;
+ this.Repr := {this} + this.right.Repr;
+ } else {
+ this.data := p;
+ this.elems := {p};
+ this.left := null;
+ this.right := null;
+ this.Repr := {this};
+ }
+ }
+
+ method Triple(p: int, q: int, r: int)
+ modifies this;
+ requires p != q;
+ requires q != r;
+ requires r != p;
+ ensures fresh(Repr - {this});
+ ensures Valid();
+ ensures elems == {p, q, r};
+ {
+ if (p < q && r > q) {
+ var gensym83 := new SetNode;
+ var gensym84 := new SetNode;
+ gensym83.Init(r);
+ gensym84.Init(p);
+ this.data := q;
+ this.elems := {p, q, r};
+ this.left := gensym84;
+ this.right := gensym83;
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ if (p < r && q > r) {
+ var gensym85 := new SetNode;
+ var gensym86 := new SetNode;
+ gensym85.Init(q);
+ gensym86.Init(p);
+ this.data := r;
+ this.elems := {p, q, r};
+ this.left := gensym86;
+ this.right := gensym85;
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ if (r < p && q > p) {
+ var gensym84 := new SetNode;
+ var gensym85 := new SetNode;
+ gensym84.Init(q);
+ gensym85.Init(r);
+ this.data := p;
+ this.elems := {p, q, r};
+ this.left := gensym85;
+ this.right := gensym84;
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ if (q < p && r > p) {
+ var gensym82 := new SetNode;
+ var gensym83 := new SetNode;
+ gensym82.Init(r);
+ gensym83.Init(q);
+ this.data := p;
+ this.elems := {p, q, r};
+ this.left := gensym83;
+ this.right := gensym82;
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ if (q < r && p > r) {
+ var gensym85 := new SetNode;
+ var gensym86 := new SetNode;
+ gensym85.Init(p);
+ gensym86.Init(q);
+ this.data := r;
+ this.elems := {p, q, r};
+ this.left := gensym86;
+ this.right := gensym85;
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ } else {
+ var gensym82 := new SetNode;
+ var gensym83 := new SetNode;
+ gensym82.Init(p);
+ gensym83.Init(r);
+ this.data := q;
+ this.elems := {p, q, r};
+ this.left := gensym83;
+ this.right := gensym82;
+ this.Repr := ({this} + this.left.Repr) + this.right.Repr;
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
+
+
diff --git a/Source/Jennisys/scripts/StartDafny-jen.bat b/Source/Jennisys/scripts/StartDafny-jen.bat
new file mode 100644
index 00000000..6f44ec4c
--- /dev/null
+++ b/Source/Jennisys/scripts/StartDafny-jen.bat
@@ -0,0 +1,2 @@
+@echo off
+"c:/boogie/Binaries/Dafny.exe" -nologo -compile:0 /print:xxx.bpl -timeLimit:60 %* > c:\tmp\jen-doo.out